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