khanat-opennel-code/code/nel/src/misc/bit_mem_stream.cpp
2016-01-15 11:34:23 +01:00

894 lines
22 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdmisc.h"
#include "nel/misc/bit_mem_stream.h"
#include "nel/misc/bit_set.h"
#ifdef LOG_ALL_TRAFFIC
#include "nel/misc/command.h"
#endif
using namespace std;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLMISC {
#ifdef LOG_ALL_TRAFFIC
bool VerboseAllTraffic = false;
#endif
const char * SerialTypeToCStr [ TBMSSerialInfo::NbSerialTypes ] = { "Bool ", "Ui32N", "Ui64N", "Float", "Btfld", "Buffr" };
/*
* Constructor
*/
CBitMemStream::CBitMemStream( bool inputStream, uint32 defaultcapacity ) :
CMemStream( inputStream, false, defaultcapacity ),
_FreeBits( 0 )
{
resetBufPos();
}
/*
* Copy constructor
*/
CBitMemStream::CBitMemStream( const CBitMemStream& other ) :
CMemStream( other ),
_FreeBits( other._FreeBits ),
_DbgInfo( other._DbgInfo )
{
}
/*
* Exchange
*/
void CBitMemStream::swap(CBitMemStream &other)
{
CMemStream::swap(other);
std::swap(_FreeBits, other._FreeBits);
_DbgInfo.swap(other._DbgInfo);
}
/*
* Serialize a buffer
*/
void CBitMemStream::serialBuffer( uint8 *buf, uint len )
{
_DbgInfo.addSerial( getPosInBit(), len*8, TBMSSerialInfo::Buffer );
uint i;
uint32 v;
if ( isReading() )
{
for ( i=0; i!=len; ++i )
{
internalSerial( v, 8 );
buf[i] = (uint8)v;
}
}
else
{
for ( i=0; i!=len; ++i )
{
v = (uint32)buf[i];
internalSerial( v, 8 );
}
}
}
/*
* Serialize one bit
*/
void CBitMemStream::serialBit( bool& bit )
{
_DbgInfo.addSerial( getPosInBit(), 1, TBMSSerialInfo::B );
uint32 ubit=0;
if ( isReading() )
{
internalSerial( ubit, 1 );
bit = ( ubit!=0 );
}
else
{
ubit = bit;
internalSerial( ubit, 1 );
}
}
//sint32 CBitMemStream::getPosInBit ()
#ifdef LOG_ALL_TRAFFIC
void CBitMemStream::_serialAndLog( const char *argstr, uint32& value, uint nbits )
{
_DbgInfo.setSymbolOfNextSerialEvent( argstr );
sint32 bitpos = getPosInBit();
serial( value, nbits );
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: %s: %u bits at bitpos %d (%u)", this, isReading()?"I":"O", argstr, nbits, bitpos, value );
}
void CBitMemStream::_serialAndLog( const char *argstr, uint64& value, uint nbits )
{
_DbgInfo.setSymbolOfNextSerialEvent( argstr );
sint32 bitpos = getPosInBit();
serial( value, nbits );
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: %s: %u bits at bitpos %d (%u)", this, isReading()?"I":"O", argstr, nbits, bitpos, value );
}
void CBitMemStream::_serialBitAndLog( const char *argstr, bool& bit )
{
_DbgInfo.setSymbolOfNextSerialEvent( argstr );
sint32 bitpos = getPosInBit();
serialBit( bit );
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: %s: 1 bit at bitpos %d (%hu)", this, isReading()?"I":"O", argstr, bitpos, (uint16)bit );
}
NLMISC_CATEGORISED_COMMAND(nel, verboseAllTraffic, "Verbose the all-traffic logs", "" )
{
if(args.size()>1)
return false;
if(args.size()==1)
{
if(args[0]==string("on")||args[0]==string("ON")||args[0]==string("true")||args[0]==string("TRUE")||args[0]==string("1"))
VerboseAllTraffic=true;
else if(args[0]==string("off")||args[0]==string("OFF")||args[0]==string("false")||args[0]==string("FALSE")||args[0]==string("0"))
VerboseAllTraffic=false;
}
nlinfo("BMS: verboseAllTraffic is %s",VerboseAllTraffic?"ON":"OFF");
return true;
}
#endif
/*
* Serialize only the nbits lower bits of value
*/
void CBitMemStream::internalSerial( uint32& value, uint nbits, bool resetvalue )
{
if ( nbits == 0 )
return;
if ( nbits > 32 )
throw EMemStream (string("trying to serial ")+NLMISC::toString(nbits)+string(" bits"));
if ( isReading() )
{
const uint8 *buffer = _Buffer.getBuffer().getPtr();
// Check that we don't read more than there is to read
uint32 pib = getPosInBit();
uint32 len = ((uint32)lengthR());
if ( pib + nbits > len * 8 )
{
//displayStream( "Stream Overflow" );
throw EStreamOverflow( "CBitMemStream overflow: Read past %u bytes", len );
}
if ( resetvalue )
{
value = 0;
}
// Clear high-order bits after _FreeBits
uint8 v = *(buffer + _Buffer.Pos) & ((1 << _FreeBits) - 1);
if ( nbits > _FreeBits )
{
//nldebug( "Reading byte %u from %u free bits (%u remaining bits)", lengthS(), _FreeBits, nbits );
value |= (v << (nbits-_FreeBits));
// ++_BufPos;
++_Buffer.Pos;
uint readbits = _FreeBits;
//displayByteBits( *_BufPos, 8, readbits-1, false );
_FreeBits = 8;
internalSerial( value, nbits - readbits, false ); // read without resetting value
}
else
{
//nlinfo( "Reading last byte %u from %u free bits (%u remaining bits)", lengthS(), _FreeBits, nbits );
//displayByteBits( *_BufPos, 8, _FreeBits-1, false );
value |= (v >> (_FreeBits-nbits));
//displayByteBits( *_BufPos, 8, _FreeBits-1, false );
_FreeBits -= nbits;
if ( _FreeBits == 0 )
{
_FreeBits = 8;
// ++_BufPos;
++_Buffer.Pos;
}
}
}
else
{
uint8 *buffer = _Buffer.getBufferWrite().getPtr();
// Clear high-order bits after nbits
//displayDwordBits( value, 32, nbits-1, false );
//uint32 mask = (-1 >> (32-nbits)); // does not work
uint32 v;
if ( nbits != 32 ) // arg of shl/sal/shr/sal ranges from 0 to 31
{
uint32 mask = (1 << nbits) - 1;
v = value & mask;
}
else
{
v = value;
}
#ifdef NL_DEBUG
// Check that the current byte is prepared
nlassert( ! ((_FreeBits == 8) && (*(buffer+_Buffer.Pos) != 0)) );
#endif
// Set
if ( nbits > _FreeBits )
{
// Longer than the room in the current byte
//nldebug( "Writing byte %u into %u free bits (%u remaining bits)", lengthS(), _FreeBits, nbits );
//displayDwordBits( value, 32, nbits-1, false );
// *_BufPos |= (v >> (nbits - _FreeBits));
*(buffer+_Buffer.Pos) |= (v >> (nbits - _FreeBits));
uint filledbits = _FreeBits;
//displayByteBits( *_BufPos, 8, filledbits-1, false );
prepareNextByte();
internalSerial( v, nbits - filledbits );
}
else
{
// Shorter or equal
//nldebug( "Writing last byte %u into %u free bits (%u remaining bits)", lengthS(), _FreeBits, nbits );
//displayByteBits( *_BufPos, 8, 7, false );
// *_BufPos |= (v << (_FreeBits-nbits));
*(buffer+_Buffer.Pos) |= (v << (_FreeBits-nbits));
//displayByteBits( *_BufPos, 8, _FreeBits-1, false );
_FreeBits -= nbits;
if ( _FreeBits == 0 )
{
prepareNextByte();
}
}
}
}
/*
* Same as CMemStream::reserve()
*/
sint32 CBitMemStream::reserve( uint byteLen )
{
sint32 p = getPos();
reserveBits( byteLen * 8 );
return p;
}
/*
* In a output bit stream, serialize nbits bits (no matter their value).
* Works even if the number of bits to add is larger than 64. See also poke() and pokeBits().
*/
void CBitMemStream::reserveBits( uint nbits )
{
#ifdef LOG_ALL_TRAFFIC
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: Reserving %u bits at bitpos %d", this, isReading()?"I":"O", nbits, getPosInBit() );
#endif
uint32 v = 0;
while ( nbits > 32 )
{
serial( v, 32 );
nbits -= 32;
}
if ( nbits != 0 )
serial( v, nbits );
}
/*
* Helper for poke(), to write a value inside an output stream (works because reserveBits sets to 0)
*/
void CBitMemStream::serialPoke( uint32 value, uint nbits )
{
uint32 v;
if ( nbits != 32 ) // arg of shl/sal/shr/sal ranges from 0 to 31
{
uint32 mask = (1 << nbits) - 1;
v = value & mask;
}
else
{
v = value;
}
uint8 *buffer = _Buffer.getBufferWrite().getPtr();
// Set
if ( nbits > _FreeBits )
{
// Longer than the room in the current byte
//nldebug( "Writing byte %u into %u free bits (%u remaining bits)", lengthS(), _FreeBits, nbits );
//displayDwordBits( value, 32, nbits-1, false );
// *_BufPos |= (v >> (nbits - _FreeBits));
*(buffer + _Buffer.Pos) |= (v >> (nbits - _FreeBits));
uint filledbits = _FreeBits;
//displayByteBits( *_BufPos, 8, filledbits-1, false );
pointNextByte(); // do not set next byte to 0!
// nlassert( _BufPos < _Buffer.getPtr() + _Buffer.size() );
nlassert( _Buffer.Pos < _Buffer.getBuffer().size() );
serialPoke( v, nbits - filledbits );
}
else
{
// Shorter or equal
//nldebug( "Writing last byte %u into %u free bits (%u remaining bits)", lengthS(), _FreeBits, nbits );
//displayByteBits( *_BufPos, 8, 7, false );
// *_BufPos |= (v << (_FreeBits-nbits));
*(buffer + _Buffer.Pos) |= (v << (_FreeBits-nbits));
//displayByteBits( *_BufPos, 8, _FreeBits-1, false );
_FreeBits -= nbits;
if ( _FreeBits == 0 )
{
pointNextByte(); // do not set next byte to 0!
// nlassert( _BufPos < _Buffer.getPtr() + _Buffer.size() );
nlassert( _Buffer.Pos < _Buffer.getBuffer().size() );
}
}
}
/*
* Rewrite the nbbits lowest bits of a value at the specified position bitpos of the current output bit stream.
*
* Preconditions:
* - bitpos+nbbits <= the current length in bit of the stream.
* - The bits poked must have been reserved by reserve() (i.e. set to 0)
*/
void CBitMemStream::poke( uint32 value, uint bitpos, uint nbits )
{
#ifdef NL_DEBUG
nlassert( (nbits <= 32) && (nbits != 0) );
nlassert( ! isReading() );
nlassert( bitpos+nbits <= (uint)getPosInBit() );
#endif
// Save the current pointers of the stream, and make them point to the required position
uint savedFreeBits = _FreeBits;
uint bytepos = bitpos >> 3;
_FreeBits = 8 - (bitpos - (bytepos << 3));
// uint8 *savedBufPos = _BufPos;
uint32 savedBufPos = _Buffer.Pos;
// _BufPos = _Buffer.getPtr() + bytepos;
_Buffer.Pos = bytepos;
// Serial
_DbgInfo.addPoke( bitpos, nbits, TBMSSerialInfo::U );
serialPoke( value, nbits );
// Restore the current pointers
_FreeBits = savedFreeBits;
// _BufPos = savedBufPos;
_Buffer.Pos = savedBufPos;
}
/* Rewrite the bitfield at the specified position bitpos of the current output bit stream.
* The size of the bitfield is *not* written into stream (unlike serialCont()).
* Precondition: bitpos+bitfield.size() <= the current length in bit of the stream. See also reserveBits().
*/
void CBitMemStream::pokeBits( const CBitSet& bitfield, uint bitpos )
{
#ifdef NL_DEBUG
nlassert( ! isReading() );
nlassert( bitpos+bitfield.size() <= (uint)getPosInBit() );
#endif
// Save the current pointers of the stream, and make them point to the required position
uint savedFreeBits = _FreeBits;
uint bytepos = bitpos >> 3;
_FreeBits = 8 - (bitpos - (bytepos << 3));
// uint8 *savedBufPos = _BufPos;
uint32 savedBufPos = _Buffer.Pos;
// _BufPos = _Buffer.getPtr() + bytepos;
_Buffer.Pos = bytepos;
// Serial
_DbgInfo.addPoke( bitpos, bitfield.size(), TBMSSerialInfo::BF );
const vector<uint32>& uintVec = bitfield.getVector();
if ( ! uintVec.empty() )
{
uint len = bitfield.size();
uint i = 0;
while ( len > 32 )
{
serialPoke( uintVec[i], 32 );
len -= 32;
++i;
}
if ( len != 0 )
serialPoke( uintVec[i], len );
}
// Restore the current pointers
_FreeBits = savedFreeBits;
// _BufPos = savedBufPos;
_Buffer.Pos = savedBufPos;
}
/*
* Read bitfield.size() bits from the input stream to fill the bitfield.
* It means you have to know the size and to resize the bitfield yourself.
*/
void CBitMemStream::readBits( NLMISC::CBitSet& bitfield )
{
#ifdef NL_DEBUG
nlassert( isReading() );
#endif
uint len = bitfield.size();
if ( len != 0 )
{
#ifdef LOG_ALL_TRAFFIC
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: Reading %u bits bitfield at bitpos %d", this, isReading()?"I":"O", len, getPosInBit() );
#endif
uint i = 0;
uint32 v;
while ( len > 32 )
{
serial( v, 32 );
//nldebug( "Bitfield: Read %u at %d", v, _BufPos-_Buffer.getPtr()-4 );
bitfield.setUint( v, i );
len -= 32;
++i;
}
serial( v, len );
//nldebug( "Bitfield: Read %u at %d", v, _BufPos-_Buffer.getPtr()-4 );
bitfield.setUint( v, i );
}
}
/*
* Serial float
*/
void CBitMemStream::serial(float &b)
{
_DbgInfo.addSerial( getPosInBit(), sizeof(b)*8, TBMSSerialInfo::F );
uint32 uf=0;
if ( isReading() )
{
internalSerial( uf, sizeof(b)*8 );
memcpy(&b, &uf, sizeof(b));
}
else
{
memcpy(&uf, &b, sizeof(b));
internalSerial( uf, sizeof(b)*8 );
}
}
/*
* Serial string
*/
void CBitMemStream::serial(std::string &b)
{
#ifdef LOG_ALL_TRAFFIC
sint32 bitpos = getPosInBit();
#endif
uint32 len=0;
// Serialize length
if ( isReading() )
{
serial( len );
if (len > length()-(uint32)getPos())
throw NLMISC::EInvalidDataStream( "BMS: Trying to read a string of %u bytes, past stream size", len );
b.resize( len );
}
else
{
len = (uint32)b.size();
if (len>1000000)
throw NLMISC::EInvalidDataStream( "BMS: Trying to write a string of %u bytes", len );
serial( len );
}
// Serialize buffer
if ( len != 0 )
{
serialBuffer( (uint8*)(&*b.begin()), len );
}
#ifdef LOG_ALL_TRAFFIC
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: String (size 32+%u*8 bits) at bitpos %d", this, isReading()?"I":"O", len, bitpos );
#endif
}
/*
* Serial string
*/
inline void CBitMemStream::serial(ucstring &b)
{
#ifdef LOG_ALL_TRAFFIC
sint32 bitpos = getPosInBit();
#endif
if ( _StringMode )
{
uint32 len=0;
// Read/Write the length.
if(isReading())
{
serial(len);
if (len > (uint32)(sint32(length())-sint32(getPos())))
throw NLMISC::EInvalidDataStream( "BMS: Trying to read an ucstring of %u bytes, past stream size", len );
b.resize(len);
}
else
{
len= (uint32)b.size();
if (len>1000000)
throw NLMISC::EInvalidDataStream( "BMS: Trying to write an ucstring of %u bytes", len );
serial(len);
}
// Read/Write the string.
for(uint i=0;i!=len;++i)
serialBuffer( (uint8*)&b[i], sizeof(b[i]) );
char sep = SEPARATOR;
serialBuffer( (uint8*)&sep, 1 );
}
else
{
IStream::serial( b );
}
#ifdef LOG_ALL_TRAFFIC
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: Ucstring at bitpos %d", this, isReading()?"I":"O", bitpos );
#endif
}
/*
* Append the contents of a bitmemstream at the end of our bitmemstream
*/
void CBitMemStream::append( const CBitMemStream& newBits )
{
nlassert ( !isReading() );
serialBuffer( const_cast<uint8*>(newBits.buffer()), newBits.getPos() );
uint nbRemainingBits = 8 - newBits._FreeBits;
_DbgInfo.addSerial( getPosInBit(), nbRemainingBits, TBMSSerialInfo::Buffer );
uint32 lastByte = (uint32)(*(newBits.buffer() + newBits.getPos())) >> newBits._FreeBits;
internalSerial( lastByte, nbRemainingBits );
}
/*
* Serial bitmemstream
*/
void CBitMemStream::serialMemStream(CMemStream &b)
{
#ifdef LOG_ALL_TRAFFIC
sint32 bitpos = getPosInBit();
#endif
uint32 len=0;
// Serialize length
if ( isReading() )
{
// fill b with data from this
serial (len);
if (len > length()-getPos())
throw NLMISC::EInvalidDataStream( "BMS: Trying to read a BMS of %u bytes, past stream size", len );
serialBuffer (b.bufferToFill (len), len);
b.resetBufPos ();
}
else
{
// fill this with data from b
len = b.length();
// Accept to write a big sized BMS
serial( len );
serialBuffer( (uint8*) b.buffer (), len );
}
#ifdef LOG_ALL_TRAFFIC
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: Sub-bitmemstream (size 32+%u*8 bits) at bitpos %d", this, isReading()?"I":"O", len, bitpos );
#endif
}
/*
* Specialisation of serialCont() for vector<bool>
*/
void CBitMemStream::serialCont(std::vector<bool> &cont)
{
#ifdef LOG_ALL_TRAFFIC
sint32 bitpos = getPosInBit();
#endif
sint32 len=0;
if(isReading())
{
serial(len);
if (len/8 > (sint32)(length()-getPos()))
{
throw NLMISC::EInvalidDataStream( "BMS: Trying to read a vec<bool> of %u bytes, past stream size", len/8 );
}
// special version for vector: adjust good size.
contReset(cont);
cont.reserve(len);
for(sint i=0;i<len;i++)
{
bool v;
serialBit(v);
cont.insert(cont.end(), v);
}
}
else
{
len= (sint32)cont.size();
serial(len);
std::vector<bool>::iterator it= cont.begin();
for(sint i=0;i<len;i++, it++)
{
bool b = *it;
serialBit( b );
}
}
#ifdef LOG_ALL_TRAFFIC
if ( VerboseAllTraffic )
nldebug( "TRAFFIC/%p/%s: Container (header: 32 bits) at bitpos %d", this, isReading()?"I":"O", bitpos );
#endif
}
/*
* Display the bits of the stream just before the current pos
*/
void CBitMemStream::displayLastBits( sint nbits, sint bitpos, NLMISC::CLog *log )
{
if ( bitpos == -1 )
bitpos = getPosInBit();
displayBitStream( *this, max(bitpos-nbits, 0), bitpos-1, log );
}
/*
* Display a part of a bitmemstream
*/
void displayBitStream( const CBitMemStream& msg, sint beginbitpos, sint endbitpos, NLMISC::CLog *log )
{
sint beginpos = beginbitpos/8;
sint endpos = endbitpos/8;
nlinfo( "BMS: beginpos %d endpos %d beginbitpos %d endbitpos %d", beginpos, endpos, beginbitpos, endbitpos );
displayByteBits( *(msg.buffer()+beginpos), 8, 8-(beginbitpos-beginpos*8), true, log );
const uint8 *p;
for ( p=msg.buffer()+beginpos+1; p<msg.buffer()+endpos-1; ++p )
{
displayByteBits( *p, 8, 0, false, log );
}
if ( endpos > beginpos )
{
displayByteBits( *(msg.buffer()+endpos), 8, 0, false, log );
}
}
/*
* Returns the stream as a string with 0 and 1.
*/
void CBitMemStream::displayStream( const char *title, CLog *log )
{
// nlassert( (_BufPos >= _Buffer.getPtr()) && (_BufPos <= _Buffer.getPtr() + _Buffer.size()) );
nlassert( _Buffer.Pos <= _Buffer.getBuffer().size() );
// Display title and information
string s = (isReading()?string("I"):string("O")) + string("BMS ") + string(title) + ": ";
string sLegend;
// if ( _BufPos == _Buffer.getPtr() )
if ( _Buffer.Pos == 0 )
{
log->displayNL( (s + "Empty").c_str() );
return;
}
// s += NLMISC::toString( "BitPos=%d Pos=%u FreeBits=%u Size=%u ", getPosInBit(), (uint32)(_BufPos-_Buffer.getPtr()), _FreeBits, _Buffer.size() );
s += NLMISC::toString( "BitPos=%d Pos=%u FreeBits=%u Size=%u ", getPosInBit(), _Buffer.Pos, _FreeBits, _Buffer.getBuffer().size() );
log->displayNL( s.c_str() );
s.clear();
// Display bitstream (output: until _BufPos/_FreeBits; input: whole buffer)
_DbgInfo.beginEventBrowsing();
sint32 eventId;
uint32 bitpos = 0;
const uint8 *p;
// uint8 *endPos = isReading() ? (_Buffer.getPtr() + _Buffer.size()) : (_BufPos+1);
const uint8 *endPos = isReading() ? (_Buffer.getBuffer().getPtr() + _Buffer.getBuffer().size()) : (_Buffer.getBuffer().getPtr() + _Buffer.Pos+1);
// for ( p=_Buffer.getPtr(); p!=endPos; ++p )
for ( p=_Buffer.getBuffer().getPtr(); p!=endPos; ++p )
{
sint i;
for ( i=7; i!=-1; --i )
{
//bitpos = (p-_Buffer.getPtr())*8 + (7-i);
if ( bitpos == (uint32)getPosInBit() )
s += "<P>"; // display the current position
s += _DbgInfo.getEventIdAtBitPos( bitpos, &eventId );
s += ( ((*p) >> i) & 1 ) ? '1' : '0';
sLegend += _DbgInfo.getEventLegendAtBitPos( *this, eventId );
++bitpos;
}
s += ' '; // a blank char between each byte
if ( bitpos % 64 == 0 ) // limit to 8 bytes per line
{
log->displayRawNL( s.c_str() );
s.clear();
}
}
if ( bitpos % 64 != 0 )
log->displayRawNL( s.c_str() );
_DbgInfo.endEventBrowsing();
// Display legend
string::size_type lineStart = 0;
string::size_type crp = sLegend.find( '\n', lineStart );
while ( crp != string::npos )
{
log->displayRawNL( sLegend.substr( lineStart, crp-lineStart ).c_str() );
lineStart = crp + 1;
crp = sLegend.find( '\n', lineStart );
}
// sLegend ends with a '\n'
}
/*
* Return a string showing the serial item
*/
std::string CBitMemStream::getSerialItem( const TBMSSerialInfo& serialItem )
{
// Save the current pointers of the stream, and make them point to the required position
uint savedFreeBits = _FreeBits;
uint bytepos = serialItem.BitPos >> 3;
_FreeBits = 8 - (serialItem.BitPos - (bytepos << 3));
// uint8 *savedBufPos = _BufPos;
uint32 savedBufPos = _Buffer.Pos;
// _BufPos = _Buffer.getPtr() + bytepos;
_Buffer.Pos = bytepos;
bool wasOutput = false;;
if ( ! isReading() )
{
setInOut( true ); // lighter than invert()
wasOutput = true;
}
// Read and format string
string s;
if ( getPosInBit() + serialItem.BitSize > lengthR() * 8 )
{
s = "<Stream Overflow>";
}
else
{
switch ( serialItem.Type )
{
case TBMSSerialInfo::B:
{
bool b;
serialBit( b );
s = NLMISC::toString( "%s", b?"TRUE":"FALSE" );
break;
}
case TBMSSerialInfo::U: // no distinction with signed int!
{
uint32 u;
serial( u, serialItem.BitSize );
s = NLMISC::toString( "%u", u );
break;
}
case TBMSSerialInfo::U64: // no distinction with signed int64!
{
uint64 u;
serial( u, serialItem.BitSize );
s = NLMISC::toString( "%" NL_I64 "u", u );
break;
}
case TBMSSerialInfo::F: // what about double?
{
float f;
serial( f );
s = NLMISC::toString( "%g", f );
break;
}
case TBMSSerialInfo::BF:
{
CBitSet bs;
bs.resize( serialItem.BitSize );
readBits( bs );
s = bs.toString();
break;
}
case TBMSSerialInfo::Buffer:
{
uint32 len = serialItem.BitSize / 8;
s.resize( len + 2 );
if ( len != 0 )
{
serialBuffer( &((uint8&)(s[1])), len );
string::size_type p;
for ( p=1; p!=len+1; ++p )
{
if ( ! isalnum(s[p]) )
s[p] = '?'; // remove end-of-c_string
}
}
s[0] = '[';
s[len+1] = ']';
break;
}
default:
break;
}
}
// Restore the current pointers
if ( wasOutput )
{
setInOut( false );
}
_FreeBits = savedFreeBits;
// _BufPos = savedBufPos;
_Buffer.Pos = savedBufPos;
return s;
}
} // NLMISC