// // The developer of the original code and/or files is Tripwire, Inc. // Portions created by Tripwire, Inc. are copyright (C) 2000 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. // /////////////////////////////////////////////////////////////////////////////// // blockrecordfile.cpp #include "stddb.h" #include "blockrecordfile.h" #include "core/archive.h" /////////////////////////////////////////////////////////////////////////////// // util_InitBlockArray /////////////////////////////////////////////////////////////////////////////// static inline void util_InitBlockArray( cBlockRecordArray& block ) { if( ! block.Initialized() ) { block.InitForExistingBlock(); } } /////////////////////////////////////////////////////////////////////////////// // ctor /////////////////////////////////////////////////////////////////////////////// cBlockRecordFile::cBlockRecordFile() : mLastAddedTo( -1 ), mbOpen ( false ) { } /////////////////////////////////////////////////////////////////////////////// // dtor /////////////////////////////////////////////////////////////////////////////// cBlockRecordFile::~cBlockRecordFile() { Close(); } /////////////////////////////////////////////////////////////////////////////// // Open /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::Open ( const TSTRING& fileName, int numPages, bool bTruncate) //throw (eArchive) { ASSERT( ! mbOpen ); // first, open the file.. // mBlockFile.Open( fileName, numPages, bTruncate ); } void cBlockRecordFile::Open( cBidirArchive* pArch, int numPages ) //throw (eArchive) { ASSERT( ! mbOpen ); bool bTruncate = ( pArch->Length() == 0 ); mBlockFile.Open( pArch, numPages ); OpenImpl( bTruncate ); } /////////////////////////////////////////////////////////////////////////////// // OpenImpl /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::OpenImpl( bool bTruncate ) //throw (eArchive) { ASSERT( ! mbOpen ); // // create the vector of block record arrays...avoid initializing them all // right now so that we don't page the whole file into memory // mvBlocks.clear(); for( int i=0; i < mBlockFile.GetNumBlocks(); i++ ) { mvBlocks.push_back( cBlockRecordArray( &mBlockFile, i ) ); // // if we are creating a new file, initialize the block for first usage... // if( bTruncate ) { mvBlocks.back().InitNewBlock(); } } // // use the integrity of the first block as a q&d check for file integrity... // ASSERT( mvBlocks.size() > 0 ); if(! mvBlocks.begin()->Initialized() ) { mvBlocks.begin()->InitForExistingBlock(); } if( ! mvBlocks.begin()->IsClassValid() ) { TSTRING str = _T("Bad file format for the Block Record File"); throw eArchiveFormat( str ); } // set the last item added to to zero (TODO -- is this the right thing to do or // should I store it in the archive? // mLastAddedTo = 0; mbOpen = true; } /////////////////////////////////////////////////////////////////////////////// // Close /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::Close() //throw (eArchive) { Flush(); mBlockFile.Close(); mbOpen = false; mvBlocks.clear(); } /////////////////////////////////////////////////////////////////////////////// // Flush /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::Flush() //throw (eArchive) { mBlockFile.Flush(); } /////////////////////////////////////////////////////////////////////////////// // AddItem /////////////////////////////////////////////////////////////////////////////// cBlockRecordFile::tAddr cBlockRecordFile::AddItem( int8* pData, int dataSize ) //throw (eArchive) { ASSERT( mbOpen ); #ifdef _BLOCKFILE_DEBUG AssertValid(); #endif tAddr rtn(0, 0); // // first, let's find room for this data item... // rtn.mBlockNum = FindRoomForData( dataSize ); // // now, insert the data... // rtn.mIndex = mvBlocks[rtn.mBlockNum].AddItem( pData, dataSize, 1 ); // // update the last added to pointer and return the location... // mLastAddedTo = rtn.mBlockNum; return rtn; } /////////////////////////////////////////////////////////////////////////////// // RemoveItem /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::RemoveItem( cBlockRecordFile::tAddr dataAddr ) //throw (eArchive) { ASSERT( mbOpen ); ASSERT( IsValidAddr( dataAddr ) ); #ifdef _BLOCKFILE_DEBUG AssertValid(); #endif mvBlocks[ dataAddr.mBlockNum ].DeleteItem( dataAddr.mIndex ); // // remove unneeded blocks at the end... // //TODO -- not implemented yet! /* while( mvBlocks.back().GetNumItems() == 0 ) { // if there are >2 blocks, and the back two blocks are both // empty, remove one. We always want to be sure there is at // least one block in the database, and we don't want to remove // single trailing empty blocks because it would perform poorly if // there were a bunch of additions and removals at the end of the file. if( (mvBlocks.size() > 2) && (mvBlocks[ mvBlocks.size()-2 ].GetNumItems() == 0 ) { // TODO -- uncomment these to implement file shrinking... mvBlocks.pop_back(); mBlockFile.DestroyLastBlock(); } } */ } /////////////////////////////////////////////////////////////////////////////// // IsValidAddr /////////////////////////////////////////////////////////////////////////////// bool cBlockRecordFile::IsValidAddr( cBlockRecordFile::tAddr addr ) //throw (eArchive) { ASSERT( mbOpen ); if( (addr.mBlockNum < 0) || (addr.mBlockNum >= mBlockFile.GetNumBlocks()) ) return false; util_InitBlockArray( mvBlocks[ addr.mBlockNum ] ); return ( mvBlocks[ addr.mBlockNum ].IsItemValid( addr.mIndex ) ); } /////////////////////////////////////////////////////////////////////////////// // GetDataForReading /////////////////////////////////////////////////////////////////////////////// int8* cBlockRecordFile::GetDataForReading( cBlockRecordFile::tAddr dataAddr, int32& dataSize ) //throw (eArchive) { ASSERT( mbOpen ); ASSERT( IsValidAddr( dataAddr ) ); #ifdef _BLOCKFILE_DEBUG AssertValid(); #endif return ( mvBlocks[ dataAddr.mBlockNum ].GetDataForReading( dataAddr.mIndex, dataSize ) ); } /////////////////////////////////////////////////////////////////////////////// // GetDataForWriting /////////////////////////////////////////////////////////////////////////////// int8* cBlockRecordFile::GetDataForWriting( cBlockRecordFile::tAddr dataAddr, int32& dataSize ) //throw (eArchive) { ASSERT( mbOpen ); ASSERT( IsValidAddr( dataAddr ) ); #ifdef _BLOCKFILE_DEBUG AssertValid(); #endif return ( mvBlocks[ dataAddr.mBlockNum ].GetDataForWriting( dataAddr.mIndex, dataSize ) ); } /////////////////////////////////////////////////////////////////////////////// // FindRoomForData /////////////////////////////////////////////////////////////////////////////// int cBlockRecordFile::FindRoomForData( int32 dataSize ) //throw (eArchive) { ASSERT( (dataSize > 0) && (dataSize <= cBlockRecordArray::MAX_DATA_SIZE) ); ASSERT( mbOpen ); #ifdef _BLOCKFILE_DEBUG AssertValid(); #endif cDebug d("cBlockRecordFile::FindRoomForData"); // first, try the last added to block... // d.TraceDetail( "Looking for room for %d bytes; first trying mLastAddedTo (%d)\n", dataSize, mLastAddedTo ); util_InitBlockArray( mvBlocks[mLastAddedTo] ); if( mvBlocks[mLastAddedTo].GetAvailableSpace() >= dataSize ) { d.TraceDetail( "---Found room in block %d\n", mLastAddedTo ); return mLastAddedTo; } // // ok, I guess we will have to iterate through all the blocks... // BlockArray::iterator it; int cnt = 0; for( it = mvBlocks.begin(); it != mvBlocks.end(); it++, cnt++ ) { util_InitBlockArray( *it ); if( it->GetAvailableSpace() >= dataSize ) { d.TraceDetail( "---Found room in block %d\n", cnt ); return cnt; } } // // if we got here, then we need to add a new block // d.TraceDetail( "---We need to add new block(%d)\n", cnt); mBlockFile.CreateBlock(); ASSERT( (mBlockFile.GetNumBlocks() == (mvBlocks.size()+1)) && (mvBlocks.size() == cnt) ); mvBlocks.push_back( cBlockRecordArray( &mBlockFile, cnt ) ); mvBlocks.back().InitNewBlock(); ASSERT( mvBlocks.back().GetAvailableSpace() >= dataSize ); return cnt; } #ifdef _BLOCKFILE_DEBUG /////////////////////////////////////////////////////////////////////////////// // TraceContents /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::TraceContents(int dl) const { // TODO -- this is probably not what I want to do, but it helps me right now... // for( BlockArray::const_iterator i = mvBlocks.begin(); i != mvBlocks.end(); i++ ) { i->TraceContents( dl ); } } /////////////////////////////////////////////////////////////////////////////// // AssertValid /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::AssertValid() const { ASSERT( (mLastAddedTo >= 0) && (mLastAddedTo < mvBlocks.size()) ); ASSERT( mvBlocks.size() == mBlockFile.GetNumBlocks() ); } /////////////////////////////////////////////////////////////////////////////// // AssertAllBlocksValid /////////////////////////////////////////////////////////////////////////////// void cBlockRecordFile::AssertAllBlocksValid() { for( BlockArray::iterator i = mvBlocks.begin(); i != mvBlocks.end(); i++ ) { i->AssertValid(); } } #endif