Windows NT 4.0 source code leak
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

/*****************************************************************************
* *
* 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 */