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