tripwire-open-source/src/core/serializerimpl.cpp

499 lines
17 KiB
C++

//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
// reserved.
//
// This program is free software. The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version. You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE. Please see the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
// serializerimpl.cpp
#include "stdcore.h"
#include "serializerimpl.h"
#include "debug.h"
#include "archive.h"
#include "ntmbs.h" // for: eCharacterEncoding
#include "crc32.h"
// static members
cSerializerImpl::SerMap cSerializerImpl::mSerCreateMap ;
cSerializerImpl::SerRefCountMap cSerializerImpl::mSerRefCountCreateMap ;
///////////////////////////////////////////////////////////////////////////////
// util_GetCrc -- calculates the crc for the narrow version of the type's AsString()
///////////////////////////////////////////////////////////////////////////////
static uint32 util_GetCRC( const cType& type )
{
//
// convert this to narrow...
//
// NOTE:RAD -- Fixed bug when going from TCHAR is Wide to Multibyte
// 09/01/99 - Increased performance by returning byte len instead of
// recalculating again in the call to crcUpdate.
// - Increased performance by removing superflous conversion
// when type.AsString() is already narrow.
//
// We only need to count the characters
// RAD: Yeesh! This is already done for us in cType::mString!!!
const uint8* pszType = (const uint8*)( type.AsString() );
int nBytes = ::strlen( (const char*)pszType );
ASSERT( sizeof(uint8) == sizeof(byte) );
ASSERT( pszType && *pszType );
//
// calculate the crc...
//
CRC_INFO crc;
crcInit( crc );
crcUpdate( crc, pszType, nBytes );
crcFinit( crc );
return crc.crc;
}
//#############################################################################
// class cSerializerImpl
//#############################################################################
cSerializerImpl::cSerializerImpl(cArchive& archive, Mode action, const TSTRING& fileName) :
mpArchive(&archive),
mMode(action),
mbInit(false),
mFileName( fileName )
{
}
cSerializerImpl::~cSerializerImpl()
{
}
bool cSerializerImpl::IsWriting() const
{
return (mMode == S_WRITE);
}
///////////////////////////////////////////////////////////////////////////////
// static object registration functions; Exactly one of these should be called for
// every object that is to be serialized.
///////////////////////////////////////////////////////////////////////////////
// TODO:dmb - following line doesn't do anything???
//std::allocator< std::pair< unsigned long, iTypedSerializable*(*)() > >;
void cSerializerImpl::RegisterSerializable(const cType& type, iTypedSerializable::CreateFunc pFunc)
{
uint32 crc = util_GetCRC( type );
if( cSerializerImpl::mSerCreateMap.find( crc ) != cSerializerImpl::mSerCreateMap.end() )
{
// duplicate entry!
//
ASSERT( false );
TOSTRINGSTREAM str;
str << _T("Duplicate entry in type table: ") << type.AsString() << std::endl;
throw eInternal(str.str());
}
cSerializerImpl::mSerCreateMap[crc] = pFunc;
}
void cSerializerImpl::RegisterSerializableRefCt(const cType& type, iSerRefCountObj::CreateFunc pFunc)
{
uint32 crc = util_GetCRC( type );
if( cSerializerImpl::mSerRefCountCreateMap.find( crc ) != cSerializerImpl::mSerRefCountCreateMap.end() )
{
// duplicate entry!
//
ASSERT( false );
TOSTRINGSTREAM str;
str << _T("Duplicate entry in type table: ") << type.AsString() << std::ends;
throw eInternal(str.str());
}
cSerializerImpl::mSerRefCountCreateMap[crc] = pFunc;
}
///////////////////////////////////////////////////////////////////////////////
// Init -- the job of init is to clear out the RefCountObj table, fill out the
// mTypeArray, and and write the header information
///////////////////////////////////////////////////////////////////////////////
void cSerializerImpl::Init()
{
cDebug d("cSerializerImpl::Init");
d.TraceDetail("Entering; IsWriting = %s\n", IsWriting() ? "true" : "false");
mRefCtObjTbl.Clear();
mbInit = true;
}
///////////////////////////////////////////////////////////////////////////////
// Finit
///////////////////////////////////////////////////////////////////////////////
void cSerializerImpl::Finit()
{
cDebug d("cSerializerImpl::Finit");
d.TraceDetail("Exiting; IsWriting = %s\n", IsWriting() ? "true" : "false");
mbInit = false;
}
///////////////////////////////////////////////////////////////////////////////
// WriteObjectDynCreate
///////////////////////////////////////////////////////////////////////////////
void cSerializerImpl::WriteObjectDynCreate(const iTypedSerializable* pObj)
{
ASSERT(mbInit);
ASSERT(IsWriting());
cDebug d("cSerializerImpl::WriteObjectDynCreate");
// CurrentPos() only works with bidir archive
//d.TraceDetail("Entering... Archive Offset = %d\n", mpArchive->CurrentPos());
d.TraceDetail(_T(" Object Type = %s\n"), pObj->GetType().AsString());
// first, we write out the header, which consists of the following:
// uint32 crc of the object's type
// int32 version of stored data
// int32 size of the chunk (counting from this point; not including the previous int32
// int32 index into mRefCountObjTbl, or 0 if it isn't refrence counted.
// if the index already exists, then no data follows; a refrence
// should just be added to the existing object.
ASSERT(mpArchive != 0);
// get the ident for this class type
//
uint32 crc = util_GetCRC( pObj->GetType() );
//
// make sure this type is registered, and figure out if it is refrence counted
//
SerRefCountMap::iterator i = mSerRefCountCreateMap.find( crc );
bool bRefCount = true;
if( i == mSerRefCountCreateMap.end() )
{
//
// maybe it is not refrence counted...
//
SerMap::iterator si = mSerCreateMap.find( crc );
if( si == mSerCreateMap.end() )
{
d.TraceError("Attempt to serialize unregistered type : %s\n", pObj->GetType().AsString());
ThrowAndAssert(eSerializerUnknownType( pObj->GetType().AsString(), mFileName, eSerializer::TY_FILE ));
}
bRefCount = false;
}
mpArchive->WriteInt32(crc);
mpArchive->WriteInt32(pObj->Version());
// write a placeholder for the size; we will come back and fill in the right value later...
mpArchive->WriteInt32(0xffffffff);
if(bRefCount)
{
// see if the object has already been serialized...
int idx;
if((idx = mRefCtObjTbl.Lookup(static_cast<const iSerRefCountObj*>(pObj))) == 0)
{
// it is not in the table yet; add it and serialize the object
idx = mRefCtObjTbl.Add(static_cast<const iSerRefCountObj*>(pObj));
d.TraceDetail("Object [%d] written for first time...\n", idx);
mpArchive->WriteInt32(idx);
pObj->Write(this);
}
else
{
// since it is already in the table, just write the index number
d.TraceDetail("Object [%d] already serialized; incrementing refrence count.\n", idx);
mpArchive->WriteInt32(idx);
}
}
else
{
// not reference counted ... just write 0
mpArchive->WriteInt32(0);
// ... then write the object.
pObj->Write(this);
}
}
///////////////////////////////////////////////////////////////////////////////
// ReadObjectDynCreate
///////////////////////////////////////////////////////////////////////////////
iTypedSerializable* cSerializerImpl::ReadObjectDynCreate()
{
cDebug d("cSerializerImpl::ReadObjectDynCreate");
//d.TraceDetail("Entering... archive offset = %d\n", mpArchive->CurrentPos());
int32 size, objIdx;
uint32 crc;
// first, get the type...
mpArchive->ReadInt32(reinterpret_cast<int32&>(crc));
// read in the version
int32 version;
mpArchive->ReadInt32(version);
// read in the size and the index...
mpArchive->ReadInt32(size);
// Save the position so we can seek correctly later on
//int64 sizePos = mpArchive->CurrentPos();
mpArchive->ReadInt32(objIdx);
if(objIdx == 0)
{
// the object is not reference counted; create and read in the object
// first, we need to create the object.
SerMap::iterator si;
si = mSerCreateMap.find(crc);
if(si == mSerCreateMap.end())
{
// unable to find the creation function...
d.TraceError("Unable to find creation function for non-ref counted object %d\n", crc);
TOSTRINGSTREAM str;
#ifdef DEBUG
// Let's only report the actual crc in debug mode
str << (int32)crc << std::ends;
#endif
ThrowAndAssert(eSerializerUnknownType(str.str(), mFileName, eSerializer::TY_FILE));
}
iTypedSerializable* pObj = ((*si).second)();
d.TraceDetail("Created non-ref counted object %s(%p)\n", pObj->GetType().AsString(), pObj);
pObj->Read(this, version);
// seek past this object in case filepos is not correct!
//mpArchive->Seek(sizePos + size, cBidirArchive::BEGINNING);
return pObj;
}
else
{
// refrence counted...
iSerRefCountObj* pObj;
pObj = mRefCtObjTbl.Lookup(objIdx);
if(pObj == NULL)
{
// not in table yet...first find the creation function
SerRefCountMap::iterator rci;
rci = mSerRefCountCreateMap.find(crc);
if(rci == mSerRefCountCreateMap.end())
{
// unable to find the creation function...
d.TraceError("Unable to find creation function for ref counted object %d\n", crc);
TOSTRINGSTREAM str;
str << (int32)crc << std::ends;
ThrowAndAssert(eSerializerUnknownType(str.str(), mFileName, eSerializer::TY_FILE));
}
pObj = ((*rci).second)();
d.TraceDetail("Creating Ref-Coutnted object [%d] %s(%p)\n", objIdx, pObj->GetType().AsString(), pObj);
pObj->Read(this);
mRefCtObjTbl.Add(pObj, objIdx);
// seek past this object in case filepos is not correct!
//mpArchive->Seek(sizePos + size, cBidirArchive::BEGINNING);
return pObj;
}
else
{
// already serialized; just return this object.
d.TraceDetail("Adding refrence to previously serialized object [%d] %s(%p)\n", objIdx, pObj->GetType().AsString(), pObj);
pObj->AddRef();
}
// seek past this object in case filepos is not correct!
//mpArchive->Seek(sizePos + size, cBidirArchive::BEGINNING);
return pObj;
}
}
///////////////////////////////////////////////////////////////////////////////
// WriteObect, ReadObject
///////////////////////////////////////////////////////////////////////////////
void cSerializerImpl::WriteObject(const iTypedSerializable* pObj)
{
ASSERT(pObj != 0);
//ASSERT(mbInit); // this isn't needed if we are not writing the type array
ASSERT(IsWriting());
cDebug d("cSerializerImpl::WriteObject");
//d.TraceDetail("Entering... Archive Offset = %d\n", mpArchive->CurrentPos());
d.TraceDetail(" Object Type = %s\n", pObj->GetType().AsString());
// first, we write out the header, which consists of the following:
// int32 refrence into mTypeArray, indicating the object's type
// int32 version of stored data
// int32 size of the chunk (counting from this point; not including the previous int32
// data the object data
ASSERT(mpArchive != 0);
// 5Nov 98 mdb -- I am removing the read and write of type info for this method; it is never used, since
// the object is already created when ReadObject() happens. The only good it might serve is in asserting that
// the input stream is valid, but I can do that with the 0xffffffff size below (asserting that it is the same
// when it is read back in)
/* int i = 0;
for(; i < mTypeArray.size(); i++)
{
if(pObj->GetType() == *mTypeArray[i])
{
// aha! this is it!
break;
}
}
if(i == mTypeArray.size())
{
// the type was not registered;
// a debate exists in my mind as to whether this should be an exception or an assertion -- mdb
d.TraceError("Attempt to serialize unregistered type : %s\n", pObj->GetType().AsString());
ThrowAndAssert(eSerializerUnknownType(pObj->GetType().AsString()));
}
mpArchive->WriteInt32(i);
*/
mpArchive->WriteInt32(0); // place holder for type array index
mpArchive->WriteInt32(pObj->Version());
// write a placeholder for the size; we will come back and fill in the right value later...
//int64 sizePos = mpArchive->CurrentPos();
mpArchive->WriteInt32(0xffffffff);
// write out the object!
pObj->Write(this);
// finally, we need to go back and patch up the size...
//int64 returnPos = mpArchive->CurrentPos();
//mpArchive->Seek(sizePos, cBidirArchive::BEGINNING);
//mpArchive->WriteInt32((int32)(returnPos - sizePos - sizeof(int32)));
//mpArchive->Seek(returnPos, cBidirArchive::BEGINNING);
}
void cSerializerImpl::ReadObject(iTypedSerializable* pObj)
{
cDebug d("cSerializerImpl::ReadObjectDynCreate");
//d.TraceDetail("Entering... archive offset = %d\n", mpArchive->CurrentPos());
// NOTE -- type index stuff is gone; see the comment in WriteObject()
int32 /*typeIdx,*/ size;
// first, get the type...
/*
mpArchive->ReadInt32(typeIdx);
if((typeIdx < 0) || (typeIdx >= mTypeArray.size()))
{
// unknown type index!
d.TraceError("Encountered bad index into TypeArray: %d\n", typeIdx);
ThrowAndAssert(eSerializerInputStremTypeArray());
}
const cType* pType = mTypeArray[typeIdx];
*/
// read in the version
int32 dummy, version;
mpArchive->ReadInt32(dummy); // old type array index
mpArchive->ReadInt32(version);
// read in the size and the index...
mpArchive->ReadInt32(size);
// use the size to assert that the file format is correct
if(size != (int)0xffffffff)
{
// unknown type index!
d.TraceError("Encountered bad size: %d\n", size);
ThrowAndAssert(eSerializerInputStremTypeArray(_T(""), mFileName, eSerializer::TY_FILE));
}
// remember current position
//int64 sizePos = mpArchive->CurrentPos();
// read in the object!
pObj->Read(this, version);
// seek past this object in case filepos is not correct!
//mpArchive->Seek(sizePos + size, cBidirArchive::BEGINNING);
}
///////////////////////////////////////////////////////////////////////////////
// archive wrapper
///////////////////////////////////////////////////////////////////////////////
void cSerializerImpl::ReadInt16(int16& ret)
{
mpArchive->ReadInt16(ret);
}
void cSerializerImpl::ReadInt32(int32& ret)
{
mpArchive->ReadInt32(ret);
}
void cSerializerImpl::ReadInt64(int64& ret)
{
mpArchive->ReadInt64(ret);
}
void cSerializerImpl::ReadString(TSTRING& ret)
{
mpArchive->ReadString(ret);
}
int cSerializerImpl::ReadBlob(void* pBlob, int count)
{
return mpArchive->ReadBlob(pBlob, count);
}
void cSerializerImpl::WriteInt16(int16 i)
{
mpArchive->WriteInt16(i);
}
void cSerializerImpl::WriteInt32(int32 i)
{
mpArchive->WriteInt32(i);
}
void cSerializerImpl::WriteInt64(int64 i)
{
mpArchive->WriteInt64(i);
}
void cSerializerImpl::WriteString(const TSTRING& s)
{
mpArchive->WriteString(s);
}
void cSerializerImpl::WriteBlob(const void* pBlob, int count)
{
mpArchive->WriteBlob(pBlob, count);
}
///////////////////////////////////////////////////////////////////////////////
// GetFileName
///////////////////////////////////////////////////////////////////////////////
TSTRING cSerializerImpl::GetFileName() const
{
return mFileName;
}