// 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/stream.h"
#include "nel/misc/mem_stream.h"
using namespace std;
namespace NLMISC
{
// ======================================================================================================
// ======================================================================================================
// EStream.
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
EStream::EStream( const IStream &f ) : Exception( "In Stream: " + f.getStreamName() + string(": Stream Error") )
{
StreamName= f.getStreamName();
}
EStream::EStream( const IStream &f, const std::string& str )
: Exception( "In Stream: " + f.getStreamName() + ": " + str )
{
StreamName= f.getStreamName();
}
EInvalidDataStream::EInvalidDataStream(const char *msg, uint size)
: EStream( NLMISC::toString( msg, size ) )
{}
EStreamOverflow::EStreamOverflow( const char *msg, uint size )
: EStream( NLMISC::toString( msg, size ) )
{}
// ======================================================================================================
// ======================================================================================================
// IStream.
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
bool IStream::_ThrowOnOlder=false;
bool IStream::_ThrowOnNewer=true;
// ======================================================================================================
void IStream::setVersionException(bool throwOnOlder, bool throwOnNewer)
{
_ThrowOnOlder=throwOnOlder;
_ThrowOnNewer=throwOnNewer;
}
// ======================================================================================================
void IStream::getVersionException(bool &throwOnOlder, bool &throwOnNewer)
{
throwOnOlder=_ThrowOnOlder;
throwOnNewer=_ThrowOnNewer;
}
/*
* Copy constructor
*/
IStream::IStream( const IStream& other )
{
operator=( other );
// By default, mode _XML is off
_XML = false;
}
/*
* Assignment operator
*/
IStream& IStream::operator=( const IStream& other )
{
_InputStream = other._InputStream;
resetPtrTable();
return *this;
}
void IStream::swap(IStream &other)
{
std::swap(_InputStream, other._InputStream);
std::swap(_NextSerialPtrId, other._NextSerialPtrId);
_IdMap.swap(other._IdMap);
std::swap(_XML, other._XML);
}
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
void IStream::serialIStreamable(IStreamable* &ptr)
{
uint64 node=0;
// Open a node
xmlPushBegin ("POLYPTR");
if(isReading())
{
// First attribute name
xmlSetAttrib ("id");
serial(node);
if(node==0)
{
ptr=NULL;
// Close the node header
xmlPushEnd ();
}
else
{
ItIdMap it;
it= _IdMap.find(node);
// Test if object already created/read.
if( it==_IdMap.end() )
{
// Read the class name.
string className;
// Second attribute name
xmlSetAttrib ("class");
serial(className);
// Close the node header
xmlPushEnd ();
// Construct object.
ptr= dynamic_cast (CClassRegistry::create(className));
if(ptr==NULL)
#ifdef NL_DEBUG
throw EUnregisteredClass(className);
#else
throw EUnregisteredClass();
#endif
#ifdef NL_DEBUG
nlassert(CClassRegistry::checkObject(ptr));
#endif
// Insert the node.
_IdMap.insert( ValueIdMap(node, ptr) );
// Read the object!
ptr->serial(*this);
}
else
{
ptr= static_cast(it->second);
// Close the node header
xmlPushEnd ();
}
}
}
else
{
if(ptr==NULL)
{
node= 0;
// First attribute name
xmlSetAttrib ("id");
serial(node);
// Close the node header
xmlPushEnd ();
}
else
{
// Assume that prt size is an int size
//#ifdef NL_DEBUG
// nlassert(sizeof(uint) == sizeof(void *));
//#endif
ItIdMap it;
it = _IdMap.find((uint64)/*(uint)*/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
xmlSetAttrib ("id");
serial(node);
// Insert the pointer in the map with the id
_IdMap.insert( ValueIdMap((uint64)/*(uint)*/ptr, (void*)/*(uint)*/node) );
#ifdef NL_DEBUG
nlassert(CClassRegistry::checkObject(ptr));
#endif
// Write the class name.
string className=ptr->getClassName();
// Second attribute name
xmlSetAttrib ("class");
serial(className);
// Close the node header
xmlPushEnd ();
// Write the object!
ptr->serial(*this);
}
else
{
// Write only the object id
xmlSetAttrib ("id");
node = (uint64)/*(uint)*/(it->second);
serial(node);
xmlPushEnd ();
}
}
}
// Close the node
xmlPop ();
}
// ======================================================================================================
void IStream::resetPtrTable()
{
_IdMap.clear();
_NextSerialPtrId = 1; // Start at 1 because 0 is the NULL pointer
}
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
uint IStream::serialVersion(uint currentVersion)
{
uint8 b=0;
uint32 v=0;
uint streamVersion;
// Open the node
xmlPush ("VERSION");
if(isReading())
{
serial(b);
if(b==0xFF)
serial(v);
else
v=b;
streamVersion=v;
// Exception test.
if(_ThrowOnOlder && streamVersion < currentVersion)
throw EOlderStream(*this);
if(_ThrowOnNewer && streamVersion > currentVersion)
throw ENewerStream(*this);
}
else
{
v= streamVersion=currentVersion;
if(v>=0xFF)
{
b=0xFF;
serial(b);
serial(v);
}
else
{
b= (uint8)v;
serial(b);
}
}
// Close the node
xmlPop ();
return streamVersion;
}
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
// ======================================================================================================
void IStream::serialCont(vector &cont)
{
sint32 len=0;
if(isReading())
{
serial(len);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
// one block serial
cont.resize(len);
if (len != 0)
serialBuffer( (uint8*)&(*cont.begin()) , len);
}
else
{
len= cont.size();
serial(len);
if (len != 0)
serialBuffer( (uint8*)&(*cont.begin()) , len);
}
}
// ======================================================================================================
void IStream::serialCont(vector &cont)
{
sint32 len=0;
if(isReading())
{
serial(len);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len);
// one block serial
cont.resize(len);
if (len != 0)
serialBuffer( (uint8*)&(*cont.begin()) , len);
}
else
{
len= cont.size();
serial(len);
if (len != 0)
serialBuffer( (uint8*)&(*cont.begin()) , len);
}
}
// ======================================================================================================
void IStream::serialCont(vector &cont)
{
sint32 len=0;
vector vec;
if(isReading())
{
serial(len);
// check stream holds enough bytes (avoid STL to crash on resize)
checkStreamSize(len/8);
// One Block Serial
cont.resize(len);
if (len != 0)
{
// read as uint8*.
sint lb= (len+7)/8;
vec.resize(lb);
serialBuffer( (uint8*)&(*vec.begin()) , lb);
for(sint i=0;i>3]>>(i&7)) & 1;
cont[i]= bit?true:false;
}
}
}
else
{
len= cont.size();
serial(len);
if (len != 0)
{
// write as uint8*.
sint lb= (len+7)/8;
vec.resize(lb);
fill_n(vec.begin(), lb, 0);
for(sint i=0;i>3]|= bit<<(i&7);
}
serialBuffer( (uint8*)&(*vec.begin()) , lb);
}
}
}
// ======================================================================================================
bool IStream::seek (sint32 offset, TSeekOrigin origin) const
{
throw ESeekNotSupported(*this);
}
// ======================================================================================================
sint32 IStream::getPos () const
{
throw ESeekNotSupported(*this);
}
// ======================================================================================================
void IStream::setInOut(bool inputStream)
{
_InputStream= inputStream;
}
// ======================================================================================================
string IStream::getStreamName() const
{
return "";
}
// ======================================================================================================
void IStream::setXMLMode (bool on)
{
_XML = on;
}
/*
* Serial memstream, bitmemstream...
*/
void IStream::serialMemStream( CMemStream &b )
{
uint32 len=0;
// Serialize length
if ( isReading() )
{
// fill b with data from this
serial (len);
serialBuffer (b.bufferToFill (len), len);
b.resetBufPos ();
}
else
{
// fill this with data from b
len = b.length();
serial( len );
serialBuffer( (uint8*) b.buffer (), len );
}
}
}