unvanquished filesystem
[xonotic/netradiant.git] / libs / stream / textstream.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #if !defined( INCLUDED_STREAM_TEXTSTREAM_H )
23 #define INCLUDED_STREAM_TEXTSTREAM_H
24
25 /// \file
26 /// \brief Text-output-formatting.
27
28 #include "itextstream.h"
29 #include "string/string.h"
30
31 #include <cctype>
32 #include <cstddef>
33 #include <cmath>
34 #include <stdio.h>
35 #include <string.h>
36 #include <algorithm>
37 #include <string>
38
39 #include "generic/arrayrange.h"
40
41 namespace TextOutputDetail
42 {
43 inline char* write_unsigned_nonzero_decimal_backward( char* ptr, unsigned int decimal ){
44         for (; decimal != 0; decimal /= 10 )
45         {
46                 *--ptr = char('0' + int(decimal % 10) );
47         }
48         return ptr;
49 }
50
51   #if defined ( _WIN64 ) || defined ( __LP64__ )
52 inline char* write_size_t_nonzero_decimal_backward( char* ptr, size_t decimal ){
53         for (; decimal != 0; decimal /= 10 )
54         {
55                 *--ptr = char('0' + (size_t)( decimal % 10 ) );
56         }
57         return ptr;
58 }
59   #endif
60
61 inline char* write_signed_nonzero_decimal_backward( char* ptr, int decimal, bool show_positive ){
62         const bool negative = decimal < 0 ;
63         ptr = write_unsigned_nonzero_decimal_backward( ptr, negative ? -decimal : decimal );
64         if ( negative ) {
65                 *--ptr = '-';
66         }
67         else if ( show_positive ) {
68                 *--ptr = '+';
69         }
70         return ptr;
71 }
72
73 inline char* write_unsigned_nonzero_decimal_backward( char* ptr, unsigned int decimal, bool show_positive ){
74         ptr = write_unsigned_nonzero_decimal_backward( ptr, decimal );
75         if ( show_positive ) {
76                 *--ptr = '+';
77         }
78         return ptr;
79 }
80
81   #if defined ( _WIN64 ) || defined ( __LP64__ )
82 inline char* write_size_t_nonzero_decimal_backward( char* ptr, size_t decimal, bool show_positive ){
83         ptr = write_size_t_nonzero_decimal_backward( ptr, decimal );
84         if ( show_positive ) {
85                 *--ptr = '+';
86         }
87         return ptr;
88 }
89   #endif
90
91 inline char* write_signed_decimal_backward( char* ptr, int decimal, bool show_positive ){
92         if ( decimal == 0 ) {
93                 *--ptr = '0';
94         }
95         else
96         {
97                 ptr = write_signed_nonzero_decimal_backward( ptr, decimal, show_positive );
98         }
99         return ptr;
100 }
101
102 inline char* write_unsigned_decimal_backward( char* ptr, unsigned int decimal, bool show_positive ){
103         if ( decimal == 0 ) {
104                 *--ptr = '0';
105         }
106         else
107         {
108                 ptr = write_unsigned_nonzero_decimal_backward( ptr, decimal, show_positive );
109         }
110         return ptr;
111 }
112
113   #if defined ( _WIN64 ) || defined ( __LP64__ )
114 inline char* write_size_t_decimal_backward( char* ptr, size_t decimal, bool show_positive ){
115         if ( decimal == 0 ) {
116                 *--ptr = '0';
117         }
118         else
119         {
120                 ptr = write_size_t_nonzero_decimal_backward( ptr, decimal, show_positive );
121         }
122         return ptr;
123 }
124   #endif
125 }
126
127
128 #ifdef WIN32
129 #define snprintf _snprintf
130 #endif
131
132 /// \brief Writes a single character \p c to \p ostream.
133 template<typename TextOutputStreamType>
134 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, char c ){
135         ostream.write( &c, 1 );
136         return ostream;
137 }
138
139 /// \brief Writes a double-precision floating point value \p d to \p ostream.
140 /// The value will be formatted either as decimal with trailing zeros removed, or with scientific 'e' notation, whichever is shorter.
141 template<typename TextOutputStreamType>
142 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const double d ){
143         const std::size_t bufferSize = 16;
144         char buf[bufferSize];
145         ostream.write( buf, snprintf( buf, bufferSize, "%g", d ) );
146         return ostream;
147 }
148
149 /// \brief Writes a single-precision floating point value \p f to \p ostream.
150 /// The value will be formatted either as decimal with trailing zeros removed, or with scientific 'e' notation, whichever is shorter.
151 template<typename TextOutputStreamType>
152 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const float f ){
153         return ostream_write( ostream, static_cast<double>( f ) );
154 }
155
156 /// \brief Writes a signed integer \p i to \p ostream in decimal form.
157 /// A '-' sign will be added if the value is negative.
158 template<typename TextOutputStreamType>
159 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const int i ){
160         const std::size_t bufferSize = 16;
161 #if 1
162         char buf[bufferSize];
163         char* begin = TextOutputDetail::write_signed_decimal_backward( buf + bufferSize, i, false );
164         ostream.write( begin, ( buf + bufferSize ) - begin );
165 #else
166         char buf[bufferSize];
167         ostream.write( buf, snprintf( buf, bufferSize, "%i", i ) );
168 #endif
169         return ostream;
170 }
171
172 typedef unsigned int Unsigned;
173
174 /// \brief Writes an unsigned integer \p i to \p ostream in decimal form.
175 template<typename TextOutputStreamType>
176 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Unsigned i ){
177         const std::size_t bufferSize = 16;
178 #if 1
179         char buf[bufferSize];
180         char* begin = TextOutputDetail::write_unsigned_decimal_backward( buf + bufferSize, i, false );
181         ostream.write( begin, ( buf + bufferSize ) - begin );
182 #else
183         char buf[bufferSize];
184         ostream.write( buf, snprintf( buf, bufferSize, "%u", i ) );
185 #endif
186         return ostream;
187 }
188
189 #if defined ( _WIN64 ) || defined ( __LP64__ )
190
191 /// \brief Writes a size_t \p i to \p ostream in decimal form.
192 template<typename TextOutputStreamType>
193 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const size_t i ){
194         // max is 18446744073709551615, buffer of 32 chars should always be enough
195         const std::size_t bufferSize = 32;
196 #if 1
197         char buf[bufferSize];
198         char* begin = TextOutputDetail::write_size_t_decimal_backward( buf + bufferSize, i, false );
199         ostream.write( begin, ( buf + bufferSize ) - begin );
200 #else
201         char buf[bufferSize];
202         ostream.write( buf, snprintf( buf, bufferSize, "%u", i ) );
203 #endif
204         return ostream;
205 }
206
207 #endif
208
209 /// \brief Writes a null-terminated \p string to \p ostream.
210 template<typename TextOutputStreamType>
211 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const char* string ){
212         ostream.write( string, strlen( string ) );
213         return ostream;
214 }
215
216 class HexChar
217 {
218 public:
219 char m_value;
220 HexChar( char value ) : m_value( value ){
221 }
222 };
223
224 /// \brief Writes a single character \p c to \p ostream in hexadecimal form.
225 template<typename TextOutputStreamType>
226 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const HexChar& c ){
227         const std::size_t bufferSize = 16;
228         char buf[bufferSize];
229         ostream.write( buf, snprintf( buf, bufferSize, "%X", c.m_value & 0xFF ) );
230         return ostream;
231 }
232
233 class FloatFormat
234 {
235 public:
236 double m_f;
237 int m_width;
238 int m_precision;
239 FloatFormat( double f, int width, int precision )
240         : m_f( f ), m_width( width ), m_precision( precision ){
241 }
242 };
243
244 /// \brief Writes a floating point value to \p ostream with a specific width and precision.
245 template<typename TextOutputStreamType>
246 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const FloatFormat& formatted ){
247         const std::size_t bufferSize = 32;
248         char buf[bufferSize];
249         ostream.write( buf, snprintf( buf, bufferSize, "%*.*lf", formatted.m_width, formatted.m_precision, formatted.m_f ) );
250         return ostream;
251 }
252
253 // never displays exponent, prints up to 10 decimal places
254 class Decimal
255 {
256 public:
257 double m_f;
258 Decimal( double f ) : m_f( f ){
259 }
260 };
261
262 /// \brief Writes a floating point value to \p ostream in decimal form with trailing zeros removed.
263 template<typename TextOutputStreamType>
264 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Decimal& decimal ){
265         const int bufferSize = 22;
266         char buf[bufferSize];
267         std::size_t length = snprintf( buf, bufferSize, "%10.10lf", decimal.m_f );
268         const char* first = buf;
269         for (; *first == ' '; ++first )
270         {
271         }
272         const char* last = buf + length - 1;
273         for (; *last == '0'; --last )
274         {
275         }
276         if ( *last == '.' ) {
277                 --last;
278         }
279         ostream.write( first, last - first + 1 );
280         return ostream;
281 }
282
283
284 /// \brief Writes a \p range of characters to \p ostream.
285 template<typename TextOutputStreamType>
286 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const StringRange& range ){
287         ostream.write( range.first, range.last - range.first );
288         return ostream;
289 }
290
291 template<typename Type>
292 class Quoted
293 {
294 public:
295 const Type& m_type;
296 Quoted( const Type& type )
297         : m_type( type ){
298 }
299 };
300
301 template<typename Type>
302 inline Quoted<Type> makeQuoted( const Type& type ){
303         return Quoted<Type>( type );
304 }
305
306 /// \brief Writes any type to \p ostream with a quotation mark character before and after it.
307 template<typename TextOutputStreamType, typename Type>
308 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Quoted<Type>& quoted ){
309         return ostream << '"' << quoted.m_type << '"';
310 }
311
312
313 class LowerCase
314 {
315 public:
316 const char* m_string;
317 LowerCase( const char* string ) : m_string( string ){
318 }
319 };
320
321 /// \brief Writes a string to \p ostream converted to lower-case.
322 template<typename TextOutputStreamType>
323 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const LowerCase& lower ){
324         for ( const char* p = lower.m_string; *p != '\0'; ++p )
325         {
326                 ostream << static_cast<char>( std::tolower( *p ) );
327         }
328         return ostream;
329 }
330
331
332 /// \brief A wrapper for a TextInputStream optimised for reading a single character at a time.
333 template<typename TextInputStreamType, int SIZE = 1024>
334 class SingleCharacterInputStream
335 {
336 TextInputStreamType& m_inputStream;
337 char m_buffer[SIZE];
338 char* m_cur;
339 char* m_end;
340
341 bool fillBuffer(){
342         m_end = m_buffer + m_inputStream.read( m_buffer, SIZE );
343         m_cur = m_buffer;
344         return m_cur != m_end;
345 }
346 public:
347
348 SingleCharacterInputStream( TextInputStreamType& inputStream ) : m_inputStream( inputStream ), m_cur( m_buffer ), m_end( m_buffer ){
349 }
350 bool readChar( char& c ){
351         if ( m_cur == m_end && !fillBuffer() ) {
352                 return false;
353         }
354
355         c = *m_cur++;
356         return true;
357 }
358 };
359
360 /// \brief A wrapper for a TextOutputStream, optimised for writing a single character at a time.
361 class SingleCharacterOutputStream : public TextOutputStream
362 {
363 enum unnamed0 { m_bufsize = 1024 };
364 TextOutputStream& m_ostream;
365 char m_buffer[m_bufsize];
366 char* m_pos;
367 const char* m_end;
368
369 const char* end() const {
370         return m_end;
371 }
372 void reset(){
373         m_pos = m_buffer;
374 }
375 void flush(){
376         m_ostream.write( m_buffer, m_pos - m_buffer );
377         reset();
378 }
379 public:
380 SingleCharacterOutputStream( TextOutputStream& ostream ) : m_ostream( ostream ), m_pos( m_buffer ), m_end( m_buffer + m_bufsize ){
381 }
382 ~SingleCharacterOutputStream(){
383         flush();
384 }
385 void write( const char c ){
386         if ( m_pos == end() ) {
387                 flush();
388         }
389         *m_pos++ = c;
390 }
391 std::size_t write( const char* buffer, std::size_t length ){
392         const char*const end = buffer + length;
393         for ( const char* p = buffer; p != end; ++p )
394         {
395                 write( *p );
396         }
397         return length;
398 }
399 };
400
401
402 /// \brief A wrapper for a TextInputStream used for reading one text line at a time.
403 template<typename TextInputStreamType, int SIZE = 1024>
404 class TextLinesInputStream
405 {
406 TextInputStreamType& m_inputStream;
407 char m_buffer[SIZE + 1];
408 char* m_cur;
409 char* m_end;
410
411 int fillBuffer(){
412         m_end = m_buffer + m_inputStream.read( m_buffer, SIZE );
413         m_cur = m_buffer;
414         m_buffer[SIZE] = '\0';
415         *m_end = '\0';
416         return m_end - m_cur;
417 }
418 public:
419
420 TextLinesInputStream( TextInputStreamType& inputStream ) : m_inputStream( inputStream ), m_cur( m_buffer ), m_end( m_buffer ){
421         m_buffer[0] = '\0';
422 }
423
424 CopiedString readLine(){
425         std::string s;
426         char* m_fin;
427
428         while ( (m_fin = strchr( m_cur, '\n' )) == 0 )
429         {
430                 s.append( m_cur, m_end - m_cur );
431                 if ( fillBuffer() <= 0 ) break;
432         }
433         if ( m_fin != 0 ) {
434                 s.append( m_cur, m_fin - m_cur + 1 );
435                 m_cur = m_fin + 1;
436         }
437
438         return CopiedString( s.c_str() );
439 }
440 };
441
442
443 /// \brief A wrapper for a TextOutputStream, optimised for writing a few characters at a time.
444 template<typename TextOutputStreamType, int SIZE = 1024>
445 class BufferedTextOutputStream : public TextOutputStream
446 {
447 TextOutputStreamType outputStream;
448 char m_buffer[SIZE];
449 char* m_cur;
450
451 public:
452 BufferedTextOutputStream( TextOutputStreamType& outputStream ) : outputStream( outputStream ), m_cur( m_buffer ){
453 }
454 ~BufferedTextOutputStream(){
455         outputStream.write( m_buffer, m_cur - m_buffer );
456 }
457 std::size_t write( const char* buffer, std::size_t length ){
458         std::size_t remaining = length;
459         for (;; )
460         {
461                 std::size_t n = std::min( remaining, std::size_t( ( m_buffer + SIZE ) - m_cur ) );
462                 m_cur = std::copy( buffer, buffer + n, m_cur );
463                 remaining -= n;
464                 if ( remaining == 0 ) {
465                         return 0;
466                 }
467                 outputStream.write( m_buffer, SIZE );
468                 m_cur = m_buffer;
469         }
470 }
471 };
472
473 #endif