Files
mars-flaim/xflaim/src/fdynbtre.cpp
dsandersoremutah c55dab446f Renamed version4 to flaim and version5 to xflaim
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-01-27 21:06:39 +00:00

1199 lines
28 KiB
C++

//------------------------------------------------------------------------------
// Desc: Contains the methods for the FFixedBlk, FBtreeRoot, FBtreeLeaf,
// FBtreeNonLeaf, and FBtreeBlk 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: fdynbtre.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "flaimsys.h"
#include "fdynsset.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
****************************************************************************/
FFixedBlk::FFixedBlk()
{
m_fnCompare = NULL;
m_pvUserData = NULL;
m_uiPosition = DYNSSET_POSITION_NOT_SET;
m_bDirty = FALSE;
m_pucBlkBuf = NULL;
}
/****************************************************************************
Desc:
****************************************************************************/
FBtreeRoot::FBtreeRoot()
{
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:
****************************************************************************/
FBtreeRoot::~FBtreeRoot()
{
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 FBtreeLeaf::setup(
FLMUINT uiEntrySize)
{
RCODE rc = NE_XFLM_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 FBtreeNonLeaf::setup(
FLMUINT uiEntrySize)
{
RCODE rc = NE_XFLM_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 FBtreeRoot::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 FBtreeBlk::reset(
FBlkTypes 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 FBtreeBlk::getNext(
void * pvEntryBuffer)
{
RCODE rc = NE_XFLM_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_XFLM_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 FBtreeBlk::getLast(
void * pvEntryBuffer)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiPos = entryCount();
// Position to the next/first entry.
if (uiPos == 0)
{
rc = RC_SET( NE_XFLM_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 FBtreeRoot::search(
void * pvEntry,
void * pvFoundEntry)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiCurLevel = m_uiLevels - 1; // Min 2 levels
FLMUINT uiBlkAddr;
// Reset the stack - only needed for debugging.
//f_memset( m_BTStack, 0, sizeof(FBtreeBlk *) * 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 FBtreeBlk::searchEntry(
void * pvEntry,
FLMUINT * puiChildAddr,
void * pvFoundEntry)
{
RCODE rc = RC_SET( NE_XFLM_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_XFLM_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 FBtreeRoot::insert(
void * pvEntry)
{
RCODE rc = NE_XFLM_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_XFLM_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_XFLM_BTREE_FULL);
goto Exit;
}
// Need to split the root block.
rc = ((FBtreeRoot *)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 FBtreeBlk::insertEntry(
void * pvEntry,
FLMUINT uiChildAddr)
{
RCODE rc = NE_XFLM_OK;
FLMBYTE * pucCurEntry;
FLMUINT uiShiftBytes; // Always shift down
if( entryCount() >= m_uiNumSlots)
{
rc = RC_SET( NE_XFLM_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 FBtreeBlk::split(
FBtreeRoot * 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_XFLM_OK;
FBtreeBlk * pPrevBlk;
FBtreeBlk * 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 FBtreeLeaf::split(
FBtreeRoot * pNewRoot) // New Non-leaf root
{
RCODE rc = NE_XFLM_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_XFLM_NOT_FOUND)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_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 FBtreeRoot::split(
void * pvCurEntry,
FLMUINT uiCurChildAddr)
{
RCODE rc = NE_XFLM_OK;
FLMBYTE * pucEntry;
FLMBYTE * pucChildAddr;
FBtreeBlk * pLeftBlk;
FBtreeBlk * pRightBlk;
FBtreeBlk * 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_XFLM_NOT_FOUND)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_NOT_FOUND)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_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 FBtreeRoot::setupTree(
FLMBYTE * pucMidEntry, // If !NULL entry to insert into root.
FBlkTypes eBlkType, // Leaf or non-leaf
FBtreeBlk ** ppLeftBlk, // (out)
FBtreeBlk ** ppRightBlk) // (out)
{
RCODE rc = NE_XFLM_OK;
FBtreeBlk * pLeftBlk = NULL;
FBtreeBlk * 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)
{
((FBtreeNonLeaf *)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 FBtreeRoot::readBlk(
FLMUINT uiBlkAddr, // Blk address to read
FBlkTypes eBlkType, // Expected access type to read
FBtreeBlk ** ppBlk) // (out) Return block
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiPos;
FLMUINT uiLRUValue = (FLMUINT)~0;
FLMUINT uiLRUPos = 0;
FBtreeBlk * 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 FBtreeRoot::newBlk(
FBtreeBlk ** ppBlk,
FBlkTypes eBlkType)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiLRUValue = (FLMUINT)~0;
FLMUINT uiPos;
FLMUINT uiLRUPos = 0;
FBtreeBlk * 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 FBtreeRoot::newCacheBlk(
FLMUINT uiCachePos,
FBtreeBlk ** ppBlk,
FBlkTypes eBlkType)
{
RCODE rc = NE_XFLM_OK;
FBtreeBlk * 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)
{
FBtreeLeaf * pLeafBlk;
if ((pLeafBlk = f_new FBtreeLeaf) == NULL)
{
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
if (RC_BAD( rc = pLeafBlk->setup( m_uiEntrySize)))
{
pLeafBlk->Release();
goto Exit;
}
pLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData);
pNewBlk = (FBtreeBlk *) pLeafBlk;
}
else
{
FBtreeNonLeaf * pNonLeafBlk;
if ((pNonLeafBlk = f_new FBtreeNonLeaf) == NULL)
{
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
if (RC_BAD( rc = pNonLeafBlk->setup( m_uiEntrySize)))
{
pNonLeafBlk->Release();
goto Exit;
}
pNonLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData);
pNewBlk = (FBtreeBlk *) pNonLeafBlk;
}
m_CacheBlks[uiCachePos].pBlk = pNewBlk;
*ppBlk = pNewBlk;
Exit:
return( rc);
}
/****************************************************************************
Desc: Read the block from disk.
****************************************************************************/
RCODE FBtreeBlk::readBlk(
IF_FileHdl * pFileHdl,
FLMUINT uiBlkAddr)
{
RCODE rc = NE_XFLM_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 FBtreeBlk::writeBlk(
IF_FileHdl * pFileHdl)
{
RCODE rc = NE_XFLM_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 FBtreeRoot::writeBlk(
FLMUINT uiWritePos)
{
RCODE rc = NE_XFLM_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 FBtreeRoot::openFile( void)
{
RCODE rc = NE_XFLM_OK;
if (!m_pFileHdl)
{
rc = gv_pFileSystem->CreateUnique( m_pszFileName,
FRSET_FILENAME_EXTENSION,
XFLM_IO_RDWR | XFLM_IO_CREATE_DIR, &m_pFileHdl);
}
return( rc);
}
/****************************************************************************
Desc: Closes and deletes the temporary file.
****************************************************************************/
void FBtreeRoot::closeFile( void)
{
if (m_pFileHdl)
{
m_pFileHdl->Close();
gv_pFileSystem->Delete( m_pszFileName);
m_pFileHdl->Release();
m_pFileHdl = NULL;
}
}