// // 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(pObj))) == 0) { // it is not in the table yet; add it and serialize the object idx = mRefCtObjTbl.Add(static_cast(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(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; }