//------------------------------------------------------------------------- // Desc: Index reference updating. // 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: fsrefupd.cpp 12321 2006-01-19 15:55:00 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" extern FLMBYTE SENLenArray[]; #define INSERT_REF 0 #define DELETE_REF 1 #define SPLIT_90_10 0 #define SPLIT_50_50 1 FSTATIC RCODE FSUpdateIxCounts( FDB * pDb, IXD * pIxd, FLMBYTE byFlags, FLMBOOL bSingleRef); FSTATIC RCODE FSOutputIxCounts( FDB * pDb, IX_STATS * pIxStats); FSTATIC RCODE FSRefCreateRec( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack); FSTATIC RCODE FSRefInsert( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack); FSTATIC RCODE FSRefDelete( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack, FLMBOOL * pbSingleRef); /**************************************************************************** Desc: Update (add or delete) a single reference ****************************************************************************/ RCODE FSRefUpdate( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry) { RCODE rc = FERR_OK; BTSK stackBuf[BH_MAX_LEVELS]; BTSK * pStack = stackBuf; FLMUINT uiDinDomain = DIN_DOMAIN( pKrefEntry->uiDrn) + 1; FLMBYTE byFlags = (FLMBYTE) pKrefEntry->uiFlags; FLMBOOL bSingleRef = FALSE; FLMBOOL bAddReference = (byFlags & KREF_DELETE_FLAG) ? FALSE : TRUE; FLMBYTE pKeyBuf[MAX_KEY_SIZ]; FSRU_try_again: if (pKrefEntry->uiFlags & KREF_ENCRYPTED_KEY) { // Can't allow updates whit these keys. flmAssert( pDb->pFile->bInLimitedMode); rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); goto Exit; } FSInitStackCache( &stackBuf[0], BH_MAX_LEVELS); pStack = stackBuf; pStack->pKeyBuf = pKeyBuf; if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, (FLMBYTE*) &pKrefEntry[1], pKrefEntry->ui16KeyLen, uiDinDomain))) { goto Exit; } // If pStack->bsStatus == REC_NOT_FOUND create a new element if found // then add the reference into the found element. if (pStack->uiCmpStatus == BT_EQ_KEY) { if ((byFlags & KREF_UNIQUE_KEY) && !(byFlags & KREF_DELETE_FLAG)) { rc = RC_SET( FERR_NOT_UNIQUE); goto Exit; } if (pLFile->pIxd->uiFlags & IXD_POSITIONING) { if (RC_BAD( rc = FSChangeCount( pDb, pStack, bAddReference))) { goto Exit; } } bSingleRef = FALSE; if (bAddReference) { if (RC_BAD( rc = FSRefInsert( pDb, pLFile, pKrefEntry, pStack))) { goto Exit; } } else { if (RC_BAD( rc = FSRefDelete( pDb, pLFile, pKrefEntry, pStack, &bSingleRef))) { goto Exit; } } } else { if (!bAddReference) { // Already been deleted, ignore the error condition and go on. flmAssert( 0); rc = FERR_OK; goto Exit; } // The B-Tree may be empty if (pLFile->uiRootBlk == BT_END) { if (RC_BAD( rc = flmLFileInit( pDb, pLFile))) { goto Exit; } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); goto FSRU_try_again; } if (pLFile->pIxd->uiFlags & IXD_POSITIONING) { if (RC_BAD( rc = FSChangeCount( pDb, pStack, TRUE))) { goto Exit; } } // Add new key|reference element. bSingleRef = TRUE; if (RC_BAD( rc = FSRefCreateRec( pDb, pLFile, pKrefEntry, pStack))) { goto Exit; } } Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); if (RC_OK( rc) && (pLFile->pIxd->uiFlags & IXD_COUNT)) { rc = FSUpdateIxCounts( pDb, pLFile->pIxd, byFlags, bSingleRef); } return (rc); } /**************************************************************************** Desc: Update the index reference and/or key counts in memory only. Counts are not written to the database until transaction commit time. ****************************************************************************/ FSTATIC RCODE FSUpdateIxCounts( FDB * pDb, IXD * pIxd, FLMBYTE byFlags, FLMBOOL bSingleRef) { RCODE rc = FERR_OK; IX_STATS * pIxStat; // See if we have already created an IX_STATS structure in memory pIxStat = pDb->pIxStats; while (pIxStat && pIxStat->uiIndexNum != pIxd->uiIndexNum) { pIxStat = pIxStat->pNext; } // Allocate an IX_STATS if we didn't find one. if (!pIxStat) { if (RC_BAD( rc = f_calloc( sizeof(IX_STATS), &pIxStat))) { goto Exit; } pIxStat->uiIndexNum = pIxd->uiIndexNum; pIxStat->pNext = pDb->pIxStats; pDb->pIxStats = pIxStat; } // Determine whether to increment or decrement the counts if (byFlags & KREF_DELETE_FLAG) { if (bSingleRef) { pIxStat->iDeltaKeys--; } pIxStat->iDeltaRefs--; } else { if (bSingleRef) { pIxStat->iDeltaKeys++; } pIxStat->iDeltaRefs++; } Exit: return (rc); } /**************************************************************************** Desc: Output the index reference and/or key counts by writing them to the tracker record. ****************************************************************************/ FSTATIC RCODE FSOutputIxCounts( FDB * pDb, IX_STATS * pIxStats) { RCODE rc = FERR_OK; FLMBOOL bCreateRec; FlmRecord * pRecord = NULL; FlmRecord * pTmpRec = NULL; void * pvField; FLMUINT uiCount; LFILE * pLFile; // If no counts changed, do nothing if (!pIxStats->iDeltaKeys && !pIxStats->iDeltaRefs) { goto Exit; } if (RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pLFile))) { goto Exit; } // Retrieve the tracker record from record cache. if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_TRACKER_CONTAINER, pIxStats->uiIndexNum, TRUE, NULL, NULL, &pRecord))) { if (rc != FERR_NOT_FOUND) { goto Exit; } bCreateRec = TRUE; rc = FERR_OK; } else { bCreateRec = FALSE; } // If there was no record, create one. Otherwise, copy the record if (bCreateRec) { if ((pTmpRec = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Create at least the root node. if (RC_BAD( rc = pTmpRec->insertLast( 0, FLM_INDEX_TAG, FLM_CONTEXT_TYPE, &pvField))) { goto Exit; } } else { if ((pTmpRec = pRecord->copy()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } // Update the key count. if (pIxStats->iDeltaKeys) { if ((pvField = pTmpRec->find( pTmpRec->root(), FLM_KEY_TAG)) == NULL) { // Cannot make key count negative. if (pIxStats->iDeltaKeys < 0) { flmAssert( 0); pIxStats->iDeltaKeys = 0; } if (RC_BAD( rc = pTmpRec->insert( pTmpRec->root(), INSERT_LAST_CHILD, FLM_KEY_TAG, FLM_NUMBER_TYPE, &pvField))) { goto Exit; } uiCount = (FLMUINT) pIxStats->iDeltaKeys; } else { if (RC_BAD( rc = pTmpRec->getUINT( pvField, &uiCount))) { goto Exit; } if (pIxStats->iDeltaKeys < 0) { pIxStats->iDeltaKeys = -pIxStats->iDeltaKeys; if ((FLMUINT) pIxStats->iDeltaKeys <= uiCount) { uiCount -= (FLMUINT) pIxStats->iDeltaKeys; } else { // Key count cannot go negative. flmAssert( 0); uiCount = 0; } } else { uiCount += (FLMUINT) pIxStats->iDeltaKeys; } } if (RC_BAD( rc = pTmpRec->setUINT( pvField, uiCount))) { goto Exit; } } // Update the references count if (pIxStats->iDeltaRefs) { if ((pvField = pTmpRec->find( pTmpRec->root(), FLM_REFS_TAG)) == NULL) { // Cannot make reference count negative. if (pIxStats->iDeltaRefs < 0) { flmAssert( 0); pIxStats->iDeltaRefs = 0; } if (RC_BAD( rc = pTmpRec->insert( pTmpRec->root(), INSERT_LAST_CHILD, FLM_REFS_TAG, FLM_NUMBER_TYPE, &pvField))) { goto Exit; } uiCount = (FLMUINT) pIxStats->iDeltaRefs; } else { if (RC_BAD( rc = pTmpRec->getUINT( pvField, &uiCount))) { goto Exit; } if (pIxStats->iDeltaRefs < 0) { pIxStats->iDeltaRefs = -pIxStats->iDeltaRefs; if ((FLMUINT) pIxStats->iDeltaRefs <= uiCount) { uiCount -= (FLMUINT) pIxStats->iDeltaRefs; } else { // Reference count cannot go negative. flmAssert( 0); uiCount = 0; } } else { uiCount += (FLMUINT) pIxStats->iDeltaRefs; } } if (RC_BAD( rc = pTmpRec->setUINT( pvField, uiCount))) { goto Exit; } } // Update or add the record. pTmpRec->setID( pIxStats->uiIndexNum); pTmpRec->setContainerID( FLM_TRACKER_CONTAINER); if (bCreateRec) { if (RC_BAD( rc = FSRecUpdate( pDb, pLFile, pTmpRec, pIxStats->uiIndexNum, REC_UPD_ADD))) { goto Exit; } // Put the record into record cache. if (RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, pIxStats->uiIndexNum, pTmpRec))) { // Remove the record that was added. (void) FSRecUpdate( pDb, pLFile, NULL, pIxStats->uiIndexNum, REC_UPD_DELETE); goto Exit; } } else { // Modify the record. if (RC_BAD( rc = FSRecUpdate( pDb, pLFile, pTmpRec, pIxStats->uiIndexNum, REC_UPD_MODIFY))) { goto Exit; } // Put the modified record into record cache. if (RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, pIxStats->uiIndexNum, pTmpRec))) { // Undo the record that was modified - replace with original // record. (void) FSRecUpdate( pDb, pLFile, pRecord, pIxStats->uiIndexNum, REC_UPD_MODIFY); goto Exit; } } Exit: if (pRecord) { pRecord->Release(); } if (pTmpRec) { pTmpRec->Release(); } return (rc); } /**************************************************************************** Desc: Free the index stats structures for an FDB. ****************************************************************************/ void FSFreeIxCounts( FDB * pDb) { IX_STATS * pNextIxStat; while (pDb->pIxStats) { pNextIxStat = pDb->pIxStats->pNext; f_free( &pDb->pIxStats); pDb->pIxStats = pNextIxStat; } } /**************************************************************************** Desc: Commit the index reference and/or key counts for a database. This routine is only called at commit time. ****************************************************************************/ RCODE FSCommitIxCounts( FDB * pDb) { RCODE rc = FERR_OK; IX_STATS * pNextIxStat; while (pDb->pIxStats) { pNextIxStat = pDb->pIxStats->pNext; if (RC_BAD( rc = FSOutputIxCounts( pDb, pDb->pIxStats))) { goto Exit; } f_free( &pDb->pIxStats); pDb->pIxStats = pNextIxStat; } Exit: if (RC_BAD( rc)) { FSFreeIxCounts( pDb); } return (rc); } /**************************************************************************** Desc: Create a new record and add the reference to it. Note: The record size is the size of the only SEN value. There is no overhead for references for the entry compression ****************************************************************************/ FSTATIC RCODE FSRefCreateRec( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack) { RCODE rc = FERR_OK; FLMUINT uiElmSize; FLMUINT uiKeyLen = pKrefEntry->ui16KeyLen; FLMUINT uiRecLen; FLMBYTE * pSen; FLMBYTE byElmBuf[MAX_KEY_SIZ + BBE_KEY + SEN_MAX_SIZ]; // Create the element overhead byElmBuf[BBE_PKC] = BBE_FIRST_FLAG | BBE_LAST_FLAG; BBE_SET_KL( byElmBuf, uiKeyLen); // Copy in the key f_memcpy( &byElmBuf[BBE_KEY], (FLMBYTE*) &pKrefEntry[1], uiKeyLen); uiElmSize = (BBE_KEY + uiKeyLen); pSen = &byElmBuf[uiElmSize]; uiRecLen = SENPutNextVal( &pSen, pKrefEntry->uiDrn); uiElmSize += BBE_SET_RL( byElmBuf, uiRecLen); // Save the element if (RC_BAD( rc = FSBtInsert( pDb, pLFile, &pStack, byElmBuf, uiElmSize))) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Insert a reference into a key|reference list Note: The algorithm positions for an insert and inserts the reference while altering the block. If a reference split is required refSplit() is called and FSRefInsert() could be recursively called. ****************************************************************************/ FSTATIC RCODE FSRefInsert( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack) { RCODE rc; FLMBYTE * pElement = CURRENT_ELM( pStack); FLMUINT uiElmLen = (FLMUINT) BBE_GET_RL( pElement); FLMUINT uiElmSize; FLMBYTE byElmBuf[MAX_KEY_SIZ + BBE_KEY + REF_SET_MAX_SIZ]; // Build the element in byElmBuf[] FSSetElmOvhd( byElmBuf, BBE_KEY, 0, pStack->uiKeyLen, pElement); f_memcpy( &byElmBuf[BBE_KEY], pStack->pKeyBuf, pStack->uiKeyLen); if (uiElmLen > REF_SET_MAX_SIZ) { // May or may not split the reference set, but will always insert DRN rc = FSRefSplit( pDb, pLFile, &pStack, byElmBuf, pKrefEntry->uiDrn, INSERT_REF, (FLMBYTE) (BBE_IS_FIRST( pElement) ? SPLIT_50_50 : SPLIT_90_10)); } else { uiElmSize = BBE_KEY + pStack->uiKeyLen; if (FSSetInsertRef( &byElmBuf[uiElmSize], BBE_REC_PTR( pElement), pKrefEntry->uiDrn, &uiElmLen)) { rc = RC_SET( FERR_BTREE_ERROR); // Entry is already there goto Exit; } BBE_SET_RL( byElmBuf, uiElmLen); rc = FSBtReplace( pDb, pLFile, &pStack, byElmBuf, uiElmSize + uiElmLen); } Exit: return (rc); } /**************************************************************************** Desc: Insert a single reference into a reference set. This is the code that supports the new DIN (Dual Integer Numbers) format for reference set compression. ****************************************************************************/ RCODE FSSetInsertRef( FLMBYTE * pDestRef, FLMBYTE * pSrcRef, FLMUINT drn, FLMUINT * puiSetLength) { DIN_STATE destState; DIN_STATE srcState; FLMUINT uiSetLength = *puiSetLength; FLMUINT uiLastDrn; FLMUINT uiOldDelta; FLMUINT uiDelta; FLMUINT uiNewDelta; FLMUINT uiOneRun; FLMUINT uiPrevOneRun; FLMUINT uiLastSrcOfs; FLMUINT uiPrevLastSrcOfs = 0; FLMUINT uiMoveLen; FLMBYTE byValue; // Initialization Section uiPrevOneRun = uiOldDelta = 0; uiLastSrcOfs = 0; RESET_DINSTATE( destState); RESET_DINSTATE( srcState); // Take care of the domain value. if (*pSrcRef == SEN_DOMAIN) { srcState.uiOffset = 1; DINNextVal( pSrcRef, &srcState); uiLastSrcOfs = srcState.uiOffset; } uiLastDrn = DINNextVal( pSrcRef, &srcState); if (drn > uiLastDrn) { // ADD TO THE FRONT. Set uiDelta and uiNewDelta uiNewDelta = (uiDelta = drn) - uiLastDrn; goto FSSIR_add_delta; } uiOldDelta = uiLastDrn; uiOneRun = 0; // Search through the set finding where the "drn" fits in. while (drn < uiLastDrn) { // Save previous last source offset & previous one runs uiPrevLastSrcOfs = uiLastSrcOfs; uiLastSrcOfs = srcState.uiOffset; uiPrevOneRun = uiOneRun; uiOneRun = 0; if (srcState.uiOffset >= uiSetLength) { // APPEND TO THE END uiDelta = uiLastDrn - drn; uiNewDelta = 0; goto FSSIR_add_delta; } // Check for a run of ONE's byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_ONE_RUN( byValue)) { // Read the number of one runs uiOneRun = DINOneRunVal( pSrcRef, &srcState); uiLastDrn -= uiOneRun; if (drn > uiLastDrn) { uiLastDrn = drn; } } else { uiOldDelta = DINNextVal( pSrcRef, &srcState); uiLastDrn -= uiOldDelta; } } // Check for duplicates if (drn == uiLastDrn) { // Duplicate found or inside of one run return (RC_SET( FERR_BTREE_ERROR)); } uiDelta = uiLastDrn + uiOldDelta - drn; uiNewDelta = drn - uiLastDrn; FSSIR_add_delta: // COMPRESS MULTIPLE ONE RUNS Special case if delta is 1. Check the // previous destinaion DIN for a 1 or a run of ones and combine to the // left. Also check to combine a one run on the right by jumping within // add_new_delta. if (uiDelta == 1) { uiOneRun = 1; if (uiPrevOneRun) { f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiPrevLastSrcOfs); uiOneRun += uiPrevOneRun; } else { f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); } if (uiNewDelta == 1) { uiOneRun++; goto FSSIR_combine_right_one_runs; } // uiNewDelta is not 1 so write to destination and go on DINPutOneRunVal( pDestRef, &destState, uiOneRun); } else { f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); // No one runs found so write the delta value DINPutNextVal( pDestRef, &destState, uiDelta); } // Add new delta value (uiOldDelta - uiDelta). If the new delta value is // 1 then check next DIN for a 1 run and combine with the uiNewDelta // run. uiNewDelta only has a value when NOT appending to the end. if (uiNewDelta) { if (uiNewDelta != 1) { DINPutNextVal( pDestRef, &destState, uiNewDelta); } else { uiOneRun = 1; FSSIR_combine_right_one_runs: if (srcState.uiOffset < uiSetLength) { // CONNECT ONE RUNS Check on the right side of the source for // another 1 run. byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_ONE_RUN( byValue)) { uiOneRun += DINOneRunVal( pSrcRef, &srcState); } } DINPutOneRunVal( pDestRef, &destState, uiOneRun); } } // FSSIR_move_rest_of_dins: if ((uiMoveLen = (FLMUINT) (uiSetLength - srcState.uiOffset)) != 0) { f_memcpy( &pDestRef[destState.uiOffset], &pSrcRef[srcState.uiOffset], uiMoveLen); destState.uiOffset += uiMoveLen; } *puiSetLength = destState.uiOffset; return (FERR_OK); } /**************************************************************************** Desc: Delete a matching reference into a key|reference list. If this was the last continuation element then remove the SEN_DOMAIN value from the previous element. ****************************************************************************/ FSTATIC RCODE FSRefDelete( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack, FLMBOOL * pbSingleRef) { RCODE rc = FERR_OK; FLMBYTE * pElement = CURRENT_ELM( pStack); FLMBYTE * pReference; FLMBYTE * pDestRefPtr; FLMUINT uiElmLen = BBE_GET_RL( pElement); FLMUINT uiElmSize; FLMUINT uiSetLength; FLMUINT uiKeyLen; FLMUINT uiSenLen; FLMBYTE byElmBuf[MAX_KEY_SIZ + BBE_KEY + REF_SET_MAX_SIZ]; // Build the element in byElmBuf[] FSSetElmOvhd( byElmBuf, BBE_KEY, 0, pStack->uiKeyLen, pElement); f_memcpy( &byElmBuf[BBE_KEY], pStack->pKeyBuf, pStack->uiKeyLen); pDestRefPtr = &byElmBuf[BBE_KEY + pStack->uiKeyLen]; uiElmSize = (BBE_KEY + pStack->uiKeyLen); if (uiElmLen > REF_SET_MAX_SIZ) { // Straight deletions MAY overflow the record portion rc = FSRefSplit( pDb, pLFile, &pStack, byElmBuf, pKrefEntry->uiDrn, DELETE_REF, SPLIT_50_50); return (rc); } pReference = BBE_REC_PTR( pElement); uiSetLength = uiElmLen; if (FSSetDeleteRef( pDestRefPtr, pReference, pKrefEntry->uiDrn, &uiSetLength)) { goto Exit; } BBE_SET_RL( byElmBuf, uiSetLength); uiElmSize += uiSetLength; // Get the reference length and hasDomainFlag if (uiSetLength) { if (*pReference == SEN_DOMAIN) { uiSetLength--; uiSetLength -= SENValLen( pReference + 1); } } if (uiSetLength == 0) { FLMBOOL bLastElm = BBE_IS_LAST( pElement); FLMBOOL bFirstElm = BBE_IS_FIRST( pElement); // Delete the element if (RC_BAD( rc = FSBtDelete( pDb, pLFile, &pStack))) { return (rc); } // Remove the LAST (only) reference in an element. There are 4 cases // to consider that deal with continuation elements: // 1. ONLY element - no list - ALL DONE // 2. FIRST of many in a list - Set BBE_FIRST_FLAG in the // next element // 3. MIDDLE of a list - ALL DONE // 4. LAST in a list Remove the domain from the previous element and // set the BBE_LAST_FLAG in the new last element. if (bFirstElm && bLastElm) // Case 1 - ONLY element { *pbSingleRef = TRUE; } else if (bFirstElm && (!bLastElm)) // Case 2 - first of many { // Log the block before modifying it. if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { return (rc); } pElement = CURRENT_ELM( pStack); BBE_SET_FIRST( pElement); } else if (!bFirstElm && bLastElm) // Case 4 - Last in a list { // Element was either the ONLY or LAST in a list. FSBtPrevElm( pDb, pLFile, pStack); // Goto the previous element pElement = CURRENT_ELM( pStack); pReference = BBE_REC_PTR( pElement); uiSetLength = BBE_GET_RL( pElement); uiKeyLen = pStack->uiKeyLen; // Build the element in byElmBuf[] - sets BBE_FIRST_FLAG if set FSSetElmOvhd( byElmBuf, BBE_KEY, 0, uiKeyLen, pElement); BBE_SET_LAST( byElmBuf); // Set last element flag f_memcpy( &byElmBuf[BBE_KEY], pStack->pKeyBuf, uiKeyLen); pDestRefPtr = &byElmBuf[BBE_KEY + uiKeyLen]; // Parse past the element information and delete the domain data if (*pReference != SEN_DOMAIN) { return (RC_SET( FERR_BTREE_ERROR)); } uiSenLen = (SENValLen( pReference + 1) + 1); pReference += uiSenLen; uiSetLength -= uiSenLen; f_memcpy( pDestRefPtr, pReference, uiSetLength); BBE_SET_RL( byElmBuf, uiSetLength); uiElmSize = (BBE_KEY + uiKeyLen + uiSetLength); // We could call FSBtReplace() except that if the element is the // last element in the block, the parent element will still // contain the 3 byte non-leaf DOMAIN number which should no // longer exist. In addition, the BBE_LAST_FLAG flag should be // set in the current element and replace doesn't do that. if (RC_BAD( rc = FSBtDelete( pDb, pLFile, &pStack))) { return (rc); } // POSSIBLE CHECK YOU MAY FORGET... You could have deleted the // ONLY element in the tree. Check and if so init a new root // block and position for insert. if (pLFile->uiRootBlk == BT_END) { if (RC_BAD( rc = flmLFileInit( pDb, pLFile))) { return (rc); } // Call FSBtSearch to setup the pStack, would rather do a goto top; if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, (FLMBYTE*) &byElmBuf[BBE_KEY], uiKeyLen, 0))) { return (rc); } } else { if (RC_BAD( rc = FSBtScanTo( pStack, &byElmBuf[BBE_KEY], uiKeyLen, 0))) { goto Exit; } } rc = FSBtInsert( pDb, pLFile, &pStack, byElmBuf, uiElmSize); } } else { // Replace the current element - wElmSize has had uiSetLength added // to it rc = FSBtReplace( pDb, pLFile, &pStack, byElmBuf, uiElmSize); } if (RC_BAD( rc)) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Delete a single reference into a reference set. This is the code that supports the new DIN (Dual Integer Numbers) format for reference set compression. ****************************************************************************/ RCODE FSSetDeleteRef( FLMBYTE * pDestRef, FLMBYTE * pSrcRef, FLMUINT drn, FLMUINT * puiSetLength) { DIN_STATE destState; DIN_STATE srcState; FLMUINT uiSetLength = *puiSetLength; FLMUINT uiMoveLen; FLMUINT uiLastSrcOfs; FLMUINT uiLastDrn; FLMUINT uiOldDelta; FLMUINT uiOneRun; FLMUINT uiTemp; FLMBYTE byValue; RESET_DINSTATE( destState); RESET_DINSTATE( srcState); uiLastSrcOfs = 0; // Take care of the domain value if (*pSrcRef == SEN_DOMAIN) { srcState.uiOffset = 1; uiLastDrn = DINNextVal( pSrcRef, &srcState); uiLastSrcOfs = srcState.uiOffset; } uiLastDrn = DINNextVal( pSrcRef, &srcState); if (drn > uiLastDrn) { // Greater than the first reference return (RC_SET( FERR_KEY_NOT_FOUND)); } if (drn == uiLastDrn) { // MATCHED THE FIRST REFERENCE Write the replacement starting DRN // and fix 1 run if there. if (uiLastSrcOfs) { // If domain - copy it f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); } if (srcState.uiOffset >= uiSetLength) { // Only 1 reference goto FSSDR_done; } byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { // Run of 2 or above DINPutNextVal( pDestRef, &destState, uiLastDrn - 1); DINPutOneRunVal( pDestRef, &destState, DINOneRunVal( pSrcRef, &srcState) - 1); } else { uiLastDrn -= DINNextVal( pSrcRef, &srcState); DINPutNextVal( pDestRef, &destState, uiLastDrn); } goto FSSDR_move_rest_of_dins; } uiOldDelta = uiLastDrn; uiOneRun = 0; // Search through the set finding where the "din" fits in. Similar // while loop as the while loop in FSSetInsertRef() above. while (drn < uiLastDrn) { uiLastSrcOfs = srcState.uiOffset; uiOneRun = 0; if (srcState.uiOffset >= uiSetLength) { return (RC_SET( FERR_KEY_NOT_FOUND)); } // Check for a run of ONE's byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { // Read the number of one runs uiOneRun = DINOneRunVal( pSrcRef, &srcState); uiLastDrn -= uiOneRun; } else { uiOldDelta = DINNextVal( pSrcRef, &srcState); uiLastDrn -= uiOldDelta; } } f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); if (uiOneRun) { // Divide out a one run. drn may match the first, middle or last // value of a one run. Copy up to the last source value to dest. uiLastDrn += uiOneRun; if (drn == uiLastDrn - 1) { // Remove the first value from the run - run is >= 2 DINPutNextVal( pDestRef, &destState, 2); if (uiOneRun > 2) { DINPutOneRunVal( pDestRef, &destState, uiOneRun - 2); } goto FSSDR_move_rest_of_dins; } else { DINPutOneRunVal( pDestRef, &destState, (uiLastDrn - drn) - 1); if (drn > uiLastDrn - uiOneRun) { FLMUINT oneRun2ndHalf = uiOneRun - (uiLastDrn - drn) - 1; DINPutNextVal( pDestRef, &destState, 2); if (oneRun2ndHalf) { DINPutOneRunVal( pDestRef, &destState, oneRun2ndHalf); } goto FSSDR_move_rest_of_dins; } else { // Delete from the END of a one run uiOldDelta = 1; goto FSSDR_combine_2_deltas; } } // Should never reach here! } // Check for duplicates else if (drn != uiLastDrn) { return (RC_SET( FERR_KEY_NOT_FOUND)); } // Hit a non-one run where drn == uiLastDrn Check the next DIN value // and add uiOldDelta to it UNLESS it is a one run, then write // uiOldDelta+1 and if uiOneRun-1 is non-zero then write uiOneRun-1 if (srcState.uiOffset < uiSetLength) { byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { DINPutNextVal( pDestRef, &destState, uiOldDelta + 1); uiOneRun = DINOneRunVal( pSrcRef, &srcState) - 1; DINPutOneRunVal( pDestRef, &destState, uiOneRun); } else { FSSDR_combine_2_deltas: if (srcState.uiOffset >= uiSetLength) { goto FSSDR_done; } uiTemp = DINNextVal( pSrcRef, &srcState); DINPutNextVal( pDestRef, &destState, uiOldDelta + uiTemp); } } FSSDR_move_rest_of_dins: if ((uiMoveLen = (FLMUINT) (uiSetLength - srcState.uiOffset)) != 0) { f_memcpy( &pDestRef[destState.uiOffset], &pSrcRef[srcState.uiOffset], uiMoveLen); destState.uiOffset += uiMoveLen; } FSSDR_done: *puiSetLength = destState.uiOffset; return (FERR_OK); } /**************************************************************************** Desc: Put a SEN value into a buffer - return the length of storage used ****************************************************************************/ FLMUINT SENPutNextVal( FLMBYTE ** pSenRV, FLMUINT senValue) { FLMBYTE * pSen = *pSenRV; FLMUINT uiSenLen; if (senValue <= SEN_1B_VAL) { *pSen++ = (FLMBYTE) senValue; } else if (senValue <= SEN_2B_VAL) { *pSen++ = (FLMBYTE) (SEN_2B_CODE + (FLMBYTE) ((senValue >> 8) & SEN_2B_MASK)); *pSen++ = (FLMBYTE) senValue; } else if (senValue <= SEN_3B_VAL) { *pSen++ = (FLMBYTE) (SEN_3B_CODE + (FLMBYTE) ((senValue >> 16) & SEN_3B_MASK)); goto SENPV_2_bytes; } else if (senValue <= SEN_4B_VAL) { *pSen++ = (FLMBYTE) (SEN_4B_CODE + (FLMBYTE) ((senValue >> 24) & SEN_4B_MASK)); goto SENPV_3_bytes; } else { *pSen++ = SEN_5B_CODE; *pSen++ = (FLMBYTE) (senValue >> 24); SENPV_3_bytes: *pSen++ = (FLMBYTE) (senValue >> 16); SENPV_2_bytes: *pSen++ = (FLMBYTE) (senValue >> 8); *pSen++ = (FLMBYTE) senValue; } uiSenLen = (FLMUINT) (pSen -*pSenRV); *pSenRV = pSen; return (uiSenLen); } /**************************************************************************** Desc: Put the next one run value - high level ****************************************************************************/ FLMUINT DINPutOneRunVal( FLMBYTE * dinPtr, DIN_STATE * state, FLMUINT uiValue) { FLMUINT uiLength = 1; FLMUINT uiOffset = state->uiOffset; FLMBYTE * pOneRun; if (uiValue == 1) { dinPtr[uiOffset] = 1; } else if (uiValue <= DIN_MAX_1B_ONE_RUN) { dinPtr[uiOffset] = (FLMBYTE) (DIN_ONE_RUN_LV | (((FLMBYTE) uiValue) - 2)); } else { dinPtr[uiOffset] = DIN_ONE_RUN_HV; pOneRun = &dinPtr[uiOffset + 1]; uiLength += SENPutNextVal( &pOneRun, uiValue); } state->uiOffset += uiLength; return (uiLength); }