logplus/logPlus/ObjectArchive.cpp
2025-10-29 17:23:30 +08:00

885 lines
16 KiB
C++

#pragma warning( push ,1)
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <QMessageBox>
#include <QFile>
#include "ObjectArchive.h"
#pragma warning( pop )
#define Q_ASSERT_IS_LOAD() assert( IsLoading() && m_pDevice );
#define Q_ASSERT_IS_STORING() assert( IsStoring() && m_pDevice );
int g_nSerializeErrorMsgBoxMaxCount = 0;
void trimspace(char *p)
{
if (p == NULL || strlen(p) == 0)
return;
while (p[strlen(p)-1] == '\n')
{
p[strlen(p)-1] = '\0';
if (strlen(p) == 0)
return;
}
}
QByteArray* CObjectArchive::GetByteArray()
{
if( m_pDevice )
{
QBuffer *pBuffer = dynamic_cast<QBuffer *>( m_pDevice );
if( pBuffer )
{
return &(pBuffer->buffer());
}
}
return NULL;
}
CObjectArchive::CObjectArchive( QByteArray& byteArray,EGmArchiveMode eMode )
{
// new buffer
QBuffer *pBuffer = new QBuffer();
pBuffer->setBuffer( &byteArray );
pBuffer->open( eMode ==eStore ? QIODevice::WriteOnly: QIODevice::ReadOnly );
// set device
setDevice( pBuffer );
m_pDevice = pBuffer;
// set mode
m_eMode = eMode;
m_bQFileOpenedByMe = true;
// have opened
m_bOpened = true;
InitArchivePosData();
}
CObjectArchive::CObjectArchive( QString sFile, EGmArchiveMode eMode,qint64 nWriteOffSet )
{
filename=sFile;
QFile *pFile=new QFile;
pFile->setFileName(sFile);
m_bOpened = true;
if(eMode == eLoad)
{
if(!pFile->open(QIODevice::ReadOnly))
{
m_bOpened=false;
}
}
else if(eMode == eStore)
{
QIODevice::OpenMode mode =QIODevice::Truncate;
if( nWriteOffSet == 0 )
{
mode =QIODevice::Truncate;
}else if( nWriteOffSet<0 )
{
mode =QIODevice::Append;
}else
{
// extend file size
if(QFile::exists( sFile ) )
{
QFile file( sFile);
if( file.open( QIODevice::ReadWrite ) )
{
bool bFileSizeOK = ( file.size() >= (nWriteOffSet-1) );
assert( bFileSizeOK );
if( !bFileSizeOK )
{
file.resize( nWriteOffSet -1);
}
}
}
}
mode = static_cast<QIODevice::OpenMode > ( mode | QIODevice::WriteOnly );
if(!pFile->open(mode) )
{
m_bOpened=false;
}
}
setDevice(pFile);
m_pDevice = pFile;
m_eMode = eMode;
m_bQFileOpenedByMe=true;
InitArchivePosData();
}
bool CObjectArchive::IsOpened()
{
return m_bOpened;
}
void CObjectArchive::Attatch(QIODevice *pDevice,EGmArchiveMode eMode )
{
Close();
QDataStream::setDevice( pDevice );
m_pDevice = pDevice;
m_eMode = eMode;
m_bQFileOpenedByMe=false;
InitArchivePosData();
}
CObjectArchive::~CObjectArchive()
{
Close();
}
bool CObjectArchive::IsLoading()
{
return (m_eMode==eLoad);
}
bool CObjectArchive::IsStoring()
{
return (m_eMode==eStore);
}
QIODevice* CObjectArchive::GetDevice()
{
return m_pDevice;
}
void CObjectArchive::Read(void* lpBuf, unsigned int nMax)
{
Q_ASSERT_IS_LOAD();
char * pBuf= NULL;
QDataStream::readBytes (pBuf, nMax);
if(pBuf!=NULL)
{
memcpy(lpBuf, pBuf, nMax);
delete [] pBuf;
}
}
void CObjectArchive::Write(const void* lpBuf, unsigned int nMax)
{
Q_ASSERT_IS_STORING();
QDataStream::writeBytes((char*)lpBuf, nMax);
}
void CObjectArchive::Close()
{
if(m_pDevice == NULL) return;
filename="";
assert( IsWriteDataOK() );
if(m_bQFileOpenedByMe)
{
m_pDevice->close();
delete m_pDevice;
}
m_pDevice = NULL;
}
char * CObjectArchive::ReadString(char * lpsz, unsigned int nMax)
{
Q_ASSERT_IS_LOAD();
// if nMax is negative (such a large number doesn't make sense given today's
// 2gb address space), then assume it to mean "keep the newline".
int nStop = (int)nMax < 0 ? -(int)nMax : (int)nMax;
unsigned char ch = 'a';
int nRead = 0;
try
{
while (nRead < nStop)
{
if(nRead == m_currLength) throw ch;
*this >> ch;
// stop and end-of-line (trailing '\n' is ignored)
if (ch == '\n')
{
// store the newline when called with negative nMax
if ((int)nMax != nStop)
lpsz[nRead++] = ch;
break;
}
lpsz[nRead++] = ch;
// if((m_pDevice->atEnd()))
// {
// qDebug("End of file!");
// throw ch;
// }
}
}
catch(unsigned char e)
{
e;
}
lpsz[nRead] = '\0';
return lpsz;
}
//CObjectArchive & CObjectArchive::operator>> ( QString & s )
//{
// return *this ::>> s;
//}
int CObjectArchive::ReadString(QString& rString)
{
Q_ASSERT_IS_LOAD();
// empty string without deallocating
const int nMaxSize = 128;
// char * lpsz = (char *)rString.data();
char * lpsz = new char[nMaxSize];
memset(lpsz, 0, nMaxSize);
char * lpszResult;
char *tmp = lpsz;
int nLen = 0;
int memSize = nMaxSize;
for (;;)
{
// memset(lpsz, 0, nLen);
lpszResult = ReadString(lpsz, (unsigned int)-nMaxSize); // store the newline
// if string is read completely or EOF
if (lpszResult == NULL ||
(nLen = ( int )strlen(lpsz)) < nMaxSize ||
lpsz[nLen-1] == '\n')
{
break;
}
lpsz += nLen;
m_currLength -= nLen;
memSize += nLen;
tmp =(char *) realloc(tmp , memSize );
memset(lpsz, 0, nLen);
}
// remove '\n' from end of string if present
trimspace(tmp);
lpszResult = tmp;
rString = QString(lpszResult);
// delete []tmp;
return lpszResult != NULL;
}
void CObjectArchive::WriteString(char * lpsz)
{
Q_ASSERT_IS_STORING();
QString str = lpsz;
*this<<str;
}
void CObjectArchive::WriteCount(unsigned long dwCount)
{
Q_ASSERT_IS_STORING();
*this<<dwCount;
}
unsigned long CObjectArchive::ReadCount()
{
Q_ASSERT_IS_LOAD();
unsigned long dwCount;
*this >> dwCount;
return dwCount;
}
CObjectArchive& CObjectArchive::PushArchive(QString& strFilePathName)
{
if(IsStoring())
{
*this<<strFilePathName;
}
else
{
*this>>strFilePathName;
}
CObjectArchive* pArchive2=new CObjectArchive(strFilePathName,m_eMode);
return pArchive2->IsOpened()?*pArchive2:*this;
}
void CObjectArchive::PopArchive(CObjectArchive& archive2)
{
if(&archive2 == this)return;
archive2.Close();
}
CObjectArchive& CObjectArchive::operator<<(int i)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<i;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(bool b )
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<b;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(unsigned int u)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<u;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(short w)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<w;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(char ch)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<(qint8)ch;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(unsigned char by)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<by;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(unsigned short w)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<w;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(long l)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<(qint64)l;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(unsigned long dw)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<(quint64)dw;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(float f)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<f;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(double d)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<d;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(qint64 n)
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<n;
return *this;
}
CObjectArchive& CObjectArchive::operator<<(const QString & s )
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this <<s;
return *this;
}
// extraction operations
CObjectArchive& CObjectArchive::operator>>(int& i)
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>i;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(bool & b )
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>b;
return *this;
}
CObjectArchive& CObjectArchive::operator>>( unsigned int & u )
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>u;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(short& w)
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>w;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(char& ch)
{
Q_ASSERT_IS_LOAD();
qint8 tch;
*(QDataStream*)this >>tch;
ch =(char)tch;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(unsigned char& by)
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>by;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(unsigned short& w)
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>w;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(unsigned long & dw)
{
Q_ASSERT_IS_LOAD();
quint64 ul;
*(QDataStream*)this >>ul;//(quint64&)dw;
dw = (unsigned long)ul;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(qint64 &n)
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>n;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(float& f)
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>f;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(double& d)
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>d;
return *this;
}
CObjectArchive& CObjectArchive::operator>>(long& l)
{
Q_ASSERT_IS_LOAD();
qint64 qltmp;
*(QDataStream*)this >>qltmp;
l = (long)qltmp;
return *this;
}
CObjectArchive& CObjectArchive::operator>>( QString & s )
{
Q_ASSERT_IS_LOAD();
*(QDataStream*)this >>s;
return *this;
}
CObjectArchive& CObjectArchive::operator>>( QAction *pAction )
{
Q_ASSERT_IS_LOAD();
bool bChecked ;
*(QDataStream*)this >> bChecked;
if( pAction )
{
pAction->setChecked( bChecked );
}
return *this;
}
CObjectArchive& CObjectArchive::operator<<( QAction *pAction )
{
Q_ASSERT_IS_STORING();
bool bChecked = true;
if( pAction )
{
bChecked = pAction->isChecked();
}
*(QDataStream*)this << bChecked ;
return *this;
}
void CObjectArchive::ReadBlock ( unsigned short &blockcid ,unsigned int &datasize )
{
( *this ) >> blockcid; //read block Id
( *this ) >> datasize; //read block data size
}
void CObjectArchive::ReadObject( unsigned short& nBlockNumber,unsigned short &ver )
{
( *this ) >> ver ; // read version
( *this ) >> nBlockNumber; // read block number
}
void CObjectArchive::BeginWriteBlock( unsigned short id , const void *pThis )
{
#ifndef NDEBUG
m_nBlockNumber++ ; // block number
#endif
/**
* set current object
*/
SetActiveObject( pThis );
/**
* assert this block is not same to previousely block id
*/
Q_ASSERTBlockID( id,false );
#ifndef NDEBUG
m_pCurObjectPos->m_setBlockId.insert( id );
#endif
/**
* write block id
*/
( *this )<< id ;
/**
* record "block size" postion to "m_pCurObjectPos->m_uCurrentBlockSizePos"
*/
GetCurPos( m_pCurObjectPos->m_uCurrentBlockSizePos );
/**
* write block data size
*/
unsigned int nSize = 0 ;
( *this )<< nSize ;
/**
* record "block data" postion to "m_pCurObjectPos->m_uCurrentBlockDataPos"
*/
GetCurPos( m_pCurObjectPos->m_uCurrentBlockDataPos );
/**
* accumulate the block Number
*/
++( m_pCurObjectPos->m_nBlockNumInObject );
}
void CObjectArchive::EndWriteBlock( unsigned short id , const void *pThis )
{
id;
#ifndef NDEBUG
m_nBlockNumber-- ; // block number
#endif
/**
* set current object
*/
SetActiveObject( pThis ) ;
/**
* assert the block id have already exist
*/
Q_ASSERTBlockID( id,true );
/**
* calculate block data size
*/
unsigned int nSize = 0;
{
qint64 uSizeNow ;
GetCurPos( uSizeNow );
nSize = ( unsigned int )( uSizeNow - m_pCurObjectPos->m_uCurrentBlockDataPos );
}
/**
* write block data size
*/
WriteSize( m_pCurObjectPos->m_uCurrentBlockSizePos, &nSize, NULL );
}
void CObjectArchive::BeginWriteObject( const void *pThis,unsigned short ver )
{
#ifndef NDEBUG
m_nPackNumber++ ; // block nuber
#endif
( *this ) << ver; //write version
/**
* insert the object and set current object
*/
m_mapObjectPos[ pThis ] = CObjectArchivePos();
SetActiveObject( pThis );
/**
* record "block number" pos to m_pCurObjectPos->m_uBlockNumPos
*/
GetCurPos( m_pCurObjectPos->m_uBlockNumPos );
/**
* temporary write block number ,later,EndWriteObject will correct the value
*/
m_pCurObjectPos->m_nBlockNumInObject = 0;
( *this )<< m_pCurObjectPos->m_nBlockNumInObject;
}
void CObjectArchive::EndWriteObject( const void *pThis )
{
#ifndef NDEBUG
m_nPackNumber-- ; // block number
#endif
/**
* set active Object
*/
SetActiveObject( pThis );
/**
* write block number
*/
WriteSize( m_pCurObjectPos->m_uBlockNumPos, NULL ,&m_pCurObjectPos->m_nBlockNumInObject );
/**
* erase the object
*/
std::map< const void *,CObjectArchivePos >::iterator it = m_mapObjectPos.find( pThis );
m_mapObjectPos.erase( it );
/**
* invalidate current object
*/
m_pCurObjectPos = NULL;
}
void CObjectArchive::SeekPos( int Pos ,SEEKFROM from )
{
/**
* calculate the really position accord with form
*/
if( from == SEEKFROM_CUR )
{
Pos = ( int )( m_pDevice->pos() + Pos );
}
/**
* seek to pos
*/
m_pDevice->seek( Pos );
}
void CObjectArchive::SetActiveObject( const void *pThis )
{
/**
* find the "pThis " object in map
*/
std::map< const void * ,CObjectArchivePos >::iterator it = m_mapObjectPos.find( pThis );
m_pCurObjectPos = ( it != m_mapObjectPos.end() ? &it->second : NULL );
assert( m_pCurObjectPos != NULL );
}
inline void CObjectArchive::GetCurPos( qint64 &pos )
{
pos = m_pDevice->pos();
};
inline void CObjectArchive::WriteSize ( const qint64 &pos , const unsigned int *pDwordSize,unsigned short *pShortSize )
{
assert( ( pDwordSize || pShortSize ) && !( pDwordSize && pShortSize ) );
/**
* record the current Position
*/
qint64 uSizeNow ;
GetCurPos( uSizeNow );
/**
* seek to Position
*/
m_pDevice->seek( pos);
/**
* write data size
*/
if( pDwordSize )
{
( *this ) << *pDwordSize;
}else if( pShortSize )
{
( *this ) << *pShortSize;
}
/**
* restore previously Position
*/
m_pDevice->seek( uSizeNow );
};
#ifndef NDEBUG
inline void CObjectArchive::Q_ASSERTBlockID( unsigned short id ,bool bHave)
{
/**
* when debug ,Assert the block is is alread existence
*/
bool bHaveThis = m_pCurObjectPos && ( m_pCurObjectPos->m_setBlockId.find( id ) != m_pCurObjectPos->m_setBlockId.end() );
assert( bHave ? bHaveThis : !bHaveThis );
};
#else
inline void CObjectArchive::Q_ASSERTBlockID( unsigned short id,bool bHave )
{
/**
* when release , do nothing
*/
id;
bHave;
};
#endif
bool CObjectArchive::IsWriteDataOK( )
{
#ifndef NDEBUG
/**
* aftern Archive data, if "m_nBlockNumber ==0 && m_nPackNumber ==0 ",
* then ,"begin object " match "end object ","beginBlock" match "endBlokc",otherwise , some Coding errors occur
*/
return m_nBlockNumber ==0 && m_nPackNumber ==0 ;
#else
return true; //when release , do nothing
#endif
}
void CObjectArchive::InitArchivePosData()
{
#ifndef NDEBUG
m_nBlockNumber = 0 ;
m_nPackNumber = 0 ;
#endif
m_pCurObjectPos = NULL;
m_nObjectID = 0;
// set Qt Datastream version
setVersion(QDataStream::Qt_4_3);
}
void CObjectArchive::SkipObject()
{
/**
* read version and block number
*/
unsigned short nBlockSize = 0;
unsigned short ver = 1;
ReadObject( nBlockSize,ver );
/**
* loop all block
*/
for( int i = 0;i < nBlockSize ; ++i )
{
/**
* read block id and block data size
*/
unsigned short id = 0 ;
unsigned int size = 0 ;
ReadBlock( id,size ) ;
/**
* skip the block data
*/
m_pDevice->seek( m_pDevice->pos()+ size );
}
}
bool CObjectArchive::WriteEmptyObject( )
{
/**
* write version( 1 ) and block Nubmer ( 0 )
* thus ,when lower version software read higher file,will cause read nothing
*/
void *p = (void *)CreateNewObjectID();
BeginWriteObject( p,1 );
EndWriteObject( p );
return true;
}
bool CObjectArchive::ReadEmptyObject( )
{
/**
* skip read the object
*/
this->SkipObject();
return true;
}
qint64 CObjectArchive::CreateNewObjectID()
{
++m_nObjectID;
return m_nObjectID;
}
void CObjectArchive::RollbackWriteObject( int nObjectBeginPos )
{
/**
* seek to the object's first pos ,then write a emtpy object
*/
m_pDevice->seek( nObjectBeginPos );
WriteEmptyObject();
}
void CObjectArchive::RollbackReadObject( int nObjectBeginPos )
{
/**
* seek to the object's first pos ,then skip the object
*/
m_pDevice->seek(nObjectBeginPos);
SkipObject();
}
CObjectArchive& CObjectArchive::operator<<( QUuid id )
{
Q_ASSERT_IS_STORING();
*(QDataStream*)this << id.toString();
return *this;
}
CObjectArchive& CObjectArchive::operator>>(QUuid &id )
{
Q_ASSERT_IS_LOAD();
QString strID;
*(QDataStream*)this >> strID;
id = strID;
return *this;
}