mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
916 lines
23 KiB
916 lines
23 KiB
/*****************************************************************************
|
|
* *
|
|
* BTLOOKUP.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1989, 1990. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* Btree lookup and helper functions. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Testing Notes *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Current Owner: JohnSc *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Released by Development: long, long ago *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Revision History: Created 04/20/89 by JohnSc
|
|
*
|
|
* 08/21/90 JohnSc autodocified
|
|
* 11/29/90 RobertBu #ifdef'ed out routines that are not used in the
|
|
* WINHELP runtime.
|
|
* 05-Feb-1991 JohnSc QFromBk() wasn't returning NULL on read failure
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include <orkin.h>
|
|
#include "_mvfs.h"
|
|
#include "imvfs.h"
|
|
|
|
#include "btpriv.h"
|
|
// _subsystem( btree );
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* Private Functions
|
|
*
|
|
\***************************************************************************/
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: CbSizeRec( qRec, qbthr )
|
|
-
|
|
* Purpose: Get the size of a record.
|
|
*
|
|
* ASSUMES
|
|
* args IN: qRec - the record to be sized
|
|
* qbthr - btree header containing record format string
|
|
*
|
|
* PROMISES
|
|
* returns: size of the record in bytes
|
|
* +++
|
|
* Method: If we've never computed the size before, we do so by looking
|
|
* at the record format string in the btree header. If the
|
|
* record is fixed size, we store the size in the header for
|
|
* next time. If it isn't fixed size, we have to look at the
|
|
* actual record to determine its size.
|
|
*
|
|
\***************************************************************************/
|
|
_private INT
|
|
CbSizeRec( qRec, qbthr )
|
|
QV qRec;
|
|
QBTHR qbthr;
|
|
{
|
|
CHAR ch;
|
|
QCH qchFormat = qbthr->bth.rgchFormat;
|
|
INT cb = 0;
|
|
BOOL fFixedSize;
|
|
|
|
|
|
if ( qbthr->cbRecordSize )
|
|
return qbthr->cbRecordSize;
|
|
|
|
fFixedSize = TRUE;
|
|
|
|
for ( qchFormat++; ch = *qchFormat; qchFormat++ )
|
|
{
|
|
switch ( ch )
|
|
{
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
cb += ch - '0';
|
|
break;
|
|
|
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
|
cb += ch + 10 - 'a';
|
|
break;
|
|
|
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
|
cb += ch + 10 - 'A';
|
|
break;
|
|
|
|
case FMT_BYTE_PREFIX:
|
|
cb += sizeof( BYTE ) + *( (QB)qRec + cb );
|
|
fFixedSize = FALSE;
|
|
break;
|
|
|
|
case FMT_WORD_PREFIX:
|
|
cb += sizeof( INT ) + *( (QW)qRec + cb );
|
|
fFixedSize = FALSE;
|
|
break;
|
|
|
|
case FMT_SZ:
|
|
cb += lstrlen( (QB)qRec + cb ) + 1;
|
|
fFixedSize = FALSE;
|
|
break;
|
|
|
|
default:
|
|
/* error */
|
|
assert( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( fFixedSize )
|
|
{
|
|
qbthr->cbRecordSize = cb;
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
- Function: FReadBlock( qcb, qbthr )
|
|
-
|
|
* Purpose: Read a block from the btree file into the cache block.
|
|
*
|
|
* ASSUMES
|
|
* args IN: qcb->bk - bk of block to read
|
|
* qbthr->cbBlock - size of disk block to read
|
|
*
|
|
* PROMISES
|
|
* returns: fTruth of success
|
|
* args OUT: qcb->db - receives block read in from file
|
|
* qcb->bFlags - fCacheValid flag set, all others cleared
|
|
*
|
|
* Side Effects: Fatal exit on read or seek failure (corrupted file or qbthr)
|
|
*
|
|
* Notes: Doesn't know about real cache, just this block
|
|
*
|
|
\***************************************************************************/
|
|
_private BOOL
|
|
FReadBlock( qcb, qbthr )
|
|
QCB qcb;
|
|
QBTHR qbthr;
|
|
{
|
|
LONG l;
|
|
|
|
|
|
assert( qcb->bk < qbthr->bth.bkEOF );
|
|
|
|
Ensure( LSeekHf( qbthr->hf, LifFromBk( qcb->bk, qbthr ), wFSSeekSet ),
|
|
LifFromBk( qcb->bk, qbthr ) );
|
|
|
|
l = qbthr->bth.cbBlock;
|
|
if ( LcbReadHf( qbthr->hf, &(qcb->db), (LONG)qbthr->bth.cbBlock ) != l )
|
|
{
|
|
SetBtreeErrorRc(RcGetFSError()) == rcSuccess ? rcInvalid : RcGetFSError();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
qcb->bFlags = fCacheValid;
|
|
SetBtreeErrorRc(rcSuccess);
|
|
return TRUE;
|
|
}
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcWriteBlock( qcb, qbthr )
|
|
-
|
|
* Purpose: Write a cached block to a file.
|
|
*
|
|
* ASSUMES
|
|
* args IN: qcb->db - the block to write
|
|
* qcb->bk - bk of block to write
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess; rcFailure; rcDiskFull (when we can detect it)
|
|
* args OUT: qbthr->hf - we write to this file
|
|
*
|
|
* Side Effects: Fatal exit on read or seek failure.
|
|
*
|
|
* Note: Don't reset dirty flag, because everyone who wants
|
|
* that done does it themselves. (?)
|
|
*
|
|
\***************************************************************************/
|
|
_private RC
|
|
RcWriteBlock( qcb, qbthr )
|
|
QCB qcb;
|
|
QBTHR qbthr;
|
|
{
|
|
assert( qcb->bk < qbthr->bth.bkEOF );
|
|
|
|
Ensure( LSeekHf( qbthr->hf, LifFromBk( qcb->bk, qbthr ), wFSSeekSet ),
|
|
LifFromBk( qcb->bk, qbthr ) );
|
|
|
|
LcbWriteHf( qbthr->hf, &(qcb->db), (LONG)qbthr->bth.cbBlock );
|
|
|
|
return SetBtreeErrorRc(RcGetFSError());
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
- Function: QFromBk( bk, wLevel, qbthr )
|
|
-
|
|
* Purpose: Convert a BK into a pointer to a cache block. Cache the
|
|
* block at the given level, if it isn't there already.
|
|
*
|
|
* ASSUMES
|
|
* args IN: bk - BK to convert
|
|
* wLevel - btree level
|
|
* qbthr -
|
|
* state IN: btree cache is locked
|
|
*
|
|
* PROMISES
|
|
* returns: pointer to the cache block, with all fields up to date
|
|
* or NULL on I/O error
|
|
* state OUT: block will be in cache at specified level; cache locked
|
|
*
|
|
\***************************************************************************/
|
|
_private QCB
|
|
QFromBk(
|
|
BK bk,
|
|
INT wLevel,
|
|
QBTHR qbthr)
|
|
{
|
|
QCB qcb;
|
|
|
|
|
|
assert( wLevel >= 0 && wLevel < qbthr->bth.cLevels );
|
|
assert( bk < qbthr->bth.bkEOF );
|
|
|
|
qcb = QCacheBlock( qbthr, wLevel );
|
|
|
|
if ( !( qcb->bFlags & fCacheValid ) || bk != qcb->bk )
|
|
{
|
|
/* requested block is not cached */
|
|
|
|
if ( ( qcb->bFlags & fCacheDirty ) && ( qcb->bFlags & fCacheValid ) )
|
|
{
|
|
if ( RcWriteBlock( qcb, qbthr ) != rcSuccess )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
qcb->bk = bk;
|
|
if ( !FReadBlock( qcb, qbthr ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetBtreeErrorRc(rcSuccess);
|
|
}
|
|
|
|
return qcb;
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcFlushCache( qbthr )
|
|
-
|
|
* Purpose: Write out dirty cache blocks
|
|
*
|
|
* ASSUMES
|
|
* args IN: qbthr - qCache is locked
|
|
*
|
|
* PROMISES
|
|
* returns: rc
|
|
* globals OUT rcBtreeError
|
|
* state OUT: btree file is up to date. cache block dirty flags reset
|
|
*
|
|
\***************************************************************************/
|
|
_private RC FAR PASCAL
|
|
RcFlushCache( qbthr )
|
|
QBTHR qbthr;
|
|
{
|
|
INT i;
|
|
QB qb;
|
|
|
|
|
|
SetBtreeErrorRc(rcSuccess);
|
|
|
|
for ( i = qbthr->bth.cLevels, qb = qbthr->qCache;
|
|
i > 0;
|
|
i--, qb += CbCacheBlock( qbthr ) )
|
|
{
|
|
if ( ( ((QCB)qb)->bFlags & ( fCacheDirty | fCacheValid ) )
|
|
==
|
|
( fCacheValid | fCacheDirty ) )
|
|
{
|
|
if ( RcWriteBlock( (QCB)qb, qbthr ) != rcSuccess )
|
|
{
|
|
break;
|
|
}
|
|
((QCB)qb)->bFlags &= ~fCacheDirty;
|
|
}
|
|
}
|
|
|
|
return RcGetBtreeError();
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
* Public Functions
|
|
*
|
|
\***************************************************************************/
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcLookupByKeyAux( hbt, key, qbtpos, qData, fNormal )
|
|
-
|
|
* Purpose: Look up a key in a btree and retrieve the data.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt - btree handle
|
|
* key - key we are looking up
|
|
* qbtpos - pointer to buffer for pos; use NULL if not wanted
|
|
* qData - pointer to buffer for record; NULL if not wanted
|
|
* fInsert - TRUE: if key would lie between two blocks, pos
|
|
* refers to proper place to insert it
|
|
* FALSE: pos returned will be valid unless
|
|
* key > all keys in btree
|
|
* state IN: cache is unlocked
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess if found, rcNoExists if not found;
|
|
* other errors like rcOutOfMemory
|
|
* args OUT: key found:
|
|
* qbtpos - btpos for this key
|
|
* qData - record for this key
|
|
*
|
|
* key not found:
|
|
* qbtpos - btpos for first key > this key
|
|
* qData - record for first key > this key
|
|
*
|
|
* key not found, no keys in btree > key:
|
|
* qbtpos - invalid (qbtpos->bk == bkNil)
|
|
* qData - undefined
|
|
* globals OUT rcBtreeError
|
|
* state OUT: All ancestor blocks back to root are cached
|
|
*
|
|
\***************************************************************************/
|
|
_public RC
|
|
RcLookupByKeyAux( hbt, key, qbtpos, qData, fInsert )
|
|
HBT hbt;
|
|
KEY key;
|
|
QBTPOS qbtpos;
|
|
QV qData;
|
|
BOOL fInsert;
|
|
{
|
|
QBTHR qbthr;
|
|
INT wLevel;
|
|
BK bk;
|
|
|
|
|
|
assert( hbt != NULL );
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
|
|
if ( qbthr->bth.cLevels <= 0 )
|
|
{
|
|
UnlockGh( hbt );
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bkNil;
|
|
}
|
|
return SetBtreeErrorRc(rcNoExists);
|
|
}
|
|
|
|
if ( qbthr->ghCache == NULL )
|
|
{
|
|
if ( RcMakeCache( qbthr ) != rcSuccess )
|
|
{
|
|
UnlockGh( hbt );
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bkNil;
|
|
}
|
|
return RcGetBtreeError();
|
|
}
|
|
}
|
|
|
|
qbthr->qCache = QLockGh( qbthr->ghCache );
|
|
|
|
for ( wLevel = 0, bk = qbthr->bth.bkRoot;
|
|
bk != bkNil && wLevel < qbthr->bth.cLevels - 1;
|
|
wLevel++ )
|
|
{
|
|
bk = qbthr->BkScanInternal( bk, key, wLevel, qbthr, NULL );
|
|
}
|
|
|
|
if ( bk == bkNil )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
|
|
if ( qbthr->RcScanLeaf( bk, key, wLevel, qbthr, qData, qbtpos ) == rcNoExists
|
|
&&
|
|
qbtpos != NULL
|
|
&&
|
|
!fInsert )
|
|
{
|
|
QCB qcb = QFromBk( qbtpos->bk, qbthr->bth.cLevels - 1, qbthr );
|
|
|
|
if ( qcb != NULL )
|
|
{
|
|
/* error code was clobbered by QFromBk: restore it */
|
|
SetBtreeErrorRc(rcNoExists);
|
|
|
|
if ( qcb != NULL && qbtpos->cKey == qcb->db.cKeys )
|
|
{
|
|
if ( qbtpos->bk == qbthr->bth.bkLast )
|
|
{
|
|
qbtpos->bk = bkNil;
|
|
}
|
|
else
|
|
{
|
|
qbtpos->bk = BkNext( qcb );
|
|
qbtpos->cKey = 0;
|
|
qbtpos->iKey = 2 * sizeof( BK );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
|
|
return RcGetBtreeError();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcFirstHbt( hbt, key, qvRec, qbtpos )
|
|
-
|
|
* Purpose: Get first key and record from btree.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt
|
|
* key - points to buffer big enough to hold a key
|
|
* (256 bytes is more than enough)
|
|
* qvRec - pointer to buffer for record or NULL if not wanted
|
|
* qbtpos- pointer to buffer for btpos or NULL if not wanted
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess if anything found, else error code.
|
|
* args OUT: key - key copied here
|
|
* qvRec - record copied here
|
|
* qbtpos- btpos of first entry copied here
|
|
*
|
|
\***************************************************************************/
|
|
_public RC PASCAL
|
|
RcFirstHbt( hbt, key, qvRec, qbtpos )
|
|
HBT hbt;
|
|
KEY key;
|
|
QV qvRec;
|
|
QBTPOS qbtpos;
|
|
{
|
|
QBTHR qbthr;
|
|
BK bk;
|
|
QCB qcb;
|
|
INT cbKey, cbRec;
|
|
QB qb;
|
|
|
|
|
|
assert( hbt != NULL );
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
|
|
if ( qbthr->bth.lcEntries == (LONG)0 )
|
|
{
|
|
UnlockGh( hbt );
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bkNil;
|
|
qbtpos->iKey = 0;
|
|
qbtpos->cKey = 0;
|
|
}
|
|
return SetBtreeErrorRc(rcNoExists);
|
|
}
|
|
|
|
bk = qbthr->bth.bkFirst;
|
|
assert( bk != bkNil );
|
|
|
|
if ( qbthr->ghCache == NULL )
|
|
{
|
|
if ( RcMakeCache( qbthr ) != rcSuccess )
|
|
{
|
|
UnlockGh( hbt );
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bkNil;
|
|
}
|
|
return RcGetBtreeError();
|
|
}
|
|
}
|
|
|
|
qbthr->qCache = QLockGh( qbthr->ghCache );
|
|
|
|
if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
|
|
qb = qcb->db.rgbBlock + 2 * sizeof( BK );
|
|
|
|
cbKey = CbSizeKey( (KEY)qb, qbthr, TRUE );
|
|
if ( (QV)key != NULL ) QvCopy( (QV)key, qb, (LONG)cbKey );
|
|
qb += cbKey;
|
|
|
|
cbRec = CbSizeRec( qb, qbthr );
|
|
if ( qvRec != NULL ) QvCopy( qvRec, qb, (LONG)cbRec );
|
|
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bk;
|
|
qbtpos->iKey = 2 * sizeof( BK );
|
|
qbtpos->cKey = 0;
|
|
}
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
|
|
return SetBtreeErrorRc(rcSuccess);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcLastHbt( hbt, key, qvRec, qbtpos )
|
|
-
|
|
* Purpose: Get last key and record from btree.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt
|
|
* key - points to buffer big enough to hold a key
|
|
* (256 bytes is more than enough)
|
|
* qvRec - points to buffer big enough for record
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess if anything found, else error code.
|
|
* args OUT: key - key copied here
|
|
* qvRec - record copied here
|
|
*
|
|
\***************************************************************************/
|
|
_public RC PASCAL
|
|
RcLastHbt( hbt, key, qvRec, qbtpos )
|
|
HBT hbt;
|
|
KEY key;
|
|
QV qvRec;
|
|
QBTPOS qbtpos;
|
|
{
|
|
QBTHR qbthr;
|
|
BK bk;
|
|
QCB qcb;
|
|
INT cbKey, cbRec, cKey;
|
|
QB qb;
|
|
|
|
|
|
assert( hbt != NULL );
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
|
|
if ( qbthr->bth.lcEntries == (LONG)0 )
|
|
{
|
|
UnlockGh( hbt );
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bkNil;
|
|
qbtpos->iKey = 0;
|
|
qbtpos->cKey = 0;
|
|
}
|
|
return SetBtreeErrorRc(rcNoExists);
|
|
}
|
|
|
|
bk = qbthr->bth.bkLast;
|
|
assert( bk != bkNil );
|
|
|
|
if ( qbthr->ghCache == NULL )
|
|
{
|
|
if ( RcMakeCache( qbthr ) != rcSuccess )
|
|
{
|
|
UnlockGh( hbt );
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bkNil;
|
|
}
|
|
return RcGetBtreeError();
|
|
}
|
|
}
|
|
|
|
qbthr->qCache = QLockGh( qbthr->ghCache );
|
|
|
|
if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
|
|
qb = qcb->db.rgbBlock + 2 * sizeof( BK );
|
|
|
|
for ( cKey = 0; cKey < qcb->db.cKeys - 1; cKey++ )
|
|
{
|
|
qb += CbSizeKey( (KEY)qb, qbthr, TRUE );
|
|
qb += CbSizeRec( qb, qbthr );
|
|
}
|
|
|
|
cbKey = CbSizeKey( (KEY)qb, qbthr, FALSE );
|
|
if ( (QV)key != NULL ) QvCopy( (QV)key, qb, (LONG)cbKey ); // decompress
|
|
|
|
cbRec = CbSizeRec( qb + cbKey, qbthr );
|
|
if ( qvRec != NULL ) QvCopy( qvRec, qb + cbKey, (LONG)cbRec );
|
|
|
|
if ( qbtpos != NULL )
|
|
{
|
|
qbtpos->bk = bk;
|
|
qbtpos->iKey = qb - (QB)qcb->db.rgbBlock;
|
|
qbtpos->cKey = cKey;
|
|
}
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
|
|
return SetBtreeErrorRc(rcSuccess);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcLookupByPos( hbt, qbtpos, key, qRec )
|
|
-
|
|
* Purpose: Map a pos into a key and rec (both optional).
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt - the btree
|
|
* qbtpos - pointer to pos
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess, rcOutOfMemory
|
|
* Note: we assert() if the pos is invalid
|
|
* args OUT: key - if not (KEY)NULL, key copied here, not to exceed iLen
|
|
* qRec - if not NULL, record copied here
|
|
*
|
|
\***************************************************************************/
|
|
_public RC FAR PASCAL
|
|
RcLookupByPos(
|
|
HBT hbt,
|
|
QBTPOS qbtpos,
|
|
KEY key,
|
|
INT iLen, // length of key buffer8
|
|
QV qRec) {
|
|
|
|
QBTHR qbthr;
|
|
QCB qcbLeaf;
|
|
QB qb;
|
|
|
|
|
|
assert( FValidPos( qbtpos ) );
|
|
assert( hbt != NULL );
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
|
|
if ( qbthr->bth.cLevels <= 0 )
|
|
{
|
|
UnlockGh( hbt );
|
|
return rcNoExists;
|
|
}
|
|
|
|
if ( qbthr->ghCache == NULL )
|
|
{
|
|
if ( RcMakeCache( qbthr ) != rcSuccess )
|
|
{
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
}
|
|
|
|
qbthr->qCache = QLockGh( qbthr->ghCache );
|
|
if ( ( qcbLeaf = QFromBk( qbtpos->bk, qbthr->bth.cLevels - 1, qbthr ) )
|
|
==
|
|
NULL )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
|
|
|
|
assert( qbtpos->cKey < qcbLeaf->db.cKeys
|
|
&&
|
|
qbtpos->cKey >= 0
|
|
&&
|
|
qbtpos->iKey >= 2 * sizeof( BK )
|
|
&&
|
|
qbtpos->iKey <= (INT)(qbthr->bth.cbBlock - sizeof( DISK_BLOCK )) );
|
|
|
|
|
|
qb = qcbLeaf->db.rgbBlock + qbtpos->iKey;
|
|
|
|
if ( key != (KEY)NULL )
|
|
{
|
|
QvCopy( (QV)key, qb, (LONG)MIN(iLen,CbSizeKey( (KEY)qb, qbthr, FALSE )) ); /* need to decompress */
|
|
qb += CbSizeKey( key, qbthr, TRUE );
|
|
}
|
|
|
|
if ( qRec != NULL )
|
|
{
|
|
QvCopy( qRec, qb, (LONG)CbSizeRec( qb, qbthr ) );
|
|
}
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
|
|
return SetBtreeErrorRc(rcSuccess);
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcNextPos( hbt, qbtposIn, qbtposOut )
|
|
-
|
|
* Purpose: get the next record from the btree
|
|
* Next means the next key lexicographically from the key
|
|
* most recently inserted or looked up
|
|
* Won't work if we looked up a key and it wasn't there
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt -
|
|
* state IN: a record has been read from or written to the file
|
|
* since the last deletion
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess; rcNoExists if no successor record
|
|
* args OUT: key - next key copied to here
|
|
* qvRec - record gets copied here
|
|
*
|
|
\***************************************************************************/
|
|
_public RC PASCAL
|
|
RcNextPos( hbt, qbtposIn, qbtposOut )
|
|
HBT hbt;
|
|
QBTPOS qbtposIn, qbtposOut;
|
|
{
|
|
LONG l;
|
|
|
|
return RcOffsetPos( hbt, qbtposIn, (LONG)1, &l, qbtposOut );
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcOffsetPos( hbt, qbtposIn, lcOffset, qlcRealOffset, qbtposOut )
|
|
-
|
|
* Purpose: Take a pos and an offset and return a new pos, offset from
|
|
* the previous pos by specified amount. If not possible
|
|
* (i.e. prev of first) return real amount offset and a pos.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt - handle to btree
|
|
* qbtposIn - position we want an offset from
|
|
* lcOffset - amount to offset (+ or - OK)
|
|
*
|
|
* PROMISES
|
|
* returns: rc
|
|
* args OUT: qbtposOut - new position offset by *qcRealOffset
|
|
* *qlcRealOffset - equal to lcOffset if legal, otherwise
|
|
* as close as is legal
|
|
*
|
|
\***************************************************************************/
|
|
_public RC PASCAL
|
|
RcOffsetPos( hbt, qbtposIn, lcOffset, qlcRealOffset, qbtposOut )
|
|
HBT hbt;
|
|
QBTPOS qbtposIn;
|
|
LONG lcOffset;
|
|
QL qlcRealOffset;
|
|
QBTPOS qbtposOut;
|
|
{
|
|
QBTHR qbthr;
|
|
RC rc = rcSuccess;
|
|
INT c;
|
|
LONG lcKey, lcDelta = (LONG)0;
|
|
QCB qcb;
|
|
BK bk;
|
|
QB qb;
|
|
|
|
|
|
assert( FValidPos( qbtposIn ) );
|
|
bk = qbtposIn->bk;
|
|
|
|
assert( hbt != NULL );
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
assert( qlcRealOffset != NULL );
|
|
|
|
if ( qbthr->bth.cLevels <= 0 )
|
|
{
|
|
UnlockGh( hbt );
|
|
return SetBtreeErrorRc(rcNoExists);
|
|
}
|
|
|
|
if ( qbthr->ghCache == NULL )
|
|
{
|
|
if ( rc = RcMakeCache( qbthr ) != rcSuccess )
|
|
{
|
|
UnlockGh( hbt );
|
|
if ( qbtposOut != NULL )
|
|
{
|
|
qbtposOut->bk = bkNil;
|
|
}
|
|
return RcGetBtreeError();
|
|
}
|
|
}
|
|
|
|
qbthr->qCache = QLockGh( qbthr->ghCache ); // >>>>what if no entries??
|
|
|
|
assert( qbthr->qCache != NULL );
|
|
|
|
if ( ( qcb = QFromBk( qbtposIn->bk, qbthr->bth.cLevels - 1, qbthr ) )
|
|
==
|
|
NULL )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
|
|
lcKey = qbtposIn->cKey + lcOffset;
|
|
|
|
/* chase prev to find the right block */
|
|
while ( lcKey < 0 )
|
|
{
|
|
bk = BkPrev( qcb );
|
|
if ( bk == bkNil )
|
|
{
|
|
bk = qcb->bk;
|
|
lcDelta = lcKey;
|
|
lcKey = 0;
|
|
break;
|
|
}
|
|
if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
lcKey += qcb->db.cKeys;
|
|
}
|
|
|
|
/* chase next to find the right block */
|
|
while ( lcKey >= qcb->db.cKeys )
|
|
{
|
|
lcKey -= qcb->db.cKeys;
|
|
bk = BkNext( qcb );
|
|
if ( bk == bkNil )
|
|
{
|
|
bk = qcb->bk;
|
|
lcDelta = lcKey + 1;
|
|
lcKey = qcb->db.cKeys - 1;
|
|
break;
|
|
}
|
|
if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
}
|
|
}
|
|
|
|
|
|
if ( bk == qbtposIn->bk && lcKey >= qbtposIn->cKey )
|
|
{
|
|
c = qbtposIn->cKey;
|
|
qb = qcb->db.rgbBlock + qbtposIn->iKey;
|
|
}
|
|
else
|
|
{
|
|
c = 0;
|
|
qb = qcb->db.rgbBlock + 2 * sizeof( BK );
|
|
}
|
|
|
|
while ( (LONG)c < lcKey )
|
|
{
|
|
qb += CbSizeKey( (KEY)qb, qbthr, TRUE );
|
|
qb += CbSizeRec( qb, qbthr );
|
|
c++;
|
|
}
|
|
|
|
if ( qbtposOut != NULL )
|
|
{
|
|
qbtposOut->bk = bk;
|
|
qbtposOut->iKey = qb - (QB)qcb->db.rgbBlock;
|
|
qbtposOut->cKey = c;
|
|
}
|
|
|
|
*qlcRealOffset = lcOffset - lcDelta;
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
|
|
return SetBtreeErrorRc(lcDelta ? rcNoExists : rcSuccess);
|
|
}
|
|
|
|
|
|
/* EOF */
|