Files
mars-flaim/flaim/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

1070 lines
27 KiB
C++

//-------------------------------------------------------------------------
// Desc: Dynamic result set - b-tree implementation.
// Tabs: 3
//
// Copyright (c) 1998-2001,2003,2005-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 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
// Make sure that the extension is in lower case characters.
#define FRSET_FILENAME_EXTENSION "frs"
/****************************************************************************
Desc:
****************************************************************************/
FFixedBlk::FFixedBlk()
{
m_fnCompare = NULL;
m_UserValue = (void *) 4;
m_uiPosition = DYNSSET_POSITION_NOT_SET;
m_bDirty = FALSE;
m_pucBlkBuf = NULL;
}
/****************************************************************************
Desc:
****************************************************************************/
FBtreeRoot::FBtreeRoot()
{
int i;
m_pFileHdl = NULL;
m_szIoPath[ 0] = 0;
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 = FERR_OK;
if( RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf)))
goto Exit;
m_uiEntrySize = uiEntrySize;
m_UserValue = (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.
Ret: FERR_OK or memory error
****************************************************************************/
RCODE FBtreeNonLeaf::setup(
FLMUINT uiEntrySize)
{
RCODE rc;
if( RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf)))
goto Exit;
m_uiEntrySize = uiEntrySize;
m_UserValue = (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,
const char * pszIoPath)
{
RCODE rc;
if( RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf)))
goto Exit;
m_uiEntrySize = uiEntrySize;
m_UserValue = (void *) uiEntrySize;
f_strcpy( m_szIoPath, pszIoPath);
reset( ACCESS_BTREE_ROOT);
nextBlk( FBTREE_END);
prevBlk( FBTREE_END);
lemBlk( FBTREE_END);
Exit:
return( rc);
}
/****************************************************************************
Desc: Setup the block as a new block
****************************************************************************/
void FBtreeBlk::reset(
FBlkTypes blkType)
{
m_eBlkType = blkType;
if( (blkType == ACCESS_BTREE_ROOT) ||
(blkType == 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 * vpEntryBuffer)
{
RCODE rc = FERR_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( FERR_EOF_HIT);
goto Exit;
}
}
f_memcpy( vpEntryBuffer, ENTRY_POS(uiPos), m_uiEntrySize);
m_uiPosition = uiPos;
Exit:
return( rc);
}
/****************************************************************************
Desc: Return the last entry in the result set.
****************************************************************************/
RCODE FBtreeBlk::getLast(
void * vpEntryBuffer)
{
RCODE rc = FERR_OK;
FLMUINT uiPos = entryCount();
// Position to the next/first entry.
if( uiPos == 0)
{
rc = RC_SET( FERR_EOF_HIT);
goto Exit;
}
uiPos--;
f_memcpy( vpEntryBuffer, 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 * vpEntry,
void * vpFoundEntry)
{
RCODE rc = FERR_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( vpEntry, &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( vpEntry, &uiBlkAddr,
uiCurLevel ? NULL : vpFoundEntry);
}
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 * vpEntry,
FLMUINT * puiChildAddr,
void * vpFoundEntry)
{
RCODE rc = RC_SET( FERR_NOT_FOUND);
FLMUINT uiLow, uiMid, uiHigh, 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( vpEntry, ENTRY_POS( uiMid), (size_t)m_UserValue);
}
else
{
iCompare = f_memcmp( vpEntry, ENTRY_POS( uiMid), (size_t)m_UserValue);
}
if( !iCompare)
{
if( vpFoundEntry)
{
f_memcpy( vpFoundEntry, ENTRY_POS( uiMid), m_uiEntrySize);
}
rc = FERR_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 * pChildAddr = ENTRY_POS( uiMid) + m_uiEntrySize;
*puiChildAddr = FB2UD( pChildAddr);
}
}
return( rc);
}
/****************************************************************************
Desc: Insert the entry into the btree - should be positioned
****************************************************************************/
RCODE FBtreeRoot::insert(
void * vpEntry)
{
RCODE rc = FERR_OK;
FLMUINT uiCurLevel;
FLMBYTE ucEntryBuf[FBTREE_MAX_LEVELS][DYNSSET_MAX_FIXED_ENTRY_SIZE];
FLMUINT uiNewBlkAddr;
if( RC_OK( rc = m_BTStack[0]->insert( vpEntry)))
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], vpEntry, 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 FERR_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( FERR_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 * vpEntry,
FLMUINT uiChildAddr)
{
RCODE rc = FERR_OK;
FLMBYTE * pucCurEntry;
FLMUINT uiShiftBytes;
if( entryCount() >= m_uiNumSlots) // full
{
rc = RC_SET( FERR_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, vpEntry, 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 * pCurEntry, // (in) Contains entry to insert
FLMUINT uiCurBlkAddr, // (in) Blk addr if non-leaf
FLMBYTE * pParentEntry, // (out) Entry to insert into parent.
FLMUINT * puiNewBlkAddr) // (out) New blk addr to insert into parent.
{
RCODE rc;
FBtreeBlk * pPrevBlk;
FBtreeBlk * pNewBlk = NULL;
FLMBYTE * pEntry = NULL;
FLMBYTE * pMidEntry;
FLMBYTE * pChildAddr;
FLMUINT uiChildAddr;
FLMUINT uiPrevBlkAddr;
FLMUINT uiMid, 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.
/*
This moves the first half into the new block. It is easier to move
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++)
{
pEntry = ENTRY_POS( uiPos);
if( blkType() != ACCESS_BTREE_LEAF)
{
pChildAddr = pEntry + m_uiEntrySize;
uiChildAddr = (FLMUINT)FB2UD(pChildAddr);
}
// m_uiPosition automatically gets incremented.
if( RC_BAD( rc = pNewBlk->insertEntry( pEntry, uiChildAddr)))
{
// Should not error.
flmAssert(0);
goto Exit;
}
}
if( m_uiPosition < uiMid)
{
// Insert this entry now
bInserted = TRUE;
(void) pNewBlk->searchEntry( pCurEntry);
if( RC_BAD( rc = pNewBlk->insertEntry( pCurEntry, uiCurBlkAddr)))
goto Exit;
}
// Let caller insert into parent entry. This rids us of recursion.
f_memcpy( pParentEntry, pEntry, m_uiEntrySize);
// Move the rest down
pEntry = ENTRY_POS( 0);
pMidEntry = ENTRY_POS( uiMid);
entryCount( entryCount() - uiMid);
uiMoveBytes = entryCount() * (m_uiEntrySize + m_uiEntryOvhd);
flmAssert( uiMoveBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr));
f_memmove( pEntry, pMidEntry, uiMoveBytes);
if( !bInserted)
{
//m_uiPosition -= uiMid;
(void) searchEntry( pCurEntry);
if( RC_BAD( rc = insertEntry( pCurEntry, 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;
FLMBYTE * pEntry;
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++)
{
pEntry = ENTRY_POS( uiPos);
if( (rc = pNewRoot->search( pEntry)) != FERR_NOT_FOUND)
{
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if( RC_BAD( rc = pNewRoot->insert( pEntry)))
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 * vpCurEntry,
FLMUINT uiCurChildAddr)
{
RCODE rc = FERR_OK;
FLMBYTE * pEntry;
FLMBYTE * pChildAddr;
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++)
{
pEntry = ENTRY_POS( uiPos);
pChildAddr = pEntry + m_uiEntrySize;
uiChildAddr = (FLMUINT)FB2UD(pChildAddr);
if( RC_BAD( rc = pLeftBlk->insertEntry( pEntry, 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++)
{
pEntry = ENTRY_POS( uiPos);
pChildAddr = pEntry + m_uiEntrySize;
uiChildAddr = (FLMUINT)FB2UD(pChildAddr);
if( (rc = pRightBlk->searchEntry( pEntry )) != FERR_NOT_FOUND)
{
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if( RC_BAD( rc = pRightBlk->insertEntry( pEntry, uiChildAddr)))
goto Exit;
}
// Reset the root block and insert new midpoint.
entryCount( 0);
lemBlk( pRightBlk->blkAddr()); // Duplicated just in case.
pEntry = ENTRY_POS( uiMid);
if( (rc = searchEntry( pEntry )) != FERR_NOT_FOUND)
{
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if( RC_BAD( rc = insertEntry( pEntry, 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( vpCurEntry, &uiChildAddr);
if( RC_BAD( rc = readBlk( uiChildAddr, ACCESS_BTREE_NON_LEAF, &pBlk)))
{
goto Exit;
}
(void) pBlk->searchEntry( vpCurEntry);
if( RC_BAD( rc = pBlk->insertEntry( vpCurEntry, uiCurChildAddr)))
{
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Setup two child blocks for a root block.
****************************************************************************/
RCODE FBtreeRoot::setupTree(
FLMBYTE * pMidEntry,
FBlkTypes blkType,
FBtreeBlk ** ppLeftBlk,
FBtreeBlk ** ppRightBlk)
{
RCODE rc = FERR_OK;
FBtreeBlk * pLeftBlk = NULL;
FBtreeBlk * pRightBlk = NULL;
if( RC_BAD( rc = newBlk( &pLeftBlk, blkType )))
goto Exit;
if( RC_BAD( rc = newBlk( &pRightBlk, blkType )))
goto Exit;
if( blkType == ACCESS_BTREE_NON_LEAF)
{
((FBtreeNonLeaf *)pRightBlk)->lemBlk( lemBlk());
}
// Fix up the linkages
pLeftBlk->nextBlk( pRightBlk->blkAddr());
pRightBlk->prevBlk( pLeftBlk->blkAddr());
lemBlk( pRightBlk->blkAddr());
if( pMidEntry)
{
// Add the midentry to the root block. Search to position and insert.
searchEntry( pMidEntry);
insertEntry( pMidEntry, 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 blkType, // Expected access type to read
FBtreeBlk **ppBlk) // (out) Return block
{
RCODE rc = FERR_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, blkType)))
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 blkType)
{
RCODE rc = FERR_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, blkType)))
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 blkType)
{
RCODE rc = FERR_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() == blkType)
{
// If block is of the same type then reset it and use it.
pNewBlk = m_CacheBlks[uiCachePos].pBlk;
pNewBlk->reset( blkType);
*ppBlk = pNewBlk;
goto Exit;
}
if( m_CacheBlks[uiCachePos].pBlk)
{
m_CacheBlks[uiCachePos].pBlk->Release();
}
if( blkType == ACCESS_BTREE_LEAF)
{
FBtreeLeaf * pLeafBlk;
if( (pLeafBlk = f_new FBtreeLeaf) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pLeafBlk->setup( m_uiEntrySize)))
{
pLeafBlk->Release();
goto Exit;
}
pLeafBlk->setCompareFunc( m_fnCompare, m_UserValue);
pNewBlk = (FBtreeBlk *) pLeafBlk;
}
else
{
FBtreeNonLeaf * pNonLeafBlk;
if( (pNonLeafBlk = f_new FBtreeNonLeaf) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pNonLeafBlk->setup( m_uiEntrySize)))
{
pNonLeafBlk->Release();
goto Exit;
}
pNonLeafBlk->setCompareFunc( m_fnCompare, m_UserValue);
pNewBlk = (FBtreeBlk *) pNonLeafBlk;
}
m_CacheBlks[uiCachePos].pBlk = pNewBlk;
*ppBlk = pNewBlk;
Exit:
return( rc);
}
/****************************************************************************
Desc: Read the block from disk.
****************************************************************************/
RCODE FBtreeBlk::readBlk(
F_FileHdl * pFileHdl,
FLMUINT uiBlkAddr)
{
RCODE rc;
FLMUINT uwBytesRead;
if( RC_BAD( rc = pFileHdl->Read(
uiBlkAddr * DYNSSET_BLOCK_SIZE, DYNSSET_BLOCK_SIZE,
m_pucBlkBuf, &uwBytesRead)))
{
flmAssert(0);
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(
F_FileHdl * pFileHdl)
{
RCODE rc;
FLMUINT uwBytesWritten;
FLMUINT uiBlkAddr = blkAddr();
if( RC_BAD( rc = pFileHdl->Write(
uiBlkAddr * DYNSSET_BLOCK_SIZE,
DYNSSET_BLOCK_SIZE,
m_pucBlkBuf,
&uwBytesWritten)))
{
// Most likely coding error rather than an I/O error.
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 = FERR_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.
VISIT: For now we use a temporary file system object on the stack
with a default configuration. In the future it would be better to
pass it in via an hSession or hShare file system object.
****************************************************************************/
RCODE FBtreeRoot::openFile()
{
RCODE rc = FERR_OK;
if( !m_pFileHdl)
{
rc = gv_FlmSysData.pFileSystem->CreateUnique( m_szIoPath,
FRSET_FILENAME_EXTENSION, F_IO_RDWR | F_IO_CREATE_DIR, &m_pFileHdl);
}
return( rc);
}
/****************************************************************************
Desc: Closes and deletes the tempoary file.
****************************************************************************/
void FBtreeRoot::closeFile()
{
if( m_pFileHdl)
{
m_pFileHdl->Close();
gv_FlmSysData.pFileSystem->Delete( m_szIoPath);
m_szIoPath[ 0] = 0;
m_pFileHdl = NULL;
}
return;
}