windows-nt/Source/XPSP1/NT/enduser/stuff/itircl/common/btree/btfill.c
2020-09-26 16:20:57 +08:00

496 lines
13 KiB
C

/*****************************************************************************
* *
* BTFILL.C *
* *
* Copyright (C) Microsoft Corporation 1990 - 1994. *
* 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. *
* *
******************************************************************************
* *
* Current Owner: Binhn *
* *
*****************************************************************************/
/*****************************************************************************
*
* Revision History: Created 08/17/90 by JohnSc
*
* 11/12/90 JohnSc RcFillHbt() wasn't setting rcBtreeError to S_OK
* 11/29/90 RobertBu #ifdef'ed out routines that are not used under
* windows.
*
*****************************************************************************/
static char s_aszModule[]= __FILE__; /* For error report */
#include <mvopsys.h>
#include <orkin.h>
#include <iterror.h>
#include <misc.h>
#include <wrapstor.h>
#include <_mvutil.h>
/*************************************************************************
*
* INTERNAL PRIVATE FUNCTIONS
*
* All of them should be declared near
*
*************************************************************************/
PRIVATE HRESULT NEAR PASCAL RcGrowCache(QBTHR);
PRIVATE KEY NEAR PASCAL KeyLeastInSubtree(QBTHR, BK, SHORT, LPVOID);
/***************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT NEAR PASCAL | RcGrowCache |
* Grow the cache by one level.
*
* @parm QBTHR | qbthr |
* Pointer to B-tree
*
* @rdesc rc
* args OUT: qbthr->bth.cLevels - incremented
* qbthr->bth.ghCache - locked
* qbthr->bth.qCache - points to locked ghCache
*
* Note: Root is at level 0, leaves at level qbthr->bth.cLevels - 1.
*
***************************************************************************/
PRIVATE HRESULT NEAR PASCAL RcGrowCache(QBTHR qbthr)
{
HANDLE gh;
QB qb;
SHORT cbcb = CbCacheBlock(qbthr);
qbthr->bth.cLevels++;
/* Allocate a new cache block
*/
if ((gh = _GLOBALALLOC(GMEM_SHARE| GMEM_MOVEABLE | GMEM_ZEROINIT,
(LONG)cbcb * qbthr->bth.cLevels)) == NULL) {
return E_OUTOFMEMORY;
}
qb = (QB)_GLOBALLOCK(gh);
/* Copy the old data */
QVCOPY(qb + cbcb, qbthr->qCache,
(LONG)cbcb * (qbthr->bth.cLevels - 1));
/* Remove the old cache */
_GLOBALUNLOCK(qbthr->ghCache);
_GLOBALFREE(qbthr->ghCache);
/* Update pointer to the new block */
qbthr->ghCache = gh;
qbthr->qCache = qb;
return S_OK;
}
/***************************************************************************
*
* @doc INTERNAL
*
* @func KEY NEAR PASCAL | KeyLeastInSubtree |
* Return the least key in the subtree speced by bk and icbLevel.
*
* @parm QBTHR | qbthr |
* Pointer to B-tree
*
* @parm BK | bk |
* bk at root of subtree
*
* @parm SHORT | icbLevel |
* level of subtree root
*
* @rdesc key - the smallest key in the subtree
* -1 if error
*
* @comm qbthr->ghCache, ->qCache - contents of cache may change
*
***************************************************************************/
PRIVATE KEY NEAR PASCAL KeyLeastInSubtree(QBTHR qbthr, BK bk,
SHORT icbLevel, PHRESULT phr)
{
QCB qcb;
SHORT icbMost = qbthr->bth.cLevels - 1;
while (icbLevel < icbMost)
{
if ((qcb = QFromBk(bk, icbLevel, qbthr, phr)) == NULL)
return (KEY)-1;
bk = *(BK UNALIGNED *UNALIGNED)qcb->db.rgbBlock;
++icbLevel;
}
if ((qcb = QFromBk(bk, icbLevel, qbthr, phr)) == NULL)
return ((KEY)-1);
return (KEY)qcb->db.rgbBlock + 2 * sizeof(BK);
}
/***************************************************************************
*
* @doc PUBLIC API
*
* @func HBT PASCAL FAR | HbtInitFill |
* Start the btree fill process. Note that the HBT returned
* is NOT a valid btree handle.
*
* @parm LPSTR | sz |
* btree name
*
* @parm BTREE_PARAMS FAR * | qbtp |
* btree creation parameters
*
* @rdesc 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.
*
* @comm Method:
* Create a btree. Create a single-block cache.
*
***************************************************************************/
PUBLIC HBT PASCAL FAR EXPORT_API HbtInitFill(LPSTR sz,
BTREE_PARAMS FAR *qbtp, PHRESULT phr)
{
HBT hbt;
QBTHR qbthr;
QCB qcb;
// Get a btree handle
if ((hbt = HbtCreateBtreeSz(sz, qbtp, phr)) == 0)
return 0;
qbthr = (QBTHR)_GLOBALLOCK(hbt);
// make a one-block cache
if ((qbthr->ghCache = _GLOBALALLOC(GMEM_ZEROINIT|GMEM_SHARE| GMEM_MOVEABLE,
(LONG)CbCacheBlock(qbthr))) == NULL)
{
SetErrCode (phr, E_OUTOFMEMORY);
_GLOBALUNLOCK(hbt);
RcAbandonHbt(hbt);
return 0;
}
qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
qcb = (QCB)qbthr->qCache;
qbthr->bth.cLevels = 1;
qbthr->bth.bkFirst = qbthr->bth.bkLast = qcb->bk = BkAlloc(qbthr, phr);
qcb->bFlags = fCacheDirty | fCacheValid;
qcb->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
- 2 * sizeof(BK);
qcb->db.cKeys = 0;
SetBkPrev(qcb, bkNil);
_GLOBALUNLOCK(qbthr->ghCache);
_GLOBALUNLOCK(hbt);
return hbt;
}
/***************************************************************************
*
* @doc PUBLIC API
*
* @func HRESULT PASCAL FAR | RcFillHbt |
* Add a key and record (in order) to the "HBT" given.
*
* @parm HBT | hbt |
* NOT a valid hbt: it was produced with HbtInitFill().
*
* @parm KEY | 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
* +++
*
* 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.
*
***************************************************************************/
HRESULT PASCAL FAR EXPORT_API RcFillHbt(HBT hbt, KEY key, QV qvRec)
{
QBTHR qbthr;
QCB qcb;
SHORT cbRec, cbKey;
QB qb;
HRESULT rc;
/* Sanity check */
if (hbt == 0 || key == 0 || qvRec == NULL)
return E_INVALIDARG;
qbthr = (QBTHR)_GLOBALLOCK(hbt);
qcb = (QCB)_GLOBALLOCK(qbthr->ghCache);
cbRec = CbSizeRec(qvRec, qbthr);
cbKey = CbSizeKey(key, qbthr, FALSE);
// Make sure key and record aren't too big for even an empty block.
if (cbRec + cbKey > (qbthr->bth.cbBlock / 2))
{
_GLOBALUNLOCK(qbthr->ghCache);
_GLOBALUNLOCK(hbt);
return E_INVALIDARG;
}
if (cbRec + cbKey > qcb->db.cbSlack) {
// key and rec don't fit in this block: write it out
SetBkNext(qcb, BkAlloc(qbthr, NULL));
if ((rc = RcWriteBlock(qcb, qbthr)) != S_OK)
{
_GLOBALUNLOCK(qbthr->ghCache);
_GLOBALFREE(qbthr->ghCache);
RcAbandonHf(qbthr->hf);
_GLOBALUNLOCK(hbt);
_GLOBALFREE(hbt);
return rc;
}
// 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++;
_GLOBALUNLOCK(qbthr->ghCache);
_GLOBALUNLOCK(hbt);
return S_OK;
}
/***************************************************************************
*
* @doc PUBLIC API
*
* @func HRESULT PASCAL FAR | RcFiniFillHbt |
* Complete filling of the hbt. After this call, the hbt is a valid
* btree handle.
*
* @parm HBT | hbt |
* NOT a valid hbt: created with RcInitFillHbt()
* and filled with keys & records by RcFillHbt().
*
* @rdesc error code
* hbt - a valid hbt (on S_OK)
*
* @comm 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 HRESULT PASCAL FAR EXPORT_API RcFiniFillHbt(HBT hbt)
{
BK bkThisMin, bkThisMost, bkThisCur, // level being scanned
bkTopMin, bkTopMost; // level being created
QBTHR qbthr;
QCB qcbThis, qcbTop;
SHORT cbKey;
KEY key;
QB qbDst;
HRESULT rc = S_OK;
if ((qbthr = (QBTHR)_GLOBALLOCK(hbt)) == NULL)
return E_INVALIDARG;
qbthr->qCache = _GLOBALLOCK(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 ((rc = RcGrowCache( qbthr)) != S_OK)
{
goto error_return;
}
qcbTop = QCacheBlock(qbthr, 0);
qcbTop->bk = bkTopMin = bkTopMost = BkAlloc(qbthr, NULL);
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 UNALIGNED *UNALIGNED)qbDst = bkThisMin;
qbDst += sizeof(BK);
for (bkThisCur = bkThisMin + 1; bkThisCur <= bkThisMost; ++bkThisCur)
{
qcbThis = QFromBk(bkThisCur, 1, qbthr, NULL);
key = (KEY)(qcbThis->db.rgbBlock + 2 * sizeof( BK));
cbKey = CbSizeKey(key, qbthr, FALSE);
if ((SHORT)(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, NULL);
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 UNALIGNED *UNALIGNED)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, NULL);
_GLOBALUNLOCK(qbthr->ghCache);
rc = RcGrowCache(qbthr);
qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
if (rc != S_OK)
{
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 UNALIGNED *UNALIGNED)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, NULL);
cbKey = CbSizeKey(key, qbthr, FALSE);
if ((SHORT)(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, NULL);
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 UNALIGNED *UNALIGNED)qbDst = bkThisCur;
qbDst += sizeof(BK);
}
}
assert(bkTopMin == bkTopMost);
qbthr->bth.bkRoot = bkTopMin;
qbthr->bth.bkEOF = bkTopMin + 1;
normal_return:
_GLOBALUNLOCK(qbthr->ghCache);
_GLOBALUNLOCK(hbt);
return rc;
error_return:
_GLOBALUNLOCK(qbthr->ghCache);
_GLOBALUNLOCK(hbt);
RcAbandonHbt(hbt);
return (rc);
}
/* EOF */