Thread: [Cppunit-cvs] cppunit2/src/opentest serializer.cpp,NONE,1.1
Brought to you by:
blep
From: Baptiste L. <bl...@us...> - 2005-06-24 08:24:03
|
Update of /cvsroot/cppunit/cppunit2/src/opentest In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9787/src/opentest Added Files: serializer.cpp Log Message: - serialization layer on top of growing buffer for one way communication. --- NEW FILE: serializer.cpp --- #include <opentest/serializer.h> #include <float.h> // For struct assertion_traits<double> #include <stdio.h> namespace OpenTest { /* 0x20: 0x21:! 0x22:" 0x23:# 0x24:$ 0x25:% 0x26:& 0x27:' 0x28:( 0x29:) 0x2a:* 0x2b:+ 0x2c:, 0x2d:- 0x2e:. 0x2f:/ 0x30:0 0x31:1 0x32:2 0x33:3 0x34:4 0x35:5 0x36:6 0x37:7 0x38:8 0x39:9 0x3a:: 0x3b:; 0x3c:< 0x3d:= 0x3e:> 0x3f:? 0x40:@ 0x41:A 0x42:B 0x43:C 0x44:D 0x45:E 0x46:F 0x47:G 0x48:H 0x49:I 0x4a:J 0x4b:K 0x4c:L 0x4d:M 0x4e:N 0x4f:O 0x50:P 0x51:Q 0x52:R 0x53:S 0x54:T 0x55:U 0x56:V 0x57:W 0x58:X 0x59:Y 0x5a:Z 0x5b:[ 0x5c:\ 0x5d:] 0x5e:^ 0x5f:_ 0x60:` 0x61:a 0x62:b 0x63:c 0x64:d 0x65:e 0x66:f 0x67:g 0x68:h 0x69:i 0x6a:j 0x6b:k 0x6c:l 0x6d:m 0x6e:n 0x6f:o 0x70:p 0x71:q 0x72:r 0x73:s 0x74:t 0x75:u 0x76:v 0x77:w 0x78:x 0x79:y 0x7a:z 0x7b:{ 0x7c:| 0x7d:} 0x7e:~ 0x7f: Encoding: 0x20-0x30: stream control character 0x30-0xff: stream content character Notes: encoding real number that way is too complicated. It also does not handle +-inf and nan. */ enum ControlChar { ccBooleanTrue = 0x21, // '!' ccString = 0x22, // '"' ccBooleanFalse = 0x23, // '#' ccLookUpDictionnaryEntry = 0x24, // '$' ccEndBuffer = 0x25, // '%' ccNoneValue = 0x26, // '&' ccNewDictionnaryEntry = 0x27, // "'" ccPropertyList = 0x28, // '(' ccNamedPropertiesEnd = 0x29, // ')' ccPositiveReal = 0x2a, // '*' ccPositiveInteger = 0x2b, // '+' ccNegativeInteger = 0x2d, // '-' ccRealDot = 0x2e, // '.' ccNegativeReal = 0x2f, // '/' ccIntegerZero = 0x30, // integer shift for encoding ccIntegerBaseShift = 6 // power of 2 for encoding base (0x40 = 1 << 6) }; Stream::Stream() : current_( buffers_.begin() ) , currentData_( 0 ) , endData_( 0 ) , writeBufferSize_( 16384 ) , error_( 0 ) { } Stream::~Stream() { while ( !buffers_.empty() ) { delete[] (unsigned char *)buffers_.back().data_; buffers_.pop_back(); } } bool Stream::inError() const { return error_ != 0; } Stream & Stream::setError( const char *errorMessage ) { if ( !error_ ) error_ = errorMessage; return *this; } void Stream::addBuffer( unsigned int length, const void *data ) { write( data, length ); current_ = buffers_.begin(); currentData_ = current_->data_; endData_ = currentData_ + length; } unsigned char Stream::readNextByte() { if ( currentData_ != endData_ ) return *currentData_++; if ( current_ != buffers_.end() ) { ++current_; if ( current_ != buffers_.end() ) { currentData_ = current_->data_; endData_ = currentData_ + current_->length_; CPPTL_ASSERT_MESSAGE( currentData_ != endData_, "invariant violation: 0 length data buffer" ); return *currentData_++; } } return ccEndBuffer; } void Stream::ungetLastByte() { if ( current_ != buffers_.end() ) { if ( current_->data_ != currentData_ ) { --currentData_; return; } CPPTL_ASSERT_MESSAGE( current_ != buffers_.begin(), "No byte to 'unput'" ); --current_; endData_ = current_->data_ + current_->length_; currentData_ = endData_ - 1; } } void Stream::read( void *buffer, unsigned int length ) { unsigned char *target = static_cast<unsigned char *>( buffer ); unsigned int lengthToCopy = CPPTL_MIN( length, endData_ - currentData_ ); memcpy( target, currentData_, lengthToCopy ); length -= lengthToCopy; currentData_ += lengthToCopy; if ( length != 0 ) // there was not enough data, use next buffers { target += lengthToCopy; ++current_; while ( current_ != buffers_.end() ) { lengthToCopy = CPPTL_MIN( length, current_->length_ ); memcpy( target, current_->data_, lengthToCopy ); target += lengthToCopy; length -= lengthToCopy; if ( length == 0 ) { currentData_ = current_->data_ + lengthToCopy; endData_ = current_->data_ + current_->length_; break; } ++current_; } if ( length != 0 ) { setError( "Attempted to read beyond buffer end." ); currentData_ = endData_; } } } void Stream::write( unsigned char byte ) { if ( currentData_ != endData_ ) *currentData_++ = byte; else write( &byte, 1 ); } void Stream::write( const void *buffer, unsigned int length ) { if ( length == 0 ) return; const unsigned char *source = static_cast<const unsigned char *>( buffer ); unsigned int availableLength = endData_ - currentData_; if ( availableLength < length ) { memcpy( currentData_, source, availableLength ); source += availableLength; length -= availableLength; } unsigned int newBufferLength = length + writeBufferSize_; BufferData dataBuffer; dataBuffer.length_ = newBufferLength; typedef unsigned char BufferElementType; unsigned char *newData = new BufferElementType[ newBufferLength ]; memcpy( newData, source, length ); dataBuffer.data_ = newData; buffers_.push_back( dataBuffer ); current_ = buffers_.end() - 1; currentData_ = newData + length; endData_ = newData + newBufferLength; } Stream & Stream::operator <<( bool value ) { write( value ? ccBooleanTrue : ccBooleanFalse ); return *this; } Stream & Stream::operator <<( int value ) { return *this << CppTL::int64_t( value ); } Stream & Stream::operator <<( unsigned int value ) { doSerializeInteger( ccPositiveInteger, value ); return *this; } #ifndef CPPTL_NO_INT64 Stream & Stream::operator <<( CppTL::int64_t value ) { if ( value >= 0 ) doSerializeInteger( ccPositiveInteger, value ); else doSerializeInteger( ccNegativeInteger, -value ); return *this; } Stream & Stream::operator <<( CppTL::uint64_t value ) { doSerializeInteger( ccPositiveInteger, value ); return *this; } #endif void Stream::doSerializeInteger( unsigned char kind, LargestUnsignedInt value ) { unsigned char buffer[ (sizeof(value) + ccIntegerBaseShift - 1) / ccIntegerBaseShift ]; unsigned char *current = buffer + sizeof(buffer); do { unsigned char reminder = value & ((1 << ccIntegerBaseShift) - 1); *--current = reminder + ccIntegerZero; value >>= ccIntegerBaseShift; } while ( value != 0 ); CPPTL_ASSERT_MESSAGE( current >= &buffer[0], "buffer underrun" ); write( kind ); write( current, buffer + sizeof(buffer) - current ); } Stream & Stream::operator <<( double value ) { #ifdef DBL_DIG const int precision = DBL_DIG; #else const int precision = 15; #endif // #ifdef DBL_DIG char buffer[precision * 2 + 32]; int length = sprintf(buffer, "%.*g", precision, value); char *end = buffer + length; for ( char *current = &buffer[0]; current != end; ++current ) { if ( *current == ccPositiveInteger ) *current = ccPositiveReal; else if ( *current == ccNegativeInteger ) *current = ccNegativeReal; else if ( *current == ccRealDot ) { // do nothing } else { CPPTL_ASSERT_MESSAGE( (unsigned char)*current >= ccIntegerZero, "Real number conversion produced stream control characters!" ); } } if ( buffer[0] == ccNegativeReal ) { write( ccNegativeReal ); write( buffer + 1, length-1 ); } else { write( ccPositiveReal ); write( buffer, length ); } return *this; } Stream & Stream::operator <<( const String &str ) { doSerializeInteger( ccString, str.length() ); write( str.c_str(), str.length() ); return *this; } void Stream::saveDictionnaryEntry( const String &str ) { IndexesByString::const_iterator it = indexesByString_.find( str ); if ( it == indexesByString_.end() ) { unsigned int index = indexesByString_.size(); indexesByString_[ str ] = index; doSerializeInteger( ccNewDictionnaryEntry, str.length() ); write( str.c_str(), str.length() ); } else { doSerializeInteger( ccLookUpDictionnaryEntry, it->second ); } } void Stream::doUnserializeInteger( LargestUnsignedInt &value ) { value = 0; unsigned char digit; while ( (digit = readNextByte()) < ccIntegerZero ) { // if ( value >= maxInt ) // should we detect overflow ? value <<= ccIntegerBaseShift; value |= LargestUnsignedInt(digit-ccIntegerZero); } ungetLastByte(); } Stream & Stream::operator >>( bool &value ) { if ( inError() ) return *this; unsigned char control = readNextByte(); if ( control == ccBooleanTrue ) value = true; else if ( control == ccBooleanFalse ) value = false; else setError( "Boolean expected" ); return *this; } #ifdef CPPTL_NO_INT64 Stream & Stream::operator >>( int &value ) { LargestInt temp; *this >> temp; value = temp; return *this; } Stream & Stream::operator >>( unsigned int &value ) { LargestUnsignedInt temp; *this >> temp; value = temp; return *this; } #endif Stream & Stream::operator >>( LargestInt &value ) { if ( inError() ) return *this; unsigned char control = readNextByte(); LargestUnsignedInt unsignedValue; if ( control == ccPositiveInteger ) { doUnserializeInteger( unsignedValue ); value = unsignedValue; } else if ( control == ccNegativeInteger ) { doUnserializeInteger( unsignedValue ); value = -unsignedValue; } else setError( "Integer expected" ); return *this; } Stream & Stream::operator >>( LargestUnsignedInt &value ) { if ( inError() ) return *this; unsigned char control = readNextByte(); if ( control == ccPositiveInteger ) doUnserializeInteger( value ); else setError( "Unsigned integer expected" ); return *this; } Stream & Stream::operator >>( double &value ) { if ( inError() ) return *this; CppTL::StringBuffer buffer; unsigned char control = readNextByte(); if ( control == ccNegativeReal ) buffer += "-"; else if ( control != ccPositiveReal ) return setError( "Unsigned integer expected" ); char digits[2]; digits[1] = 0; while ( true ) { unsigned char digit = readNextByte(); if ( digit == ccPositiveReal ) buffer += "+"; else if ( digit == ccNegativeReal ) buffer += "-"; else if ( digit == ccRealDot || digit >= ccIntegerZero ) { digits[0] = digit; buffer += digits; } else // control char break; } ungetLastByte(); char *actualEnd = 0; value = strtod( buffer.c_str(), &actualEnd ); if ( actualEnd != buffer.c_str() + buffer.length() ) setError( "Unable to unserialize real number correctly." ); return *this; } Stream & Stream::operator >>( String &str ) { if ( inError() ) return *this; unsigned char control = readNextByte(); if ( control == ccString ) readString( str ); else setError( "String expected" ); return *this; } void Stream::readDictionnaryEntry ( unsigned char control, String &str ) { if ( control == ccLookUpDictionnaryEntry ) { LargestUnsignedInt index; doUnserializeInteger( index ); if ( index >= stringsByIndex_.size() ) setError( "Invalid dictionnary look-up index" ); else str = stringsByIndex_[index]; } else if ( control == ccNewDictionnaryEntry ) { readString( str ); stringsByIndex_.push_back( str ); } else setError( "Dictionnary string expected" ); } void Stream::readString( String &str ) { LargestUnsignedInt length; doUnserializeInteger( length ); if ( length <= sizeof(stringBuffer_) ) { read( stringBuffer_, length ); str = String( stringBuffer_, stringBuffer_ + length ); } else { CppTL::StringBuffer largeBuffer; largeBuffer.truncate( length ); read( &largeBuffer[0], length ); str = String( largeBuffer ); } } Stream & Stream::operator <<( const Properties &properties ) { if ( properties.hasList() ) { doSerializeInteger( ccPropertyList, properties.listSize() ); for ( unsigned int valueIndex =0; valueIndex < properties.listSize(); ++valueIndex ) *this << properties.at( valueIndex ); } Properties::PropertyEnum enumProperties = properties.properties(); if ( enumProperties.hasNext() ) { do { const Property &property = enumProperties.next(); saveDictionnaryEntry( property.name() ); *this << property.value(); } while ( enumProperties.hasNext() ); write( ccNamedPropertiesEnd ); } return *this; } bool Stream::isNamedPropertyControl( unsigned char control ) { return control == ccLookUpDictionnaryEntry || control == ccNewDictionnaryEntry; } Stream & Stream::operator >>( Properties &properties ) { unsigned char control = readNextByte(); if ( control == ccPropertyList ) { LargestUnsignedInt length; doUnserializeInteger( length ); while ( length-- ) { Value value; *this >> value; properties.append( value ); } control = readNextByte(); } if ( !isNamedPropertyControl(control) ) { if ( !properties.hasList() ) setError( "Expected Properties." ); else ungetLastByte(); return *this; } do { String name; readDictionnaryEntry( control, name ); Value value; *this >> value; properties.set( name, value ); control = readNextByte(); } while ( isNamedPropertyControl(control) ); if ( control != ccNamedPropertiesEnd ) setError( "Unexpected end of named property list." ); return *this; } Stream & Stream::operator <<( const Value &value ) { switch ( value.type() ) { case Value::vtNone: write( ccNoneValue ); break; case Value::vtBoolean: *this << value.asBool(); break; #ifndef CPPTL_NO_INT64 case Value::vtSignedInteger: *this << value.asInt64(); break; case Value::vtUnsignedInteger: *this << value.asUInt64(); break; #else case Value::vtSignedInteger: *this << value.asInt(); break; case Value::vtUnsignedInteger: *this << value.asUInt(); break; #endif case Value::vtReal: *this << value.asReal(); break; case Value::vtString: *this << value.asString(); break; case Value::vtProperties: *this << value.asProperties(); break; default: CPPTL_ASSERT_MESSAGE( false, "Unmanaged value type." ); break; } return *this; } Stream & Stream::operator >>( Value &value ) { unsigned char control = readNextByte(); switch ( control ) { case ccBooleanTrue: value = Value( true ); break; case ccBooleanFalse: value = Value( false ); break; case ccString: { String str; readString( str ); value = Value( str ); } break; case ccLookUpDictionnaryEntry: case ccNewDictionnaryEntry: case ccPropertyList: { ungetLastByte(); value = Value( Properties() ); *this >> value.asProperties(); } break; case ccNoneValue: value = Value(); break; case ccPositiveReal: case ccNegativeReal: { double real; ungetLastByte(); *this >> real; value = Value( real ); } break; case ccPositiveInteger: { LargestUnsignedInt integer; ungetLastByte(); *this >> integer; value = Value( integer ); } break; case ccNegativeInteger: { LargestInt integer; ungetLastByte(); *this >> integer; value = Value( integer ); } break; default: CPPTL_ASSERT_MESSAGE( false, "Unmanaged value type." ); break; } return *this; } } // namespace OpenTest |