git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@209 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1189 lines
34 KiB
C++
1189 lines
34 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: B-tree searching.
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1990-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: fssearch.cpp 12321 2006-01-19 15:55:00 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
FSTATIC FLMUINT FSKeyCmp(
|
|
BTSK_p pStack,
|
|
FLMBYTE * key,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT drnDomain);
|
|
|
|
|
|
/***************************************************************************
|
|
Desc: Search the b-tree for a matching key. Set up stack for updates.
|
|
If index search using dinDomain. dinDomain MUST be 0 for data recs.
|
|
dinDomain is ONE more than the expected target for index ref sets.
|
|
This is because of the compare routine must have dinDomain contain
|
|
a value in order to test the domain.
|
|
|
|
In: stackRV - points to 'n' level stack, returns leaf stack level
|
|
key - points to key requested to search for
|
|
keylen - length of the key
|
|
dinDomain - domain of index key requested
|
|
|
|
Out: Stack set up for each level & stack[level].bsStatus set to...
|
|
BT_EQ_KEY (0) if equal key was found
|
|
BT_GT_KEY (1) if greater than key was found
|
|
BT_END_OF_DATA (0xFFFF) if marker was hit before eq or gt key found
|
|
|
|
Return: RCODE - FERR_OK,FERR_OLD_VIEW or other error
|
|
Notes: All buffers for the stack (pKeyBuf and BlkBuf must have been
|
|
allocated before the call.
|
|
Notes: btSearch responsible for bsLevel & bsBlock in stack structure.
|
|
*****************************************************************************/
|
|
RCODE FSBtSearch(
|
|
FDB_p pDb,
|
|
LFILE * pLFile, /* Logical file definition */
|
|
BTSK_p * pStackRV, /* Stack of variables for each level */
|
|
FLMBYTE * key, /* The input key to search for */
|
|
FLMUINT keyLen, /* Length of the key (not null term) */
|
|
FLMUINT dinDomain /* INDEXES ONLY - lower bounds of din */
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK; // Technically, don't need to set, but we
|
|
BTSK_p pStack = *pStackRV;
|
|
FLMBYTE * pKeyBuf = pStack->pKeyBuf;// Used to set key buf on each btsk.
|
|
FLMUINT uiBlkAddr;
|
|
FLMUINT uiKeyBufSize;
|
|
LFILE TmpLFile;
|
|
// don't want a maintenance problem.
|
|
|
|
uiKeyBufSize = (pLFile->uiLfType == LF_INDEX) ? MAX_KEY_SIZ : DIN_KEY_SIZ;
|
|
|
|
/* Get the correct root block specified in the LFILE. */
|
|
|
|
if( RC_BAD( rc = FSGetRootBlock( pDb, &pLFile, &TmpLFile, pStack)))
|
|
{
|
|
if (rc == FERR_NO_ROOT_BLOCK)
|
|
{
|
|
flmAssert( pLFile->uiRootBlk == BT_END);
|
|
rc = FERR_OK;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
/**----------------------------------------------
|
|
*** MAIN LOOP
|
|
*** Read each block going down the b-tree.
|
|
*** Save state information in the pStack[].
|
|
***---------------------------------------------*/
|
|
|
|
for(;;)
|
|
{
|
|
pStack->uiFlags = FULL_STACK;
|
|
pStack->uiKeyBufSize = uiKeyBufSize;
|
|
if( pStack->uiBlkType != BHT_NON_LEAF_DATA)
|
|
{
|
|
rc = FSBtScan( pStack, key, keyLen, dinDomain);
|
|
}
|
|
else
|
|
{
|
|
rc = FSBtScanNonLeafData( pStack, keyLen == 1
|
|
? *key
|
|
: flmBigEndianToUINT32( key));
|
|
}
|
|
if( RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// VISIT: Verify byLevel for cyclic loops.
|
|
|
|
if( !pStack->uiLevel) // Leaf level?
|
|
break; // Done
|
|
|
|
uiBlkAddr = FSChildBlkAddr( pStack );
|
|
|
|
pStack++; // Next btree pStack level.
|
|
pStack->pKeyBuf = pKeyBuf; // need to set for each pStack.
|
|
|
|
if( RC_BAD(rc = FSGetBlock( pDb, pLFile, uiBlkAddr, pStack )))
|
|
goto Exit;
|
|
}
|
|
*pStackRV = pStack; // Set the stack return value.
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Search the right-most end of the b-tree. Set up stack for updates.
|
|
Out: Stack set up for each level & stack[level].bsStatus set to...
|
|
BT_EQ_KEY (0) if equal key was found
|
|
BT_GT_KEY (1) if greater than key was found
|
|
BT_END_OF_DATA (0xFFFF) if marker was hit before eq or gt key found
|
|
*****************************************************************************/
|
|
RCODE FSBtSearchEnd(
|
|
FDB_p pDb,
|
|
LFILE * pLFile, /* Logical file definition */
|
|
BTSK_p * pStackRV, /* Stack of variables for each level */
|
|
FLMUINT uiDrn) /* Used to position and setup for update */
|
|
{
|
|
RCODE rc = FERR_OK; // Technically, don't need to set, but we
|
|
BTSK_p pStack = *pStackRV;
|
|
FLMBYTE * pKeyBuf = pStack->pKeyBuf;// Used to set key buf on each btsk.
|
|
FLMBYTE key[ DIN_KEY_SIZ + 4 ];/* Key buffer pointed to by stack */
|
|
FLMUINT uiBlkAddr;
|
|
LFILE TmpLFile;
|
|
|
|
/* Get the correct root block specified in the LFILE. */
|
|
if( RC_BAD( rc = FSGetRootBlock( pDb, &pLFile, &TmpLFile, pStack)))
|
|
{
|
|
if (rc == FERR_NO_ROOT_BLOCK)
|
|
{
|
|
flmAssert( pLFile->uiRootBlk == BT_END);
|
|
rc = FERR_OK;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
flmUINT32ToBigEndian( (FLMUINT32)uiDrn, key);
|
|
for(;;)
|
|
{
|
|
pStack->uiFlags = FULL_STACK;
|
|
pStack->uiKeyBufSize = DIN_KEY_SIZ;
|
|
|
|
// Remove all scanning from non-leaf data blocks (both formats).
|
|
if( pStack->uiLevel)
|
|
{
|
|
pStack->uiCurElm = pStack->uiBlkEnd; // Position past last element
|
|
FSBtPrevElm( pDb, pLFile, pStack ); // Build full key in pKeyBuf[]
|
|
}
|
|
else
|
|
{
|
|
if( pStack->uiBlkType != BHT_NON_LEAF_DATA)
|
|
{
|
|
rc = FSBtScan( pStack, key, DIN_KEY_SIZ, 0);
|
|
}
|
|
else
|
|
{
|
|
rc = FSBtScanNonLeafData( pStack, uiDrn);
|
|
}
|
|
if( RC_BAD( rc))
|
|
goto Exit;
|
|
}
|
|
if( !pStack->uiLevel) // Leaf level?
|
|
break; // Done
|
|
|
|
uiBlkAddr = FSChildBlkAddr( pStack );
|
|
pStack++; // Next btree pStack level.
|
|
pStack->pKeyBuf = pKeyBuf; // need to set for each pStack.
|
|
|
|
if( RC_BAD(rc = FSGetBlock( pDb, pLFile, uiBlkAddr, pStack )))
|
|
goto Exit;
|
|
}
|
|
*pStackRV = pStack; // Set the stack return value.
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns the root block of a passed-in LFILE
|
|
****************************************************************************/
|
|
RCODE FSGetRootBlock(
|
|
FDB_p pDb,
|
|
LFILE ** ppLFile, /* Logical file definition */
|
|
LFILE * pTmpLFile,
|
|
BTSK_p pStack) /* Stack of variables for each level */
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
LFILE * pLFile = *ppLFile;
|
|
FLMUINT uiBlkAddr;
|
|
FLMBOOL bRereadLFH = FALSE;
|
|
|
|
/*
|
|
Make Sure this is the correct root block in the LFILE area.
|
|
If not then read in the LFH structure and try again.
|
|
It would be nice to have a routine that reads only root blocks.
|
|
DSS: Added check for uiBlkAddr >= pDb->Loghdr.uiLogicalEOF
|
|
because the pLFile could have a root block address of an aborted
|
|
update transaction where the root block has not yet been fixed up by
|
|
the aborting transaction (in a shared environment).
|
|
*/
|
|
|
|
if( ((uiBlkAddr = pLFile->uiRootBlk) == BT_END) ||
|
|
(uiBlkAddr >= pDb->LogHdr.uiLogicalEOF))
|
|
{
|
|
bRereadLFH = TRUE;
|
|
}
|
|
else if( RC_BAD(rc = FSGetBlock( pDb, pLFile, uiBlkAddr, pStack)))
|
|
{
|
|
if( rc == FERR_DATA_ERROR || (rc == FERR_OLD_VIEW && !pDb->uiKilledTime))
|
|
{
|
|
bRereadLFH = TRUE;
|
|
pStack->uiBlkAddr = BT_END;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else /* Check for valid root block - Root Flag and Logical file number */
|
|
{
|
|
FLMBYTE * pBlk = pStack->pBlk;
|
|
|
|
if( !(BH_IS_ROOT_BLK( pBlk))
|
|
|| (pLFile->uiLfNum != FB2UW( &pBlk[ BH_LOG_FILE_NUM ])))
|
|
{
|
|
bRereadLFH = TRUE;
|
|
FSReleaseBlock( pStack, FALSE);
|
|
pStack->uiBlkAddr = BT_END;
|
|
}
|
|
}
|
|
|
|
/* Reread the LFH from disk if we do not have the root block */
|
|
if( bRereadLFH)
|
|
{
|
|
/*
|
|
If we are in a read transaction and we are using an HFSHARE
|
|
structure that is shared among multiple threads, copy the LFILE
|
|
structure so that we don't mess up a thread that may be doing
|
|
an update. The only members of the LFILE that might be
|
|
different after the FSLFileRead are as follows:
|
|
|
|
uiRootBlk and VER11 elements are: byLevel, uiNextDrn, and uiLastBlk.
|
|
|
|
None of these members are needed outside this routine during a
|
|
read transaction, so it is OK to use a temporary LFILE inside
|
|
this routine.
|
|
*/
|
|
|
|
if( flmGetDbTransType( pDb) == FLM_READ_TRANS)
|
|
{
|
|
f_memcpy( pTmpLFile, pLFile, sizeof( LFILE));
|
|
pLFile = pTmpLFile;
|
|
}
|
|
|
|
if( RC_BAD( rc = flmLFileRead( pDb, pLFile)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
/* If there is no root block, return right away */
|
|
|
|
if( (uiBlkAddr = pLFile->uiRootBlk) == BT_END)
|
|
{
|
|
/*
|
|
The caller of FSGetRootBlock is expected to check for and
|
|
handle FERR_NO_ROOT_BLOCK. It should NEVER be returned
|
|
to the application.
|
|
|
|
NOTE: Checking for BT_END_OF_DATA will not work in every
|
|
case to check for no root block because it is not always
|
|
initialized before calling FSGetRootBlock, so it could
|
|
have garbage in it if we don't end up going through this
|
|
code path.
|
|
*/
|
|
|
|
rc = RC_SET( FERR_NO_ROOT_BLOCK);
|
|
pStack->uiCmpStatus = BT_END_OF_DATA;
|
|
pStack->uiBlkAddr = BT_END;
|
|
goto Exit;
|
|
}
|
|
if( RC_BAD(rc = FSGetBlock( pDb, pLFile, uiBlkAddr, pStack)))
|
|
goto Exit; /* Usually returns OLD_VIEW or BLOCK_MODIFIED */
|
|
}
|
|
|
|
Exit:
|
|
*ppLFile = pLFile;
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Scan a b-tree block for a matching key at any b-tree block level.
|
|
Notes: This routine has been optimized for speed. Routine calls
|
|
have been taken out in order to improve the performance.
|
|
*****************************************************************************/
|
|
RCODE FSBtScan(
|
|
BTSK_p pStack, // [in/out] Stack of variables for each level
|
|
FLMBYTE * pSearchKey, // The input key to search for
|
|
FLMUINT uiSearchKeyLen, // Length of the key (not null terminated)
|
|
FLMUINT dinDomain) // INDEXES ONLY - lower bounds of din
|
|
{
|
|
RCODE rc = FERR_OK;// MUST initialize.
|
|
FLMBYTE * pCurElm; // Points to the current element.
|
|
FLMBYTE * pBlk; // Points to the cache block.
|
|
FLMBYTE * pKeyBuf; // Points to pStack->pKeyBuf (optimization).
|
|
FLMBYTE * pElmKey; // Points to the key within the element.
|
|
FLMUINT uiRecLen = 0; // Length of the record portion.
|
|
FLMUINT uiPrevKeyCnt; // Number left end bytes compressed
|
|
FLMUINT uiElmKeyLen; // Length of the current element's key portion
|
|
FLMUINT uiBlkType; // B-tree block type - Leaf or non-leaf.
|
|
FLMUINT uiElmOvhd; // Number bytes overhead for element.
|
|
FLMUINT uiBytesMatched; // Number of bytes matched with pSearchKey
|
|
// and cur element. Related to uiPrevKeyCnt.
|
|
uiBlkType = pStack->uiBlkType;
|
|
flmAssert( uiBlkType != BHT_NON_LEAF_DATA);
|
|
|
|
// Initialize stack variables for possibly better performance.
|
|
pKeyBuf = pStack->pKeyBuf;
|
|
pBlk = pStack->pBlk;
|
|
uiElmOvhd = pStack->uiElmOvhd;
|
|
pStack->uiCurElm = BH_OVHD;
|
|
pStack->uiKeyLen = pStack->uiPKC = pStack->uiPrevElmPKC = 0;
|
|
uiBytesMatched = 0;
|
|
|
|
for( ;;) // while bsCurElm < bsBlkEnd
|
|
{
|
|
pCurElm = &pBlk[ pStack->uiCurElm];
|
|
uiElmKeyLen = BBE_GETR_KL( pCurElm );
|
|
|
|
// Read in RAW mode - doesn't do all bit checking
|
|
|
|
if( (uiPrevKeyCnt = (BBE_GETR_PKC( pCurElm ))) > BBE_PKC_MAX)
|
|
{
|
|
uiElmKeyLen += (uiPrevKeyCnt & BBE_KL_HBITS) << BBE_KL_SHIFT_BITS;
|
|
uiPrevKeyCnt &= BBE_PKC_MAX;
|
|
}
|
|
|
|
// Should not have a non-zero PKC if we are on the first element
|
|
// of a block
|
|
|
|
if( uiPrevKeyCnt && pStack->uiCurElm == BH_OVHD)
|
|
{
|
|
rc = RC_SET( FERR_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the record portion length when on the leaf blocks.
|
|
|
|
if( uiBlkType == BHT_LEAF)
|
|
{
|
|
uiRecLen = BBE_GET_RL( pCurElm );
|
|
}
|
|
|
|
pStack->uiPrevElmPKC = pStack->uiPKC;
|
|
|
|
// The zero length key is the terminating element in a right-most block.
|
|
|
|
if( (pStack->uiKeyLen = uiPrevKeyCnt + uiElmKeyLen) == 0)
|
|
{
|
|
pStack->uiPrevElmPKC = f_min( uiBytesMatched, BBE_PKC_MAX);
|
|
pStack->uiPKC = 0;
|
|
pStack->uiCmpStatus = BT_END_OF_DATA;
|
|
goto Exit;
|
|
}
|
|
|
|
// Handle special case of left-end compression maxing out.
|
|
if( uiPrevKeyCnt == BBE_PKC_MAX && BBE_PKC_MAX < uiBytesMatched)
|
|
{
|
|
uiBytesMatched = BBE_PKC_MAX;
|
|
}
|
|
|
|
// Check out this element to see if the key matches.
|
|
if( uiPrevKeyCnt == uiBytesMatched)
|
|
{
|
|
pElmKey = &pCurElm[ uiElmOvhd ];
|
|
for(;;)
|
|
{
|
|
// All bytes of the search key are matched?
|
|
if( uiBytesMatched == uiSearchKeyLen)
|
|
{
|
|
pStack->uiPKC = f_min( uiBytesMatched, BBE_PKC_MAX);
|
|
// Build pKeyBuf with the search key because it matches.
|
|
// Current key is either equal or greater than search key.
|
|
|
|
if( uiSearchKeyLen < pStack->uiKeyLen)
|
|
{
|
|
f_memcpy( &pKeyBuf[ uiSearchKeyLen], pElmKey,
|
|
pStack->uiKeyLen - uiSearchKeyLen);
|
|
pStack->uiCmpStatus = BT_GT_KEY;
|
|
}
|
|
else
|
|
{
|
|
if( dinDomain)
|
|
{
|
|
FLMBYTE * pCurRef = pCurElm;
|
|
if( (dinDomain - 1) <
|
|
FSGetDomain( &pCurRef, (FLMBYTE)uiElmOvhd))
|
|
{
|
|
// Keep going...
|
|
goto Next_Element;
|
|
}
|
|
}
|
|
pStack->uiCmpStatus = BT_EQ_KEY;
|
|
}
|
|
f_memcpy( pKeyBuf, pSearchKey, uiSearchKeyLen);
|
|
goto Exit;
|
|
}
|
|
// .. else matches all the bytes in the element key.
|
|
if( uiBytesMatched == pStack->uiKeyLen)
|
|
{
|
|
pStack->uiPKC = f_min( uiBytesMatched, BBE_PKC_MAX);
|
|
// Need an outer break call here - forced to do a goto.
|
|
goto Next_Element;
|
|
}
|
|
|
|
// Compare the next byte in the search key and element
|
|
if( pSearchKey[ uiBytesMatched] != *pElmKey)
|
|
break;
|
|
uiBytesMatched++;
|
|
pElmKey++;
|
|
}
|
|
pStack->uiPKC = f_min( uiBytesMatched, BBE_PKC_MAX);
|
|
|
|
// Check if we are done comparing, if so build pKeyBuf[].
|
|
if( pSearchKey[ uiBytesMatched] < *pElmKey)
|
|
{
|
|
if( uiBytesMatched)
|
|
{
|
|
f_memcpy( pKeyBuf, pSearchKey, uiBytesMatched);
|
|
}
|
|
f_memcpy( &pKeyBuf[ uiBytesMatched], pElmKey,
|
|
pStack->uiKeyLen - uiBytesMatched);
|
|
pStack->uiCmpStatus = BT_GT_KEY;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if( uiPrevKeyCnt < uiBytesMatched)
|
|
{
|
|
// Current key > search key. Set pKeyBuf and break out.
|
|
pStack->uiPKC = uiPrevKeyCnt;
|
|
if( uiPrevKeyCnt)
|
|
{
|
|
// VISIT: Call a small memcpy here.
|
|
f_memcpy( pKeyBuf, pSearchKey, uiPrevKeyCnt);
|
|
}
|
|
f_memcpy( &pKeyBuf[ uiPrevKeyCnt], &pCurElm[ uiElmOvhd], uiElmKeyLen);
|
|
pStack->uiCmpStatus = BT_GT_KEY;
|
|
goto Exit;
|
|
}
|
|
// else the key is less than the search key (uiPrevKeyCnt > uiBytesMatched).
|
|
|
|
Next_Element:
|
|
|
|
/* Position to the next element */
|
|
pStack->uiCurElm += uiElmKeyLen + ((uiBlkType == BHT_LEAF )
|
|
? (BBE_KEY + uiRecLen)
|
|
: (BNE_IS_DOMAIN(pCurElm) ? (BNE_DOMAIN_LEN + uiElmOvhd)
|
|
: uiElmOvhd));
|
|
|
|
// Most common check first.
|
|
if( pStack->uiCurElm < pStack->uiBlkEnd)
|
|
continue;
|
|
|
|
if( pStack->uiCurElm == pStack->uiBlkEnd)
|
|
{
|
|
// On the equals conditition it may be OK in some very special cases.
|
|
pStack->uiCmpStatus = BT_END_OF_DATA;
|
|
goto Exit;
|
|
}
|
|
// Marched off the end of the block - something is corrupt.
|
|
rc = RC_SET( FERR_CACHE_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Binary search into a non-leaf data record block.
|
|
In: Stack, key, keyLen, dinDomain (value for indexes only else 0)
|
|
Out: Stack set up - bsStatus set to following...
|
|
BT_EQ_KEY (0) if equal key was found
|
|
BT_GT_KEY (1) if greater than key was found
|
|
BT_END_OF_DATA (0xFFFF) if marker was hit before eq or gt key found
|
|
Return: RCODE - FERR_OK
|
|
*****************************************************************************/
|
|
RCODE FSBtScanNonLeafData(
|
|
BTSK_p pStack,
|
|
FLMUINT uiDrn)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pBlk = pStack->pBlk; // Points to the cache block.
|
|
FLMUINT uiLow = 0,
|
|
uiMid,
|
|
uiHigh = ((pStack->uiBlkEnd - BH_OVHD) >> 3) - 1;
|
|
FLMUINT uiTblSize = uiHigh;
|
|
FLMUINT uiCurDrn;
|
|
|
|
pStack->uiCmpStatus = BT_GT_KEY;
|
|
for(;;)
|
|
{
|
|
uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2
|
|
|
|
uiCurDrn = flmBigEndianToUINT32( &pBlk[ BH_OVHD + (uiMid << 3)]);
|
|
if( uiCurDrn == 0)
|
|
{
|
|
// Special case - at the end of a rightmost block.
|
|
pStack->uiCmpStatus = BT_EQ_KEY; //BT_END_OF_DATA;
|
|
break;
|
|
}
|
|
if( uiDrn == uiCurDrn)
|
|
{
|
|
// Remember a data record can span multiple blocks (same DRN).
|
|
while( uiMid)
|
|
{
|
|
uiCurDrn = flmBigEndianToUINT32(
|
|
&pBlk[ BH_OVHD + ((uiMid - 1) << 3)]);
|
|
|
|
if( uiDrn != uiCurDrn)
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiMid--;
|
|
}
|
|
pStack->uiCmpStatus = BT_EQ_KEY;
|
|
break;
|
|
}
|
|
|
|
// Down to one item if too high then position to next item.
|
|
if( uiLow >= uiHigh)
|
|
{
|
|
if( (uiDrn > uiCurDrn) && uiMid < uiTblSize)
|
|
uiMid++;
|
|
break;
|
|
}
|
|
|
|
// If too high then try lower section
|
|
if( uiDrn < uiCurDrn)
|
|
{
|
|
// First item too high?
|
|
if( uiMid == 0)
|
|
break;
|
|
uiHigh = uiMid - 1;
|
|
}
|
|
else // try upper section because mid value is too low.
|
|
{
|
|
if( uiMid == uiTblSize)
|
|
{
|
|
uiMid++;
|
|
pStack->uiCmpStatus = BT_END_OF_DATA;
|
|
break; // Done - Hit the top
|
|
}
|
|
uiLow = uiMid + 1; /* Too low */
|
|
}
|
|
}
|
|
|
|
// Set curElm and the key buffer.
|
|
pStack->uiCurElm = BH_OVHD + (uiMid << 3);
|
|
flmUINT32ToBigEndian( (FLMUINT32)uiCurDrn, pStack->pKeyBuf);
|
|
|
|
//Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Read the block information and initialize all needed pStack elements.
|
|
Assumes that bsBlock is correct. Normalizes BH_TYPE to[ 0,1] values.
|
|
Note that byElmOvhd cannot be set for non-leaf blocks unless
|
|
the version number is passed in.
|
|
Out: stack updated with needed block contents.
|
|
Notes: Same code is in FSGetBlock().
|
|
****************************************************************************/
|
|
void FSBlkToStack(
|
|
BTSK_p pStack)
|
|
{
|
|
FLMBYTE * pBlk = pStack->pBlk;
|
|
FLMUINT uiBlkType;
|
|
|
|
pStack->uiBlkType = uiBlkType = (FLMUINT)(BH_GET_TYPE( pBlk ));
|
|
|
|
/**
|
|
*** The standard overhead is used in the pStack
|
|
*** Compares are made to determine if the element is extended.
|
|
**/
|
|
if( uiBlkType == BHT_LEAF)
|
|
{
|
|
pStack->uiElmOvhd = BBE_KEY;
|
|
}
|
|
else if( uiBlkType == BHT_NON_LEAF_DATA)
|
|
{
|
|
pStack->uiElmOvhd = BNE_DATA_OVHD;
|
|
}
|
|
else if( uiBlkType == BHT_NON_LEAF)
|
|
{
|
|
pStack->uiElmOvhd = BNE_KEY_START;
|
|
}
|
|
else if( uiBlkType == BHT_NON_LEAF_COUNTS)
|
|
{
|
|
pStack->uiElmOvhd = BNE_KEY_COUNTS_START;
|
|
}
|
|
else
|
|
{
|
|
flmAssert(0);
|
|
pStack->uiElmOvhd = BNE_KEY_START;
|
|
}
|
|
|
|
pStack->uiKeyLen = pStack->uiPKC = pStack->uiPrevElmPKC = 0;
|
|
pStack->uiCurElm = BH_OVHD;
|
|
pStack->uiBlkEnd = (FLMUINT)FB2UW( &pBlk[ BH_ELM_END ] );
|
|
pStack->uiLevel = (FLMUINT)pBlk[ BH_LEVEL ];
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Scan to a specific element (pStack->uiCurElm) in a b-tree block.
|
|
Builds the current key in pStack->pKeyBuf and sets up the stack
|
|
for an insert of an element.
|
|
The block must exist and never contain a lone LEM (last elm marker).
|
|
Notes: This may be called at ANY b-tree level.
|
|
*****************************************************************************/
|
|
RCODE FSBtScanTo(
|
|
BTSK_p pStack, // Stack of variables for each level
|
|
FLMBYTE * pSearchKey, // The input key to search for
|
|
FLMUINT uiSearchKeyLen, // Length of the key (not null term)
|
|
FLMUINT dinDomain // INDEXES ONLY - lower bounds of din
|
|
)
|
|
{
|
|
FLMBYTE * pCurElm; // Points to the current element.
|
|
FLMBYTE * pBlk; // Points to block - optimization
|
|
FLMBYTE * pKeyBuf = pStack->pKeyBuf; // Key buffer to fill
|
|
FLMBYTE * pPrevElm; // Points to previous element.
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiPrevKeyCnt = 0; // Current elements previous key count.
|
|
FLMUINT uiElmKeyLen = 0; // Current element key length.
|
|
FLMUINT uiTargetCurElm = pStack->uiCurElm;// Target value to scan to.
|
|
FLMUINT uiElmOvhd; // Number bytes overhead for element.
|
|
FLMUINT uiKeyBufLen; // Length of key in pKeyBuf.
|
|
|
|
// Initialize section
|
|
FSBlkToStack( pStack ); // Read information & put to stack.
|
|
pBlk = pStack->pBlk;
|
|
uiElmOvhd = pStack->uiElmOvhd;
|
|
|
|
if( uiTargetCurElm > pStack->uiBlkEnd)
|
|
{
|
|
uiTargetCurElm = pStack->uiBlkEnd;
|
|
}
|
|
|
|
// The code is easy for non-leaf data blocks.
|
|
if( pStack->uiBlkType == BHT_NON_LEAF_DATA)
|
|
{
|
|
// target may be any byte offset in the block.
|
|
while( pStack->uiCurElm < uiTargetCurElm )
|
|
{
|
|
pStack->uiCurElm += BNE_DATA_OVHD;
|
|
}
|
|
if( uiTargetCurElm < pStack->uiBlkEnd)
|
|
{
|
|
flmCopyDrnKey( pKeyBuf, &pBlk[ pStack->uiCurElm ]);
|
|
pStack->uiCmpStatus = BT_EQ_KEY;
|
|
}
|
|
else
|
|
{
|
|
pStack->uiCmpStatus = BT_END_OF_DATA;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Note: There is no way pPrevElm can be accessed and point to NULL
|
|
// unless the block is corrupt and starts with a PKC value.
|
|
pCurElm = NULL;
|
|
uiKeyBufLen = 0;
|
|
while( pStack->uiCurElm < uiTargetCurElm)
|
|
{
|
|
pPrevElm = pCurElm;
|
|
pCurElm = &pBlk[ pStack->uiCurElm ];
|
|
uiPrevKeyCnt = BBE_GET_PKC( pCurElm );
|
|
uiElmKeyLen = BBE_GET_KL( pCurElm );
|
|
if( (pStack->uiKeyLen = uiPrevKeyCnt + uiElmKeyLen) > pStack->uiKeyBufSize)
|
|
{
|
|
rc = RC_SET( FERR_CACHE_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy the minimum number of bytes from the previous element.
|
|
// A memcpy may be slower because this shouldn't copy very many bytes.
|
|
if( uiPrevKeyCnt > uiKeyBufLen)
|
|
{
|
|
FLMUINT uiCopyLength = uiPrevKeyCnt - uiKeyBufLen;
|
|
FLMBYTE * pSrcPtr = &pPrevElm[ uiElmOvhd];
|
|
|
|
flmAssert( pCurElm != NULL);
|
|
// VISIT: Replace with an inline memcpy.
|
|
while( uiCopyLength--)
|
|
{
|
|
pKeyBuf[ uiKeyBufLen++] = *pSrcPtr++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiKeyBufLen = uiPrevKeyCnt;
|
|
}
|
|
|
|
// Position to the next element
|
|
|
|
if( pStack->uiBlkType == BHT_LEAF)
|
|
{
|
|
pStack->uiCurElm += (FLMUINT)(BBE_LEN( pCurElm ));
|
|
if( pStack->uiCurElm + BBE_LEM_LEN >= pStack->uiBlkEnd)
|
|
{
|
|
f_memcpy( &pKeyBuf[ uiKeyBufLen], &pCurElm[ uiElmOvhd], uiElmKeyLen);
|
|
|
|
if( uiSearchKeyLen && (pStack->uiCurElm < pStack->uiBlkEnd))
|
|
{
|
|
// This is a rare and unsure case where caller needs to have
|
|
// pStack->uiPrevElmPKC set correctly.
|
|
FSKeyCmp( pStack, pSearchKey, uiSearchKeyLen, dinDomain);
|
|
}
|
|
goto Hit_End;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pStack->uiCurElm += (FLMUINT)(BNE_LEN( pStack, pCurElm)); /* Adds in DOMAIN */
|
|
|
|
if( pStack->uiCurElm >= pStack->uiBlkEnd)
|
|
{
|
|
// Make sure that pKeyBuf has the last element's key.
|
|
f_memcpy( &pKeyBuf[ uiKeyBufLen], &pCurElm[ uiElmOvhd], uiElmKeyLen);
|
|
|
|
Hit_End:
|
|
pStack->uiKeyLen = 0;
|
|
pStack->uiPrevElmPKC = pStack->uiPKC; /* Change previous element */
|
|
pStack->uiPKC = 0;
|
|
pStack->uiCmpStatus = BT_END_OF_DATA;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
// Check to see if the scan hit where you wanted, if so setup stack & pKeyBuf.
|
|
|
|
if( pStack->uiCurElm == uiTargetCurElm)
|
|
{
|
|
// BE CAREFUL. Names with "target" point to this element. All other
|
|
// references include pCurElm point to the previous element.
|
|
FLMBYTE * pTargetCurElm = CURRENT_ELM( pStack);
|
|
FLMUINT uiTargetPrevKeyCnt = BBE_GET_PKC( pTargetCurElm);
|
|
FLMUINT uiTargetElmKeyLen = BBE_GET_KL( pTargetCurElm);
|
|
|
|
// Compare the current key so that prevPKC and PKC are set.
|
|
pStack->uiCmpStatus = BT_EQ_KEY;
|
|
if( pCurElm)
|
|
{
|
|
if( uiSearchKeyLen)
|
|
{
|
|
// Copy the entire key into keyBuf to compare - output is ->uiPKC
|
|
f_memcpy( &pKeyBuf[ uiPrevKeyCnt], &pCurElm[ uiElmOvhd], uiElmKeyLen);
|
|
pStack->uiCmpStatus = FSKeyCmp( pStack, pSearchKey,
|
|
uiSearchKeyLen, dinDomain);
|
|
}
|
|
else if( uiTargetPrevKeyCnt > uiKeyBufLen)
|
|
|
|
{
|
|
// Copy what is necessary. uiPrevKeyCnt is equal to uiKeyBufLen.
|
|
|
|
FLMUINT uiCopyLength = uiTargetPrevKeyCnt - uiKeyBufLen;
|
|
FLMBYTE * pSrcPtr = &pCurElm[ uiElmOvhd];
|
|
|
|
// VISIT: Replace with an inline memcpy.
|
|
while( uiCopyLength--)
|
|
{
|
|
pKeyBuf[ uiKeyBufLen++] = *pSrcPtr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( (pStack->uiKeyLen =
|
|
uiTargetPrevKeyCnt + uiTargetElmKeyLen) > pStack->uiKeyBufSize)
|
|
{
|
|
rc = RC_SET( FERR_CACHE_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiTargetElmKeyLen)
|
|
{
|
|
f_memcpy( &pKeyBuf[ uiTargetPrevKeyCnt],
|
|
&pTargetCurElm[ uiElmOvhd], uiTargetElmKeyLen);
|
|
|
|
if( uiSearchKeyLen)
|
|
{
|
|
pStack->uiCmpStatus = FSKeyCmp( pStack, pSearchKey, uiSearchKeyLen, dinDomain );
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
/*
|
|
This will be hit on a condition where we want to insert "ABCD (10)" into
|
|
ABCD (15)
|
|
ABCD (5)
|
|
between the two keys. (10) is the DIN value. Because the keys are equal
|
|
we don't have to call compare again. The uiPKC is the uiPrevKeyCnt value.
|
|
*/
|
|
pStack->uiPrevElmPKC = pStack->uiPKC;
|
|
pStack->uiPKC = uiTargetPrevKeyCnt;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Copy the remaining bytes of the current key into the buffer.
|
|
if( pCurElm)
|
|
{
|
|
f_memcpy( &pKeyBuf[ uiPrevKeyCnt], &pCurElm[ uiElmOvhd], uiElmKeyLen);
|
|
}
|
|
pStack->uiCmpStatus = BT_GT_KEY;
|
|
// Not necessary to compare because we just wanted a rough position & keyBuf.
|
|
}
|
|
|
|
Exit:
|
|
return( rc );
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Standard key compare routine for a key and a b-tree element
|
|
Return: BT_EQ_KEY (0) if equal key was found
|
|
BT_GT_KEY (1) if greater than key was found
|
|
BT_LT_KEY (2) if less than (keep going)
|
|
*****************************************************************************/
|
|
FSTATIC FLMUINT FSKeyCmp(
|
|
BTSK_p pStack, /* Stack of variables for each level */
|
|
FLMBYTE * key, /* The input key to search for */
|
|
FLMUINT uiKeyLen, /* Length of the key (not null term) */
|
|
FLMUINT dinDomain) /* INDEXES ONLY - lower bounds of din*/
|
|
{
|
|
FLMBYTE * pCurElm;
|
|
FLMBYTE * pKeyBuf; /* Current element's key */
|
|
FLMUINT uiCmp; /* Return value */
|
|
FLMUINT uiCompareLen; /* Length to compare */
|
|
FLMUINT uiOrigCompareLen; /* Original compare length */
|
|
FLMUINT uiCurElmKeyLen; /* Current element's length */
|
|
FLMUINT uiPKCTemp;
|
|
|
|
/* Get again the current element's key length & compute compare length */
|
|
uiCurElmKeyLen = pStack->uiKeyLen;
|
|
uiOrigCompareLen = uiCompareLen = f_min( uiKeyLen, uiCurElmKeyLen );
|
|
pKeyBuf = pStack->pKeyBuf; /* Point to the local key buffer */
|
|
pStack->uiPrevElmPKC = pStack->uiPKC; /* Change previous element */
|
|
pStack->uiPKC = 0;
|
|
|
|
while( uiCompareLen--)
|
|
{
|
|
if( *key++ == *pKeyBuf++) /* Just do a left-right compare */
|
|
continue;
|
|
|
|
uiPKCTemp = uiOrigCompareLen - (uiCompareLen + 1);
|
|
pStack->uiPKC = (uiPKCTemp > BBE_PKC_MAX) ? BBE_PKC_MAX : uiPKCTemp;
|
|
/* Not equal so return */
|
|
return( (*(--key) < *(--pKeyBuf)) ? BT_GT_KEY : BT_LT_KEY );
|
|
}
|
|
|
|
/* Set the prev key count value */
|
|
pStack->uiPKC = (uiOrigCompareLen <= BBE_PKC_MAX)
|
|
? uiOrigCompareLen
|
|
: BBE_PKC_MAX;
|
|
|
|
|
|
/** Set return status, If equal then compare the dinDomain if needed. **/
|
|
uiCmp = uiKeyLen > uiCurElmKeyLen ? BT_LT_KEY :
|
|
(uiKeyLen < uiCurElmKeyLen ? BT_GT_KEY : BT_EQ_KEY) ;
|
|
|
|
if( (uiCmp == BT_EQ_KEY) && dinDomain)
|
|
{
|
|
pCurElm = CURRENT_ELM( pStack );
|
|
if( (dinDomain - 1) < FSGetDomain( &pCurElm, (FLMBYTE)pStack->uiElmOvhd ))
|
|
{
|
|
uiCmp = BT_LT_KEY; /* Keep going */
|
|
}
|
|
}
|
|
return( uiCmp );
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Desc: Goto the next element within the block
|
|
Notes: The key is NOT moved into the byKeyBuf[]
|
|
LEAF: Returns FERR_BT_END_OF_DATA if positioned on the last-element-marker
|
|
(LEM) or is NOW positioned on the LEM.
|
|
****************************************************************************/
|
|
RCODE FSBlkNextElm(
|
|
BTSK_p pStack) /* Stack of variables for each level */
|
|
{
|
|
FLMBYTE * elmPtr;
|
|
FLMUINT uiElmSize;
|
|
RCODE rc = FERR_BT_END_OF_DATA; /* Code assumes at the end */
|
|
|
|
elmPtr = &pStack->pBlk[ pStack->uiCurElm ];
|
|
|
|
if( pStack->uiBlkType == BHT_LEAF)
|
|
{
|
|
uiElmSize = BBE_LEN( elmPtr );
|
|
if( pStack->uiCurElm + BBE_LEM_LEN < pStack->uiBlkEnd )
|
|
{
|
|
if( (pStack->uiCurElm += uiElmSize) + BBE_LEM_LEN < pStack->uiBlkEnd)
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pStack->uiBlkType == BHT_NON_LEAF_DATA)
|
|
uiElmSize = BNE_DATA_OVHD;
|
|
else
|
|
uiElmSize = (FLMUINT) BNE_LEN( pStack, elmPtr); /* Adds in DOMAIN if present */
|
|
|
|
if( pStack->uiCurElm < pStack->uiBlkEnd)
|
|
{
|
|
/* Check if this is not the last element within the block */
|
|
if( (pStack->uiCurElm += uiElmSize) < pStack->uiBlkEnd)
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
return( rc );
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Go to the next element in the logical b-tree while building the key
|
|
Notes: You may be at any level of the b-tree!
|
|
*****************************************************************************/
|
|
RCODE FSBtNextElm(
|
|
FDB_p pDb,
|
|
LFILE * pLFile, /* Logical file definition */
|
|
BTSK_p pStack) /* Stack of variables for each level */
|
|
{
|
|
RCODE rc = FERR_OK; /* Return code */
|
|
|
|
if( pStack->uiCurElm < BH_OVHD ) /* Before first element in block? */
|
|
{
|
|
pStack->uiCurElm = BH_OVHD;
|
|
}
|
|
else if( (rc = FSBlkNextElm( pStack)) == FERR_BT_END_OF_DATA)
|
|
{
|
|
FLMBYTE * pBlk = BLK_ELM_ADDR( pStack, BH_NEXT_BLK );
|
|
FLMUINT blkNum = FB2UD( pBlk );
|
|
if( blkNum != BT_END ) /* If not end, read in the next block */
|
|
{
|
|
/* Current element was last element in the block - goto next block */
|
|
|
|
if( RC_OK(rc = FSGetBlock( pDb, pLFile, blkNum, pStack )))
|
|
{
|
|
/* Set blk end and adjust parent block to next element */
|
|
pBlk = pStack->pBlk;
|
|
pStack->uiBlkEnd = (FLMUINT)FB2UW( &pBlk[ BH_ELM_END ]);
|
|
pStack->uiCurElm = BH_OVHD;
|
|
pStack->uiPKC = 0;
|
|
pStack->uiPrevElmPKC = 0;
|
|
|
|
if( pStack->uiFlags & FULL_STACK) /* Adjust the stack if needed */
|
|
rc = FSAdjustStack( pDb, pLFile, pStack, TRUE);
|
|
}
|
|
}
|
|
} /* At this point if rc == FERR_BT_END_OF_DATA then is at last block, else OK */
|
|
|
|
if( RC_OK(rc)) /* If there is a next element, setup stack and byKeyBuf[] */
|
|
{
|
|
FLMBYTE * pCurElm = CURRENT_ELM( pStack );
|
|
FLMUINT uiKeyLen;
|
|
|
|
if( pStack->uiBlkType == BHT_NON_LEAF_DATA)
|
|
{
|
|
flmCopyDrnKey( pStack->pKeyBuf, pCurElm);
|
|
goto Exit;
|
|
}
|
|
|
|
/* Copy key to the stack->pKeyBuf & check for end key */
|
|
if( (uiKeyLen = BBE_GET_KL( pCurElm)) != 0)
|
|
{
|
|
FLMUINT uiPKC = (FLMUINT)(BBE_GET_PKC( pCurElm ));
|
|
|
|
if( uiKeyLen + uiPKC <= pStack->uiKeyBufSize )
|
|
{
|
|
pStack->uiKeyLen = (uiKeyLen + uiPKC);
|
|
f_memcpy( &pStack->pKeyBuf[ uiPKC],
|
|
&pCurElm[ pStack->uiElmOvhd ], uiKeyLen);
|
|
}
|
|
else
|
|
{
|
|
rc = RC_SET( FERR_CACHE_ERROR);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
return( rc );
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Adjust a full stack if pStack->byFlags & FULL_STACK
|
|
If the stack->byFlags is set to FULL_STACK then the parent must
|
|
point to the next element when changing to the next block in order
|
|
for FSBtInsert() or FSBtDelete() to work correctly during block
|
|
splits.
|
|
*****************************************************************************/
|
|
RCODE FSAdjustStack(
|
|
FDB_p pDb,
|
|
LFILE * pLFile, /* Logical file definition */
|
|
BTSK_p pStack, /* Stack of variables for each level */
|
|
FLMBOOL bMovedNext)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
pStack->uiFlags = FULL_STACK;
|
|
|
|
/**---------------------------------------------------------------------
|
|
*** Pop the stack and go to the next element
|
|
*** This is a recursive call back to FSBtNextElm() or FSBtPrevElm()
|
|
*** Watch out, this will not work if the concurrency model changes
|
|
*** to a b-tree locking method like other products use.
|
|
***--------------------------------------------------------------------*/
|
|
|
|
/* Pop the pStack going to the parents block */
|
|
pStack--;
|
|
|
|
/* It is very rare that block will need to be read. Maybe
|
|
some sort of split case. The block should have already have been
|
|
read.
|
|
*/
|
|
|
|
if( RC_OK(rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
|
|
{
|
|
rc = bMovedNext
|
|
? FSBtNextElm( pDb, pLFile, pStack) /* Next element */
|
|
: FSBtPrevElm( pDb, pLFile, pStack); /* Previous element */
|
|
}
|
|
|
|
/* Push the pStack and unpin the current block */
|
|
pStack++;
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Read in a block from the cache and set most stack elements.
|
|
*****************************************************************************/
|
|
|
|
RCODE FSGetBlock(
|
|
FDB_p pDb,
|
|
LFILE * pLFile, // Logical file definition
|
|
FLMUINT uiBlkAddr, // Block Address to read.
|
|
BTSK_p pStack) // Stack of variables for each level
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pBlk;
|
|
|
|
/*
|
|
Release whatever block might be there first. If no block is
|
|
there (pStack->pSCache == NULL), FSReleaseBlock does nothing. Stacks
|
|
are ALWAYS initialized to set pSCache to NULL, so this is OK to call
|
|
even if stack has never been used to read a block yet.
|
|
*/
|
|
|
|
if( pStack->pSCache)
|
|
{
|
|
/* If we already have the block we want, keep it! */
|
|
|
|
if( pStack->pSCache->uiBlkAddress != uiBlkAddr)
|
|
{
|
|
FSReleaseBlock( pStack, FALSE);
|
|
}
|
|
}
|
|
|
|
if( !pStack->pSCache)
|
|
{
|
|
flmAssert( !pStack->pBlk);
|
|
|
|
if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF,
|
|
uiBlkAddr, NULL, &pStack->pSCache)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pStack->pBlk = pBlk = pStack->pSCache->pucBlk;
|
|
if( pStack->uiBlkAddr != uiBlkAddr)
|
|
{
|
|
FLMUINT uiBlkType;
|
|
|
|
pStack->uiBlkAddr = uiBlkAddr;
|
|
|
|
// set other pStack elements.
|
|
|
|
pStack->uiBlkType = uiBlkType = (FLMUINT)(BH_GET_TYPE( pBlk ));
|
|
|
|
/**
|
|
*** The standard overhead is used in the stack
|
|
*** Compares are made to determine if the element is extended.
|
|
**/
|
|
|
|
if( uiBlkType == BHT_LEAF)
|
|
{
|
|
pStack->uiElmOvhd = BBE_KEY;
|
|
}
|
|
else if( uiBlkType == BHT_NON_LEAF_DATA)
|
|
{
|
|
pStack->uiElmOvhd = BNE_DATA_OVHD;
|
|
}
|
|
else if( uiBlkType == BHT_NON_LEAF)
|
|
{
|
|
pStack->uiElmOvhd = BNE_KEY_START;
|
|
}
|
|
else if( uiBlkType == BHT_NON_LEAF_COUNTS)
|
|
{
|
|
pStack->uiElmOvhd = BNE_KEY_COUNTS_START;
|
|
}
|
|
else
|
|
{
|
|
rc = RC_SET( FERR_DATA_ERROR);
|
|
FSReleaseBlock( pStack, FALSE);
|
|
goto Exit;
|
|
}
|
|
|
|
pStack->uiKeyLen = pStack->uiPKC = pStack->uiPrevElmPKC = 0;
|
|
pStack->uiLevel = (FLMUINT)pBlk[ BH_LEVEL ];
|
|
pStack->uiCurElm = BH_OVHD;
|
|
}
|
|
pStack->uiBlkEnd = (FLMUINT)FB2UW( &pBlk[ BH_ELM_END ] );
|
|
|
|
Exit:
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Release all of the cache associated with a stack.
|
|
***************************************************************************/
|
|
void FSReleaseStackCache(
|
|
BTSK_p pStack,
|
|
FLMUINT uiNumLevels,
|
|
FLMBOOL bMutexAlreadyLocked)
|
|
{
|
|
FLMBOOL bSemLocked = FALSE;
|
|
|
|
while( uiNumLevels)
|
|
{
|
|
if( pStack->pSCache)
|
|
{
|
|
if( !bSemLocked && !bMutexAlreadyLocked)
|
|
{
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
bSemLocked = TRUE;
|
|
}
|
|
ScaReleaseCache( pStack->pSCache, TRUE);
|
|
pStack->pSCache = NULL;
|
|
pStack->pBlk = NULL;
|
|
}
|
|
uiNumLevels--;
|
|
pStack++;
|
|
}
|
|
if( bSemLocked)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
}
|
|
|
|
|