//------------------------------------------------------------------------------ // Desc: Contains the methods for the F_FixedBlk, F_BtreeRoot, F_BtreeLeaf, // F_BtreeNonLeaf, and F_BtreeBlk classes. // Tabs: 3 // // Copyright (c) 1998-2007 Novell, Inc. All Rights Reserved. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; version 2.1 // of the License. // // This library 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 // Library Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; 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->closeFile(); fileSystem.deleteFile( m_pszFileName); m_pFileHdl->Release(); m_pFileHdl = NULL; } }