969 lines
25 KiB
C++
969 lines
25 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/>.
|
|
|
|
#ifndef NL_MEM_STREAM_H
|
|
#define NL_MEM_STREAM_H
|
|
|
|
#include "types_nl.h"
|
|
#include "stream.h"
|
|
#include "object_vector.h"
|
|
#include "fast_mem.h"
|
|
#include "smart_ptr.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace NLMISC
|
|
{
|
|
|
|
/// Exception class for CMemStream
|
|
struct EMemStream : public NLMISC::EStream
|
|
{
|
|
EMemStream( const std::string& str ) : EStream( str ) {}
|
|
};
|
|
|
|
|
|
/** This class implement a copy on write behavior for memory stream buffer.
|
|
* The goal is to allow buffer sharing between CMemStream object, so that
|
|
* a CMemStream can be copied or passed by value as input/output parameter
|
|
* without copying the data buffer (thus making a CMemStrem copy almost free).
|
|
* This class reference a TMemStreamBuffer object with a smart pointer,
|
|
* when some code call the getBufferWrite() method to obtain write access
|
|
* to the memory buffer, if the ref count is more than 1, then we make a copy
|
|
* of the internal buffer for the current object.
|
|
*
|
|
* \Author Boris Boucher
|
|
*/
|
|
class CMemStreamBuffer
|
|
{
|
|
public:
|
|
typedef CObjectVector<uint8, false> TBuffer;
|
|
private:
|
|
struct TMemStreamBuffer : public CRefCount
|
|
{
|
|
// the buffer himself
|
|
TBuffer _Buffer;
|
|
};
|
|
|
|
typedef CSmartPtr<TMemStreamBuffer> TMemStreamBufferPtr;
|
|
|
|
TMemStreamBufferPtr _SharedBuffer;
|
|
|
|
public:
|
|
|
|
mutable uint32 Pos;
|
|
|
|
/// constructor, allocate a shared buffer
|
|
CMemStreamBuffer()
|
|
{
|
|
_SharedBuffer = new TMemStreamBuffer;
|
|
Pos = 0;
|
|
}
|
|
|
|
|
|
/// Return a read accessor to the buffer
|
|
const TBuffer &getBuffer() const
|
|
{
|
|
return _SharedBuffer->_Buffer;
|
|
}
|
|
|
|
/** Return a write accessor to the buffer, create acopy it if more than
|
|
* one CMemStreamBuffer reference then buffer.
|
|
*/
|
|
TBuffer &getBufferWrite()
|
|
{
|
|
if (_SharedBuffer->getRefCount() > 1)
|
|
{
|
|
// we need to duplicate the buffer
|
|
_SharedBuffer = new TMemStreamBuffer(*_SharedBuffer);
|
|
}
|
|
return _SharedBuffer->_Buffer;
|
|
}
|
|
|
|
/// Exchange the buffer of two CMemStreamBuffer (just swap memory pointer)
|
|
void swap(CMemStreamBuffer &other)
|
|
{
|
|
std::swap(_SharedBuffer, other._SharedBuffer);
|
|
std::swap(Pos, other.Pos);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* Memory stream.
|
|
*
|
|
* How output mode works:
|
|
* The buffer size is increased by factor 2. It means the stream can be smaller than the buffer size.
|
|
* The size of the stream is the current position in the stream (given by lengthS() which is equal
|
|
* to getPos()), because data is always written at the end (except when using poke()).
|
|
* About seek() particularities: see comment of the seek() method.
|
|
*
|
|
* buffer() ----------------------------------- getPos() ---------------- size()
|
|
* data already serialized out |
|
|
* length() = lengthS()
|
|
*
|
|
* How input mode works:
|
|
* The stream is exactly the buffer (the size is given by lengthR()). Data is read inside the stream,
|
|
* at the current position (given by getPos()). If you try to read data while getPos() is equal to
|
|
* lengthR(), you'll get an EStreamOverflow exception.
|
|
*
|
|
* buffer() ----------------------------------- getPos() ------------------------- size()
|
|
* data already serialized in data not read yet |
|
|
* length() = lengthR()
|
|
*
|
|
* \seealso CBitMemStream
|
|
* \seealso NLNET::CMessage
|
|
* \author Olivier Cado, Vianney Lecroart
|
|
* \author Nevrax France
|
|
* \date 2000, 2002
|
|
*/
|
|
class CMemStream : public NLMISC::IStream
|
|
{
|
|
public:
|
|
|
|
/// Initialization constructor
|
|
CMemStream( bool inputStream=false, bool stringmode=false, uint32 defaultcapacity=0 ) :
|
|
NLMISC::IStream( inputStream ), _StringMode( stringmode )
|
|
{
|
|
_DefaultCapacity = std::max( (uint32)defaultcapacity, (uint32)16 ); // prevent from no allocation
|
|
_Buffer.getBufferWrite().resize (_DefaultCapacity);
|
|
_Buffer.Pos = 0;
|
|
}
|
|
|
|
/// Copy constructor
|
|
CMemStream( const CMemStream& other ) :
|
|
IStream (other)
|
|
{
|
|
operator=( other );
|
|
}
|
|
|
|
/// Assignment operator
|
|
CMemStream& operator=( const CMemStream& other )
|
|
{
|
|
IStream::operator= (other);
|
|
_Buffer = other._Buffer;
|
|
_StringMode = other._StringMode;
|
|
_DefaultCapacity = other._DefaultCapacity;
|
|
return *this;
|
|
}
|
|
|
|
/// allocated memory exchange
|
|
void swap(CMemStream &other);
|
|
|
|
/// Set string mode
|
|
void setStringMode( bool stringmode ) { _StringMode = stringmode; }
|
|
|
|
/// Return string mode
|
|
bool stringMode() const { return _StringMode; }
|
|
|
|
/** 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 toString( bool hexFormat=false ) const;
|
|
|
|
/// Method inherited from IStream
|
|
virtual void serialBuffer(uint8 *buf, uint len);
|
|
|
|
/// Method inherited from IStream
|
|
virtual void serialBit(bool &bit);
|
|
|
|
/**
|
|
* Moves the stream pointer to a specified location.
|
|
*
|
|
* 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()).
|
|
*
|
|
* NB: If the stream doesn't support the seek fonctionnality, it throws ESeekNotSupported.
|
|
* Default implementation:
|
|
* { throw ESeekNotSupported; }
|
|
* \param offset is the wanted offset from the origin.
|
|
* \param origin is the origin of the seek
|
|
* \return true if seek sucessfull.
|
|
* \see ESeekNotSupported SeekOrigin getPos
|
|
*/
|
|
virtual bool seek (sint32 offset, TSeekOrigin origin) const throw(EStream);
|
|
|
|
/**
|
|
* Get the location of the stream pointer.
|
|
*
|
|
* NB: If the stream doesn't support the seek fonctionnality, it throws ESeekNotSupported.
|
|
* Default implementation:
|
|
* { throw ESeekNotSupported; }
|
|
* \param offset is the wanted offset from the origin.
|
|
* \param origin is the origin of the seek
|
|
* \return the new offset regarding from the origin.
|
|
* \see ESeekNotSupported SeekOrigin seek
|
|
*/
|
|
virtual sint32 getPos () const throw(EStream)
|
|
{
|
|
return sint32(_Buffer.Pos);
|
|
}
|
|
|
|
/**
|
|
* When writing, skip 'len' bytes and return the position of the blank space for a future poke().
|
|
* Warning: this has nothing to do with the semantics of reserve() in std::vector!
|
|
*/
|
|
sint32 reserve( uint len )
|
|
{
|
|
sint32 pos = sint32(_Buffer.Pos);
|
|
if ( ! isReading() )
|
|
{
|
|
increaseBufferIfNecessary( len );
|
|
_Buffer.Pos += len;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
/**
|
|
* When writing, fill a value previously reserved by reserve()
|
|
* (warning: you MUST have called reserve() with sizeof(T) before poking).
|
|
* Usually it's an alternative to use serialCont with a vector.
|
|
* Example:
|
|
* uint8 counter=0;
|
|
* sint32 counterPos = msgout.reserve( sizeof(counter) );
|
|
* counter = serialSelectedItems( msgout );
|
|
* msgout.poke( counter, counterPos );
|
|
*/
|
|
template <class T>
|
|
void poke( T value, sint32 pos )
|
|
{
|
|
if ( ! isReading() )
|
|
{
|
|
uint8 *pokeBufPos = _Buffer.getBufferWrite().getPtr() + pos;
|
|
nlassert( pokeBufPos + sizeof(T) <= pokeBufPos+_Buffer.Pos );
|
|
*(T*)pokeBufPos = value;
|
|
}
|
|
}
|
|
|
|
/// Clears the message
|
|
virtual void clear()
|
|
{
|
|
resetPtrTable();
|
|
_Buffer.getBufferWrite().clear();
|
|
if (!isReading())
|
|
{
|
|
_Buffer.getBufferWrite().resize (_DefaultCapacity);
|
|
}
|
|
_Buffer.Pos = 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the length (size) of the message, in bytes.
|
|
* If isReading(), it is the number of bytes that can be read,
|
|
* otherwise it is the number of bytes that have been written.
|
|
*/
|
|
virtual uint32 length() const
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
return lengthR();
|
|
}
|
|
else
|
|
{
|
|
return lengthS();
|
|
}
|
|
}
|
|
|
|
/// Returns the size of the buffer (can be greater than the length, especially in output mode)
|
|
uint32 size() const
|
|
{
|
|
return _Buffer.getBuffer().size();
|
|
}
|
|
|
|
/** Returns a pointer to the message buffer (read only)
|
|
* Returns NULL if the buffer is empty
|
|
*/
|
|
virtual const uint8 *buffer() const
|
|
{
|
|
return _Buffer.getBuffer().getPtr();
|
|
}
|
|
|
|
|
|
/**
|
|
* When you fill the buffer externaly (using bufferAsVector) you have to reset the BufPos calling this method
|
|
*
|
|
* If you are using the stream only in output mode, you can use this method as a faster version
|
|
* of clear() *if you don't serialize pointers*.
|
|
*/
|
|
void resetBufPos() { _Buffer.Pos = 0; }
|
|
|
|
/**
|
|
* Resize the message buffer and fill data at position 0.
|
|
* Input stream: the current position is set at the beginning;
|
|
* Output stream: the current position is set after the filled data.
|
|
*/
|
|
void fill( const uint8 *srcbuf, uint32 len )
|
|
{
|
|
if (len == 0) return;
|
|
|
|
_Buffer.getBufferWrite().resize( len );
|
|
CFastMem::memcpy( _Buffer.getBufferWrite().getPtr(), srcbuf, len );
|
|
if (isReading())
|
|
{
|
|
_Buffer.Pos = 0;
|
|
}
|
|
else
|
|
{
|
|
_Buffer.Pos = _Buffer.getBuffer().size();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resize the buffer.
|
|
* Warning: the position is unchanged, only the size is changed.
|
|
*/
|
|
void resize (uint32 size);
|
|
|
|
/**
|
|
* Resize the stream with the specified size, set the current position at the beginning
|
|
* of the stream and return a pointer to the stream buffer.
|
|
*
|
|
* Precondition: the stream is an input stream.
|
|
*
|
|
* Suggested usage: construct an input stream, resize and get the buffer using bufferToFillAndRead(),
|
|
* fill it with raw data using any filling function (warning: don't fill more than 'msgsize'
|
|
* bytes!), then you are ready to read, using serial(), the data you've just filled.
|
|
*/
|
|
uint8 *bufferToFill( uint32 msgsize )
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert( isReading() );
|
|
#endif
|
|
if ( msgsize == 0 )
|
|
return NULL;
|
|
|
|
_Buffer.getBufferWrite().resize( msgsize );
|
|
_Buffer.Pos = 0;
|
|
return _Buffer.getBufferWrite().getPtr();
|
|
}
|
|
|
|
/**
|
|
* Transforms the message from input to output or from output to input
|
|
*
|
|
* Precondition:
|
|
* - If the stream is in input mode, it must not be empty (nothing filled), otherwise the position
|
|
* will be set to the end of the preallocated buffer (see DefaultCapacity).
|
|
* Postcondition:
|
|
* - Read->write, the position is set at the end of the stream, it is possible to add more data
|
|
- - Write->Read, the position is set at the beginning of the stream
|
|
*/
|
|
virtual void invert()
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
// In->Out: We want to write (serialize out) what we have read (serialized in)
|
|
uint32 sizeOfReadStream = lengthR();
|
|
resetPtrTable();
|
|
setInOut( false );
|
|
_Buffer.Pos = sizeOfReadStream;
|
|
}
|
|
else
|
|
{
|
|
// Out->In: We want to read (serialize in) what we have written (serialized out)
|
|
resetPtrTable();
|
|
setInOut( true );
|
|
// TODO : is it necessary ?
|
|
_Buffer.getBufferWrite().resize (_Buffer.Pos);
|
|
_Buffer.Pos = 0;
|
|
}
|
|
}
|
|
|
|
/// Force to reset the ptr table
|
|
void resetPtrTable() { IStream::resetPtrTable() ; }
|
|
|
|
/// Increase the buffer size if 'len' can't enter, otherwise, do nothing
|
|
#ifdef NL_OS_WINDOWS
|
|
__forceinline
|
|
#endif
|
|
void increaseBufferIfNecessary(uint32 len)
|
|
{
|
|
uint32 oldBufferSize = _Buffer.getBuffer().size();
|
|
if (_Buffer.Pos + len > oldBufferSize)
|
|
{
|
|
// need to increase the buffer size
|
|
_Buffer.getBufferWrite().resize(oldBufferSize*2 + len);
|
|
}
|
|
}
|
|
|
|
|
|
template <class T> void fastSerial (T &val)
|
|
{
|
|
#ifdef NL_LITTLE_ENDIAN
|
|
if(isReading())
|
|
{
|
|
// Check that we don't read more than there is to read
|
|
if ( lengthS()+sizeof(T) > length() ) // calls virtual length (cf. sub messages)
|
|
throw EStreamOverflow();
|
|
// Serialize in
|
|
val = *(T*)(_Buffer.getBuffer().getPtr() + _Buffer.Pos);
|
|
}
|
|
else
|
|
{
|
|
increaseBufferIfNecessary (sizeof(T));
|
|
*(T*)(_Buffer.getBufferWrite().getPtr() + _Buffer.Pos) = val;
|
|
}
|
|
_Buffer.Pos += sizeof (T);
|
|
#else // NL_LITTLE_ENDIAN
|
|
IStream::serial( val );
|
|
#endif // NL_LITTLE_ENDIAN
|
|
}
|
|
|
|
template <class T>
|
|
void fastWrite( const T& value )
|
|
{
|
|
//nldebug( "MEMSTREAM: Writing %u-byte value in %p at pos %u", sizeof(value), this, _BufPos - _Buffer.getPtr() );
|
|
increaseBufferIfNecessary (sizeof(T));
|
|
*(T*)(_Buffer.getBufferWrite().getPtr() + _Buffer.Pos) = value;
|
|
|
|
_Buffer.Pos += sizeof (T);
|
|
}
|
|
|
|
template <class T>
|
|
void fastRead( T& value )
|
|
{
|
|
//nldebug( "MEMSTREAM: Reading %u-byte value in %p at pos %u", sizeof(value), this, _BufPos - _Buffer.getPtr() );
|
|
// Check that we don't read more than there is to read
|
|
if ( lengthS()+sizeof(value) > length() ) // calls virtual length (cf. sub messages)
|
|
{
|
|
throw EStreamOverflow();
|
|
}
|
|
// Serialize in
|
|
value = *(T*)(_Buffer.getBuffer().getPtr() + _Buffer.Pos);
|
|
_Buffer.Pos += sizeof(value);
|
|
}
|
|
|
|
|
|
/// Template serialization (should take the one from IStream)
|
|
template<class T>
|
|
void serial(T &obj) { obj.serial(*this); }
|
|
|
|
|
|
template<class T>
|
|
void serialCont(std::vector<T> &cont) {IStream::serialCont(cont);}
|
|
template<class T>
|
|
void serialCont(std::list<T> &cont) {IStream::serialCont(cont);}
|
|
template<class T>
|
|
void serialCont(std::deque<T> &cont) {IStream::serialCont(cont);}
|
|
template<class T>
|
|
void serialCont(std::set<T> &cont) {IStream::serialCont(cont);}
|
|
template<class T>
|
|
void serialCont(std::multiset<T> &cont) {IStream::serialCont(cont);}
|
|
template<class K, class T>
|
|
void serialCont(std::map<K, T> &cont) {IStream::serialCont(cont);}
|
|
template<class K, class T>
|
|
void serialCont(std::multimap<K, T> &cont) {IStream::serialCont(cont);}
|
|
/// Specialisation of serialCont() for vector<uint8>
|
|
virtual void serialCont(std::vector<uint8> &cont) {IStream::serialCont(cont);}
|
|
/// Specialisation of serialCont() for vector<sint8>
|
|
virtual void serialCont(std::vector<sint8> &cont) {IStream::serialCont(cont);}
|
|
/// Specialisation of serialCont() for vector<bool>
|
|
virtual void serialCont(std::vector<bool> &cont) {IStream::serialCont(cont);}
|
|
|
|
|
|
|
|
template<class T0,class T1>
|
|
void serial(T0 &a, T1 &b)
|
|
{ serial(a); serial(b);}
|
|
template<class T0,class T1,class T2>
|
|
void serial(T0 &a, T1 &b, T2 &c)
|
|
{ serial(a); serial(b); serial(c);}
|
|
template<class T0,class T1,class T2,class T3>
|
|
void serial(T0 &a, T1 &b, T2 &c, T3 &d)
|
|
{ serial(a); serial(b); serial(c); serial(d);}
|
|
template<class T0,class T1,class T2,class T3,class T4>
|
|
void serial(T0 &a, T1 &b, T2 &c, T3 &d, T4 &e)
|
|
{ serial(a); serial(b); serial(c); serial(d); serial(e);}
|
|
template<class T0,class T1,class T2,class T3,class T4,class T5>
|
|
void serial(T0 &a, T1 &b, T2 &c, T3 &d, T4 &e, T5 &f)
|
|
{ serial(a); serial(b); serial(c); serial(d); serial(e); serial(f);}
|
|
|
|
/** \name Base types serialisation, redefined for string mode
|
|
* Those method are a specialisation of template method "void serial(T&)".
|
|
*/
|
|
//@{
|
|
virtual void serial(uint8 &b) ;
|
|
virtual void serial(sint8 &b) ;
|
|
virtual void serial(uint16 &b) ;
|
|
virtual void serial(sint16 &b) ;
|
|
virtual void serial(uint32 &b) ;
|
|
virtual void serial(sint32 &b) ;
|
|
virtual void serial(uint64 &b) ;
|
|
virtual void serial(sint64 &b) ;
|
|
virtual void serial(float &b) ;
|
|
virtual void serial(double &b) ;
|
|
virtual void serial(bool &b) ;
|
|
#ifndef NL_OS_CYGWIN
|
|
virtual void serial(char &b) ;
|
|
#endif
|
|
virtual void serial(std::string &b) ;
|
|
virtual void serial(ucstring &b) ;
|
|
//@}
|
|
|
|
|
|
///\name String-specific methods
|
|
//@{
|
|
|
|
/// Input: read len bytes at most from the stream until the next separator, and return the number of bytes read. The separator is then skipped.
|
|
uint serialSeparatedBufferIn( uint8 *buf, uint len );
|
|
|
|
/// Output: writes len bytes from buf into the stream
|
|
void serialSeparatedBufferOut( uint8 *buf, uint len );
|
|
|
|
/// Serialisation in hexadecimal
|
|
virtual void serialHex(uint32 &b);
|
|
|
|
//@}
|
|
|
|
protected:
|
|
|
|
/// Returns the serialized length (number of bytes written or read)
|
|
virtual uint32 lengthS() const
|
|
{
|
|
return _Buffer.Pos; // not calling getPos() because virtual and not const!
|
|
}
|
|
|
|
/**
|
|
* Returns the "read" message size (number of bytes to read)
|
|
* Do not use directly, use length() instead in reading mode (which is virtual)
|
|
*/
|
|
uint32 lengthR() const
|
|
{
|
|
return size();
|
|
}
|
|
|
|
/** Get the size for this stream. return 0 by default. Only implemented for input stream that know their size.
|
|
* Used internally to detect OverFlow with vector<> for instance
|
|
*/
|
|
virtual uint getDbgStreamSize() const;
|
|
|
|
CMemStreamBuffer _Buffer;
|
|
|
|
mutable bool _StringMode;
|
|
|
|
uint32 _DefaultCapacity;
|
|
};
|
|
|
|
// Input
|
|
#define readnumber(dest,thetype,digits,convfunc) \
|
|
char number_as_cstring [digits+1]; \
|
|
uint realdigits = serialSeparatedBufferIn( (uint8*)number_as_cstring, digits ); \
|
|
number_as_cstring[realdigits] = '\0'; \
|
|
dest = (thetype)convfunc( number_as_cstring );
|
|
|
|
// Output
|
|
#define writenumber(src,format,digits) \
|
|
char number_as_cstring [digits+1]; \
|
|
sprintf( number_as_cstring, format, src ); \
|
|
serialSeparatedBufferOut( (uint8*)number_as_cstring, (uint)strlen(number_as_cstring) );
|
|
|
|
/*
|
|
* atoihex
|
|
*/
|
|
inline uint32 atoihex( const char* ident )
|
|
{
|
|
uint32 number;
|
|
sscanf( ident, "%x", &number );
|
|
return number;
|
|
}
|
|
|
|
inline uint32 atoui( const char *ident)
|
|
{
|
|
return (uint32) strtoul (ident, NULL, 10);
|
|
}
|
|
|
|
static const char SEPARATOR = ' ';
|
|
static const int SEP_SIZE = 1; // the code is easier to read with that
|
|
|
|
//
|
|
// inline serial functions
|
|
//
|
|
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(uint8 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, uint8, 3, atoi ); // 255
|
|
}
|
|
else
|
|
{
|
|
writenumber( (uint16)b,"%hu", 3 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(sint8 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, sint8, 4, atoi ); // -128
|
|
}
|
|
else
|
|
{
|
|
writenumber( (sint16)b, "%hd", 4 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(uint16 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
// No byte swapping in text mode
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, uint16, 5, atoi ); // 65535
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%hu", 5 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(sint16 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, sint16, 6, atoi ); // -32768
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%hd", 6 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(uint32 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, uint32, 10, atoui ); // 4294967295
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%u", 10 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(sint32 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, sint32, 11, atoi ); // -2147483648
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%d", 11 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(uint64 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, uint64, 20, atoiInt64 ); // 18446744073709551615
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%"NL_I64"u", 20 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(sint64 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, sint64, 20, atoiInt64 ); // -9223372036854775808
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%"NL_I64"d", 20 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(float &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, float, 128, atof ); // ?
|
|
}
|
|
else
|
|
{
|
|
writenumber( (double)b, "%f", 128 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(double &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, double, 128, atof ); //
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%f", 128 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(bool &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
serialBit(b);
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef NL_OS_CYGWIN
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(char &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
char buff [2];
|
|
if ( isReading() )
|
|
{
|
|
serialBuffer( (uint8*)buff, 2 );
|
|
b = buff[0];
|
|
}
|
|
else
|
|
{
|
|
buff[0] = b;
|
|
buff[1] = SEPARATOR;
|
|
serialBuffer( (uint8*)buff, 2 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastSerial (b);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(std::string &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
uint32 len=0;
|
|
// Read/Write the length.
|
|
if(isReading())
|
|
{
|
|
serial(len);
|
|
checkStreamSize((uint)len);
|
|
/*if (len>1000000)
|
|
throw NLMISC::EInvalidDataStream( "CMemStream/str: Trying to read a string of %u bytes", len );
|
|
*/
|
|
b.resize(len);
|
|
}
|
|
else
|
|
{
|
|
len= (uint32)b.size();
|
|
if (len>1000000)
|
|
throw NLMISC::EInvalidDataStream( "CMemStream/str: Trying to write a string 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
|
|
{
|
|
if (isReading())
|
|
{
|
|
if (!isXML())
|
|
{
|
|
uint32 len=0;
|
|
fastSerial(len);
|
|
checkStreamSize((uint)len);
|
|
/*
|
|
if (len>1000000)
|
|
throw NLMISC::EInvalidDataStream( "CMemStream: Trying to read a string of %u bytes", len );
|
|
*/
|
|
b.resize(len);
|
|
if (len > 0)
|
|
{
|
|
// can serial all in a single call to serialBuffer, since sizeof(char) == 1
|
|
serialBuffer((uint8 *) &b[0], len);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IStream::serial( b );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IStream::serial( b );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ======================================================================================================
|
|
inline void CMemStream::serial(ucstring &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
uint32 len=0;
|
|
// Read/Write the length.
|
|
if(isReading())
|
|
{
|
|
serial(len);
|
|
checkStreamSize((uint)len);
|
|
/*
|
|
if (len>1000000)
|
|
throw NLMISC::EInvalidDataStream( "CMemStream/str: Trying to read an ucstring of %u bytes", len );
|
|
*/
|
|
b.resize(len);
|
|
}
|
|
else
|
|
{
|
|
len= (uint32)b.size();
|
|
if (len>1000000)
|
|
throw NLMISC::EInvalidDataStream( "CMemStream/str: 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 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Serialisation in hexadecimal
|
|
*/
|
|
inline void CMemStream::serialHex(uint32 &b)
|
|
{
|
|
if ( _StringMode )
|
|
{
|
|
if ( isReading() )
|
|
{
|
|
readnumber( b, uint32, 10, atoihex ); // 4294967295
|
|
}
|
|
else
|
|
{
|
|
writenumber( b, "%x", 10 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IStream::serial( b );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif // NL_MEM_STREAM_H
|
|
|
|
/* End of mem_stream.h */
|