2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
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.
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.
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
22 #if !defined( INCLUDED_STREAM_TEXTSTREAM_H )
23 #define INCLUDED_STREAM_TEXTSTREAM_H
25 #include "globaldefs.h"
28 /// \brief Text-output-formatting.
30 #include "itextstream.h"
31 #include "string/string.h"
41 #include "generic/arrayrange.h"
43 namespace TextOutputDetail
45 inline char* write_unsigned_nonzero_decimal_backward( char* ptr, unsigned int decimal ){
46 for (; decimal != 0; decimal /= 10 )
48 *--ptr = char('0' + int(decimal % 10) );
54 inline char* write_size_t_nonzero_decimal_backward( char* ptr, size_t decimal ){
55 for (; decimal != 0; decimal /= 10 )
57 *--ptr = char('0' + (size_t)( decimal % 10 ) );
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 );
69 else if ( show_positive ) {
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 ) {
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 ) {
93 inline char* write_signed_decimal_backward( char* ptr, int decimal, bool show_positive ){
99 ptr = write_signed_nonzero_decimal_backward( ptr, decimal, show_positive );
104 inline char* write_unsigned_decimal_backward( char* ptr, unsigned int decimal, bool show_positive ){
105 if ( decimal == 0 ) {
110 ptr = write_unsigned_nonzero_decimal_backward( ptr, decimal, show_positive );
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 ) {
122 ptr = write_size_t_nonzero_decimal_backward( ptr, decimal, show_positive );
131 #define snprintf _snprintf
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 );
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 ) );
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 ) );
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;
164 char buf[bufferSize];
165 char* begin = TextOutputDetail::write_signed_decimal_backward( buf + bufferSize, i, false );
166 ostream.write( begin, ( buf + bufferSize ) - begin );
168 char buf[bufferSize];
169 ostream.write( buf, snprintf( buf, bufferSize, "%i", i ) );
174 typedef unsigned int Unsigned;
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;
181 char buf[bufferSize];
182 char* begin = TextOutputDetail::write_unsigned_decimal_backward( buf + bufferSize, i, false );
183 ostream.write( begin, ( buf + bufferSize ) - begin );
185 char buf[bufferSize];
186 ostream.write( buf, snprintf( buf, bufferSize, "%u", i ) );
191 #if GDEF_ARCH_BITS_64
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;
199 char buf[bufferSize];
200 char* begin = TextOutputDetail::write_size_t_decimal_backward( buf + bufferSize, i, false );
201 ostream.write( begin, ( buf + bufferSize ) - begin );
203 char buf[bufferSize];
204 ostream.write( buf, snprintf( buf, bufferSize, "%u", i ) );
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 ) );
222 HexChar( char value ) : m_value( value ){
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 ) );
241 FloatFormat( double f, int width, int precision )
242 : m_f( f ), m_width( width ), m_precision( precision ){
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 ) );
255 // never displays exponent, prints up to 10 decimal places
260 Decimal( double f ) : m_f( f ){
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 )
274 const char* last = buf + length - 1;
275 for (; *last == '0'; --last )
278 if ( *last == '.' ) {
281 ostream.write( first, last - first + 1 );
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 );
293 template<typename Type>
298 Quoted( const Type& type )
303 template<typename Type>
304 inline Quoted<Type> makeQuoted( const Type& type ){
305 return Quoted<Type>( type );
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 << '"';
318 const char* m_string;
319 LowerCase( const char* string ) : m_string( string ){
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 )
328 ostream << static_cast<char>( std::tolower( *p ) );
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
338 TextInputStreamType& m_inputStream;
344 m_end = m_buffer + m_inputStream.read( m_buffer, SIZE );
346 return m_cur != m_end;
350 SingleCharacterInputStream( TextInputStreamType& inputStream ) : m_inputStream( inputStream ), m_cur( m_buffer ), m_end( m_buffer ){
352 bool readChar( char& c ){
353 if ( m_cur == m_end && !fillBuffer() ) {
362 /// \brief A wrapper for a TextOutputStream, optimised for writing a single character at a time.
363 class SingleCharacterOutputStream : public TextOutputStream
365 enum unnamed0 { m_bufsize = 1024 };
366 TextOutputStream& m_ostream;
367 char m_buffer[m_bufsize];
371 const char* end() const {
378 m_ostream.write( m_buffer, m_pos - m_buffer );
382 SingleCharacterOutputStream( TextOutputStream& ostream ) : m_ostream( ostream ), m_pos( m_buffer ), m_end( m_buffer + m_bufsize ){
384 ~SingleCharacterOutputStream(){
387 void write( const char c ){
388 if ( m_pos == end() ) {
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 )
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
408 TextInputStreamType& m_inputStream;
409 char m_buffer[SIZE + 1];
414 m_end = m_buffer + m_inputStream.read( m_buffer, SIZE );
416 m_buffer[SIZE] = '\0';
418 return m_end - m_cur;
422 TextLinesInputStream( TextInputStreamType& inputStream ) : m_inputStream( inputStream ), m_cur( m_buffer ), m_end( m_buffer ){
426 CopiedString readLine(){
430 while ( (m_fin = strchr( m_cur, '\n' )) == 0 )
432 s.append( m_cur, m_end - m_cur );
433 if ( fillBuffer() <= 0 ) break;
436 s.append( m_cur, m_fin - m_cur + 1 );
440 return CopiedString( s.c_str() );
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
449 TextOutputStreamType outputStream;
454 BufferedTextOutputStream( TextOutputStreamType& outputStream ) : outputStream( outputStream ), m_cur( m_buffer ){
456 ~BufferedTextOutputStream(){
457 outputStream.write( m_buffer, m_cur - m_buffer );
459 std::size_t write( const char* buffer, std::size_t length ){
460 std::size_t remaining = length;
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 );
466 if ( remaining == 0 ) {
469 outputStream.write( m_buffer, SIZE );