git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@213 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1352 lines
29 KiB
C++
1352 lines
29 KiB
C++
//-------------------------------------------------------------------------
|
|
// 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);
|
|
}
|