// NeL - MMORPG Framework
// 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 .
#include "stdmisc.h"
#include "nel/misc/mem_stream.h"
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLMISC
{
void CMemStream::swap(CMemStream &other)
{
IStream::swap(other);
_Buffer.swap(other._Buffer);
std::swap(_StringMode, other._StringMode);
std::swap(_DefaultCapacity, other._DefaultCapacity);
}
/*
* serial (inherited from IStream)
*/
void CMemStream::serialBuffer(uint8 *buf, uint len)
{
// commented for optimum performance
// nlassert (len > 0);
if (len == 0)
return;
nlassert (buf != NULL);
if ( isReading() )
{
// Check that we don't read more than there is to read
//checkStreamSize(len);
uint32 pos = lengthS();
uint32 total = length();
if ( pos+len > total ) // calls virtual length (cf. sub messages)
{
throw EStreamOverflow( "CMemStream serialBuffer overflow: Read past %u bytes", total );
}
// Serialize in
CFastMem::memcpy( buf, _Buffer.getBuffer().getPtr()+_Buffer.Pos, len );
_Buffer.Pos += len;
}
else
{
// Serialize out
increaseBufferIfNecessary (len);
CFastMem::memcpy( _Buffer.getBufferWrite().getPtr()+_Buffer.Pos, buf, len );
_Buffer.Pos += len;
}
}
/*
* serialBit (inherited from IStream)
*/
void CMemStream::serialBit(bool &bit)
{
uint8 u;
if ( isReading() )
{
serial( u );
bit = (u!=0);
}
else
{
u = (uint8)bit;
serial( u );
}
}
/*
* seek (inherited from IStream)
*
* Warning: in output mode, seek(end) does not point to the end of the serialized data,
* but on the end of the whole allocated buffer (see size()).
* If you seek back and want to return to the end of the serialized data, you have to
* store the position (a better way is to use reserve()/poke()).
*
* Possible enhancement:
* In output mode, keep another pointer to track the end of serialized data.
* When serializing, increment the pointer if its value exceeds its previous value
* (to prevent from an "inside serial" to increment it).
* Then a seek(end) would get back to the pointer.
*/
bool CMemStream::seek (sint32 offset, TSeekOrigin origin) const throw(EStream)
{
switch (origin)
{
case begin:
if (offset > (sint)length())
return false;
if (offset < 0)
return false;
_Buffer.Pos = offset;
break;
case current:
if (getPos ()+offset > (sint)length())
return false;
if (getPos ()+offset < 0)
return false;
_Buffer.Pos += offset;
break;
case end:
if (offset < -(sint)length())
return false;
if (offset > 0)
return false;
_Buffer.Pos = _Buffer.getBuffer().size()+offset;
break;
}
return true;
}
/*
* Resize the buffer.
* Warning: the position is unchanged, only the size is changed.
*/
void CMemStream::resize (uint32 size)
{
if (size == length()) return;
// need to increase the buffer size
_Buffer.getBufferWrite().resize(size);
}
/*
* Input: read from the stream until the next separator, and return the number of bytes read. The separator is then skipped.
*/
uint CMemStream::serialSeparatedBufferIn( uint8 *buf, uint len )
{
nlassert( _StringMode && isReading() );
// Check that we don't read more than there is to read
if ( ( _Buffer.Pos == _Buffer.getBuffer().size() ) || // we are at the end
( ( lengthS()+len+SEP_SIZE > length() ) && (_Buffer.getBuffer()[_Buffer.getBuffer().size()-1] != SEPARATOR ) ) ) // we are before the end // calls virtual length (cf. sub messages)
{
throw EStreamOverflow();
}
// Serialize in
uint32 i = 0;
const uint8 *pos = _Buffer.getBuffer().getPtr()+_Buffer.Pos;
while ( (i oldBufferSize)
{
// need to increase the buffer size
_Buffer.getBufferWrite().resize(oldBufferSize*2 + len + SEP_SIZE);
}
CFastMem::memcpy( _Buffer.getBufferWrite().getPtr()+_Buffer.Pos, buf, len );
_Buffer.Pos += len;
*(_Buffer.getBufferWrite().getPtr()+_Buffer.Pos) = SEPARATOR;
_Buffer.Pos += SEP_SIZE;
}
/* Returns a readable string to display it to the screen. It's only for debugging purpose!
* Don't use it for anything else than to debugging, the string format could change in the future.
* \param hexFormat If true, display all bytes in hexadecimal, else display as chars (above 31, otherwise '.')
*/
std::string CMemStream::toString( bool hexFormat ) const
{
std::string s;
uint32 len = length();
if ( hexFormat )
{
for ( uint i=0; i!=len; ++i )
s += NLMISC::toString( "%2X ", buffer()[i] );
}
else
{
for ( uint i=0; i!=len; ++i )
s += NLMISC::toString( "%c", (buffer()[i]>31) ? buffer()[i] : '.' );
}
return s;
}
// ***************************************************************************
uint CMemStream::getDbgStreamSize() const
{
if(isReading())
return length();
else
return 0;
}
}