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
26 /// \brief Text-output-formatting.
28 #include "itextstream.h"
29 #include "string/string.h"
39 #include "generic/arrayrange.h"
41 namespace TextOutputDetail
43 inline char* write_unsigned_nonzero_decimal_backward( char* ptr, unsigned int decimal ){
44 for (; decimal != 0; decimal /= 10 )
46 *--ptr = char('0' + int(decimal % 10) );
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 )
55 *--ptr = char('0' + (size_t)( decimal % 10 ) );
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 );
67 else if ( show_positive ) {
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 ) {
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 ) {
91 inline char* write_signed_decimal_backward( char* ptr, int decimal, bool show_positive ){
97 ptr = write_signed_nonzero_decimal_backward( ptr, decimal, show_positive );
102 inline char* write_unsigned_decimal_backward( char* ptr, unsigned int decimal, bool show_positive ){
103 if ( decimal == 0 ) {
108 ptr = write_unsigned_nonzero_decimal_backward( ptr, decimal, show_positive );
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 ) {
120 ptr = write_size_t_nonzero_decimal_backward( ptr, decimal, show_positive );
129 #define snprintf _snprintf
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 );
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 ) );
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 ) );
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;
162 char buf[bufferSize];
163 char* begin = TextOutputDetail::write_signed_decimal_backward( buf + bufferSize, i, false );
164 ostream.write( begin, ( buf + bufferSize ) - begin );
166 char buf[bufferSize];
167 ostream.write( buf, snprintf( buf, bufferSize, "%i", i ) );
172 typedef unsigned int Unsigned;
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;
179 char buf[bufferSize];
180 char* begin = TextOutputDetail::write_unsigned_decimal_backward( buf + bufferSize, i, false );
181 ostream.write( begin, ( buf + bufferSize ) - begin );
183 char buf[bufferSize];
184 ostream.write( buf, snprintf( buf, bufferSize, "%u", i ) );
189 #if defined ( _WIN64 ) || defined ( __LP64__ )
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;
197 char buf[bufferSize];
198 char* begin = TextOutputDetail::write_size_t_decimal_backward( buf + bufferSize, i, false );
199 ostream.write( begin, ( buf + bufferSize ) - begin );
201 char buf[bufferSize];
202 ostream.write( buf, snprintf( buf, bufferSize, "%u", i ) );
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 ) );
220 HexChar( char value ) : m_value( value ){
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 ) );
239 FloatFormat( double f, int width, int precision )
240 : m_f( f ), m_width( width ), m_precision( precision ){
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 ) );
253 // never displays exponent, prints up to 10 decimal places
258 Decimal( double f ) : m_f( f ){
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 )
272 const char* last = buf + length - 1;
273 for (; *last == '0'; --last )
276 if ( *last == '.' ) {
279 ostream.write( first, last - first + 1 );
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 );
291 template<typename Type>
296 Quoted( const Type& type )
301 template<typename Type>
302 inline Quoted<Type> makeQuoted( const Type& type ){
303 return Quoted<Type>( type );
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 << '"';
316 const char* m_string;
317 LowerCase( const char* string ) : m_string( string ){
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 )
326 ostream << static_cast<char>( std::tolower( *p ) );
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
336 TextInputStreamType& m_inputStream;
342 m_end = m_buffer + m_inputStream.read( m_buffer, SIZE );
344 return m_cur != m_end;
348 SingleCharacterInputStream( TextInputStreamType& inputStream ) : m_inputStream( inputStream ), m_cur( m_buffer ), m_end( m_buffer ){
350 bool readChar( char& c ){
351 if ( m_cur == m_end && !fillBuffer() ) {
360 /// \brief A wrapper for a TextOutputStream, optimised for writing a single character at a time.
361 class SingleCharacterOutputStream : public TextOutputStream
363 enum unnamed0 { m_bufsize = 1024 };
364 TextOutputStream& m_ostream;
365 char m_buffer[m_bufsize];
369 const char* end() const {
376 m_ostream.write( m_buffer, m_pos - m_buffer );
380 SingleCharacterOutputStream( TextOutputStream& ostream ) : m_ostream( ostream ), m_pos( m_buffer ), m_end( m_buffer + m_bufsize ){
382 ~SingleCharacterOutputStream(){
385 void write( const char c ){
386 if ( m_pos == end() ) {
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 )
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
406 TextInputStreamType& m_inputStream;
407 char m_buffer[SIZE + 1];
412 m_end = m_buffer + m_inputStream.read( m_buffer, SIZE );
414 m_buffer[SIZE] = '\0';
416 return m_end - m_cur;
420 TextLinesInputStream( TextInputStreamType& inputStream ) : m_inputStream( inputStream ), m_cur( m_buffer ), m_end( m_buffer ){
424 CopiedString readLine(){
428 while ( (m_fin = strchr( m_cur, '\n' )) == 0 )
430 s.append( m_cur, m_end - m_cur );
431 if ( fillBuffer() <= 0 ) break;
434 s.append( m_cur, m_fin - m_cur + 1 );
438 return CopiedString( s.c_str() );
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
447 TextOutputStreamType outputStream;
452 BufferedTextOutputStream( TextOutputStreamType& outputStream ) : outputStream( outputStream ), m_cur( m_buffer ){
454 ~BufferedTextOutputStream(){
455 outputStream.write( m_buffer, m_cur - m_buffer );
457 std::size_t write( const char* buffer, std::size_t length ){
458 std::size_t remaining = length;
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 );
464 if ( remaining == 0 ) {
467 outputStream.write( m_buffer, SIZE );