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