Files
mars-flaim/flaim/src/fssplblk.cpp

911 lines
22 KiB
C++

//-------------------------------------------------------------------------
// Desc: B-tree block splitting.
// Tabs: 3
//
// Copyright (c) 1991-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: fssplblk.cpp 12289 2006-01-19 14:56:21 -0700 (Thu, 19 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC RCODE FSBtResetStack(
FDB * pDb,
LFILE * pLFile,
BTSK ** ppStack,
FLMBYTE * pElement,
FLMUINT uiElmLen);
FSTATIC RCODE FSMoveToNextBlk(
FDB * pDb,
LFILE * pLFile,
BTSK ** ppStack,
FLMUINT nextBlkNum,
FLMBYTE * pElement,
FLMUINT elmLen,
FLMUINT uiBlockSize,
FLMUINT * blkNumRV,
FLMUINT * curElmRV);
/****************************************************************************
Desc: Split a block using different algorithms depending on context
****************************************************************************/
RCODE FSBlkSplit(
FDB * pDb,
LFILE * pLFile,
BTSK ** ppStack,
FLMBYTE * pElement,
FLMUINT elmLen)
{
RCODE rc = FERR_OK;
FLMUINT uiBlockSize = pDb->pFile->FileHdr.uiBlockSize;
BTSK * pStack = *ppStack;
FLMUINT oldCurElm = pStack->uiCurElm;
FLMUINT uiElmOvhd = pStack->uiElmOvhd;
FLMUINT tempWord;
FLMUINT elmKeyLen;
FLMUINT prevKeyCnt;
FLMUINT curElm;
FLMBYTE * curElmPtr;
FLMBYTE * pBlk;
FLMUINT blkNum = pStack->uiBlkAddr;
FLMUINT uiBlkEnd;
BTSK newBlkStk;
BTSK nextBlkStk;
FLMBYTE * newBlkPtr;
FLMBYTE * nextBlkPtr;
FLMUINT newBlkNum;
FLMUINT nextBlkNum;
FLMUINT blkNumRestore = 0;
FLMUINT curElmRestore = 0;
FLMBOOL bNewRootFlag;
FLMBOOL bDoubleSplit;
DB_STATS * pDbStats;
bNewRootFlag = FALSE;
bDoubleSplit = FALSE;
FSInitStackCache( &newBlkStk, 1);
FSInitStackCache( &nextBlkStk, 1);
if ((pDbStats = pDb->pDbStats) != NULL)
{
LFILE_STATS* pLFileStats;
if ((pLFileStats = fdbGetLFileStatPtr( pDb, pLFile)) != NULL)
{
pLFileStats->bHaveStats = pDbStats->bHaveStats = TRUE;
pLFileStats->ui64BlockSplits++;
}
}
// If there is room to move data to the next block then do it
// and update the parent block with the new last element. Otherwise...
// Divide data from current block and next block into the new block
// (2/3 split). Delete parent element and update parent /w 2 elements.
if (pStack->uiFlags & NO_STACK)
{
if (RC_BAD( rc = FSBtResetStack( pDb, pLFile, &pStack, pElement, elmLen)))
{
goto Exit;
}
}
// Log the block before modifying it
if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
{
goto Exit;
}
pBlk = pStack->pBlk;
bNewRootFlag = BH_IS_ROOT_BLK( pBlk);
if ((nextBlkNum = FB2UD( &pBlk[BH_NEXT_BLK])) != BT_END)
{
// Try to move the elements from the current block to the next block
// while inserting the element. If this succeeds than we are better
// off than spliting blocks. If succeeds then all block operations
// have been taken care of.
if (RC_OK( rc = FSMoveToNextBlk( pDb, pLFile, &pStack, nextBlkNum,
pElement, elmLen, uiBlockSize, &blkNumRestore, &curElmRestore)))
{
goto FSBlkSplit_position;
}
if (rc != FERR_BLOCK_FULL)
{
goto Exit;
}
}
// Initialize variables, create a new block and setup header. This is
// common stuff that will be done if you are splitting a RIGHT-MOST
// block or a middle block. Remember we could be working with leaf or
// non-leaf blks.
if (RC_BAD( rc = ScaCreateBlock( pDb, pLFile, &newBlkStk.pSCache)))
{
goto Exit;
}
newBlkStk.pBlk = newBlkStk.pSCache->pucBlk;
newBlkPtr = GET_CABLKPTR( &newBlkStk);
newBlkNum = GET_BH_ADDR( newBlkPtr);
pBlk = pStack->pBlk;
uiBlkEnd = pStack->uiBlkEnd;
UD2FBA( nextBlkNum, &newBlkPtr[BH_NEXT_BLK]);
UD2FBA( blkNum, &newBlkPtr[BH_PREV_BLK]);
UD2FBA( newBlkNum, &pBlk[BH_NEXT_BLK]);
// Write over the root bit if present as set type.
newBlkPtr[BH_TYPE] = pBlk[BH_TYPE] = (FLMBYTE) (BH_GET_TYPE( pBlk));
newBlkPtr[BH_LEVEL] = pBlk[BH_LEVEL];
tempWord = FB2UW( &pBlk[BH_LOG_FILE_NUM]);
UW2FBA( tempWord, &newBlkPtr[BH_LOG_FILE_NUM]);
UW2FBA( BH_OVHD, &newBlkPtr[BH_BLK_END]);
// In both cases (middle or end split) if you split at the wrong place
// you will still not be able to fit the element[] in the block. The
// while loop will insure at least a split into two blocks where there
// may not be room to move any elements from the next block.
pStack->uiCurElm = (nextBlkNum == BT_END)
? (FFILE_MAX_FILL * pDb->pFile->FileHdr.uiBlockSize / 100)
: ((uiBlockSize / 20) * 13); // Leave at least 65% full
if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0)))
{
goto Exit;
}
if (pStack->uiCmpStatus == BT_GT_KEY)
{
// Save the key in the pKeyBuf
curElmPtr = &pBlk[pStack->uiCurElm];
if (pStack->uiBlkType == BHT_NON_LEAF_DATA)
{
flmCopyDrnKey( pStack->pKeyBuf, curElmPtr);
}
else
{
elmKeyLen = (FLMUINT) (BBE_GET_KL( curElmPtr));
if (elmKeyLen)
{
// Copy key into pKeyBuf
prevKeyCnt = (FLMUINT) (BBE_GET_PKC( curElmPtr));
f_memcpy( &pStack->pKeyBuf[prevKeyCnt], &curElmPtr[uiElmOvhd],
elmKeyLen);
}
}
}
curElm = pStack->uiCurElm;
// Check to see if the new element will fit whereever it goes. Don't
// try to optimally place it because the next block may move stuff
// over.
while ((curElm > oldCurElm) &&
(curElm + elmLen + uiElmOvhd + uiElmOvhd > uiBlockSize))
{
FSBtPrevElm( pDb, pLFile, pStack);
curElm = pStack->uiCurElm;
}
newBlkStk.uiBlkAddr = newBlkNum;
newBlkStk.pKeyBuf = pStack->pKeyBuf;
FSBlkToStack( &newBlkStk);
newBlkStk.uiKeyBufSize = pStack->uiKeyBufSize;
curElmPtr = &pBlk[curElm];
if (curElm == oldCurElm)
{
// Decide whether to place the new element in the current or new
// block. Give preference to placing with the current block.
if ((curElm + elmLen + uiElmOvhd < uiBlockSize) &&
((uiBlkEnd - curElm) > uiElmOvhd))
{
// Place with the current block
goto Addto_Current_Blk;
}
if (uiBlkEnd > curElm)
{
// Move if not at end of block
FSBlkMoveElms( &newBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm),
pStack->pKeyBuf);
}
// Set the block end in the current blocks header & restore values
pStack->uiBlkEnd = curElm;
UW2FBA( curElm, &pBlk[BH_BLK_END]);
blkNumRestore = newBlkNum;
// Move if not at end of block
curElmRestore = BH_OVHD;
newBlkStk.uiCurElm = curElmRestore;
if (newBlkStk.uiBlkEnd + elmLen + uiElmOvhd > uiBlockSize)
{
// Double split - move element later
bDoubleSplit = 1;
}
else
{
FSBlkMoveElms( &newBlkStk, pElement, elmLen, NULL);
}
}
else if (curElm > oldCurElm)
{
Addto_Current_Blk:
// First move stuff over to the new blk
FSBlkMoveElms( &newBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm),
pStack->pKeyBuf);
// Set the block end in the current blocks header
pStack->uiBlkEnd = curElm;
UW2FBA( curElm, &pBlk[BH_BLK_END]);
blkNumRestore = blkNum;
curElmRestore = oldCurElm;
// Setup to insert element
pStack->uiCurElm = curElmRestore;
FSBlkMoveElms( pStack, pElement, elmLen, NULL);
}
else if (curElm < oldCurElm)
{
blkNumRestore = newBlkNum;
FSBlkMoveElms( &newBlkStk, curElmPtr, (FLMUINT) (oldCurElm - curElm),
pStack->pKeyBuf);
newBlkStk.uiCurElm = newBlkStk.uiBlkEnd;
curElmRestore = newBlkStk.uiCurElm;
// May not fit with 1K blocks - check for double split
if (curElmRestore + elmLen +
(uiBlkEnd - oldCurElm) + uiElmOvhd > uiBlockSize)
{
bDoubleSplit = 1;
}
else
{
FSBlkMoveElms( &newBlkStk, pElement, elmLen, NULL);
}
newBlkStk.uiCurElm = newBlkStk.uiBlkEnd;
pStack->uiCurElm = oldCurElm;
if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0)))
{
goto Exit;
}
if (oldCurElm < uiBlkEnd)
{
FSBlkMoveElms( &newBlkStk, &pBlk[oldCurElm],
(FLMUINT) (uiBlkEnd - oldCurElm), pStack->pKeyBuf);
}
// Set the block end in the current blocks header
pStack->uiBlkEnd = curElm;
UW2FBA( curElm, &pBlk[BH_BLK_END]);
}
// All done with the current block - unpin and set to dirty.
//
// All done moving data from current block to new block. If created new
// right most block check if new to create new root block and init new
// root (easy) else try to move stuff from the next block into the new
// block.
if (nextBlkNum == BT_END)
{
FLMUINT uiLfNum = pLFile->uiLfNum;
// We are done with block
FSReleaseBlock( &newBlkStk, FALSE);
// At the root?
if (bNewRootFlag)
{
FLMBYTE byType;
// Create a new root block
if (pStack->uiLevel + 1 >= BH_MAX_LEVELS)
{
rc = RC_SET( FERR_BTREE_FULL);
goto Exit;
}
// Set the block type
if (pLFile->uiLfType == LF_INDEX)
{
if (pLFile->pIxd->uiFlags & IXD_POSITIONING)
{
byType = BHT_NON_LEAF_COUNTS + BHT_ROOT_BLK;
}
else
{
byType = BHT_NON_LEAF + BHT_ROOT_BLK;
}
}
else
{
byType = BHT_NON_LEAF_DATA + BHT_ROOT_BLK;
}
// Move all pStack elements down by doing a shift
shiftN( (FLMBYTE*) pStack,
(FLMUINT) (sizeof(BTSK) * (pStack->uiLevel + 1)),
(FLMINT) sizeof(BTSK));
// Create a new block
if (RC_BAD( rc = ScaCreateBlock( pDb, pLFile, &pStack->pSCache)))
{
goto Exit;
}
pBlk = pStack->pBlk = pStack->pSCache->pucBlk;
// Set prev/next block addresses to BT_END
UD2FBA( BT_END, &pBlk[BH_PREV_BLK]);
UD2FBA( BT_END, &pBlk[BH_NEXT_BLK]);
UW2FBA( BH_OVHD, &pBlk[BH_BLK_END]);
// Set logical file number in the block header and block type
UW2FBA( uiLfNum, &pBlk[BH_LOG_FILE_NUM]);
pBlk[BH_TYPE] = byType;
pBlk[BH_LEVEL] = (FLMBYTE) (++(pStack->uiLevel));
pStack->uiBlkAddr = GET_BH_ADDR( pBlk);
FSBlkToStack( pStack);
pLFile->uiRootBlk = pStack->uiBlkAddr;
// Always update the pLFile because level is incremented
rc = flmLFileWrite( pDb, pLFile);
pStack++;
*ppStack = pStack;
if (RC_BAD( rc))
{
goto Exit;
}
}
}
else
{
FLMINT iBytesToMove;
// Move stuff from the right block
// Remember that newBlk is still pinned
nextBlkStk.pKeyBuf = pStack->pKeyBuf;
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, nextBlkNum, &nextBlkStk)))
{
goto Exit;
}
if (RC_BAD( rc = FSLogPhysBlk( pDb, &nextBlkStk)))
{
goto Exit;
}
nextBlkStk.uiKeyBufSize = pStack->uiKeyBufSize;
nextBlkPtr = nextBlkStk.pBlk;
UD2FBA( newBlkNum, &nextBlkPtr[BH_PREV_BLK]);
// Try to move so that the two blocks have about the same free space
iBytesToMove = (FLMINT) ((nextBlkStk.uiBlkEnd - newBlkStk.uiBlkEnd) / 2);
if (iBytesToMove > 100)
{
// Log the block before modifying it.
nextBlkStk.uiCurElm = iBytesToMove + BH_OVHD;
if (RC_BAD( rc = FSBtScanTo( &nextBlkStk, NULL, 0, 0)))
{
goto Exit;
}
while ((nextBlkStk.uiCurElm > BH_OVHD) &&
(nextBlkStk.uiCurElm + newBlkStk.uiBlkEnd +
uiElmOvhd - BH_OVHD >= uiBlockSize))
{
(void) FSBtPrevElm( pDb, pLFile, &nextBlkStk);
}
// Never try to move elements from the next block to the new
// block if we are positioned on the first element. Never try
// to move if on LEM or at the end
if ((nextBlkStk.uiCurElm > BH_OVHD) &&
(nextBlkStk.uiCurElm + uiElmOvhd < nextBlkStk.uiBlkEnd))
{
FLMUINT tempEnd;
// Save the key in the pKeyBuf
curElmPtr = &nextBlkPtr[nextBlkStk.uiCurElm];
if (pStack->uiBlkType != BHT_NON_LEAF_DATA)
{
elmKeyLen = (FLMUINT) (BBE_GET_KL( curElmPtr));
if (elmKeyLen)
{
// Copy key into pKeyBuf
prevKeyCnt = (FLMUINT) (BBE_GET_PKC( curElmPtr));
f_memcpy( &(nextBlkStk.pKeyBuf)[prevKeyCnt],
&curElmPtr[uiElmOvhd], elmKeyLen);
}
}
tempWord = nextBlkStk.uiCurElm;
newBlkStk.uiCurElm = newBlkStk.uiBlkEnd;
FSBlkMoveElms( &newBlkStk, &nextBlkPtr[BH_OVHD],
(FLMUINT) (tempWord - BH_OVHD), NULL);
// Move the elements in the next block DOWN expanding PKC
tempEnd = nextBlkStk.uiBlkEnd;
// Make sure uiBlkEnd is reality or move will not work!
nextBlkStk.uiBlkEnd = nextBlkStk.uiCurElm = BH_OVHD;
UW2FBA( BH_OVHD, &nextBlkPtr[BH_BLK_END]);
FSBlkMoveElms( &nextBlkStk, &nextBlkPtr[tempWord],
(FLMUINT) (tempEnd - tempWord), nextBlkStk.pKeyBuf);
if ((pStack - 1)->uiBlkType == BHT_NON_LEAF_COUNTS)
{
if (RC_BAD( rc = FSUpdateAdjacentBlkCounts( pDb, pLFile, pStack,
&nextBlkStk)))
{
goto Exit;
}
}
}
}
FSReleaseBlock( &newBlkStk, FALSE);
}
// Insert the new last element in the current block replacing what is
// there. Insert the last element from the new block. All blocks should
// be dirty and unpined! This means we have to read them in again.
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, blkNum, pStack)))
{
goto Exit;
}
if (pStack->uiCurElm >= pStack->uiBlkEnd)
{
pStack->uiCurElm = curElm;
}
// Passing 0 means insert only.
if (RC_BAD( rc = FSNewLastBlkElm( pDb, pLFile, &pStack,
(nextBlkNum == BT_END) ? 0 : FSNLBE_LESS)))
{
*ppStack = pStack;
goto Exit;
}
if (RC_BAD( rc = FSAdjustStack( pDb, pLFile, pStack, TRUE)))
{
if (rc != FERR_BT_END_OF_DATA)
{
goto Exit;
}
}
// Parent is positioned to the nextBlk element.
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, newBlkNum, pStack)))
{
goto Exit;
}
if ((nextBlkNum == BT_END) && !bNewRootFlag)
{
FLMUINT uiNewRefCount;
FLMUINT uiOldRefCount;
FLMBYTE* pTmpElement;
// Only update the counts if the inserting a key and not replacing.
if ((pStack - 1)->uiBlkType == BHT_NON_LEAF_COUNTS)
{
if (RC_BAD( rc = FSBlockCounts( pStack, BH_OVHD, pStack->uiBlkEnd,
NULL, NULL, &uiNewRefCount)))
{
goto Exit;
}
}
pStack--;
// Modify the parent last element marker (LEM) to point to the new
// last block. THEN delete the previous element that also pointer to
// the new last block.
//
// Read the parent block
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
{
return (rc);
}
if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
{
return (rc);
}
// Change where element points to in the last element marker (LEM)
pTmpElement = pStack->pBlk + pStack->uiCurElm;
FSSetChildBlkAddr( pTmpElement, newBlkNum, pStack->uiElmOvhd);
if (pStack->uiBlkType == BHT_NON_LEAF_COUNTS)
{
uiOldRefCount = FB2UD( &pTmpElement[BNE_CHILD_COUNT]);
if (RC_BAD( rc = FSChangeBlkCounts( pDb, pStack,
(FLMINT) (uiNewRefCount - uiOldRefCount))))
{
goto Exit;
}
UD2FBA( uiNewRefCount, &pTmpElement[BNE_CHILD_COUNT]);
}
pStack++;
}
else
{
// Inserts the new element into the tree.
rc = FSNewLastBlkElm( pDb, pLFile, &pStack, 0);
}
FSBlkSplit_position:
*ppStack = pStack;
// Read in block - should be positioned to the current element inserted
if (RC_OK( rc))
{
// Parent element is on the newBlock - see if you need to back up
if (blkNumRestore == blkNum)
{
if (RC_BAD( rc = FSAdjustStack( pDb, pLFile, pStack, FALSE)))
{
if (rc != FERR_BT_END_OF_DATA)
{
goto Exit;
}
}
}
if (RC_OK( rc = FSGetBlock( pDb, pLFile, blkNumRestore, pStack)))
{
// Set up the key buffer (pKeyBuf) to be correct for future
// inserts
pStack->uiCurElm = curElmRestore;
if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0)))
{
goto Exit;
}
if (bDoubleSplit)
{
// Now insert the element if flag set This will cause another
// split. RECURSIVE CALL
rc = FSBlkSplit( pDb, pLFile, ppStack, pElement, elmLen);
}
}
}
Exit:
FSReleaseBlock( &newBlkStk, FALSE);
FSReleaseBlock( &nextBlkStk, FALSE);
return (rc);
}
/****************************************************************************
Desc: Reset the pStack to set up for a block split
****************************************************************************/
FSTATIC RCODE FSBtResetStack(
FDB * pDb, // Pointer to database DBC structure.
LFILE * pLFile, // Logical file definition
BTSK ** ppStack, // Stack of variables for each level
FLMBYTE * pElement, // The input element to insert
FLMUINT elmLen) // Length of the element
{
RCODE rc;
BTSK * pStack = *ppStack; // Stack holding all state info
FLMUINT oldPKC = pStack->uiPKC; // Save old PKC value
FLMUINT oldPvElmPKC = pStack->uiPrevElmPKC; // Save old prev elm PKC value
FLMUINT oldBlock = pStack->uiBlkAddr; // Save old block number
FLMUINT oldCurElm = pStack->uiCurElm; // Save old current element value
FLMUINT uiElmOvhd = pStack->uiElmOvhd;
if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, &pElement[uiElmOvhd],
(elmLen - uiElmOvhd), 0)))
{
return (rc);
}
// In case of continuation elements, parse to matching curElm
while ((oldBlock != pStack->uiBlkAddr) && (oldCurElm != pStack->uiCurElm))
{
if ((rc = FSBtNextElm( pDb, pLFile, pStack)) == FERR_BT_END_OF_DATA)
{
return (RC_SET( FERR_BTREE_ERROR));
}
else if (RC_BAD( rc))
{
return (rc);
}
}
// Reset original PKC values
pStack->uiPKC = oldPKC;
pStack->uiPrevElmPKC = oldPvElmPKC;
*ppStack = pStack;
pStack->uiFlags = FULL_STACK;
return (FERR_OK);
}
/****************************************************************************
Desc: Try to move elements between two blocks while inserting an element
****************************************************************************/
FSTATIC RCODE FSMoveToNextBlk(
FDB * pDb,
LFILE * pLFile,
BTSK ** ppStack,
FLMUINT nextBlkNum,
FLMBYTE * pElement,
FLMUINT elmLen,
FLMUINT uiBlockSize,
FLMUINT * blkNumRV,
FLMUINT * curElmRV)
{
RCODE rc = FERR_OK;
BTSK * pStack = *ppStack;
FLMUINT uiBlkEnd = pStack->uiBlkEnd;
FLMUINT oldCurElm = pStack->uiCurElm;
FLMBYTE * curElmPtr;
BTSK nextBlkStk;
FLMUINT nextBlkFreeBytes;
FLMUINT elmKeyLen;
FLMUINT prevKeyCnt;
FLMUINT curElm;
FLMUINT uiElmOvhd = pStack->uiElmOvhd;
FLMBOOL bInsertInCurrentBlock;
FLMBYTE * pBlk = pStack->pBlk;
FSInitStackCache( &nextBlkStk, 1);
nextBlkStk.pKeyBuf = pStack->pKeyBuf;
if (RC_BAD( rc = FSGetBlock( pDb, pLFile, nextBlkNum, &nextBlkStk)))
{
goto Exit;
}
nextBlkFreeBytes = (FLMUINT) (uiBlockSize - nextBlkStk.uiBlkEnd - uiElmOvhd);
pStack->uiCurElm = (uiBlkEnd - (nextBlkFreeBytes / 2));
if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0)))
{
goto Exit;
}
rc = (pStack->uiCmpStatus == BT_END_OF_DATA)
? FERR_BT_END_OF_DATA
: FERR_OK;
for (; RC_OK( rc); rc = FSBlkNextElm( pStack))
{
// The current element is positioned to mininum split point. Keep
// testing till at end of block or split will fit within both blocks
// while still adding the element to insert.
//
// Save the key in the pKeyBuf so can move entire element
curElmPtr = CURRENT_ELM( pStack);
if (pStack->uiBlkType == BHT_NON_LEAF_DATA)
{
prevKeyCnt = elmKeyLen = 0;
}
else
{
prevKeyCnt = (FLMUINT) (BBE_GET_PKC( curElmPtr));
elmKeyLen = (FLMUINT) (BBE_GET_KL( curElmPtr));
}
if (elmKeyLen)
{
// Copy key into pKeyBuf
f_memcpy( &pStack->pKeyBuf[prevKeyCnt], &curElmPtr[uiElmOvhd],
elmKeyLen);
}
// Will element be inserted into the current block?
if (oldCurElm <= (curElm = pStack->uiCurElm))
{
// Insert the element in the current block - could be at the end
if (curElm + elmLen + uiElmOvhd > uiBlockSize)
{
rc = FERR_BT_END_OF_DATA;
break;
}
// Left fits - try right block - could fail because of pkc value/
if (prevKeyCnt + (uiBlkEnd - curElm) >= nextBlkFreeBytes)
{
// Doesn't fit - try again
continue;
}
bInsertInCurrentBlock = TRUE;
}
else
{
// Moving elements from current block so no need to check it
// Cannot remember why I put BBE_PKC_MAX in the line below
if (elmLen + prevKeyCnt + BBE_PKC_MAX +
(uiBlkEnd - curElm) >= nextBlkFreeBytes)
{
continue;
}
bInsertInCurrentBlock = FALSE;
}
if (RC_BAD( rc = FSLogPhysBlk( pDb, &nextBlkStk)))
{
goto Exit;
}
if (bInsertInCurrentBlock)
{
FSBlkMoveElms( &nextBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm),
pStack->pKeyBuf);
pStack->uiBlkEnd = curElm;
UW2FBA( curElm, &pBlk[BH_BLK_END]);
*curElmRV = oldCurElm;
pStack->uiCurElm = (*curElmRV);
FSBlkMoveElms( pStack, pElement, elmLen, NULL);
*blkNumRV = pStack->uiBlkAddr;
}
else
{
FSBlkMoveElms( &nextBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm),
pStack->pKeyBuf);
pStack->uiBlkEnd = curElm;
UW2FBA( curElm, &pBlk[BH_BLK_END]);
*curElmRV = (FLMUINT) (BH_OVHD + prevKeyCnt + (oldCurElm - curElm));
nextBlkStk.uiCurElm = (*curElmRV);
FSBlkMoveElms( &nextBlkStk, pElement, elmLen, NULL);
*blkNumRV = nextBlkNum;
}
if (pLFile->pIxd && (pLFile->pIxd->uiFlags & IXD_POSITIONING))
{
if (RC_BAD( rc = FSUpdateAdjacentBlkCounts( pDb, pLFile, pStack,
&nextBlkStk)))
{
goto Exit;
}
}
break;
}
// If cannot move then return
if (RC_BAD( rc))
{
if (rc == FERR_BT_END_OF_DATA)
{
// Restore old current element
pStack->uiCurElm = oldCurElm;
rc = RC_SET( FERR_BLOCK_FULL);
}
goto Exit;
}
// Fix up the elements in the parent block pointing to the new last
// element in the current block. REMEMBER - pStack may change on you!
rc = FSNewLastBlkElm( pDb, pLFile, ppStack, FSNLBE_LESS | FSNLBE_POSITION);
Exit:
FSReleaseBlock( &nextBlkStk, FALSE);
return (rc);
}