khanat-opennel-code/code/nel/include/nel/misc/stream.h

1738 lines
42 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_STREAM_H
#define NL_STREAM_H
#include "types_nl.h"
#include "ucstring.h"
#include "class_registry.h"
#include "common.h"
#include <utility>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
namespace NLMISC
{
class IStream;
class CMemStream;
// ======================================================================================================
// ======================================================================================================
// Stream System.
// ======================================================================================================
// ======================================================================================================
// For Big/little Endian.
# define NLMISC_BSWAP16(src) (src) = (((src)>>8)&0xFF) | (((src)&0xFF)<<8)
# if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
# define NLMISC_BSWAP32(src) _asm mov eax,(src) _asm bswap eax _asm mov (src),eax
# else
# define NLMISC_BSWAP32(src) (src) = (((src)>>24)&0xFF) | ((((src)>>16)&0xFF)<<8) | ((((src)>>8)&0xFF)<<16) | (((src)&0xFF)<<24)
# endif
# define NLMISC_BSWAP64(src) (src) = (((src)>>56)&0xFF) | ((((src)>>48)&0xFF)<<8) | ((((src)>>40)&0xFF)<<16) | ((((src)>>32)&0xFF)<<24) | ((((src)>>24)&0xFF)<<32) | ((((src)>>16)&0xFF)<<40) | ((((src)>>8)&0xFF)<<48) | (((src)&0xFF)<<56)
// convert a 4 characters string to uint32
#ifdef NL_LITTLE_ENDIAN
# define NELID(x) (uint32((x[0] << 24) | (x[1] << 16) | (x[2] << 8) | (x[3])))
#else
# define NELID(x) (uint32((x[3] << 24) | (x[2] << 16) | (x[1] << 8) | (x[0])))
#endif
// ======================================================================================================
/**
* Stream Exception.
* \author Lionel Berenguier
* \author Vianney Lecroart
* \author Nevrax France
* \date 2000
*/
struct EStream : public Exception
{
EStream() : Exception( "Stream Error" ) {}
EStream( const std::string& str ) : Exception( str ) {}
EStream( const IStream &f );
EStream( const IStream &f, const std::string& str );
virtual ~EStream() throw() {}
// May Not be Filled...
std::string StreamName;
};
struct EOlderStream : public EStream
{
EOlderStream() : EStream("The version in stream is older than the class" ) {}
EOlderStream(const IStream &f) : EStream(f, "The version in stream is older than the class" ) {}
};
struct ENewerStream : public EStream
{
ENewerStream() : EStream("The version in stream is newer than the class" ) {}
ENewerStream(const IStream &f) : EStream(f, "The version in stream is newer than the class" ) {}
};
struct EInvalidDataStream : public EStream
{
EInvalidDataStream() : EStream("Invalid data format" ) {}
EInvalidDataStream(const IStream &f) : EStream(f, "Invalid data format" ) {}
// msg must contain "%u" for the position of 'size'
EInvalidDataStream(const char *msg, uint size);
};
struct ESeekNotSupported : public EStream
{
ESeekNotSupported() : EStream("Seek fonctionnality is not supported" ) {}
ESeekNotSupported(const IStream &f) : EStream(f, "Seek fonctionnality is not supported" ) {}
};
struct ENotOutputStream : public EStream
{
ENotOutputStream() : EStream("The stream is NOT an output stream, can't write in" ) {}
ENotOutputStream(const IStream &f) : EStream(f, "The stream is NOT an output stream, can't write in" ) {}
};
struct ENotInputStream : public EStream
{
ENotInputStream () : EStream("The stream is NOT an input stream, cannot read in" ) {}
ENotInputStream (const IStream &f) : EStream(f, "The stream is NOT an input stream, cannot read in" ) {}
};
/// This exception is raised when someone tries to serialize in more than there is.
struct EStreamOverflow : public EStream
{
EStreamOverflow() : EStream( "Stream Overflow Error" ) {}
// msg must contain "%u" for the position of 'size'
EStreamOverflow(const char *msg, uint size);
};
class IStreamable;
// ======================================================================================================
/**
* A IO stream interface.
* This is the base interface for stream objects. Differents kind of streams may be implemented,
* by specifying serialBuffer() methods.
*
* \b Deriver \b Use:
*
* The deriver must:
* - construct object specifying his type, see IStream(). A stream may be setup Input or Output at construction, but cannot
* change during his life.
* - specify serialBuffer(), to save or load pack of bytes.
* - specify serialBit(), to save or load a bit.
* - call resetPtrTable() when the stream reset itself (e.g.: CIFile::close() )
*
* Sample of streams: COutMemoryStream, CInFileStream ...
*
* \b Client \b Use:
*
* An object which can be serialized, must provide a "void serial(IStream &)" method. In this method, he can use
* any of the IStream method to help himself like:
* - serial() with a base type (uint32, string, char...), or even with an object which provide "void serial(IStream &)"
* - template serial(T0&, T1&, ...) to serialize multiple object/variables in one call (up to 6).
* - serialCont() to serialize containers.
* - serialVersion() to check/store a version number of his class.
* - serialPtr() to use the ptr support of IStream (see serialPtr() for more information)
* - isReading() to know if he write in the stream, or if he read.
*
* The using is very simple as shown in this example:
*
* \code
class A
{
public:
float x;
uint32 y;
Class1 a; // this class must provide a serial() method too...
Base *c,*d; // Base must derive from IStreamable
vector<Class2> tab;
public:
void serial(IStream &f)
{
sint streamver= f.serialVersion(3);
f.serial(x,y,a);
f.serialPtr(c);
f.serialCont(tab);
if(streamver>=2)
f.serialPtr(d);
}
};
\endcode
*
* NB: \b YOU \b CANNOT use serial with a int / uint / sint type, since those type have unspecified length.
* \author Lionel Berenguier
* \author Vianney Lecroart
* \author Nevrax France
* \date 2000
*/
class IStream
{
public:
/**
* Set the behavior of IStream regarding input stream that are older/newer than the class.
* If throwOnOlder==true, IStream throws a EOlderStream when needed.
* If throwOnNewer==true, IStream throws a ENewerStream when needed.
*
* By default, the behavior is throwOnOlder=false, throwOnNewer=true.
* \see serialVersion() getVersionException()
*/
static void setVersionException(bool throwOnOlder, bool throwOnNewer);
/**
* Get the behavior of IStream regarding input stream that are older/newer than the class.
* \see serialVersion() setVersionException()
*/
static void getVersionException(bool &throwOnOlder, bool &throwOnNewer);
public:
/**
* Constructor.
* Notice that those behavior can be set at construction only.
* \param inputStream is the stream an Input (read) stream?
*/
explicit IStream(bool inputStream);
/// Destructor.
virtual ~IStream() {}
/// Copy constructor
IStream( const IStream& other );
/// Assignment operator
IStream& operator=( const IStream& other );
/// exchange
void swap(IStream &other);
/// Is this stream a Read/Input stream?
bool isReading() const;
// is it a xml stream ?
bool isXML() const { return _XML; }
/**
* Template Object serialisation.
* \param obj any object providing a "void serial(IStream&)" method. The object doesn't have to derive from IStreamable.
*
* the VC++ error "error C2228: left of '.serial' must have class/struct/union type" means you don't provide
* a serial() method to your object. Or you may have use serial with a int / uint / sint type. REMEMBER YOU CANNOT
* do this, since those type have unspecified length.
*/
template<class T>
void serial(T &obj) { obj.serial(*this); }
// an utility template to unconst a type
template <class T> static T& unconst(const T &t) { return const_cast<T&>(t);}
#define nlWriteSerial(_stream, _obj) \
if ((_stream).isReading()) \
throw NLMISC::ENotOutputStream(); \
(_stream).serial(NLMISC::IStream::unconst(_obj));
#define nlWrite(_stream, _serialType, _obj) \
if ((_stream).isReading()) \
throw NLMISC::ENotOutputStream(); \
(_stream)._serialType(NLMISC::IStream::unconst(_obj));
#define nlReadSerial(_stream, _obj) \
if (!(_stream).isReading()) \
throw NLMISC::ENotInputStream(); \
NLMISC::IStream::unconst(_stream).serial(_obj);
#define nlRead(_stream, _serialType, _obj) \
if (!(_stream).isReading()) \
throw NLMISC::ENotInputStream(); \
NLMISC::IStream::unconst(_stream)._serialType(_obj);
// helper macro to serialize boolean encoded as 'bool foo : 1' (they can't be referenced)
#define nlSerialBitBool(_stream, _boolean) \
{ \
bool tmpBool = _boolean; \
_stream.serial(tmpBool); \
_boolean = tmpBool; \
}
/** \name Base type serialization.
* Those method are a specialization 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) ;
//@}
/// Template enum serialisation. Serialized as a sint32.
template<class T>
void serialEnum(T &em)
{
sint32 i;
if(isReading())
{
serial(i);
em = (T)i;
}
else
{
i = em;
serial(i);
}
}
/// Template short enum serialisation. Serialized as a uint8 (with checking).
template<class T>
void serialShortEnum(T &em)
{
uint8 i;
if(isReading())
{
serial(i);
em = (T)i;
}
else
{
nlassert(em < 0xff);
i = uint8(em);
serial(i);
}
}
/// Serial memstream, bitmemstream...
virtual void serialMemStream( CMemStream &b );
/** \name BitField serialisation.
* Unlike other serial method, The reading bitfield is returned!! If !this->isReading(), bf is returned.
*
* MUST use it simply like this: a= serialBitFieldX(a); // where X== 8, 16 or 32.
*
* NB: Performance warning: the data is stored as an uint8, uint16 or uint32, according to the method you use.
*/
//@{
/// Serialisation of bitfield <=8 bits.
uint8 serialBitField8(uint8 bf);
/// Serialisation of bitfield <=16 bits.
uint16 serialBitField16(uint16 bf);
/// Serialisation of bitfield <=32 bits.
uint32 serialBitField32(uint32 bf);
//@}
/** \name Multiple serialisation.
* Template for easy multiple serialisation.
*/
//@{
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 standard STL containers serialisation.
* Known Supported containers: vector<>, list<>, deque<>, set<>, multiset<>, map<>, multimap<>
* Support up to sint32 length containers.
* \see serialContPtr() serialContPolyPtr()
*/
template<class T, class Allocator>
void serialCont(std::vector<T, Allocator> &cont) {serialVector(cont);}
template<class T>
void serialCont(std::list<T> &cont) {serialSTLCont(cont);}
template<class T>
void serialCont(std::deque<T> &cont) {serialSTLCont(cont);}
template<class T>
void serialCont(std::set<T> &cont) {serialSTLCont(cont);}
template<class T>
void serialCont(std::multiset<T> &cont) {serialSTLCont(cont);}
template<class K, class T>
void serialCont(std::map<K, T> &cont) {serialMap(cont);}
template<class K, class T, class H>
void serialCont(CHashMap<K, T, H> &cont) {serialMap(cont);}
template<class K, class T>
void serialCont(std::multimap<K, T> &cont) {serialMultimap(cont);}
/** \name standard STL containers serialisation.
* Thse variants suppose contained type is a NeL smart pointer.
* Known Supported containers: map<>
* Support up to sint32 length containers.
* \see serialCont() serialContPtr() serialContPolyPtr()
*/
template<class K, class T>
void serialPtrCont(std::map<K, T> &cont) {serialPtrMap(cont);}
/// Specialisation of serialCont() for vector<uint8>
virtual void serialCont(std::vector<uint8> &cont) ;
/// Specialisation of serialCont() for vector<sint8>
virtual void serialCont(std::vector<sint8> &cont) ;
/// Specialisation of serialCont() for vector<bool>
virtual void serialCont(std::vector<bool> &cont) ;
/** \name standard STL containers serialisation. Elements must be pointers on a base type (uint...) or on a
* object providing "void serial(IStream&)" method.
* Known Supported containers: vector<>, list<>, deque<>, set<>, multiset<>
* Support up to sint32 length containers.
* \see serialCont() serialContPolyPtr()
*/
template<class T, class Allocator>
void serialContPtr(std::vector<T, Allocator> &cont) {serialVectorPtr(cont);}
template<class T>
void serialContPtr(std::list<T> &cont) {serialSTLContPtr(cont);}
template<class T>
void serialContPtr(std::deque<T> &cont) {serialSTLContPtr(cont);}
template<class T>
void serialContPtr(std::set<T> &cont) {serialSTLContPtr(cont);}
template<class T>
void serialContPtr(std::multiset<T> &cont) {serialSTLContPtr(cont);}
/** \name standard STL containers serialisation. Elements must be pointers on a IStreamable object.
* Known Supported containers: vector<>, list<>, deque<>, set<>, multiset<>
* Support up to sint32 length containers.
* \see serialCont() serialContPtr()
*/
template<class T, class Allocator>
void serialContPolyPtr(std::vector<T, Allocator> &cont) {serialVectorPolyPtr(cont);}
template<class T>
void serialContPolyPtr(std::list<T> &cont) {serialSTLContPolyPtr(cont);}
template<class T>
void serialContPolyPtr(std::deque<T> &cont) {serialSTLContPolyPtr(cont);}
template<class T>
void serialContPolyPtr(std::set<T> &cont) {serialSTLContPolyPtr(cont);}
template<class T>
void serialContPolyPtr(std::multiset<T> &cont) {serialSTLContPolyPtr(cont);}
template<class K, class T>
void serialContPolyPtr(std::map<K, T> &cont) {serialMapPolyPtr(cont);}
/**
* Serialize Non Polymorphic Objet Ptr.
* Works with NULL pointers. If the same object is found mutliple time in the stream, ONLY ONE instance is written!
* NB: The ptr is serialised as a uint64 (64 bit compliant).
* \param ptr a pointer on a base type or an object.
* \see resetPtrTable()
*/
template<class T>
void serialPtr(T* &ptr)
{
uint64 node;
// Open the node header
xmlPushBegin ("PTR");
xmlSetAttrib ("id");
if(isReading())
{
serial(node);
// Close the header
xmlPushEnd ();
if(node==0)
ptr=NULL;
else
{
ItIdMap it;
it= _IdMap.find(node);
// Test if object already created/read.
if( it==_IdMap.end() )
{
// Construct object.
ptr= new T;
if(ptr==NULL)
throw EStream();
// Insert the node.
_IdMap.insert( ValueIdMap(node, ptr) );
// Read the object!
serial(*ptr);
}
else
ptr= static_cast<T*>(it->second);
}
}
else
{
if(ptr==NULL)
{
node= 0;
serial(node);
// Close the header
xmlPushEnd ();
}
else
{
ItIdMap it;
it = _IdMap.find((uint64)ptr);
// Test if object has been already written
if( it==_IdMap.end() )
{
// Not yet written
// Get the next available ID
node = _NextSerialPtrId++;
// Serial the id
serial(node);
// Insert the pointer in the map with the id
_IdMap.insert( ValueIdMap((uint64)ptr, (void*)node) );
// Close the header
xmlPushEnd ();
// Write the object
serial(*ptr);
}
else
{
// Write only the object id
node = (uint64)(it->second);
serial(node);
// Close the header
xmlPushEnd ();
}
}
}
// Close the node
xmlPop ();
}
/**
* Serialize Polymorphic Objet Ptr.
* Works with NULL pointers. If the same object is found mutliple time in the stream, ONLY ONE instance is written!
* NB: The ptr is serialised as a uint64 (64 bit compliant).
* \param ptr a pointer on a IStreamable object.
* \see resetPtrTable()
*/
template<class T>
void serialPolyPtr(T* &ptr)
{ IStreamable *p=ptr; serialIStreamable(p); ptr= static_cast<T*>(p);}
/**
* Serialize a version number.
* Each object should store/read first a version number, using this method.
* Then he can use the streamVersion returned to see how he should serialise himself.
*
* NB: Version Number is read/store as a uint8, or uint32 if too bigger..
* \param currentVersion the current version of the class, provided by user.
* \return the version of the stream. If the stream is an Output stream, currentVersion is returned.
* \see setVersionException() getVersionException()
*/
uint serialVersion(uint currentVersion) ;
/**
* Serialize a check value.
* An object can stream a check value to check integrity or format of filed or streamed data.
* Just call serial check with a const value. Write will serial the value. Read will
* check the value is the same. If it is not, it will throw EInvalidDataStream exception.
*
* NB: The type of the value must implement an operator == and must be serializable.
* \param value the value used to the check.
* \see EInvalidDataStream
*/
template<class T>
void serialCheck(const T& value)
{
// Open a node
xmlPush ("CHECK");
if (isReading())
{
T read;
serial (read);
if (read!=value)
throw EInvalidDataStream(*this);
}
else
{
serial (const_cast<T&>(value));
}
// Close the node
xmlPop ();
}
/// \name Seek fonctionnality
/**
* Parameters for seek().
* begin seek from the beginning of the stream.
* current seek from the current location of the stream pointer.
* end seek from the end of the stream.
*/
enum TSeekOrigin { begin, current, end };
/**
* Moves the stream pointer to a specified location.
*
* NB: If the stream doesn't support the seek fonctionnality, it throw 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;
/**
* Get the location of the stream pointer.
*
* NB: If the stream doesn't support the seek fonctionnality, it throw 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;
/** Get a name for this stream. maybe a fileName if FileStream.
* Default is to return "".
*/
virtual std::string getStreamName() const;
/** \name XML user interface
*
* Those functions are used to add information in your stream to structure it like
* a XML document. Exemple of a serial sequence :
\code
// Start the opening of a new node named Identity
stream.xmlPush ("Identity")
// Serial some infos
stream.serial (name);
stream.serial (pseudo);
// Open a new node header named Address
stream.xmlPushBegin ("Address");
// Set a property name
stream.xmlSetAttrib ("Street")
// Serial the property
stream.serial ("Street");
// Close the new node header
stream.xmlPushEnd ();
// Serial in this node
stream.serial (cityName);
// Close the address node
stream.xmlPop ();
// Add a comment
stream.xmlComment ("Hello");
// Close the identity node
stream.xmlPop ();
\endcode
*
* The result will be an xml document structured like this:
*
\code
<Identity>
Corvazier Hulud
<Address Street="rue du Faubourg Saint Antoine">
Paris
<\Address>
<!-- Hello -->
<\Identity>
\endcode
*
* Node header serials are the serialisations done between xmlPushBegin() and xmlPushEnd() call. There is some restrictions on them:
* Node header serials are only available for basic types (numbers and strings).
* xmlSetAttrib() must be called before node header serial.
*
* Note that XML documents only have ONE root node, so all serialisation must be done between a xmlPush() and a xmlPop() call.
*
* When a xml input stream will try to open a node, it will scan all currrent child nodes to find the good one.
* So input xml stream can skip unknown node.
*/
/**
* xmlSerial() serial a values into a node.
*/
template<class T>
void xmlSerial (T& value0, const std::string &nodeName)
{
// Open the node
xmlPush (nodeName);
// Serial the value
serial (value0);
// Close the node
xmlPop ();
}
template<class T>
void xmlSerial (T& value0, T& value1, const std::string &nodeName)
{
// Open the node
xmlPush (nodeName);
// Serial the values
serial (value0, value1);
// Close the node
xmlPop ();
}
template<class T>
void xmlSerial (T& value0, T& value1, T& value2, const std::string &nodeName)
{
// Open the node
xmlPush (nodeName);
// Serial the values
serial (value0, value1, value2);
// Close the node
xmlPop ();
}
template<class T>
void xmlSerial (T& value0, T& value1, T& value2, T& value3, const std::string &nodeName)
{
// Open the node
xmlPush (nodeName);
// Serial the values
serial (value0, value1, value2, value3);
// Close the node
xmlPop ();
}
/**
* xmlPush() open recurcively a new node. You must call xmlPop to close this node.
*
* \name is the name of the node to open
* \return true if you can open the node, false if the stream is between a xmlPushBegin() and a xmlPushEnd() call.
*/
bool xmlPush (const std::string &name)
{
// XML Mode ?
if (_XML)
{
// Open the header
bool res=xmlPushBeginInternal (name);
if (res)
// close the header
xmlPushEndInternal ();
// Return the result
return res;
}
// Return ok
return true;
}
/**
* xmlPushBegin() open recurcively a new node and open its header. You must call xmlPushEnd() to close the header and xmlPop() to close this node.
*
* \name is the name of the node to open
* \return true if you can open the node header, false if the stream is between a xmlPushBegin() and a xmlPushEnd() call.
*/
bool xmlPushBegin (const std::string &name)
{
// XML Mode ?
if (_XML)
{
return xmlPushBeginInternal (name);
}
// Return ok
return true;
}
/**
* xmlPushEnd() close the node header.
*
* \return true if you can close the node header, false if no node header have been opened with xmlPushBegin().
*/
bool xmlPushEnd ()
{
// XML Mode ?
if (_XML)
{
return xmlPushEndInternal ();
}
// Return ok
return true;
}
/**
* xmlPop() close the node.
*
* \return true if you can close the node, false if the node can't be closed (its header is still opened) or if there is no node to close.
*/
bool xmlPop ()
{
// XML Mode ?
if (_XML)
{
return xmlPopInternal ();
}
// Return ok
return true;
}
/**
* xmlSetAttrib() set the name of the next node header attribute serialised.
*
* \param name is the name of the node header attribute serialised.
* \return true if the attribute name have been set, false if the node header is not open (the call is not between xmlPushBegin and xmlPushEnd)
*/
bool xmlSetAttrib (const std::string &name)
{
// XML Mode ?
if (_XML)
{
return xmlSetAttribInternal (name);
}
// Return ok
return true;
}
/**
* xmlBreakLine() insert a break line in the XML stream.
*
* \return true if the break line is added, return false if no node is opened.
*/
bool xmlBreakLine ()
{
// XML Mode ?
if (_XML)
{
return xmlBreakLineInternal ();
}
// Return ok
return true;
}
/**
* xmlComment() insert a comment line in the XML stream.
*
* \return true if the comment is added, return false if no node is opened.
*/
bool xmlComment (const std::string &comment)
{
// XML Mode ?
if (_XML)
{
return xmlCommentInternal (comment);
}
// Return ok
return true;
}
protected:
/// \name XML implementation interface
/** Set the XML mode
* \param on is true to enable XML mode else false
*/
void setXMLMode (bool on);
/// xmlPushBegin implementation
virtual bool xmlPushBeginInternal (const std::string &/* name */) { return true; };
/// xmlPushEnd implementation
virtual bool xmlPushEndInternal () { return true; };
/// xmlPop implementation
virtual bool xmlPopInternal () { return true; };
/// xmlBreakLine implementation
virtual bool xmlSetAttribInternal (const std::string &/* name */) { return true; };
/// xmlBreakLine implementation
virtual bool xmlBreakLineInternal () { return true; };
/// xmlComment implementation
virtual bool xmlCommentInternal (const std::string &/* comment */) { return true; };
/**
* for Deriver: reset the PtrTable in the stream.
* If Derived stream provide reset()-like methods, they must call this method in their reset() methods.
* For example, CFile::close() must call it, so it will work correctly with next serialPtr()
*/
void resetPtrTable();
/**
* Change, in live, the state of the inputStream. This could be useful in certain case.
* The deriver which would want to do such a thing must call this method, and implement his own behavior.
* In certain case, it should call resetPtrTable() if he want to reset the stream ptr info (maybe always)...
*/
void setInOut(bool inputStream);
/** 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 {return 0;}
/**
* Elementarily check at least n bytes can be serialized from this stream (or throw EStreamOverflow)
*/
void checkStreamSize(uint numBytes) const
{
uint ssize = getDbgStreamSize();
if (ssize > 0 && ssize < numBytes)
throw EStreamOverflow("stream does not contain at least %u bytes for check", numBytes);
}
public:
//@{
/** Method to be specified by the Deriver.
* \warning Do not call these methods from outside, unless you really know what you are doing.
* Using them instead of serial() can lead to communication problems between different platforms !
*/
virtual void serialBuffer(uint8 *buf, uint len) =0;
virtual void serialBit(bool &bit) =0;
//@}
/// This method first serializes the size of the buffer and after the buffer itself, it enables
/// the possibility to serial with a serialCont() on the other side.
virtual void serialBufferWithSize(uint8 *buf, uint32 len)
{
serial (len);
serialBuffer (buf, len);
}
private:
bool _InputStream;
static bool _ThrowOnOlder;
static bool _ThrowOnNewer;
// Ptr registry. We store 64 bit Id, to be compatible with future 64+ bits pointers.
uint32 _NextSerialPtrId;
CHashMap<uint64, void*> _IdMap;
typedef CHashMap<uint64, void*>::iterator ItIdMap;
typedef CHashMap<uint64, void*>::value_type ValueIdMap;
// Ptr serialization.
void serialIStreamable(IStreamable* &ptr) ;
private:
/**
* standard STL containers serialization. Don't work with map<> and multimap<>.
* Support up to sint32 length containers. serialize just len element of the container.
*/
template<class T>
void serialSTLContLen(T &cont, sint32 len)
{
typedef typename T::value_type __value_type;
typedef typename T::iterator __iterator;
if(isReading())
{
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
for(sint i=0;i<len;i++)
{
xmlPush ("ELM");
__value_type v;
serial(v);
(void)cont.insert(cont.end(), v);
xmlPop ();
}
}
else
{
__iterator it= cont.begin();
for(sint i=0;i<len;i++, it++)
{
xmlPush ("ELM");
serial(const_cast<__value_type&>(*it));
xmlPop ();
}
}
}
/**
* standard STL containers serialisation. Don't work with map<> and multimap<>.
* Support up to sint32 length containers.
*
* the object T must provide:
* \li typedef iterator; (providing operator++() and operator*())
* \li typedef value_type; (a base type (uint...), or an object providing "void serial(IStream&)" method.)
* \li void clear();
* \li size_type size() const;
* \li iterator begin();
* \li iterator end();
* \li iterator insert(iterator it, const value_type& x);
*
* Known Supported containers: vector<>, list<>, deque<>, set<>, multiset<>.
* \param cont a STL container (vector<>, set<> ...).
*/
template<class T>
void serialSTLCont(T &cont)
{
// Open a node header
xmlPushBegin ("CONTAINER");
// Attrib size
xmlSetAttrib ("size");
sint32 len=0;
if(isReading())
{
serial(len);
cont.clear();
}
else
{
len= (sint32)cont.size();
serial(len);
}
// Close the header
xmlPushEnd ();
serialSTLContLen(cont, len);
// Close the node
xmlPop ();
}
protected:
/**
* special version for serializing a vector.
* Support up to sint32 length containers.
*/
template<class T>
void serialVector(T &cont)
{
typedef typename T::value_type __value_type;
typedef typename T::iterator __iterator;
// Open a node header
xmlPushBegin ("VECTOR");
// Attrib size
xmlSetAttrib ("size");
sint32 len=0;
if(isReading())
{
serial(len);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
// Open a node header
xmlPushEnd ();
// special version for vector: adjut good size.
contReset(cont);
cont.resize (len);
// Read the vector
for(sint i=0;i<len;i++)
{
xmlPush ("ELM");
serial(cont[i]);
xmlPop ();
}
}
else
{
len = (sint32)cont.size();
serial(len);
// Close the node header
xmlPushEnd ();
// Write the vector
__iterator it= cont.begin();
for(sint i=0;i<len;i++, it++)
{
xmlPush ("ELM");
serial(const_cast<__value_type&>(*it));
xmlPop ();
}
}
// Close the node
xmlPop ();
}
private:
/**
* standard STL containers serialisation. Don't work with map<> and multimap<>. Ptr version.
* Support up to sint32 length containers. serialize just len element of the container.
*/
template<class T>
void serialSTLContLenPtr(T &cont, sint32 len)
{
typedef typename T::value_type __value_type;
typedef typename T::iterator __iterator;
if(isReading())
{
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
for(sint i=0;i<len;i++)
{
__value_type v;
serialPtr(v);
cont.insert(cont.end(), v);
}
}
else
{
__iterator it= cont.begin();
for(sint i=0;i<len;i++, it++)
{
serialPtr(const_cast<__value_type&>(*it));
}
}
}
/**
* standard STL containers serialisation. Don't work with map<> and multimap<>. Ptr version.
* Support up to sint32 length containers.
*/
template<class T>
void serialSTLContPtr(T &cont)
{
// Open a node header
xmlPushBegin ("CONTAINER");
// Attrib size
xmlSetAttrib ("size");
sint32 len=0;
if(isReading())
{
serial(len);
cont.clear();
}
else
{
len= cont.size();
serial(len);
}
// Close the node header
xmlPushEnd ();
serialSTLContLenPtr(cont, len);
// Close the node
xmlPop ();
}
/**
* special version for serializing a vector. Ptr version.
* Support up to sint32 length containers.
*/
template<class T>
void serialVectorPtr(T &cont)
{
typedef typename T::value_type __value_type;
typedef typename T::iterator __iterator;
// Open a node header
xmlPushBegin ("VECTOR");
// Attrib size
xmlSetAttrib ("size");
sint32 len=0;
if(isReading())
{
serial(len);
// special version for vector: adjut good size.
contReset(cont);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
cont.reserve(len);
}
else
{
len= (sint32)cont.size();
serial(len);
}
// Close the node header
xmlPushEnd ();
serialSTLContLenPtr(cont, len);
// Close the node
xmlPop ();
}
private:
/**
* standard STL containers serialisation. Don't work with map<> and multimap<>. PolyPtr version
* Support up to sint32 length containers. serialize just len element of the container.
*/
template<class T>
void serialSTLContLenPolyPtr(T &cont, sint32 len)
{
typedef typename T::value_type __value_type;
typedef typename T::iterator __iterator;
if(isReading())
{
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
for(sint i=0;i<len;i++)
{
__value_type v=NULL;
serialPolyPtr(v);
cont.insert(cont.end(), v);
}
}
else
{
__iterator it= cont.begin();
for(sint i=0;i<len;i++, it++)
{
serialPolyPtr(const_cast<__value_type&>(*it));
}
}
}
/**
* Map serialisation. PolyPtr version
* Support up to sint32 length containers. serialize just len element of the container.
*/
template<class T>
void serialMapContLenPolyPtr(T &cont, sint32 len)
{
typedef typename T::key_type __key_type;
typedef typename T::data_type __data_type;
typedef typename T::iterator __iterator;
if(isReading())
{
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++)
{
__key_type k;
xmlPush ("KEY");
serial ( k );
xmlPop ();
xmlPush ("ELM");
__data_type v=NULL;
v.serialPolyPtr(*this);
cont[k] = v;
xmlPop ();
}
}
else
{
__iterator it= cont.begin();
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++, it++)
{
xmlPush ("KEY");
serial( const_cast<__key_type&>((*it).first) );
xmlPop ();
xmlPush ("ELM");
__data_type v= const_cast<__data_type&>(it->second);
v.serialPolyPtr(*this);
xmlPop ();
}
}
}
/**
* standard STL containers serialisation. Don't work with map<> and multimap<>. PolyPtr version
* Support up to sint32 length containers.
*/
template<class T>
void serialSTLContPolyPtr(T &cont)
{
sint32 len=0;
if(isReading())
{
serial(len);
cont.clear();
}
else
{
len= cont.size();
serial(len);
}
serialSTLContLenPolyPtr(cont, len);
}
/**
* special version for serializing a vector. PolyPtr version
* Support up to sint32 length containers.
*/
template<class T>
void serialVectorPolyPtr(T &cont)
{
typedef typename T::value_type __value_type;
typedef typename T::iterator __iterator;
// Open a node header
xmlPushBegin ("VECTOR");
// Attrib size
xmlSetAttrib ("size");
sint32 len=0;
if(isReading())
{
serial(len);
// special version for vector: adjut good size.
contReset(cont);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
cont.reserve(len);
}
else
{
len= (sint32)cont.size();
serial(len);
}
// Close the node header
xmlPushEnd ();
serialSTLContLenPolyPtr(cont, len);
// Close the node
xmlPop ();
}
/**
* special version for serializing a map. PolyPtr version
* Support up to sint32 length containers.
*/
template<class K, class T>
void serialMapPolyPtr(std::map<K, T> &cont)
{
// Open a node header
xmlPushBegin ("MAP");
// Attrib size
xmlSetAttrib ("size");
sint32 len=0;
if(isReading())
{
serial(len);
cont.clear();
}
else
{
len= cont.size();
serial(len);
}
serialMapContLenPolyPtr(cont, len);
// Close the node
xmlPop ();
}
private:
/**
* STL map<> and multimap<> serialisation.
* Support up to sint32 length containers.
*
* the object T must provide:
* \li typedef iterator; (providing operator++() and operator*())
* \li typedef value_type; (must be a std::pair<>)
* \li typedef key_type; (must be the type of the key)
* \li void clear();
* \li size_type size() const;
* \li iterator begin();
* \li iterator end();
* \li iterator insert(iterator it, const value_type& x);
*
* Known Supported containers: map<>, multimap<>.
* \param cont a STL map<> or multimap<> container.
*/
template<class T>
void serialMultimap(T &cont)
{
typedef typename T::value_type __value_type;
typedef typename T::key_type __key_type;
typedef typename T::iterator __iterator;
// Open a node header
xmlPushBegin ("MULTIMAP");
// Attrib size
xmlSetAttrib ("size");
sint32 len;
if(isReading())
{
cont.clear();
serial(len);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++)
{
__value_type v;
xmlPush ("KEY");
serial ( const_cast<__key_type&>(v.first) );
xmlPop ();
xmlPush ("ELM");
serial (v.second);
xmlPop ();
cont.insert(cont.end(), v);
}
}
else
{
len= (sint32)cont.size();
serial(len);
__iterator it= cont.begin();
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++, it++)
{
xmlPush ("KEY");
serial( const_cast<__key_type&>((*it).first) );
xmlPop ();
xmlPush ("ELM");
serial((*it).second);
xmlPop ();
}
}
// Close the node
xmlPop ();
}
/**
* STL map<>
* Support up to sint32 length containers.
*
* the object T must provide:
* \li typedef iterator; (providing operator++() and operator*())
* \li typedef value_type; (must be a std::pair<>)
* \li typedef key_type; (must be the type of the key)
* \li void clear();
* \li size_type size() const;
* \li iterator begin();
* \li iterator end();
* \li iterator insert(iterator it, const value_type& x);
*
* Known Supported containers: map<>
* \param cont a STL map<> container.
*/
template<class T>
void serialMap(T &cont)
{
typedef typename T::value_type __value_type;
typedef typename T::key_type __key_type;
typedef typename T::iterator __iterator;
// Open a node header
xmlPushBegin ("MAP");
// Attrib size
xmlSetAttrib ("size");
sint32 len;
if(isReading())
{
cont.clear();
serial(len);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++)
{
// MALKAV 05/07/02 : prevent a copy of the value, copy the key instead
__key_type k;
xmlPush ("KEY");
serial ( k );
xmlPop ();
xmlPush ("ELM");
serial (cont[k]);
xmlPop ();
}
}
else
{
len= (sint32)cont.size();
serial(len);
__iterator it= cont.begin();
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++, it++)
{
xmlPush ("KEY");
serial( const_cast<__key_type&>((*it).first) );
xmlPop ();
xmlPush ("ELM");
serial((*it).second);
xmlPop ();
}
}
// Close the node
xmlPop ();
}
/**
* STL map<>
* Support up to sint32 length containers. Container must contain NeL smart pointers.
*
* the object T must provide:
* \li typedef iterator; (providing operator++() and operator*())
* \li typedef value_type; (must be a std::pair<>)
* \li typedef key_type; (must be the type of the key)
* \li void clear();
* \li size_type size() const;
* \li iterator begin();
* \li iterator end();
* \li iterator insert(iterator it, const value_type& x);
*
* Known Supported containers: map<>
* \param cont a STL map<> container.
*/
template<class T>
void serialPtrMap(T &cont)
{
typedef typename T::value_type::second_type __ptr_type;
typedef typename __ptr_type::element_type __value_type;
typedef typename T::key_type __key_type;
typedef typename T::iterator __iterator;
// Open a node header
xmlPushBegin ("MAP");
// Attrib size
xmlSetAttrib ("size");
sint32 len;
if(isReading())
{
cont.clear();
serial(len);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++)
{
// MALKAV 05/07/02 : prevent a copy of the value, copy the key instead
__key_type k;
xmlPush ("KEY");
serial ( k );
xmlPop ();
xmlPush ("ELM");
cont[k] = __ptr_type(new __value_type());
serial (*cont[k]);
xmlPop ();
}
}
else
{
len= (sint32)cont.size();
serial(len);
__iterator it= cont.begin();
// Close the node header
xmlPushEnd ();
for(sint i=0;i<len;i++, it++)
{
xmlPush ("KEY");
serial( const_cast<__key_type&>((*it).first) );
xmlPop ();
xmlPush ("ELM");
serial(*(*it).second);
xmlPop ();
}
}
// Close the node
xmlPop ();
}
// Mode XML
bool _XML;
};
// ======================================================================================================
// ======================================================================================================
// Handle for streaming Polymorphic classes.
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
/**
* An Object Streamable interface. Any polymorphic class which want to use serial() in a polymorphic way, must derive
* from this interface.
* \author Lionel Berenguier
* \author Vianney Lecroart
* \author Nevrax France
* \date 2000
*/
class IStreamable : public IClassable
{
public:
virtual void serial(IStream &f) =0;
};
} // NLMISC.
// Inline Implementation.
#include "stream_inline.h"
#endif // NL_STREAM_H
/* End of stream.h */