#pragma warning( push ,1) #include #include #include #include #include #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( 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 ( 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<> dwCount; return dwCount; } CObjectArchive& CObjectArchive::PushArchive(QString& strFilePathName) { if(IsStoring()) { *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 <>(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; }