diff --git a/ftk/src/ftkdynbtree.cpp b/ftk/src/ftkdynbtree.cpp new file mode 100644 index 0000000..feabf9f --- /dev/null +++ b/ftk/src/ftkdynbtree.cpp @@ -0,0 +1,1202 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for the F_FixedBlk, F_BtreeRoot, F_BtreeLeaf, +// F_BtreeNonLeaf, and F_BtreeBlk classes. +// +// Tabs: 3 +// +// Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id$ +//------------------------------------------------------------------------------ + +#include "ftksys.h" +#include "ftkdynrset.h" + +// Make sure that the extension is in lower case characters. + +#define FRSET_FILENAME_EXTENSION "frs" + +/**************************************************************************** + +Organization: + + These modules are orgianized by function and NOT by class. + For example, all of the searchEntry modules are together. + All of the split and getFirst/next/last... modules are together. + +****************************************************************************/ + + +/**************************************************************************** + Constructors and Setup Methods +****************************************************************************/ + +/**************************************************************************** +Desc: Set common variables +****************************************************************************/ +F_FixedBlk::F_FixedBlk() +{ + m_fnCompare = NULL; + m_pvUserData = NULL; + m_uiPosition = DYNSSET_POSITION_NOT_SET; + m_bDirty = FALSE; + m_pucBlkBuf = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_BtreeRoot::F_BtreeRoot() +{ + int i; + + m_pFileHdl = NULL; + m_pszFileName = NULL; + m_eBlkType = ACCESS_BTREE_ROOT; + m_uiEntryOvhd = 4; + m_uiLRUCount = 1; + m_uiLevels = 1; + m_uiNewBlkAddr = 0; + m_uiHighestWrittenBlkAddr = 0; + m_uiTotalEntries = 0; + + // Initialize the cache blocks. + for( i = 0; i < FBTREE_CACHE_BLKS; i++) + { + m_CacheBlks[i].uiBlkAddr = 0xFFFFFFFF; + m_CacheBlks[i].uiLRUValue = 0; + m_CacheBlks[i].pBlk = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_BtreeRoot::~F_BtreeRoot() +{ + int i; + + closeFile(); + + for( i = 0; i < FBTREE_CACHE_BLKS; i++) + { + if( m_CacheBlks[i].pBlk) + { + m_CacheBlks[i].pBlk->Release(); + } + } +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE F_BtreeLeaf::setup( + FLMUINT uiEntrySize) +{ + RCODE rc = NE_FLM_OK; + + if (RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + { + goto Exit; + } + + m_uiEntrySize = uiEntrySize; + m_pvUserData = (void *) uiEntrySize; + reset( ACCESS_BTREE_LEAF); + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + reset( ACCESS_BTREE_LEAF); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE F_BtreeNonLeaf::setup( + FLMUINT uiEntrySize) +{ + RCODE rc = NE_FLM_OK; + + if (RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + { + goto Exit; + } + + m_uiEntrySize = uiEntrySize; + m_pvUserData = (void *) uiEntrySize; + reset( ACCESS_BTREE_NON_LEAF); + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE F_BtreeRoot::setup( + FLMUINT uiEntrySize, + char * pszFileName) +{ + RCODE rc; + + if (RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + { + goto Exit; + } + + m_uiEntrySize = uiEntrySize; + m_pvUserData = (void *) uiEntrySize; + reset( ACCESS_BTREE_ROOT); + m_pszFileName = pszFileName; + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup the block as a new block +****************************************************************************/ +void F_BtreeBlk::reset( + eDynRSetBlkTypes eBlkType) +{ + m_eBlkType = eBlkType; + + if (eBlkType == ACCESS_BTREE_ROOT || eBlkType == ACCESS_BTREE_NON_LEAF) + { + m_uiEntryOvhd = 4; + } + else + { + m_uiEntryOvhd = 0; + } + m_uiNumSlots = (DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)) / + ( m_uiEntrySize + m_uiEntryOvhd); + entryCount( 0); + m_uiPosition = DYNSSET_POSITION_NOT_SET; + m_bDirty = FALSE; +} + +/**************************************************************************** +Desc: Return the next entry in the result set. If the result set + is not positioned then the first entry will be returned. +****************************************************************************/ +RCODE F_BtreeBlk::getNext( + void * pvEntryBuffer) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiPos = m_uiPosition; + + // Position to the next/first entry. + + if (uiPos == DYNSSET_POSITION_NOT_SET) + { + uiPos = 0; + } + else + { + if (++uiPos > entryCount()) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + } + f_memcpy( pvEntryBuffer, ENTRY_POS(uiPos), m_uiEntrySize); + m_uiPosition = uiPos; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Return the last entry in the result set. +****************************************************************************/ +RCODE F_BtreeBlk::getLast( + void * pvEntryBuffer) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiPos = entryCount(); + + // Position to the next/first entry. + + if (uiPos == 0) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + uiPos--; + f_memcpy( pvEntryBuffer, ENTRY_POS(uiPos), m_uiEntrySize); + m_uiPosition = uiPos; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Search a btree. Position for get* or for insert. +****************************************************************************/ +RCODE F_BtreeRoot::search( + void * pvEntry, + void * pvFoundEntry) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiCurLevel = m_uiLevels - 1; // Min 2 levels + FLMUINT uiBlkAddr; + + // Reset the stack - only needed for debugging. + //f_memset( m_BTStack, 0, sizeof(F_BtreeBlk *) * FBTREE_MAX_LEVELS); + + // Search this root block. + + m_BTStack[ uiCurLevel] = this; + (void) searchEntry( pvEntry, &uiBlkAddr); + + while( uiCurLevel--) + { + // Read the next block and place at uiCurLevel (backwards from FS). + + if( RC_BAD( rc = readBlk( uiBlkAddr, + uiCurLevel ? ACCESS_BTREE_NON_LEAF : ACCESS_BTREE_LEAF, + &m_BTStack[ uiCurLevel] ))) + { + goto Exit; + } + + // Set the rc - only for the leaf block, otherwise rc should be ignored. + + rc = m_BTStack[ uiCurLevel]->searchEntry( pvEntry, &uiBlkAddr, + uiCurLevel ? NULL : pvFoundEntry); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Search a single block tree. Position for get* or for insert. + Do a binary search on all of the entries to find a match. + If no match then position to the entry where an insert + will take place. +****************************************************************************/ +RCODE F_BtreeBlk::searchEntry( + void * pvEntry, + FLMUINT * puiChildAddr, + void * pvFoundEntry) +{ + RCODE rc = RC_SET( NE_FLM_NOT_FOUND); + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblSize; + FLMINT iCompare; + + // check for zero entries. + + if (!entryCount()) + { + uiMid = 0; + goto Exit; + } + uiHigh = uiTblSize = entryCount() - 1; + uiLow = 0; + for(;;) + { + uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 + + // Use compare routine + + if (m_fnCompare) + { + iCompare = m_fnCompare( pvEntry, ENTRY_POS( uiMid), m_pvUserData); + } + else + { + iCompare = f_memcmp( pvEntry, ENTRY_POS( uiMid), m_uiEntrySize); + } + + if (iCompare == 0) + { + if (pvFoundEntry) + { + f_memcpy( pvFoundEntry, ENTRY_POS( uiMid), m_uiEntrySize); + } + rc = NE_FLM_OK; + goto Exit; + } + + // Check if we are done - where wLow equals uiHigh or mid is at end. + + if (iCompare < 0) + { + if (uiMid == uiLow || uiLow == uiHigh) + { + break; + } + uiHigh = uiMid - 1; // Too high + } + else + { + if (uiMid == uiHigh || uiLow == uiHigh) + { + + // Go up one for the correct position? + + uiMid++; + break; + } + uiLow = uiMid + 1; // Too low + } + } + +Exit: + + m_uiPosition = uiMid; + if (puiChildAddr && blkType() != ACCESS_BTREE_LEAF) + { + if (uiMid == entryCount()) + { + *puiChildAddr = lemBlk(); + } + else + { + FLMBYTE * pucChildAddr = ENTRY_POS(uiMid) + m_uiEntrySize; + *puiChildAddr = (FLMUINT)FB2UD( pucChildAddr); + } + } + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the btree - should be positioned +****************************************************************************/ +RCODE F_BtreeRoot::insert( + void * pvEntry) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiCurLevel; + FLMBYTE ucEntryBuf[FBTREE_MAX_LEVELS][DYNSSET_MAX_FIXED_ENTRY_SIZE]; + FLMUINT uiNewBlkAddr; + + if (RC_OK( rc = m_BTStack[0]->insert( pvEntry))) + { + goto Exit; + } + + // Failed to input at the left level. Do block split(s). + // This is an iterative and NOT a recursive split algorithm. + // The debugging, and cases to test should be lots easier this way. + + f_memcpy( ucEntryBuf[0], pvEntry, m_uiEntrySize); + uiCurLevel = 0; + uiNewBlkAddr = FBTREE_END; + for(;;) + { + + // Split while adding the element. + + if (RC_BAD( rc = (m_BTStack[uiCurLevel])->split( + this, + ucEntryBuf[ uiCurLevel], + uiNewBlkAddr, + ucEntryBuf[ uiCurLevel+1], + &uiNewBlkAddr))) + { + goto Exit; + } + + uiCurLevel++; + flmAssert( uiCurLevel < m_uiLevels); + + if (RC_OK( rc = m_BTStack[uiCurLevel]->insertEntry( + ucEntryBuf[uiCurLevel], uiNewBlkAddr))) + { + goto Exit; + } + + // Only returns NE_FLM_OK or FAILURE. + + // Root split? + + if (uiCurLevel + 1 == m_uiLevels) + { + flmAssert( m_uiLevels + 1 <= FBTREE_MAX_LEVELS); + if (m_uiLevels + 1 > FBTREE_MAX_LEVELS) + { + rc = RC_SET( NE_FLM_BTREE_FULL); + goto Exit; + } + + // Need to split the root block. + rc = ((F_BtreeRoot *)m_BTStack[uiCurLevel])->split( + ucEntryBuf[uiCurLevel], uiNewBlkAddr ); + break; + } + } + +Exit: + + if (RC_OK(rc)) + { + m_uiTotalEntries++; + } + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the buffer. +****************************************************************************/ +RCODE F_BtreeBlk::insertEntry( + void * pvEntry, + FLMUINT uiChildAddr) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucCurEntry; + FLMUINT uiShiftBytes; // Always shift down + + if( entryCount() >= m_uiNumSlots) + { + rc = RC_SET( NE_FLM_FAILURE); + goto Exit; + } + flmAssert( m_uiPosition != DYNSSET_POSITION_NOT_SET); + pucCurEntry = ENTRY_POS( m_uiPosition); + if ((uiShiftBytes = (entryCount() - m_uiPosition) * + (m_uiEntrySize + m_uiEntryOvhd)) != 0) + { + + // Big hairy assert. Finds coding bugs and corruptions. + + flmAssert( m_uiPosition * (m_uiEntrySize + m_uiEntryOvhd) + + uiShiftBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)); + + f_memmove( pucCurEntry + m_uiEntrySize + m_uiEntryOvhd, + pucCurEntry, uiShiftBytes); + } + f_memcpy( pucCurEntry, pvEntry, m_uiEntrySize); + if( m_uiEntryOvhd) + { + UD2FBA( (FLMUINT32)uiChildAddr, &pucCurEntry[m_uiEntrySize]); + } + entryCount( entryCount() + 1); + m_uiPosition++; + m_bDirty = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Move first half of entries into new block. Reset previous block + to point to new block. Add new last entry in new block to parent. + Fixup prev/next linkages. +****************************************************************************/ +RCODE F_BtreeBlk::split( + F_BtreeRoot * pRoot, + FLMBYTE * pucCurEntry, // (in) Contains entry to insert + FLMUINT uiCurBlkAddr, // (in) Blk addr if non-leaf + FLMBYTE * pucParentEntry, // (out) Entry to insert into parent. + FLMUINT * puiNewBlkAddr) // (out) New blk addr to insert into parent. +{ + RCODE rc = NE_FLM_OK; + F_BtreeBlk * pPrevBlk; + F_BtreeBlk * pNewBlk = NULL; + FLMBYTE * pucEntry = NULL; + FLMBYTE * pucMidEntry; + FLMBYTE * pucChildAddr; + FLMUINT uiChildAddr; + FLMUINT uiPrevBlkAddr; + FLMUINT uiMid; + FLMUINT uiPos; + FLMUINT uiMoveBytes; + FLMBOOL bInserted = FALSE; + + // Allocate a new block for the split. + + if (RC_BAD( rc = pRoot->newBlk( &pNewBlk, blkType() ))) + { + goto Exit; + } + pNewBlk->AddRef(); // Pin the block - may get flushed out. + + + // the last half into the new block, but that would force the parent + // entry to be changed. This may take a little longer, but it is much + // more easier to code. + + // Call search entry once just to setup for insert. + + (void) pNewBlk->searchEntry( ENTRY_POS( 0)); + + // get the count and move more then half into the new block. + + uiMid = (entryCount() + 5) >> 1; + uiChildAddr = FBTREE_END; + + for (uiPos = 0; uiPos < uiMid; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + if (blkType() != ACCESS_BTREE_LEAF) + { + pucChildAddr = pucEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pucChildAddr); + } + + // m_uiPosition automatically gets incremented. + + if (RC_BAD( rc = pNewBlk->insertEntry( pucEntry, uiChildAddr))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + } + + if (m_uiPosition < uiMid) + { + + // Insert this entry now + + bInserted = TRUE; + (void) pNewBlk->searchEntry( pucCurEntry); + if (RC_BAD( rc = pNewBlk->insertEntry( pucCurEntry, uiCurBlkAddr))) + { + goto Exit; + } + } + + // Let caller insert into parent entry. This rids us of recursion. + + f_memcpy( pucParentEntry, pucEntry, m_uiEntrySize); + + // Move the rest down + + pucEntry = ENTRY_POS( 0); + pucMidEntry = ENTRY_POS( uiMid); + + entryCount( entryCount() - uiMid); + uiMoveBytes = entryCount() * (m_uiEntrySize + m_uiEntryOvhd); + flmAssert( uiMoveBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)); + f_memmove( pucEntry, pucMidEntry, uiMoveBytes); + + if( !bInserted) + { + + // m_uiPosition -= uiMid; + + (void) searchEntry( pucCurEntry); + if (RC_BAD( rc = insertEntry( pucCurEntry, uiCurBlkAddr))) + { + goto Exit; + } + } + + // VISIT: Could position stack to point to current element to insert. + + // Fixup the prev/next block linkages. + + if (prevBlk() != FBTREE_END) + { + if (RC_BAD( rc = pRoot->readBlk( prevBlk(), blkType(), &pPrevBlk ))) + { + goto Exit; + } + + pPrevBlk->nextBlk( pNewBlk->blkAddr()); + uiPrevBlkAddr = pPrevBlk->blkAddr(); + } + else + { + uiPrevBlkAddr = FBTREE_END; + } + pNewBlk->prevBlk( uiPrevBlkAddr); + pNewBlk->nextBlk( blkAddr()); + prevBlk( pNewBlk->blkAddr()); + + *puiNewBlkAddr = pNewBlk->blkAddr(); + +Exit: + + if (pNewBlk) + { + pNewBlk->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Reinsert all entries given a new root block. + Caller will release 'this'. Used ONLY for building the first + ROOT and two leaves of the tree. +****************************************************************************/ +RCODE F_BtreeLeaf::split( + F_BtreeRoot * pNewRoot) // New Non-leaf root +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry; + FLMUINT uiPos; + FLMUINT uiEntryCount = entryCount(); + FLMUINT uiMid = (uiEntryCount + 1) >> 1; + + if (RC_BAD( rc = pNewRoot->setupTree( ENTRY_POS(uiMid), + ACCESS_BTREE_LEAF, NULL, NULL))) + { + goto Exit; + } + + for (uiPos = 0; uiPos < uiEntryCount; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + if ((rc = pNewRoot->search( pucEntry)) != NE_FLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = pNewRoot->insert( pucEntry))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Split the root block and make two new non-leaf blocks. + The secret here is that the root block never moves (cheers!). + This takes a little longer but is worth the work because the + root block never goes out to disk and is not in the cache. +****************************************************************************/ +RCODE F_BtreeRoot::split( + void * pvCurEntry, + FLMUINT uiCurChildAddr) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry; + FLMBYTE * pucChildAddr; + F_BtreeBlk * pLeftBlk; + F_BtreeBlk * pRightBlk; + F_BtreeBlk * pBlk; + FLMUINT uiChildAddr; + FLMUINT uiPos; + FLMUINT uiEntryCount = entryCount(); + FLMUINT uiMid = (uiEntryCount + 1) >> 1; + + if (RC_BAD( rc = setupTree( NULL, ACCESS_BTREE_NON_LEAF, + &pLeftBlk, &pRightBlk))) + { + goto Exit; + } + + // Call search entry once just to setup for insert. + + (void) pLeftBlk->searchEntry( ENTRY_POS( 0)); + + // Take the entries from the root block and move into leafs. + + for (uiPos = 0; uiPos <= uiMid; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + pucChildAddr = pucEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pucChildAddr); + + if (RC_BAD( rc = pLeftBlk->insertEntry( pucEntry, uiChildAddr))) + { + goto Exit; + } + } + + // Call search entry once just to setup for insert. + + (void) pRightBlk->searchEntry( ENTRY_POS( 0)); + + for (uiPos = uiMid + 1; uiPos < uiEntryCount; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + pucChildAddr = pucEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pucChildAddr); + + if ((rc = pRightBlk->searchEntry( pucEntry )) != NE_FLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = pRightBlk->insertEntry( pucEntry, uiChildAddr))) + { + goto Exit; + } + } + + // Reset the root block and insert new midpoint. + + entryCount( 0); + lemBlk( pRightBlk->blkAddr()); // Duplicated just in case. + pucEntry = ENTRY_POS( uiMid); + + if ((rc = searchEntry( pucEntry )) != NE_FLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = insertEntry( pucEntry, pLeftBlk->blkAddr() ))) + { + goto Exit; + } + + // Insert the current entry (parameters) into the left or right blk. + // This could be done a number of different ways. + (void) searchEntry( pvCurEntry, &uiChildAddr); + if (RC_BAD( rc = readBlk( uiChildAddr, ACCESS_BTREE_NON_LEAF, &pBlk))) + { + goto Exit; + } + (void) pBlk->searchEntry( pvCurEntry); + if (RC_BAD( rc = pBlk->insertEntry( pvCurEntry, uiCurChildAddr))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup two child blocks for a root block. +****************************************************************************/ +RCODE F_BtreeRoot::setupTree( + FLMBYTE * pucMidEntry, // If !NULL entry to insert into root. + eDynRSetBlkTypes eBlkType, // Leaf or non-leaf + F_BtreeBlk ** ppLeftBlk, // (out) + F_BtreeBlk ** ppRightBlk) // (out) +{ + RCODE rc = NE_FLM_OK; + F_BtreeBlk * pLeftBlk = NULL; + F_BtreeBlk * pRightBlk = NULL; + + if (RC_BAD( rc = newBlk( &pLeftBlk, eBlkType))) + { + goto Exit; + } + + if (RC_BAD( rc = newBlk( &pRightBlk, eBlkType))) + { + goto Exit; + } + + if (eBlkType == ACCESS_BTREE_NON_LEAF) + { + ((F_BtreeNonLeaf *)pRightBlk)->lemBlk( lemBlk()); + } + + // Fix up the linkages + + pLeftBlk->nextBlk( pRightBlk->blkAddr()); + pRightBlk->prevBlk( pLeftBlk->blkAddr()); + lemBlk( pRightBlk->blkAddr()); + + if (pucMidEntry) + { + + // Add the midentry to the root block. Search to position and insert. + + searchEntry( pucMidEntry); + insertEntry( pucMidEntry, pLeftBlk->blkAddr()); + } + m_uiLevels++; + + if (ppLeftBlk) + { + *ppLeftBlk = pLeftBlk; + } + if (ppRightBlk) + { + *ppRightBlk = pRightBlk; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read in the block or get it from the cache. +****************************************************************************/ +RCODE F_BtreeRoot::readBlk( + FLMUINT uiBlkAddr, // Blk address to read + eDynRSetBlkTypes eBlkType, // Expected access type to read + F_BtreeBlk ** ppBlk) // (out) Return block +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiPos; + FLMUINT uiLRUValue = (FLMUINT)~0; + FLMUINT uiLRUPos = 0; + F_BtreeBlk * pNewBlk; + + for (uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + if (m_CacheBlks[uiPos].uiBlkAddr == uiBlkAddr) + { + goto Exit; + } + + // The ref count is used for pinning the block. + + if (m_CacheBlks[uiPos].pBlk && + m_CacheBlks[uiPos].pBlk->getRefCount() == 1 && + uiLRUValue > m_CacheBlks[uiPos].uiLRUValue) + { + uiLRUValue = m_CacheBlks[uiPos].uiLRUValue; + uiLRUPos = uiPos; + } + + // There better not be a hole by this point. + + flmAssert( m_CacheBlks[uiPos].pBlk != NULL); + } + uiPos = uiLRUPos; + + // Read from disk? + + flmAssert( m_pFileHdl != NULL); + + if (RC_BAD( rc = newCacheBlk( uiPos, &pNewBlk, eBlkType))) + { + goto Exit; + } + + // Pick the LRU block and make that object do the reading + // so it can reset all internals and get used to being a different blk. + + pNewBlk->blkAddr( uiBlkAddr); + m_CacheBlks[uiPos].uiBlkAddr = uiBlkAddr; + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + + if (RC_BAD( rc = pNewBlk->readBlk( m_pFileHdl, uiBlkAddr))) + { + + // Release the block because the reset() changed the object type. + // May hit the assert above. + + m_CacheBlks[uiPos].pBlk->Release(); + m_CacheBlks[uiPos].pBlk = NULL; + goto Exit; + } + +Exit: + + if (RC_OK(rc)) + { + *ppBlk = m_CacheBlks[uiPos].pBlk; + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + } + return( rc); +} + +/**************************************************************************** +Desc: Get a new block using an exising or newly allocated block from + the cache. Initializes the block. May be leaf or non-leaf + but NOT the root block. +****************************************************************************/ +RCODE F_BtreeRoot::newBlk( + F_BtreeBlk ** ppBlk, + eDynRSetBlkTypes eBlkType) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLRUValue = (FLMUINT)~0; + FLMUINT uiPos; + FLMUINT uiLRUPos = 0; + F_BtreeBlk * pNewBlk; + + for (uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + + // The ref count is used for pinning the block. + + if (getRefCount() == 1 && + uiLRUValue > m_CacheBlks[uiPos].uiLRUValue) + { + uiLRUValue = m_CacheBlks[uiPos].uiLRUValue; + uiLRUPos = uiPos; + } + + // use the first hole. + + if (m_CacheBlks[uiPos].pBlk == NULL) + { + uiLRUPos = uiPos; + break; + } + } + uiPos = uiLRUPos; + if (RC_BAD( rc = newCacheBlk( uiPos, &pNewBlk, eBlkType))) + { + goto Exit; + } + + pNewBlk->blkAddr( newBlkAddr()); + m_CacheBlks[uiPos].uiBlkAddr = pNewBlk->blkAddr(); + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + + pNewBlk->entryCount(0); + pNewBlk->lemBlk( FBTREE_END); + pNewBlk->nextBlk( FBTREE_END); + pNewBlk->prevBlk( FBTREE_END); + *ppBlk = pNewBlk; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Release the existing cache block and setup and alloc new blk. +****************************************************************************/ +RCODE F_BtreeRoot::newCacheBlk( + FLMUINT uiCachePos, + F_BtreeBlk ** ppBlk, + eDynRSetBlkTypes eBlkType) +{ + RCODE rc = NE_FLM_OK; + F_BtreeBlk * pNewBlk = NULL; + + if (m_CacheBlks[uiCachePos].pBlk) + { + if (m_CacheBlks[uiCachePos].pBlk->isDirty()) + { + if (RC_BAD( rc = writeBlk( uiCachePos))) + { + goto Exit; + } + } + } + + if (m_CacheBlks[uiCachePos].pBlk != NULL && + m_CacheBlks[uiCachePos].pBlk->blkType() == eBlkType) + { + + // If block is of the same type then reset it and use it. + + pNewBlk = m_CacheBlks[uiCachePos].pBlk; + pNewBlk->reset( eBlkType); + *ppBlk = pNewBlk; + goto Exit; + } + + if (m_CacheBlks[uiCachePos].pBlk) + { + m_CacheBlks[uiCachePos].pBlk->Release(); + } + if (eBlkType == ACCESS_BTREE_LEAF) + { + F_BtreeLeaf * pLeafBlk; + if ((pLeafBlk = f_new F_BtreeLeaf) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pLeafBlk->setup( m_uiEntrySize))) + { + pLeafBlk->Release(); + goto Exit; + } + pLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData); + pNewBlk = (F_BtreeBlk *) pLeafBlk; + } + else + { + F_BtreeNonLeaf * pNonLeafBlk; + if ((pNonLeafBlk = f_new F_BtreeNonLeaf) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pNonLeafBlk->setup( m_uiEntrySize))) + { + pNonLeafBlk->Release(); + goto Exit; + } + pNonLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData); + pNewBlk = (F_BtreeBlk *) pNonLeafBlk; + } + m_CacheBlks[uiCachePos].pBlk = pNewBlk; + *ppBlk = pNewBlk; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read the block from disk. +****************************************************************************/ +RCODE F_BtreeBlk::readBlk( + IF_FileHdl * pFileHdl, + FLMUINT uiBlkAddr) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesRead; + + if (RC_BAD( rc = pFileHdl->read( + uiBlkAddr * DYNSSET_BLOCK_SIZE, DYNSSET_BLOCK_SIZE, + m_pucBlkBuf, &uiBytesRead))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + if (blkAddr() != uiBlkAddr) + { + + // Most likely a coding error rather than an I/O error. + + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Write the block to disk. +****************************************************************************/ +RCODE F_BtreeBlk::writeBlk( + IF_FileHdl * pFileHdl) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesWritten; + FLMUINT uiBlkAddr = blkAddr(); + + if (RC_BAD( rc = pFileHdl->write( + uiBlkAddr * DYNSSET_BLOCK_SIZE, + DYNSSET_BLOCK_SIZE, + m_pucBlkBuf, + &uiBytesWritten))) + { + goto Exit; + } + + m_bDirty = FALSE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Write all blocks that are dirty and have an addrees lower + than the input block address and then write this block. + Write in order so that + we don't have to write zeros for any block. +****************************************************************************/ +RCODE F_BtreeRoot::writeBlk( + FLMUINT uiWritePos) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiPos; + FLMUINT uiHighBlkAddr = m_CacheBlks[uiWritePos].uiBlkAddr; + + if (!m_pFileHdl) + { + if (RC_BAD( rc = openFile())) + { + goto Exit; + } + } + for (uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + if( (uiWritePos != uiPos) && + (m_CacheBlks[uiPos].pBlk) && + (m_CacheBlks[uiPos].uiBlkAddr >= m_uiHighestWrittenBlkAddr) && + (m_CacheBlks[uiPos].uiBlkAddr < uiHighBlkAddr) && + (m_CacheBlks[uiPos].pBlk->isDirty()) ) + { + + // Recursive call to write out lower blocks if needed. + + if (RC_BAD( rc = writeBlk( uiPos))) + { + goto Exit; + } + } + } + m_CacheBlks[ uiWritePos].pBlk->writeBlk( m_pFileHdl); + if (m_CacheBlks[uiWritePos].uiBlkAddr > m_uiHighestWrittenBlkAddr) + { + m_uiHighestWrittenBlkAddr = m_CacheBlks[uiWritePos].uiBlkAddr; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Close the file if previously opened and creates the file. +****************************************************************************/ +RCODE F_BtreeRoot::openFile( void) +{ + RCODE rc = NE_FLM_OK; + + if (!m_pFileHdl) + { + F_FileSystem fileSystem; + + rc = fileSystem.createUniqueFile( m_pszFileName, + FRSET_FILENAME_EXTENSION, + FLM_IO_RDWR | FLM_IO_CREATE_DIR, &m_pFileHdl); + } + return( rc); +} + +/**************************************************************************** +Desc: Closes and deletes the temporary file. +****************************************************************************/ +void F_BtreeRoot::closeFile( void) +{ + if (m_pFileHdl) + { + F_FileSystem fileSystem; + + m_pFileHdl->close(); + fileSystem.deleteFile( m_pszFileName); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } +} diff --git a/ftk/src/ftkdynrset.cpp b/ftk/src/ftkdynrset.cpp new file mode 100644 index 0000000..1545133 --- /dev/null +++ b/ftk/src/ftkdynrset.cpp @@ -0,0 +1,328 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for F_DynSearchSet class. +// +// Tabs: 3 +// +// Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id$ +//------------------------------------------------------------------------------ + +#include "ftk.h" +#include "ftkdynrset.h" + +#define HASH_POS(vp) \ + (((FLMUINT)(FB2UD((FLMBYTE*)vp)) % m_uiNumSlots) * m_uiEntrySize) + +static const FLMBYTE ucZeros [ DYNSSET_MAX_FIXED_ENTRY_SIZE ] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/**************************************************************************** +Desc: Setup the result set with input values. This method must be + called and only called once. +****************************************************************************/ +RCODE F_DynSearchSet::setup( + char * pszTmpDir, + FLMUINT uiEntrySize) +{ + RCODE rc = NE_FLM_OK; + F_HashBlk * pHashBlk; + + // Set the input variables. + + if( pszTmpDir ) + { + f_strcpy( m_szFileName, pszTmpDir); // Dest <- src + } + else + { + f_memset( m_szFileName, 0, F_PATH_MAX_SIZE); + } + m_uiEntrySize = uiEntrySize; + + if ((pHashBlk = f_new F_HashBlk) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + pHashBlk->setup( uiEntrySize); + m_pAccess = (F_FixedBlk *) pHashBlk; + m_pvUserData = (void *) uiEntrySize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Add a fixed length entry to the dynamic search result set. +****************************************************************************/ +RCODE F_DynSearchSet::addEntry( + void * pvEntry) +{ + RCODE rc = NE_FLM_OK; + +Add_Again: + + if (RC_OK( rc = m_pAccess->search( pvEntry))) + { + rc = RC_SET( NE_FLM_EXISTS); + } + else if (rc == NE_FLM_NOT_FOUND) + { + + // Insert the entry. + + if ((rc = m_pAccess->insert( pvEntry)) == NE_FLM_FAILURE) + { + // Find the type of access method implemented + + if (m_pAccess->blkType() == ACCESS_HASH) + { + F_BtreeLeaf * pBtreeBlk; + FLMBYTE ucEntryBuffer[ DYNSSET_MAX_FIXED_ENTRY_SIZE]; + + // Go from a hash to a b-tree object. Alloc and move stuff over. + + if ((pBtreeBlk = f_new F_BtreeLeaf) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + pBtreeBlk->setup( m_uiEntrySize); + pBtreeBlk->setCompareFunc( m_fnCompare, m_pvUserData); + for( rc = m_pAccess->getFirst( ucEntryBuffer ); + RC_OK(rc); + rc = m_pAccess->getNext( ucEntryBuffer) ) + { + // Call search to setup for insert. + (void) pBtreeBlk->search( ucEntryBuffer); + if (RC_BAD( rc = pBtreeBlk->insert( ucEntryBuffer))) + { + pBtreeBlk->Release(); + goto Exit; + } + } + rc = NE_FLM_OK; + m_pAccess->Release(); + m_pAccess = pBtreeBlk; + goto Add_Again; + } + else if( m_pAccess->blkType() == ACCESS_BTREE_LEAF) + { + F_BtreeRoot * pFullBtree; + + // Go from 1 block to 3 changing root blocks and free m_pAccess + // All new splits will be taken care of automatically. + + if ((pFullBtree = f_new F_BtreeRoot) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + if( RC_BAD( rc = pFullBtree->setup( m_uiEntrySize, m_szFileName))) + { + pFullBtree->Release(); + goto Exit; + } + pFullBtree->setCompareFunc( m_fnCompare, m_pvUserData); + if (RC_BAD( rc = ((F_BtreeLeaf *)m_pAccess)->split( pFullBtree))) + { + goto Exit; + } + m_pAccess->Release(); + m_pAccess = pFullBtree; + goto Add_Again; + } + else + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Find matching entry. Position for Get* or for insert. +****************************************************************************/ +RCODE F_HashBlk::search( + void * pvEntry, + void * pvFoundEntry) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiHashPos = HASH_POS( pvEntry); + FLMINT iCompare; + + for (;;) + { + + // If all zeros then setup to insert at this position. + + if (!f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + if (m_fnCompare) + { + iCompare = m_fnCompare( pvEntry, &m_pucBlkBuf[ uiHashPos], + m_pvUserData); + } + else + { + iCompare = f_memcmp( pvEntry, &m_pucBlkBuf[ uiHashPos], + m_uiEntrySize); + } + + if (iCompare == 0) + { + + // Found match. + + if (pvFoundEntry) + { + f_memcpy( pvFoundEntry, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + } + break; + } + + // Go to the next entry + + uiHashPos += m_uiEntrySize; + if (uiHashPos >= DYNSSET_HASH_BUFFER_SIZE) + { + uiHashPos = 0; + } + } + +Exit: + + m_uiPosition = uiHashPos; + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the buffer. +****************************************************************************/ +RCODE F_HashBlk::insert( + void * pvEntry) +{ + RCODE rc = NE_FLM_OK; + + if( getTotalEntries() > ((m_uiNumSlots * 7) / 10)) + { + rc = RC_SET( NE_FLM_FAILURE); + goto Exit; + } + + f_memcpy( &m_pucBlkBuf[ m_uiPosition], pvEntry, m_uiEntrySize); + m_uiTotalEntries++; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Return the next entry in the result set. If the result set + is not positioned then the first entry will be returned. +****************************************************************************/ +RCODE F_HashBlk::getNext( + void * pvEntryBuffer) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiHashPos; + + // Position to the next/first entry. + + if (m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + uiHashPos = 0; + } + else + { + uiHashPos = m_uiPosition + m_uiEntrySize; + } + + for ( ; ; uiHashPos += m_uiEntrySize) + { + if (uiHashPos >= DYNSSET_HASH_BUFFER_SIZE) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + // If all zeros then setup to insert at this position. + + if (f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + f_memcpy( pvEntryBuffer, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + m_uiPosition = uiHashPos; + goto Exit; + } + } + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: Returns the last entry in the result set. +****************************************************************************/ +RCODE F_HashBlk::getLast( + void * pvEntryBuffer) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiHashPos; + + // Position to the next/first entry. + + uiHashPos = DYNSSET_HASH_BUFFER_SIZE; + + for( ; ; ) + { + uiHashPos -= m_uiEntrySize; + + // If all zeros then setup to insert at this position. + + if (f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + f_memcpy( pvEntryBuffer, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + m_uiPosition = uiHashPos; + goto Exit; + } + if (uiHashPos == 0) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + } + +Exit: + + return( rc); +} diff --git a/ftk/src/ftkdynrset.h b/ftk/src/ftkdynrset.h new file mode 100644 index 0000000..8a05397 --- /dev/null +++ b/ftk/src/ftkdynrset.h @@ -0,0 +1,514 @@ +//------------------------------------------------------------------------------ +// Desc: FLAIM Dynamic search result set class. +// +// Tabs: 3 +// +// Copyright (c) 1998-2000, 2002-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id$ +//------------------------------------------------------------------------------ + +#ifndef FTKDYNRSET_H +#define FTKDYNRSET_H + +/***************************************************************************** +***** +** Definitions +***** +*****************************************************************************/ + +/* +A block size of 8K will perform well in minimizing the number of reads +to obtain a block. A 6K may perform better if the file is located +across the network. +*/ + +#define DYNSSET_BLOCK_SIZE 0x4000 +#define DYNSSET_HASH_BUFFER_SIZE 0x2000 +#define DYNSSET_MIN_FIXED_ENTRY_SIZE 4 +// Change ucZeros in fdynsset.cpp if this changes. +#define DYNSSET_MAX_FIXED_ENTRY_SIZE 32 +#define DYNSSET_POSITION_NOT_SET 0xFFFFFFFF + +#define FBTREE_CACHE_BLKS 32 +#define FBTREE_END 0xFFFFFFFF +#define FBTREE_MAX_LEVELS 4 + +/***************************************************************************** +***** +** Forward References +***** +*****************************************************************************/ + +class F_HashBlk; +class F_BtreeBlk; +class F_BtreeRoot; +class F_BtreeNonLeaf; +class F_BtreeLeaf; + +/*=========================================================================== + Block Header Definition + +Desc: Actually stored as the first section of each block. + We can write this structure because the same process will + read the block header i.e. portability is not a problem. +===========================================================================*/ + +typedef struct +{ + FLMUINT uiBlkAddr; + FLMUINT uiPrevBlkAddr; + FLMUINT uiNextBlkAddr; + FLMUINT uiLEMAddr; + FLMUINT uiNumEntries; +} FixedBlkHdr; + +/*=========================================================================== + Fixed Length HASH Access Method +===========================================================================*/ +class F_HashBlk : public F_FixedBlk +{ +public: + + F_HashBlk() + { + // Base class constructors are called before this constructor is. + + m_eBlkType = ACCESS_HASH; + m_pucBlkBuf = m_ucHashBlk; + f_memset( m_ucHashBlk, 0, sizeof( m_ucHashBlk)); + m_uiTotalEntries = 0; + } + + ~F_HashBlk() + { + // Set to NULL so we don't free the block + m_pucBlkBuf = NULL; + } + + FINLINE RCODE setup( + FLMUINT uiEntrySize + ) + { + m_uiEntrySize = uiEntrySize; + m_uiNumSlots = DYNSSET_HASH_BUFFER_SIZE / uiEntrySize; + return( NE_FLM_OK); + } + + FINLINE RCODE getCurrent( + void * pvEntryBuffer + ) + { + + // Position to the next/first entry. + + if (m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + return RC_SET( NE_FLM_NOT_FOUND); + } + + f_memcpy( pvEntryBuffer, &m_pucBlkBuf[ m_uiPosition], m_uiEntrySize); + return( NE_FLM_OK); + } + + FINLINE RCODE getFirst( + void * pvEntryBuffer + ) + { + m_uiPosition = DYNSSET_POSITION_NOT_SET; + return( getNext( pvEntryBuffer)); + } + + RCODE getLast( + void * pvEntryBuffer); + + RCODE getNext( + void * pvEntryBuffer); + + FINLINE FLMUINT getTotalEntries( void) + { + return m_uiTotalEntries; + } + + RCODE insert( + void * pvEntry); + + RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL); + +private: + + FLMUINT m_uiTotalEntries; + // Allocate the hash block to save 1 allocation. + // We need to make the hash as fast as possible. + FLMBYTE m_ucHashBlk[ DYNSSET_HASH_BUFFER_SIZE]; +}; + + +/*=========================================================================== + Virtual F_BtreeBlk +===========================================================================*/ + +// Leaf and non-leaf entry position. Don't do any ++ or -- ! ! ! ! + +#define ENTRY_POS(uiPos) (m_pucBlkBuf + sizeof( FixedBlkHdr) + \ + (uiPos * (m_uiEntrySize+m_uiEntryOvhd))) + +class F_BtreeBlk : public F_FixedBlk +{ +public: + + F_BtreeBlk() + { + } + + ~F_BtreeBlk() + { + if (m_pucBlkBuf) + { + f_free( &m_pucBlkBuf); + } + } + + // virtual methods that must be implemented + + FINLINE RCODE getCurrent( + void * pvEntryBuffer + ) + { + // Position to the next/first entry. + + if (m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + return RC_SET( NE_FLM_NOT_FOUND); + } + + f_memcpy( pvEntryBuffer, ENTRY_POS( m_uiPosition), m_uiEntrySize); + return( NE_FLM_OK); + } + + FINLINE RCODE getFirst( + void * pvEntryBuffer + ) + { + m_uiPosition = DYNSSET_POSITION_NOT_SET; + return( getNext( pvEntryBuffer)); + } + + RCODE getLast( + void * pvEntryBuffer); + + RCODE getNext( + void * pvEntryBuffer); + + RCODE readBlk( + IF_FileHdl * pFileHdl, + FLMUINT uiBlkAddr); + + void reset( + eDynRSetBlkTypes eBlkType); + + RCODE split( + F_BtreeRoot * pParent, + FLMBYTE * pucCurEntry, + FLMUINT uiCurBlkAddr, + FLMBYTE * pucParentEntry, + FLMUINT * puiNewBlkAddr); + + RCODE writeBlk( + IF_FileHdl * pFileHdl); + + // Virtual methods + + virtual FLMUINT getTotalEntries( void) = 0; + + virtual RCODE insert( + void * pvEntry) = 0; + + virtual RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL) = 0; + + virtual RCODE searchEntry( + void * pvEntry, + FLMUINT * puiChildAddr = NULL, + void * pvFoundEntry = NULL); + + // Implemented as inline functions. + // Even though these are b-tree specific - keep them here to + // avoid having a b-tree block class. Most are not used for hash. + + FINLINE FLMUINT blkAddr( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiBlkAddr); + } + + FINLINE void blkAddr( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + + // Get and set the number of entries in the block + + FINLINE FLMUINT entryCount( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiNumEntries); + } + + FINLINE void entryCount( + FLMUINT uiNumEntries) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiNumEntries = uiNumEntries; + m_bDirty = TRUE; + } + + RCODE insertEntry( + void * pvEntry, + FLMUINT uiChildAddr = FBTREE_END); + + + // Get and set the last element marker in the block. + + FINLINE FLMUINT lemBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiLEMAddr); + } + + FINLINE void lemBlk( + FLMUINT uiLEMAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiLEMAddr = uiLEMAddr; + m_bDirty = TRUE; + } + + // Get and set the next block address element + + FINLINE FLMUINT nextBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiNextBlkAddr); + } + + FINLINE void nextBlk( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiNextBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + + // Get and set the previous block address element + + FINLINE FLMUINT prevBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiPrevBlkAddr); + } + + FINLINE void prevBlk( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiPrevBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + +protected: + + // Variables + + FLMUINT m_uiEntryOvhd; // Overhead in the entry. +}; + + +/*=========================================================================== + Fixed Length B-tree Leaf - may be a root +===========================================================================*/ + +class F_BtreeLeaf : public F_BtreeBlk +{ +public: + + F_BtreeLeaf() + { + m_eBlkType = ACCESS_BTREE_LEAF; + m_uiEntryOvhd = 0; + } + + ~F_BtreeLeaf() + { + } + + RCODE setup( + FLMUINT uiEntrySize); + + FINLINE FLMUINT getTotalEntries( void) + { + return( (FLMUINT)entryCount()); + } + + FINLINE RCODE insert( + void * pvEntry) + { + return( insertEntry( pvEntry, FBTREE_END)); + } + + FINLINE RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL) + { + return( searchEntry( pvEntry, NULL, pvFoundEntry)); + } + + RCODE split( + F_BtreeRoot * pNewRoot); + +}; + +typedef struct +{ + FLMUINT uiBlkAddr; + FLMUINT uiLRUValue; + F_BtreeBlk * pBlk; // Points to leaf or non-leaf block +} FBTREE_CACHE; + +/*=========================================================================== + Fixed Length B-tree non-leaf block +===========================================================================*/ + +class F_BtreeNonLeaf : public F_BtreeBlk +{ +public: + + F_BtreeNonLeaf() + { + m_eBlkType = ACCESS_BTREE_NON_LEAF; + m_uiEntryOvhd = sizeof( FLMUINT32); + } + + ~F_BtreeNonLeaf() + { + } + + RCODE setup( + FLMUINT uiEntrySize); + + FINLINE FLMUINT getTotalEntries( void) + { + return( (FLMUINT) entryCount()); + } + + FINLINE RCODE insert( + void * // pvEntry + ) + { + return( NE_FLM_OK); + } + + FINLINE RCODE search( + void *, // pvEntry, + void * pvFoundEntry = NULL + ) + { + F_UNREFERENCED_PARM( pvFoundEntry); + flmAssert( 0); + return( NE_FLM_OK); + } +}; + +/*=========================================================================== + Fixed Length B-tree Non-Leaf Root +===========================================================================*/ +class F_BtreeRoot : public F_BtreeNonLeaf +{ +public: + F_BtreeRoot(); + ~F_BtreeRoot(); + + RCODE setup( + FLMUINT uiEntrySize, + char * pszFileName); + + + void closeFile( void); + + FINLINE FLMUINT getTotalEntries( void) + { + return( m_uiTotalEntries); + } + + RCODE insert( + void * pvEntry); + + RCODE newBlk( + F_BtreeBlk ** ppBlk, + eDynRSetBlkTypes eBlkType); + + FINLINE FLMUINT newBlkAddr( void) + { + return( m_uiNewBlkAddr++); + } + + RCODE newCacheBlk( + FLMUINT uiCachePos, + F_BtreeBlk ** ppBlk, + eDynRSetBlkTypes eBlkType); + + RCODE openFile( void); + + RCODE readBlk( + FLMUINT uiBlkAddr, + eDynRSetBlkTypes eBlkType, + F_BtreeBlk ** ppBlk); + + RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL); + + RCODE setupTree( + FLMBYTE * pucMidEntry, // If !NULL entry to insert into root. + eDynRSetBlkTypes eBlkType, // Leaf or non-leaf + F_BtreeBlk ** ppLeftBlk, // (out) + F_BtreeBlk ** ppRightBlk); // (out) + + RCODE split( + void * pvCurEntry, + FLMUINT uiCurChildAddr); + + RCODE writeBlk( + FLMUINT uiWritePos); + +private: + + FLMUINT m_uiLevels; // Number of levels in the b-tree + FLMUINT m_uiTotalEntries; // Count of total entries + FLMUINT m_uiNewBlkAddr; // Next new blk addr. + FLMUINT m_uiHighestWrittenBlkAddr; + + IF_FileHdl * m_pFileHdl; // File handle or NULL if not open. + char * m_pszFileName; // File created for result set or default. + + // Cache of 'n' blocks to apply LRU algorithm. + + FLMUINT m_uiLRUCount; // Count to find least rec. used. + FBTREE_CACHE m_CacheBlks[ FBTREE_CACHE_BLKS]; + + // B-tree stack. + F_BtreeBlk * m_BTStack[ FBTREE_MAX_LEVELS]; +}; + +#endif // ifndef FTKDYNRSET_H