// // 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. // /////////////////////////////////////////////////////////////////////////////// // blockrecordarray.cpp #include "stddb.h" #include "blockrecordarray.h" /////////////////////////////////////////////////////////////////////////////// // util_GetIndexArray -- returns a reference to the index array inside a block // Right now, this is just a cast, but if the blocks ever had headers, this // would also take that offset into account /////////////////////////////////////////////////////////////////////////////// inline cBlockRecordArray::tIndexArray& util_GetIndexArray(cBlockFile::Block* pBlock) { return (* reinterpret_cast( pBlock->GetData() ) ); } /////////////////////////////////////////////////////////////////////////////// // util_OffsetToAddr -- given an offset from the end of a block (such as // is stored in tRecordIndex) return a pointer to memory inside the block // that corresponds to the given offset /////////////////////////////////////////////////////////////////////////////// inline int8* util_OffsetToAddr(cBlockFile::Block* pBlock, int offset) { return ( pBlock->GetData() + (cBlockFile::BLOCK_SIZE - offset) ); } /////////////////////////////////////////////////////////////////////////////// // ctor /////////////////////////////////////////////////////////////////////////////// cBlockRecordArray::cBlockRecordArray(cBlockFile* pBlockFile, int blockNum) : mpBlockFile ( pBlockFile ), mBlockNum ( blockNum ), mSpaceAvailable ( -1 ), mNumItems ( 0 ), mbInit ( false ) { } cBlockRecordArray::cBlockRecordArray() : mpBlockFile ( 0 ), mBlockNum ( -1 ), mSpaceAvailable ( -1 ), mNumItems ( 0 ), mbInit ( false ) { } void cBlockRecordArray::Init(cBlockFile* pBlockFile, int blockNum) { ASSERT(! pBlockFile ); mpBlockFile = pBlockFile; mBlockNum = blockNum; mSpaceAvailable = -1; mNumItems = 0; mbInit = false; } /////////////////////////////////////////////////////////////////////////////// // dtor /////////////////////////////////////////////////////////////////////////////// cBlockRecordArray::~cBlockRecordArray() { } /////////////////////////////////////////////////////////////////////////////// // InitNewBlock /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::InitNewBlock() //throw (eArchive) { ASSERT( mpBlockFile != 0); ASSERT( (mBlockNum >= 0) && (mBlockNum < mpBlockFile->GetNumBlocks()) ); #ifdef _BLOCKFILE_DEBUG mpBlockFile->AssertValid(); #endif cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); tIndexArray& array = util_GetIndexArray( pBlock ); // // set everything to zero.. // memset( pBlock->GetData(), 0, cBlockFile::BLOCK_SIZE ); pBlock->SetDirty(); // // set up the guard byte for the index array... // array.maRecordIndex[0].SetOffset ( -1 ); array.maRecordIndex[0].SetMainIndex ( INVALID_INDEX ); // // remember that we have been initialized // mbInit = true; // // update the free space and max index variables... // mNumItems = 0; UpdateFreeSpace( pBlock ); WriteHeaderInfo( pBlock ); } /////////////////////////////////////////////////////////////////////////////// // InitForExistingBlock /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::InitForExistingBlock() //throw (eArchive) { ASSERT( mpBlockFile != 0); ASSERT( (mBlockNum >= 0) && (mBlockNum < mpBlockFile->GetNumBlocks()) ); ASSERT( ! mbInit ); // // read in our header info... // cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); ReadHeaderInfo(pBlock); // // remember that we have been initialized // mbInit = true; // // some debug out... // cDebug d("cBlockRecordArray::InitForExistingBlock"); d.TraceDetail(" Executed for block %d; NumItems=%d, SpaceAvail=%d\n", mBlockNum, mNumItems, mSpaceAvailable); } /////////////////////////////////////////////////////////////////////////////// // WriteHeaderInfo /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::WriteHeaderInfo(cBlockFile::Block* pBlock) { ASSERT( mpBlockFile != 0); ASSERT( Initialized() ); util_GetIndexArray(pBlock).mHeader.SetNumItems ( mNumItems ); util_GetIndexArray(pBlock).mHeader.SetSpaceAvail( mSpaceAvailable ); pBlock->SetDirty(); } /////////////////////////////////////////////////////////////////////////////// // ReadHeaderInfo /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::ReadHeaderInfo (cBlockFile::Block* pBlock) { ASSERT( mpBlockFile != 0); mNumItems = util_GetIndexArray(pBlock).mHeader.GetNumItems(); mSpaceAvailable = util_GetIndexArray(pBlock).mHeader.GetSpaceAvail(); } /////////////////////////////////////////////////////////////////////////////// // UpdateFreeSpace /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::UpdateFreeSpace(cBlockFile::Block* pBlock) { ASSERT( Initialized() ); ASSERT( mpBlockFile != 0); // first, take into account the size of all of the index structures. Note that // we add one to the current number because we want to account for the new one // that might be added if another item were added to the array // int availSpace = cBlockFile::BLOCK_SIZE - sizeof(tHeader) - ( (GetNumItems() + 1) * sizeof(tRecordIndex) ); // // always take the MARGIN away, to give ourselves some breathing room between the indexes and // the data // availSpace -= MARGIN; // // subtract from availSpace the amount of room that the actual data is taking up... // note that it is always true that the top index points to a valid value // if( GetNumItems() > 0 ) { int topOffset = util_GetIndexArray(pBlock).maRecordIndex[ GetNumItems()-1 ].GetOffset(); availSpace -= topOffset; } mSpaceAvailable = availSpace < 0 ? 0 : availSpace ; #ifdef _BLOCKFILE_DEBUG AssertValid(); #endif } /////////////////////////////////////////////////////////////////////////////// // IsItemValid /////////////////////////////////////////////////////////////////////////////// bool cBlockRecordArray::IsItemValid(int index) const //throw (eArchive) { ASSERT( Initialized() ); if ( (index < 0) || (index >= GetNumItems()) ) return false; cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); return (util_GetIndexArray(pBlock).maRecordIndex[ index ].GetMainIndex() != INVALID_INDEX ); } /////////////////////////////////////////////////////////////////////////////// // AddItem /////////////////////////////////////////////////////////////////////////////// int cBlockRecordArray::AddItem(int8* pData, int dataSize, int mainIndex) //throw (eArchive) { // make ourselves initialized, if we are not right now... // if( ! Initialized() ) { InitForExistingBlock(); } ASSERT( dataSize <= GetAvailableSpace() ); ASSERT( GetNumItems() <= MAX_RECORDS ); ASSERT( dataSize > 0 ); // I am not sure if this is the right thing to do ! ASSERT( mpBlockFile != 0); ASSERT( mainIndex != INVALID_INDEX ); ASSERT( Initialized() ); #ifdef _BLOCKFILE_DEBUG AssertValid(); mpBlockFile->AssertValid(); #endif cDebug d("cBlockRecordArray::AddItem"); d.TraceDetail( "Entering... adding item of size %d to block %d\n", dataSize, mBlockNum ); // // get the block we need and a reference into the index array.... // cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); tIndexArray& indexArray = util_GetIndexArray(pBlock); // // find an unused entry to put the item into... // tRecordIndex* pIndex = indexArray.maRecordIndex; int newIndex = 0; for( ; newIndex < mNumItems; newIndex++, pIndex++ ) { if( pIndex->GetMainIndex() == INVALID_INDEX ) break; } d.TraceDetail( "Adding new item to index %d (max index + 1 = %d)\n", newIndex, mNumItems ); // write the data we know for sure at this point... // pIndex->SetMainIndex( mainIndex ); pBlock->SetDirty(); // // calculate the offset for the new data // int prevOffset = ( newIndex == 0 ? 0 : indexArray.maRecordIndex[ newIndex-1 ].GetOffset() ); pIndex->SetOffset( prevOffset + dataSize ); if( newIndex == mNumItems ) { // // increment the max number of indexes... // mNumItems++; indexArray.maRecordIndex[ mNumItems ].SetOffset ( -1 ); indexArray.maRecordIndex[ mNumItems ].SetMainIndex ( INVALID_INDEX ); } else { // we are going to have to shift up the data that is above us... // int topOffset = indexArray.maRecordIndex[GetNumItems()-1].GetOffset(); int8* pTop = util_OffsetToAddr( pBlock, topOffset ); int amtToMove = topOffset - prevOffset; ASSERT( amtToMove >= 0 ); memmove( pTop - dataSize, pTop, amtToMove ); // // now, we need to iterate over all of the existing entries, updating their offsets... // tRecordIndex* pMovedIndex = pIndex + 1; for( int i = newIndex + 1; i < mNumItems; i++, pMovedIndex++ ) { pMovedIndex->SetOffset( pMovedIndex->GetOffset() + dataSize ); } } // // write the data... // memcpy( util_OffsetToAddr( pBlock, pIndex->GetOffset() ), pData, dataSize ); // // update the free space... // indexArray.maRecordIndex[ mNumItems ].SetOffset ( -1 ); indexArray.maRecordIndex[ mNumItems ].SetMainIndex ( INVALID_INDEX ); UpdateFreeSpace( pBlock ); WriteHeaderInfo( pBlock ); #ifdef _BLOCKFILE_DEBUG AssertValid(); mpBlockFile->AssertValid(); d.TraceDetail( "*** Add Complete; here are the new contents ***\n"); TraceContents( cDebug::D_NEVER ); #endif return newIndex; } /////////////////////////////////////////////////////////////////////////////// // DeleteItem /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::DeleteItem(int index) //throw (eArchive) { // make ourselves initialized, if we are not right now... // if( ! Initialized() ) { InitForExistingBlock(); } ASSERT( mpBlockFile != 0); ASSERT( IsItemValid(index) ); ASSERT( Initialized() ); #ifdef _BLOCKFILE_DEBUG AssertValid(); mpBlockFile->AssertValid(); #endif cDebug d("cBlockRecordArray::DeleteItem"); d.TraceDetail( "Entering... Removing item index %d from block %d\n", index, mBlockNum); // // get the block we need and a reference into the index array.... // cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); tIndexArray& indexArray = util_GetIndexArray(pBlock); pBlock->SetDirty(); // // if this is the last entry, then our job is easy... // if( (index + 1) == mNumItems ) { mNumItems--; // we need to clean out all the entries that are empty at the end of the index list... // while( (mNumItems > 0) && (indexArray.maRecordIndex[ mNumItems-1 ].GetMainIndex() == INVALID_INDEX) ) { mNumItems--; } // // set the guard bytes... // indexArray.maRecordIndex[ mNumItems ].SetOffset ( -1 ); indexArray.maRecordIndex[ mNumItems ].SetMainIndex ( INVALID_INDEX ); } else { // we are going to shift all entries above the deleted entry down; // first, figure out how far we are shifting and the source address... // int sizeToShift = indexArray.maRecordIndex[GetNumItems()-1].GetOffset() - indexArray.maRecordIndex[index].GetOffset(); int distToShift = indexArray.maRecordIndex[index].GetOffset(); if( index > 0 ) { distToShift -= indexArray.maRecordIndex[index - 1].GetOffset(); } int8* pSrc = util_OffsetToAddr( pBlock, indexArray.maRecordIndex[GetNumItems()-1].GetOffset() ); // // copy the data.. // ASSERT ( pBlock->IsValidAddr(pSrc) && pBlock->IsValidAddr(pSrc + distToShift + sizeToShift - 1) ); ASSERT ( sizeToShift >= 0 ); ASSERT ( distToShift >= 0 ); memmove ( pSrc + distToShift, pSrc, sizeToShift ); // // mark this entry as unused... // note that the offset is set to be the same as the item before it. // tRecordIndex* pIndex = &indexArray.maRecordIndex[ index ]; pIndex->SetMainIndex ( INVALID_INDEX ); pIndex->SetOffset ( (index == 0 ? 0 : indexArray.maRecordIndex[ index-1 ].GetOffset()) ); // // now, iterate over the items, moving their offsets down... // pIndex++; for( int i = index+1; i < GetNumItems(); i++, pIndex++ ) { pIndex->SetOffset( pIndex->GetOffset() - distToShift ); } } UpdateFreeSpace( pBlock ); WriteHeaderInfo( pBlock ); #ifdef _BLOCKFILE_DEBUG AssertValid(); mpBlockFile->AssertValid(); d.TraceDetail( "*** Removal Complete; here are the new contents ***\n"); TraceContents( cDebug::D_NEVER ); #endif } /////////////////////////////////////////////////////////////////////////////// // GetDataForReading /////////////////////////////////////////////////////////////////////////////// int8* cBlockRecordArray::GetDataForReading( int index, int32& dataSize ) //throw (eArchive) { // make ourselves initialized, if we are not right now... // if( ! Initialized() ) { InitForExistingBlock(); } ASSERT( IsItemValid(index) ); cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); tIndexArray& indexArray = util_GetIndexArray(pBlock); // // figure out the size of the block // dataSize = indexArray.maRecordIndex[index].GetOffset(); if( index != 0 ) { dataSize -= indexArray.maRecordIndex[index-1].GetOffset(); } #ifdef _BLOCKFILE_DEBUG AssertValid(); mpBlockFile->AssertValid(); #endif return( util_OffsetToAddr(pBlock, indexArray.maRecordIndex[index].GetOffset()) ); } /////////////////////////////////////////////////////////////////////////////// // GetDataForWriting /////////////////////////////////////////////////////////////////////////////// int8* cBlockRecordArray::GetDataForWriting( int index, int32& dataSize ) //throw (eArchive) { // make ourselves initialized, if we are not right now... // if( ! Initialized() ) { InitForExistingBlock(); } ASSERT( IsItemValid(index) ); cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); tIndexArray& indexArray = util_GetIndexArray(pBlock); pBlock->SetDirty(); // // figure out the size of the block // dataSize = indexArray.maRecordIndex[index].GetOffset(); if( index != 0 ) { dataSize -= indexArray.maRecordIndex[index-1].GetOffset(); } #ifdef _BLOCKFILE_DEBUG AssertValid(); mpBlockFile->AssertValid(); #endif return( util_OffsetToAddr(pBlock, indexArray.maRecordIndex[index].GetOffset()) ); } /////////////////////////////////////////////////////////////////////////////// // IsClassValid /////////////////////////////////////////////////////////////////////////////// #define BRA_ASSERT(x) if(x) ; else { ASSERT(false); return false; } bool cBlockRecordArray::IsClassValid() const { ASSERT( mpBlockFile != 0); cDebug d( "cBlockRecordArray::AssertValid" ); if( ! mbInit ) { // if we haven't been initialized yet, then I assume that we are valid... // return true; } // // get the block we need and a reference into the index array.... // try { cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); tIndexArray& array = util_GetIndexArray(pBlock); // // make sure that the num items and available space make sense... // BRA_ASSERT( (mNumItems >= 0) && (mNumItems <= MAX_RECORDS) ); BRA_ASSERT( (mSpaceAvailable >= 0) && (mSpaceAvailable <= MAX_DATA_SIZE) ); // // assert that the top index points to a valid value... // if(mNumItems != 0) { BRA_ASSERT( array.maRecordIndex[mNumItems-1].GetMainIndex() != INVALID_INDEX ); BRA_ASSERT( array.maRecordIndex[mNumItems-1].GetOffset() != -1 ); } int prevOff = 0; int i; for( i = 0; i < mNumItems; i++ ) { BRA_ASSERT( array.maRecordIndex[i].GetOffset() >= prevOff ); prevOff = array.maRecordIndex[i].GetOffset(); } // make sure the guard byte is there... // BRA_ASSERT( array.maRecordIndex[i].GetOffset() == -1 ); // // make sure the final offset is less than the highest record index offset. // BRA_ASSERT( (int8*)&array.maRecordIndex[i] < util_OffsetToAddr( pBlock, prevOff ) ); // // TODO -- is there anything else that is worth checking? } catch( eArchive& e ) { d.TraceError( "*** cBlockFile::GetBlock threw an eArchive: %d %s\n", e.GetID(), e.GetMsg().c_str() ); BRA_ASSERT( false ); } // if we got here, then everything worked ok. // return true; } #ifdef _BLOCKFILE_DEBUG /////////////////////////////////////////////////////////////////////////////// // TraceContents /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::TraceContents(int dl) const { if( dl == -1 ) dl = cDebug::D_DEBUG; cDebug d( "cBlockRecordArray::TraceContents" ); d.Trace( dl, "---------- Block Record Array ----------\n" ); d.Trace( dl, "----------------------------------------\n" ); d.Trace( dl, "Block Number %d\n", mBlockNum ); d.Trace( dl, "Space Available %d\n", mSpaceAvailable ); d.Trace( dl, "Num Entries %d\n", mNumItems); cBlockFile::Block* pBlock = mpBlockFile->GetBlock( mBlockNum ); tIndexArray& indexArray = util_GetIndexArray(pBlock); for( int i=0; i < mNumItems; i++ ) { d.Trace( dl, "(%d)\tOffset %d\tMain Idx %d\n", i, indexArray.maRecordIndex[i].GetOffset(), indexArray.maRecordIndex[i].GetMainIndex() ); } d.Trace( dl, "----------------------------------------\n" ); } /////////////////////////////////////////////////////////////////////////////// // AssertValid /////////////////////////////////////////////////////////////////////////////// void cBlockRecordArray::AssertValid() const { ASSERT( IsClassValid() ); } #endif