//------------------------------------------------------------------------- // Desc: Delete element from b-tree block. // Tabs: 3 // // Copyright (c) 1991-2001,2003-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: fsdelelm.cpp 12284 2006-01-19 14:54:14 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /*************************************************************************** Desc: Delete the current element from a b-tree block and write block. The stack must point to the next element after the deleted element. The next element could be in a different block so be careful. Notes: The order of the high level if's is VERY important. The order of changing the blocks is not important because of logging. *****************************************************************************/ RCODE FSBtDelete( FDB * pDb, LFILE * pLFile, BTSK_p * pStackRV ) { RCODE rc; BTSK_p pStack = *pStackRV; SCACHE * pTmpSCache; FLMBYTE * pBlk; FLMBOOL bReleaseTmpCache = FALSE; FLMUINT uiNextBlk; FLMUINT uiPrevBlk; FLMUINT uiLastElm; /* Offset of last element in a block */ FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMBYTE pLEMBuffer[ 12 ]; /* Last element marker buffer */ /* Log block before modifying it */ if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) goto Exit; // If this is a non-leaf positioning index, update parent counts. if( pLFile->pIxd && (pLFile->pIxd->uiFlags & IXD_POSITIONING)) { if( pStack->uiLevel) { FLMUINT uiRefCount; FLMBYTE * pElm = pStack->pBlk + pStack->uiCurElm; // Reduce the counts from the parent up. uiRefCount = FB2UD( &pElm[ BNE_CHILD_COUNT]); if( RC_BAD( rc = FSChangeBlkCounts( pDb, pStack, (FLMINT) (0 - uiRefCount) ))) { goto Exit; } } } if( RC_BAD(rc = FSBlkDelElm( pStack ))) goto Exit; /** *** Take care of deletion of the ONLY element in the block. *** There is NO WAY this could be a root or right end block because *** of the last element marker (LEM) takes up 2 or 4 bytes **/ if( pStack->uiBlkEnd == BH_OVHD) { /** *** Free up this empty block and fixup the NEXT/PREV links. **/ uiNextBlk = FB2UD( &pStack->pBlk[ BH_NEXT_BLK ]); rc = FSBlockFixLinks( pDb, pLFile, pStack->pSCache); pStack->pSCache = NULL; pStack->pBlk = NULL; if( RC_BAD( rc)) goto Exit; /** *** Remove the element that pointed to this block **/ if( RC_OK( rc = FSDelParentElm( pDb, pLFile, &pStack ))) { pStack->uiBlkAddr = uiNextBlk; /* Read the next block and set stack to point to next element */ if( RC_OK(rc = FSGetBlock( pDb, pLFile, uiNextBlk, pStack))) { pStack->uiCurElm = BH_OVHD; /**------------------------------------------------------------ *** Added 07/15/93 *** Need to reset keybuf[] for deletes in a while loop like the *** data record delete code in fsrecupd.c. *** This may be cause of some corruptions while deleting *** lots of records. ***-----------------------------------------------------------*/ FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM ); } } *pStackRV = pStack; } /** Deleting a RIGHT MOST B-tree block (along the right side). *** Check if only the LEM (last element marker) is left in the block. *** A block CANNOT contain just a LEM. Move the LEM & free the block. *** *** There are 3 cases to watch for: *** EMPTY B-TREE - free the root/block save next record in lfArea *** EMPTY ROOT - free root and goto child & make that root *** EMPTY BLOCK - cannot contain only a LEM - move to previous block *** *** Must not move LASST DRN Marker - there is no need to do this. *** **/ else if( (FB2UD( &pStack->pBlk[ BH_NEXT_BLK ]) == BT_END) && ( (pStack->uiBlkEnd == BH_OVHD + uiElmOvhd ) // || ( (pLFile->uiLfType == LF_CONTAINER) // && (stack->uiBlkEnd == BH_OVHD + DRN_LAST_MARKER_LEN+ uiElmOvhd) // && (stack->uiBlkType == BHT_LEAF) ) ) //) { /** *** EMPTY B-TREE *** Level is not in the current LFILE version so check if the root *** block is the same as this block. **/ if( (pStack->uiBlkType == BHT_LEAF) && (pLFile->uiRootBlk == pStack->uiBlkAddr )) /* Root is leaf */ { /** *** Return the empty root block to the system *** Code (11/26/91) supports empty b-trees. We have gone back *** and forth on returning empty root blocks to the system. *** Setup stack for emtpy state and modify the LFILE on disk. *** *** The data record b-tree can NEVER be empty because of *** the next record DRN record should always hang around. *** *** ALL CALLING ROUTINES MUST CHECK for( STACK->uiBlkAddr == BT_END) **/ pStack->uiBlkAddr = BT_END; { /* Get the next DRN and save in the LFD */ FLMBYTE * ptr = &pStack->pBlk[ BH_OVHD ]; ptr += BBE_GETR_KL( ptr ) + BBE_KEY; pLFile->uiNextDrn = (FLMUINT) FB2UD( ptr); } rc = FSBlockFree( pDb, pStack->pSCache); pStack->pSCache = NULL; pStack->pBlk = NULL; if( RC_BAD( rc)) goto Exit; pLFile->uiRootBlk = BT_END; rc = flmLFileWrite( pDb, pLFile); } /** *** EMPTY ROOT BLOCK *** *** Remove root block and set new root block to child. **/ else if( pLFile->uiRootBlk == pStack->uiBlkAddr) { // Obtain child block and set to lfArea to assign new root block uiNextBlk = FSChildBlkAddr( pStack ); rc = FSBlockFree( pDb, pStack->pSCache); pStack->pSCache = NULL; pStack->pBlk = NULL; if( RC_BAD( rc)) { goto Exit; } pLFile->uiRootBlk = uiNextBlk; if( RC_BAD( rc = flmLFileWrite( pDb, pLFile))) { goto Exit; } // Assign the new root block if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiNextBlk, NULL, &pTmpSCache))) { goto Exit; } bReleaseTmpCache = TRUE; if( RC_BAD( rc = ScaLogPhysBlk( pDb, &pTmpSCache))) { goto Exit; } // Set the root block flag on this block & mark dirty BH_SET_ROOT_BLK( pTmpSCache->pucBlk ); ScaReleaseCache( pTmpSCache, FALSE); bReleaseTmpCache = FALSE; /** *** Date Added: 8/19/93 *** It is possible to add an element so that a new root block *** gets created AND THEN Deleted. I saw this on OS2 when they *** got up to 6 levels in the btree. Because of this the ROOT *** block in the stack MUST always be pStack[0]. *** The three lines below do this. Tested on Brad Daw's db. **/ shiftN( (FLMBYTE *) pStack + sizeof(BTSK), sizeof(BTSK) * pStack->uiLevel, /* think uiLevel + 1 - 1 */ 0-(sizeof(BTSK))); /* We don't want a pointer to the same shared cache in two different elements of the pStack. NULL out the one that was moved down. */ (pStack + pStack->uiLevel + 1)->pSCache = NULL; (pStack + pStack->uiLevel + 1)->pBlk = NULL; pStack--; /* Point 1 element BEFORE stack starts ! */ *pStackRV = pStack; /* Don't worry about positioning to the next element - root gone */ } else { /** *** ONLY LAST ELEMENT MARKER (LEM) REMAINS *** (leaf or (non-leaf but NOT ROOT)) *** A block must NEVER contain only the last element marker (LEM). *** This may free up a leaf block or a non-leaf block but never *** a root block (root checks above.) *** *** This is the most complex case. *** Move the last element marker (LEM) to the end of the *** previous block. Be carefull to delete the *** correct element in the parent block. *** The algorithm states that there MUST ALWAYS be room to insert *** the LEM into a block without splitting that block. *** *** Because of logging the block modifications do not need to be *** flushed in a specific order to prevent corruption. */ uiPrevBlk = FB2UD( &pStack->pBlk[ BH_PREV_BLK ]); /* Save the last element marker, may be a non-leaf element */ f_memcpy( pLEMBuffer, &pStack->pBlk[ BH_OVHD ], uiElmOvhd ); /* Free the block */ rc = FSBlockFree( pDb, pStack->pSCache); pStack->pSCache = NULL; pStack->pBlk = NULL; if( RC_BAD( rc)) goto Exit; /* Pop stack - point to parent */ pStack--; /** *** Modify the parent blocks last element marker (LEM) to point to *** the new last block. THEN delete the previous element that *** also points to the new last block. **/ /* Read the parent block */ if( RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack))) { goto Exit; } if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } /* Change where element points to in the last element marker (LEM) */ FSSetChildBlkAddr( BLK_ELM_ADDR( pStack, pStack->uiCurElm), uiPrevBlk, pStack->uiElmOvhd ); /* FSBtPrevElm may return -1 which is NOT OK */ if( RC_BAD( rc = FSBtPrevElm( pDb, pLFile, pStack))) { rc = (rc == FERR_BT_END_OF_DATA) ? RC_SET( FERR_BTREE_ERROR) : rc ; goto Exit; } /* Delete the element that points to the new last block */ if( RC_BAD( rc = FSBtDelete( pDb, pLFile, &pStack))) goto Exit; pStack++; /** *** Read in the new last block *** Move in the old LEM to the end of the block *** Position pStack to LEM **/ if( RC_BAD( rc = FSGetBlock( pDb, pLFile, uiPrevBlk, pStack))) { goto Exit; } /* Log block before modifying */ if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) goto Exit; pBlk = pStack->pBlk; /* Set the pStack elements */ uiLastElm = pStack->uiBlkEnd; pStack->uiCurElm = uiLastElm; /* Place the LEM (last element marker) in the block */ f_memcpy( &pBlk[ uiLastElm ], pLEMBuffer, uiElmOvhd ); /* Set next block pointer to BT_END */ UD2FBA( BT_END, &pBlk[ BH_NEXT_BLK ]); /* Adjust end pointers */ pStack->uiBlkEnd = uiLastElm + uiElmOvhd; UW2FBA( (FLMUINT16)pStack->uiBlkEnd, &pBlk[ BH_BLK_END ]); *pStackRV = pStack; } } /** *** Deleted the LAST (but NOT ONLY) element in the block. *** The parent block must have its current element changed to reflect *** the new last element key in this block. The algoritm MUST insert *** the next last element key and THEN delete the old key or *** you may have a new root block which is your next block! *** Comentary: Some b-tree algorithms have key markers that are shorter *** in the non-leaf blocks. This right-end non-leaf trunctaion is a *** very good idea, but ALL of the b-tree code has to conform to this *** rule and it is not a trivial thing to support. */ else if( pStack->uiBlkEnd == pStack->uiCurElm) { pBlk = pStack->pBlk; /* Double check in case of corrupt this is not a right most block */ if( FB2UD( &pBlk[ BH_NEXT_BLK ]) != BT_END) { rc = FSNewLastBlkElm( pDb, pLFile, &pStack, FSNLBE_LESS | FSNLBE_POSITION ); *pStackRV = pStack; } } /** *** Else - normal delete - everything should be correct and the *** stack is positioned to the next element after the deleted element. *** The pKeyBuf[] however, is NOT always set to be the current elms key. *** Check to see if there is room to make a 3/2 combine & do it. **/ else if( pStack->uiBlkEnd <= (FFILE_MIN_FILL * pDb->pFile->FileHdr.uiBlockSize / 100)) { rc = FSCombineBlks( pDb, pLFile, &pStack ); *pStackRV = pStack; /* Stack may have changed */ } Exit: if (bReleaseTmpCache) { ScaReleaseCache( pTmpSCache, FALSE); } return( rc); } /**************************************************************************** Desc: Delete the parent element from where you are at in the stack. Notes: In the future we should either pin down the current block or reread it in. ****************************************************************************/ RCODE FSDelParentElm( FDB * pDb, LFILE * pLFile, BTSK_p * pStackRV ) { RCODE rc; BTSK_p pStack = *pStackRV; /* Stack pointer */ pStack--; if( RC_BAD(rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack))) goto Exit; /* Ignore status value from FSBtScanTo - just position for delete */ if( RC_BAD(rc = FSBtScanTo( pStack, NULL, 0, (FLMUINT) 0))) goto Exit; /* FSBlkBuildPKC( pStack, pStack->pKeyBuf ); but does not build key from current element Much faster than scanTo() */ /* Call will position to the next element which pts to the next blk */ rc = FSBtDelete( pDb, pLFile, &pStack ); Exit: pStack++; /* Go down the pStack to the original level */ *pStackRV = pStack; return( rc ); } /**************************************************************************** Desc: There is a new last block element, store key in parent and remove current element if( uiFlags & FSNLBE_LESS or GREATER is set Notes: Take note of the uiFlags - they can change what is done ****************************************************************************/ RCODE FSNewLastBlkElm( FDB * pDb, LFILE * pLFile, BTSK_p * pStackRV, FLMUINT uiFlags) // FSNLBE_GREATER, *_LESS, CK_COMBINE, POSITION or 0 { RCODE rc; BTSK_p pStack = *pStackRV; FLMBYTE * pBlk = pStack->pBlk; FLMBYTE * pCurElm; // Points to the current last element FLMUINT uiOldCurElm = pStack->uiCurElm; FLMUINT uiNextBlk = 0; FLMUINT uiDomain = 0; // Domain is index reference range FLMUINT uiElmLen; // Element length FLMUINT uiKeyLen; FLMUINT uiNewElmOvhd; FLMUINT uiRefCount; FLMBYTE * pKey; FLMBYTE pElmBuffer[ MAX_KEY_SIZ + BNE_KEY_COUNTS_START + BNE_DOMAIN_LEN ]; uiNewElmOvhd = (pStack-1)->uiElmOvhd; uiElmLen = uiNewElmOvhd; if( uiNewElmOvhd == BNE_DATA_OVHD) { pKey = pElmBuffer; } else { pElmBuffer[ BBE_PKC ] = 0; // Set PKC, DOMAIN to zero pElmBuffer[ BBE_KL ] = 0; pKey = pElmBuffer + uiNewElmOvhd; // Only update the counts if the inserting a key and not replacing. if( uiNewElmOvhd == BNE_KEY_COUNTS_START) { if( RC_BAD( rc = FSBlockCounts( pStack, BH_OVHD, pStack->uiBlkEnd, NULL, NULL, &uiRefCount))) { goto Exit; } UD2FBA( uiRefCount, &pElmBuffer[ BNE_CHILD_COUNT]); } } // Build the pElmBuffer[] with the new last key in the block FSSetChildBlkAddr( pElmBuffer, pStack->uiBlkAddr, uiNewElmOvhd); if( (uiNextBlk = FB2UD( &pBlk[ BH_NEXT_BLK ])) == BT_END) { if( uiNewElmOvhd == BNE_DATA_OVHD) { UD2FBA( 0xFFFFFFFF, pElmBuffer); } uiKeyLen = 0; uiElmLen = uiNewElmOvhd; goto no_domain; } // else - fall through pStack->uiCurElm = pStack->uiBlkEnd;// Position past last element FSBtPrevElm( pDb, pLFile, pStack ); // Build full key in pKeyBuf[] uiKeyLen = pStack->uiKeyLen; // Copy the key if( uiNewElmOvhd == BNE_DATA_OVHD) { flmCopyDrnKey( pElmBuffer, pStack->pKeyBuf); } else if( uiKeyLen) { f_memcpy( &pElmBuffer[ uiNewElmOvhd ], pStack->pKeyBuf, uiKeyLen ); BBE_SET_KL( pElmBuffer, uiKeyLen ); uiElmLen += uiKeyLen; } // If this is a boundqed index reference set then store DOMAIN pCurElm = CURRENT_ELM( pStack ); uiDomain = ( pLFile->uiLfType == LF_INDEX) ? FSGetDomain( &pCurElm, pStack->uiElmOvhd ) : (FLMUINT) 0; if( uiDomain) { BNE_SET_DOMAIN( pElmBuffer ); pElmBuffer[ uiElmLen++ ] = (FLMBYTE) (uiDomain >> 16 ); pElmBuffer[ uiElmLen++ ] = (FLMBYTE) (uiDomain >> 8 ); pElmBuffer[ uiElmLen++ ] = (FLMBYTE) (uiDomain & 0xFF); } no_domain: /** *** Go to the parent block, insert new element *** and delete the old last element in the block. *** Pinning the current block is not suggested because *** you could pin number of (levels - 1) blocks. **/ pStack--; if( RC_BAD(rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack))) goto Exit; /* If greater you should be positioned AFTER the matching element */ if( pStack->uiBlkEnd > BH_OVHD) /* Don't call if NO elements */ { /* Set up the pStack elements for insert - passing keyLen sets up pStack*/ if( RC_BAD(rc = FSBtScanTo( pStack, pKey, uiKeyLen, uiDomain ))) goto Exit; } else pStack->uiPrevElmPKC = pStack->uiPKC = 0; /* Insert the element into the parent block - watch for splits! */ if( RC_OK( rc = FSBtInsert( pDb, pLFile, &pStack, pElmBuffer, uiElmLen))) { if( uiFlags & FSNLBE_LESS ) { /* Go to the next element, may read a new block! */ if( RC_OK( rc = FSBtNextElm( pDb, pLFile, pStack ))) { if( RC_OK( rc = FSBtDelete( pDb, pLFile, &pStack ))) { /* Now position to the current element - back one */ if( !(uiFlags & FSNLBE_POSITION)) { rc = FSBtPrevElm( pDb, pLFile, pStack ); rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc; } } else { rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc; } } } else if( uiFlags & FSNLBE_GREATER ) { if( RC_OK( rc = FSBtPrevElm( pDb, pLFile, pStack ))) if( RC_OK(rc = FSBtDelete( pDb, pLFile, &pStack ))) { /* Position to the next element if flag is set */ if( uiFlags & FSNLBE_POSITION) { rc = FSBtNextElm( pDb, pLFile, pStack ); rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc; } } } } Exit: /**----------------------------------------------------------- *** Pop the pStack and position to next element *** in the block that you expect to be positioned to. *** You should be positioned to the correct parent element. ***----------------------------------------------------------*/ pStack++; *pStackRV = pStack; /* Update callers pStack */ if( RC_OK(rc)) { if( ( uiFlags & FSNLBE_POSITION ) && (uiNextBlk != BT_END )) { pStack->uiBlkAddr = uiNextBlk; uiOldCurElm = BH_OVHD; } /* Read the next block and set pStack. */ if( RC_OK( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack))) { pStack->uiCurElm = uiOldCurElm; /* Restore original curElm value */ FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM ); } } return( rc ); } /*************************************************************************** Desc: Delete the current element from a b-tree block without writing block. The pStack must point to the next element after the deleted element. Notes: Code handles deletion of an element at any level of the b-tree. *****************************************************************************/ RCODE FSBlkDelElm( BTSK_p pStack /* Stack of variables for each level */ ) { RCODE rc; FLMBYTE * pDelElm; /* Points to deleted element */ FLMBYTE * pCurElm; /* Points to current elm to move down*/ FLMBYTE * pBlk = pStack->pBlk;/* Points to block for speed */ FLMUINT uiCurElmOfs; /* Current (next) element's offset */ FLMUINT uiDelElmPkc; /* # of carry bytes for deleted elm */ FLMUINT uiCurElmPkc; /* Current element's Prev key count */ FLMUINT uiCurElmKeyLen; /* Current element's key length */ FLMUINT uiOldCurElm = pStack->uiCurElm; FLMUINT uiElmOvhd = pStack->uiElmOvhd; /* Element overhead for block */ FLMINT iDelElmSize; /* Deleted element's size - signed !!*/ FLMINT iPkcLost; /* Number bytes to expand for next elm*/ /* MUST be a signed word value */ pDelElm = &pBlk[ pStack->uiCurElm ]; if( RC_OK(rc = FSBlkNextElm( pStack ))) { /** *** Setup to remove what pDelElm is pointing to *** This is NOT the last element in the block so move down *** the rest of the block data. **/ uiCurElmOfs = pStack->uiCurElm; iDelElmSize = (FLMINT)(uiCurElmOfs - uiOldCurElm); pCurElm = &pBlk[ uiCurElmOfs ]; if( pStack->uiBlkType != BHT_NON_LEAF_DATA) { uiDelElmPkc = (FLMUINT)(BBE_GET_PKC( pDelElm )); uiCurElmPkc = (FLMUINT)(BBE_GET_PKC( pCurElm )); /* If current element uses bytes from the deleted element... */ if( uiCurElmPkc > uiDelElmPkc) { iPkcLost = (FLMINT)(uiCurElmPkc - uiDelElmPkc); /** Create the new deleted element and setup for the shiftN() below *** moving pCurElm. **/ uiCurElmPkc -= iPkcLost; uiCurElmKeyLen = (FLMUINT) (BBE_GET_KL( pCurElm ) + iPkcLost); BBE_SET_PKC( pDelElm, uiCurElmPkc ); /* Clears all bits */ BBE_SET_KL( pDelElm, uiCurElmKeyLen ); *pDelElm |= (FLMBYTE)(BBE_IS_FIRST_LAST(pCurElm )); if( pStack->uiBlkType == BHT_LEAF) { BBE_SET_RL( pDelElm, BBE_GET_RL( pCurElm )); } else { f_memcpy( pDelElm + BNE_CHILD_BLOCK, pCurElm + BNE_CHILD_BLOCK, uiElmOvhd - BNE_CHILD_BLOCK ); } /* Adjust iDelElmSize & uiCurElmOfs to delete current element overhead */ iDelElmSize -= iPkcLost; uiCurElmOfs += uiElmOvhd; /* Move any extra bytes from the deleted element refered in curElm */ f_memcpy( pDelElm + uiElmOvhd, &pStack->pKeyBuf[uiDelElmPkc], iPkcLost); /* Fall through and copy the rest of the block down */ } } /* Shift down - starting at pCurElm */ shiftN( &pBlk[ uiCurElmOfs ], pStack->uiBlkEnd - uiCurElmOfs, (FLMINT)(0 - iDelElmSize) ); pStack->uiBlkEnd -= iDelElmSize; } else if( rc == FERR_BT_END_OF_DATA) { rc = FERR_OK; if( pStack->uiCurElm == pStack->uiBlkEnd) /* Deleted last element in blk */ pStack->uiBlkEnd = uiOldCurElm; else { /* Need to move the last element marker (LEM) down */ /* This should only be used on leaf blocks - suggest using shiftN() */ pBlk[ uiOldCurElm ] = BBE_FIRST_FLAG | BBE_LAST_FLAG; pBlk[ uiOldCurElm + BBE_KL ] = pBlk[ uiOldCurElm + BBE_RL ] = 0; pStack->uiBlkEnd = uiOldCurElm + uiElmOvhd; } } else goto Exit; /* Modify the element end offset & restore curElm & prevElm on pStack */ UW2FBA( (FLMUINT16) pStack->uiBlkEnd, &pBlk[ BH_BLK_END ]); pStack->uiCurElm = uiOldCurElm; Exit: return( rc ); } /**************************************************************************** Desc: Sets the parent element's child block value ****************************************************************************/ void FSSetChildBlkAddr( FLMBYTE * pElement, FLMUINT uiBlkAddr, FLMUINT uiBlkOvhd) { FLMBYTE * pChildAddr; if( uiBlkOvhd == BNE_KEY_START || uiBlkOvhd == BNE_KEY_COUNTS_START) { pChildAddr = pElement + BNE_CHILD_BLOCK; UD2FBA( uiBlkAddr, pChildAddr ); } else if( uiBlkOvhd == BNE_DATA_OVHD) { pChildAddr = pElement + BNE_DATA_CHILD_BLOCK; UD2FBA( uiBlkAddr, pChildAddr ); } return; }