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.
491 lines
15 KiB
491 lines
15 KiB
/*****************************************************************************
|
|
* *
|
|
* BTFILL.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1990. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* *
|
|
* Functions for creating a btree by adding keys in order. This is faster *
|
|
* and the resulting btree is more compact and has adjacent leaf nodes. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Testing Notes *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Current Owner: JohnSc *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Released by Development: 00/00/00 *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Revision History: Created 08/17/90 by JohnSc
|
|
*
|
|
* 11/12/90 JohnSc RcFillHbt() wasn't setting rcBtreeError to rcSuccess
|
|
* 11/29/90 RobertBu #ifdef'ed out routines that are not used under
|
|
* windows.
|
|
* 02/04/91 Maha changed ints to INT for MAC
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include <orkin.h>
|
|
#include "_mvfs.h"
|
|
#include "imvfs.h"
|
|
|
|
#include "btpriv.h"
|
|
|
|
// _subsystem( btree );
|
|
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Prototypes *
|
|
* *
|
|
*****************************************************************************/
|
|
RC NEAR PASCAL RcGrowCache( QBTHR qbthr );
|
|
KEY NEAR PASCAL KeyLeastInSubtree( QBTHR qbthr, BK bk, INT icbLevel );
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcGrowCache( qbthr )
|
|
-
|
|
* Purpose: Grow the cache by one level.
|
|
*
|
|
* ASSUMES
|
|
* args IN: qbthr->ghCache - unlocked
|
|
* globals IN: rcBtreeError
|
|
*
|
|
* PROMISES
|
|
* returns: rc
|
|
* args OUT: qbthr->bth.cLevels - incremented
|
|
* qbthr->bth.ghCache - locked
|
|
* qbthr->bth.qCache - points to locked ghCache
|
|
* globals OUT: rcBtreeError - set to rcOutOfMemory on error
|
|
*
|
|
* Note: Root is at level 0, leaves at level qbthr->bth.cLevels - 1.
|
|
*
|
|
\***************************************************************************/
|
|
_private RC NEAR PASCAL
|
|
RcGrowCache( QBTHR qbthr )
|
|
{
|
|
GH gh;
|
|
QB qb;
|
|
INT cbcb = CbCacheBlock( qbthr );
|
|
|
|
|
|
qbthr->bth.cLevels++;
|
|
|
|
gh = GhAlloc(GMEM_SHARE| 0, (LONG)cbcb * qbthr->bth.cLevels );
|
|
if ( gh == NULL )
|
|
{
|
|
return SetBtreeErrorRc(rcOutOfMemory);
|
|
}
|
|
qb = QLockGh( gh );
|
|
|
|
QvCopy( qb + cbcb,
|
|
qbthr->qCache,
|
|
(LONG)cbcb * ( qbthr->bth.cLevels - 1 ) );
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
FreeGh( qbthr->ghCache );
|
|
qbthr->ghCache = gh;
|
|
qbthr->qCache = qb;
|
|
|
|
return SetBtreeErrorRc(rcSuccess);
|
|
}
|
|
/***************************************************************************\
|
|
*
|
|
- Function: KeyLeastInSubtree( qbthr, bk, icbLevel )
|
|
-
|
|
* Purpose: Return the least key in the subtree speced by bk and
|
|
* icbLevel.
|
|
*
|
|
* ASSUMES
|
|
* args IN: qbthr -
|
|
* bk - bk at root of subtree
|
|
* icbLevel - level of subtree root
|
|
*
|
|
* PROMISES
|
|
* returns: key - the smallest key in the subtree
|
|
* args OUT: qbthr->ghCache, ->qCache - contents of cache may change
|
|
* globals OUT: rcBtreeError?
|
|
*
|
|
\***************************************************************************/
|
|
_private KEY NEAR PASCAL
|
|
KeyLeastInSubtree( QBTHR qbthr, BK bk, INT icbLevel )
|
|
{
|
|
QCB qcb;
|
|
INT icbMost = qbthr->bth.cLevels - 1;
|
|
|
|
while ( icbLevel < icbMost )
|
|
{
|
|
qcb = QFromBk( bk, icbLevel, qbthr );
|
|
bk = *(BK FAR *)qcb->db.rgbBlock;
|
|
++icbLevel;
|
|
}
|
|
|
|
qcb = QFromBk( bk, icbLevel, qbthr );
|
|
return (KEY)qcb->db.rgbBlock + 2 * sizeof( BK );
|
|
}
|
|
|
|
// EXPORTED FUNCTIONS
|
|
/***************************************************************************\
|
|
*
|
|
- Function: HbtInitFill( sz, qbtp )
|
|
-
|
|
* Purpose: Start the btree fill process. Note that the HBT returned
|
|
* is NOT a valid btree handle.
|
|
*
|
|
* ASSUMES
|
|
* args IN: sz - btree name
|
|
* qbtp - btree creation parameters
|
|
*
|
|
* PROMISES
|
|
* returns: an HBT that isn't a valid btree handle until RcFiniFillHbt()
|
|
* is called on it (with intervening RcFillHbt()'s)
|
|
* The only valid operations on this HBT are
|
|
* RcFillHbt() - add keys in order one at a time
|
|
* RcAbandonHbt() - junk the hbt
|
|
* RcFiniFillHbt() - finish adding keys. After this, the
|
|
* hbt is a normal btree handle.
|
|
* +++
|
|
*
|
|
* Method: Create a btree. Create a single-block cache.
|
|
*
|
|
\***************************************************************************/
|
|
_public HBT PASCAL
|
|
HbtInitFill( LPSTR sz, BTREE_PARAMS FAR *qbtp )
|
|
{
|
|
HBT hbt;
|
|
QBTHR qbthr;
|
|
QCB qcb;
|
|
RC rc;
|
|
|
|
|
|
// Get a btree handle
|
|
|
|
hbt = HbtCreateBtreeSz( sz, qbtp );
|
|
if ( hbt == NULL ) return NULL;
|
|
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
|
|
|
|
// make a one-block cache
|
|
|
|
qbthr->ghCache = GhAlloc(GMEM_SHARE| 0, (LONG)CbCacheBlock( qbthr ) );
|
|
if ( qbthr->ghCache == NULL )
|
|
{
|
|
SetBtreeErrorRc(rcOutOfMemory);
|
|
goto error_return;
|
|
}
|
|
|
|
qbthr->qCache = QLockGh( qbthr->ghCache );
|
|
assert( qbthr->qCache != NULL );
|
|
qcb = (QCB)qbthr->qCache;
|
|
|
|
qbthr->bth.cLevels = 1;
|
|
qbthr->bth.bkFirst =
|
|
qbthr->bth.bkLast =
|
|
qcb->bk = BkAlloc( qbthr );
|
|
qcb->bFlags = fCacheDirty | fCacheValid;
|
|
qcb->db.cbSlack = qbthr->bth.cbBlock - sizeof( DISK_BLOCK ) + 1
|
|
- 2 * sizeof( BK );
|
|
qcb->db.cKeys = 0;
|
|
SetBkPrev( qcb, bkNil );
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return hbt;
|
|
|
|
error_return:
|
|
UnlockGh( hbt );
|
|
rc = RcGetBtreeError();
|
|
RcAbandonHbt( hbt );
|
|
SetBtreeErrorRc(rc);
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcFillHbt( hbt, key, qvRec )
|
|
-
|
|
* Purpose: Add a key and record (in order) to the "HBT" given.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt - NOT a valid hbt: it was produced with HbtInitFill().
|
|
* key - key to add. Must be greater than all keys previously
|
|
* added.
|
|
* qvRec- record associated with key
|
|
*
|
|
* PROMISES
|
|
* returns: error code
|
|
* args OUT: hbt - key, record added
|
|
* globals OUT: rcBtreeError
|
|
* +++
|
|
*
|
|
* Method: If key and record don't fit in current leaf, allocate a
|
|
* new one and make it the current one.
|
|
* Add key and record to current block.
|
|
*
|
|
\***************************************************************************/
|
|
_public RC PASCAL
|
|
RcFillHbt( HBT hbt, KEY key, QV qvRec )
|
|
{
|
|
QBTHR qbthr;
|
|
QCB qcb;
|
|
INT cbRec, cbKey;
|
|
QB qb;
|
|
|
|
|
|
assert( hbt != NULL );
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
assert( key != (KEY)NULL );
|
|
assert( qvRec != NULL );
|
|
|
|
qcb = QLockGh( qbthr->ghCache );
|
|
assert( qcb != NULL );
|
|
|
|
cbRec = CbSizeRec( qvRec, qbthr );
|
|
cbKey = CbSizeKey( key, qbthr, FALSE );
|
|
|
|
if ( cbRec + cbKey > qcb->db.cbSlack )
|
|
{
|
|
RC rc;
|
|
|
|
// key and rec don't fit in this block: write it out
|
|
SetBkNext( qcb, BkAlloc( qbthr ) );
|
|
rc = RcWriteBlock( qcb, qbthr );
|
|
if ( rc != rcSuccess )
|
|
{
|
|
UnlockGh( qbthr->ghCache );
|
|
FreeGh( qbthr->ghCache );
|
|
RcAbandonHf( qbthr->hf );
|
|
UnlockGh( hbt );
|
|
FreeGh( hbt );
|
|
return rc = RcGetBtreeError();
|
|
}
|
|
|
|
// recycle the block
|
|
SetBkPrev( qcb, qcb->bk );
|
|
qcb->bk = BkNext( qcb );
|
|
qcb->bFlags = fCacheDirty | fCacheValid;
|
|
qcb->db.cbSlack = qbthr->bth.cbBlock - sizeof( DISK_BLOCK ) + 1
|
|
- 2 * sizeof( BK );
|
|
qcb->db.cKeys = 0;
|
|
}
|
|
|
|
// add key and rec to the current block;
|
|
|
|
qb = (QB)&(qcb->db) + qbthr->bth.cbBlock - qcb->db.cbSlack;
|
|
QvCopy( qb, (QV)key, (LONG)cbKey );
|
|
QvCopy( qb + cbKey, qvRec, (LONG)cbRec );
|
|
qcb->db.cKeys++;
|
|
qcb->db.cbSlack -= ( cbKey + cbRec );
|
|
qbthr->bth.lcEntries++;
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
|
|
return SetBtreeErrorRc(rcSuccess);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcFiniFillHbt( hbt )
|
|
-
|
|
* Purpose: Complete filling of the hbt. After this call, the hbt
|
|
* is a valid btree handle.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt - NOT a valid hbt: created with RcInitFillHbt()
|
|
* and filled with keys & records by RcFillHbt().
|
|
* globals IN: rcBtreeError
|
|
*
|
|
* PROMISES
|
|
* returns: error code (rcBtreeError)
|
|
* args OUT: hbt - a valid hbt (on rcSuccess)
|
|
* globals OUT: rcBtreeError
|
|
* +++
|
|
*
|
|
* Method: Take the first key of each leaf block, creating a layer
|
|
* of internal nodes.
|
|
* Take the first key in each node in this layer to create
|
|
* another layer of internal nodes. Repeat until we get
|
|
* we get a layer with only one node. That's the root.
|
|
*
|
|
\***************************************************************************/
|
|
_public RC PASCAL
|
|
RcFiniFillHbt( HBT hbt )
|
|
{
|
|
BK bkThisMin, bkThisMost, bkThisCur, // level being scanned
|
|
bkTopMin, bkTopMost; // level being created
|
|
QBTHR qbthr;
|
|
QCB qcbThis, qcbTop;
|
|
INT cbKey;
|
|
KEY key;
|
|
QB qbDst;
|
|
RC rc;
|
|
|
|
|
|
assert( hbt != NULL );
|
|
qbthr = QLockGh( hbt );
|
|
assert( qbthr != NULL );
|
|
|
|
qbthr->qCache = QLockGh( qbthr->ghCache ); // we know cache is valid
|
|
|
|
qcbThis = QCacheBlock( qbthr, 0 );
|
|
|
|
SetBkNext( qcbThis, bkNil );
|
|
|
|
bkThisMin = qbthr->bth.bkFirst;
|
|
bkThisMost = qbthr->bth.bkLast = qcbThis->bk;
|
|
|
|
|
|
if ( bkThisMin == bkThisMost ) // only one leaf
|
|
{
|
|
qbthr->bth.bkRoot = bkThisMin;
|
|
goto normal_return;
|
|
}
|
|
|
|
if ( rcSuccess != RcGrowCache( qbthr ) )
|
|
{
|
|
goto error_return;
|
|
}
|
|
|
|
qcbTop = QCacheBlock( qbthr, 0 );
|
|
qcbTop->bk = bkTopMin = bkTopMost = BkAlloc( qbthr );
|
|
qcbTop->bFlags = fCacheDirty | fCacheValid;
|
|
qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof( DISK_BLOCK ) + 1
|
|
- sizeof( BK );
|
|
qcbTop->db.cKeys = 0;
|
|
|
|
// Get first key from each leaf node and build a layer of internal nodes.
|
|
|
|
// add bk of first leaf to the node
|
|
qbDst = qcbTop->db.rgbBlock;
|
|
*(BK FAR *)qbDst = bkThisMin;
|
|
qbDst += sizeof( BK );
|
|
|
|
for ( bkThisCur = bkThisMin + 1; bkThisCur <= bkThisMost; ++bkThisCur )
|
|
{
|
|
qcbThis = QFromBk( bkThisCur, 1, qbthr );
|
|
|
|
key = (KEY)( qcbThis->db.rgbBlock + 2 * sizeof( BK ) );
|
|
cbKey = CbSizeKey( key, qbthr, FALSE );
|
|
|
|
if ( (INT)(cbKey + sizeof( BK )) > qcbTop->db.cbSlack )
|
|
{
|
|
// key and bk don't fit in this block: write it out
|
|
rc = RcWriteBlock( qcbTop, qbthr );
|
|
|
|
// recycle the block
|
|
qcbTop->bk = bkTopMost = BkAlloc( qbthr );
|
|
qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof( DISK_BLOCK ) + 1
|
|
- sizeof( BK ); // (bk added below)
|
|
qcbTop->db.cKeys = 0;
|
|
qbDst = qcbTop->db.rgbBlock;
|
|
}
|
|
else
|
|
{
|
|
qcbTop->db.cbSlack -= cbKey + sizeof( BK );
|
|
QvCopy( qbDst, (QB)key, cbKey );
|
|
qbDst += cbKey;
|
|
qcbTop->db.cKeys++;
|
|
}
|
|
|
|
*(BK FAR *)qbDst = bkThisCur;
|
|
qbDst += sizeof( BK );
|
|
}
|
|
|
|
|
|
// Keep adding layers of internal nodes until we have a root.
|
|
|
|
while ( bkTopMost > bkTopMin )
|
|
{
|
|
bkThisMin = bkTopMin;
|
|
bkThisMost = bkTopMost;
|
|
bkTopMin = bkTopMost = BkAlloc( qbthr );
|
|
|
|
UnlockGh( qbthr->ghCache );
|
|
rc = RcGrowCache( qbthr );
|
|
qbthr->qCache = QLockGh( qbthr->ghCache );
|
|
if ( rc != rcSuccess )
|
|
{
|
|
goto error_return;
|
|
}
|
|
|
|
qcbTop = QCacheBlock( qbthr, 0 );
|
|
qcbTop->bk = bkTopMin;
|
|
qcbTop->bFlags = fCacheDirty | fCacheValid;
|
|
qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof( DISK_BLOCK ) + 1
|
|
- sizeof( BK );
|
|
qcbTop->db.cKeys = 0;
|
|
|
|
// add bk of first node of this level to current node of top level;
|
|
qbDst = qcbTop->db.rgbBlock;
|
|
*(BK FAR *)qbDst = bkThisMin;
|
|
qbDst += sizeof( BK );
|
|
|
|
|
|
// for ( each internal node in this level after first )
|
|
for ( bkThisCur = bkThisMin + 1; bkThisCur <= bkThisMost; ++bkThisCur )
|
|
{
|
|
key = KeyLeastInSubtree( qbthr, bkThisCur, 1 );
|
|
|
|
cbKey = CbSizeKey( key, qbthr, FALSE );
|
|
|
|
if ( (INT)(cbKey + sizeof( BK )) > qcbTop->db.cbSlack )
|
|
{
|
|
// key and bk don't fit in this block: write it out
|
|
rc = RcWriteBlock( qcbTop, qbthr );
|
|
|
|
// recycle the block
|
|
qcbTop->bk = bkTopMost = BkAlloc( qbthr );
|
|
qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof( DISK_BLOCK ) + 1
|
|
- sizeof( BK ); // (bk added below)
|
|
qcbTop->db.cKeys = 0;
|
|
qbDst = qcbTop->db.rgbBlock;
|
|
}
|
|
else
|
|
{
|
|
qcbTop->db.cbSlack -= cbKey + sizeof( BK );
|
|
QvCopy( qbDst, (QB)key, cbKey );
|
|
qbDst += cbKey;
|
|
qcbTop->db.cKeys++;
|
|
}
|
|
|
|
*(BK FAR *)qbDst = bkThisCur;
|
|
qbDst += sizeof( BK );
|
|
}
|
|
}
|
|
|
|
assert( bkTopMin == bkTopMost );
|
|
|
|
qbthr->bth.bkRoot = bkTopMin;
|
|
qbthr->bth.bkEOF = bkTopMin + 1;
|
|
|
|
normal_return:
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
return RcGetBtreeError();
|
|
|
|
error_return:
|
|
UnlockGh( qbthr->ghCache );
|
|
UnlockGh( hbt );
|
|
rc = RcGetBtreeError();
|
|
RcAbandonHbt( hbt );
|
|
return SetBtreeErrorRc(rc);
|
|
}
|
|
|
|
/* EOF */
|