/***************************************************************************** * * * BTDELETE.C * * * * Copyright (C) Microsoft Corporation 1989 - 1994. * * All Rights reserved. * * * ****************************************************************************** * * * Module Intent * * * * Btree deletion functions and helpers. * * * ****************************************************************************** * * * Current Owner: Binhn * * * *****************************************************************************/ static char s_aszModule[]= __FILE__; /* For error report */ #include #include #include #include #include #include <_mvutil.h> /* Put all functions into the same segment to avoid memory fragmentation * and load time surplus for the MAC */ // #pragma code_seg ("MVFS") /*************************************************************************** * * Public Functions * ***************************************************************************/ /***************************************************************************\ * * @doc PUBLIC API * * @func HRESULT PASCAL FAR | RcDeleteHbt | * delete a key from a btree. * Just copy over the key and update cbSlack. * Doesn't yet merge blocks less than half full or update key in * parent key if we deleted the first key in a block. * * * @parm HBT | hbt | * handle of the btree * * @parm KEY | key | * the key to delete from the btree * * @rdesc S_OK if delete works; ERR_NOTEXIST * * @rcomm Unfinished: doesn't do block merges or parent updates. * ***************************************************************************/ PUBLIC HRESULT PASCAL FAR EXPORT_API RcDeleteHbt(HBT hbt, KEY key) { QBTHR qbthr; HF hf; HRESULT rc; QCB qcb; QB qb; SHORT cb; BTPOS btpos; if ((qbthr = (QBTHR)_GLOBALLOCK(hbt)) == NULL) return(E_INVALIDARG); hf = qbthr->hf; if (qbthr->bth.bFlags & fFSOpenReadOnly) { rc = E_NOPERMISSION; exit0: _GLOBALUNLOCK(hbt); return rc; } /* look up the key */ if ((rc = RcLookupByKey(hbt, key, &btpos, NULL)) != S_OK) goto exit0; qbthr->qCache = _GLOBALLOCK(qbthr->ghCache); // copy over this key and rec with subsequent keys and recs if ((qcb = QCacheBlock(qbthr, qbthr->bth.cLevels - 1)) == NULL) { rc = E_FAIL; exit1: _GLOBALUNLOCK(qbthr->ghCache); goto exit0; } qb = qcb->db.rgbBlock + btpos.iKey; cb = CbSizeKey((KEY)qb, qbthr, TRUE); cb += CbSizeRec(qb + cb, qbthr); QVCOPY(qb, qb + cb, (LONG)(qbthr->bth.cbBlock + (QB)&(qcb->db) - qb - cb - qcb->db.cbSlack)); qcb->db.cbSlack += cb; // if this was the first key in the leaf block, update key in parent // >>>>> code goes here // if block is now less than half full, merge blocks // >>>>> code goes here qcb->db.cKeys--; qcb->bFlags |= fCacheDirty; qbthr->bth.lcEntries--; qbthr->bth.bFlags |= fFSDirty; rc = S_OK; goto exit1; } /***************************************************************************\ * * @doc PUBLIC API * * @func HRESULT PASCAL FAR | RcTraverseHbt | * Traverses entire btree, calling a user function at each entry. * Optionally, the user can cause the current entry to be deleted. * * @parm HBT | hbt | * handle of the btree * * @parm TRAVERSE_FUNC | fnCallback | * Callback of the form DWORD Callback(KEY key, QB rec, DWORD dwUser) * return TRAVERSE_DONE for normal exit, TRAVERSE_DELETE to delete this * entry, TRAVERSE_INTERRUPT to interrupt and stop traversing. * * @parm DWORD | dwUser | * User data that gets passed into the callback. * * @rdesc S_OK if delete works * @comm Assumes that RcDeleteHbt does not do block merges. Keys and * records are limited to 256 bytes in this function * ***************************************************************************/ typedef DWORD (FAR PASCAL *TRAVERSE_FUNC) (KEY key, QB rec, DWORD dwUser); PUBLIC HRESULT PASCAL FAR EXPORT_API RcTraverseHbt(HBT hbt, TRAVERSE_FUNC fnCallback, DWORD dwUser) { QBTHR qbthr; HRESULT rc; BK bk; QCB qcb; QB qb; BTPOS btpos; BYTE * pKeyData; BYTE * pRecData; HRESULT errb; SHORT cbKey, cbRec; if ((hbt==NULL) || (!fnCallback)) return E_INVALIDARG; if ((qbthr = (QBTHR)_GLOBALLOCK(hbt)) == NULL) return(E_INVALIDARG); if (qbthr->bth.lcEntries == (LONG)0) { rc = E_NOTEXIST; exit0: _GLOBALUNLOCK(hbt); return rc; } if ((bk = qbthr->bth.bkFirst) == bkNil) { rc = E_ASSERT; goto exit0; } if (qbthr->ghCache == NULL) { if ((rc = RcMakeCache(qbthr)) != S_OK) goto exit0; } qbthr->qCache = _GLOBALLOCK(qbthr->ghCache); if ((qcb = QFromBk(bk, (SHORT)(qbthr->bth.cLevels - 1), qbthr, &errb)) == NULL) { rc = errb; exit1: _GLOBALUNLOCK(qbthr->ghCache); goto exit0; } qb = qcb->db.rgbBlock + 2 * sizeof(BK); pKeyData=qb; cbKey = CbSizeKey((KEY)qb, qbthr, TRUE); qb += cbKey; pRecData=qb; cbRec = CbSizeRec(qb, qbthr); btpos.bk = bk; btpos.iKey = 2 * sizeof(BK); btpos.cKey = 0; while (1) { DWORD dwRes=(*fnCallback)((KEY)pKeyData,(QB)pRecData,dwUser); BTPOS btposNew; HRESULT errMore; errMore=RcNextPos( hbt, &btpos, &btposNew ); //==ERR_NOTEXIST) if (dwRes==1) // delete case { if (qbthr->bth.bFlags & fFSOpenReadOnly) { rc = E_NOPERMISSION; goto exit1; } // delete entry, and keep btposNew up to date during delete qcb = QFromBk(btpos.bk, (SHORT)(qbthr->bth.cLevels - 1), qbthr,&errb); //qb = qcb->db.rgbBlock + btpos.iKey; qb = qcb->db.rgbBlock + btpos.iKey; QVCOPY(qb, qb + cbKey + cbRec, (LONG)(qbthr->bth.cbBlock + (QB)&(qcb->db) - qb - (cbKey + cbRec) - qcb->db.cbSlack)); qcb->db.cbSlack += cbKey + cbRec; qcb->db.cKeys--; qcb->bFlags |= fCacheDirty; qbthr->bth.lcEntries--; qbthr->bth.bFlags |= fFSDirty; if (btposNew.bk == btpos.bk) { btposNew.iKey-=cbKey + cbRec; btposNew.cKey--; } } else if (dwRes == 2) { rc = E_INTERRUPT; goto exit1; } if (errMore==S_OK) { btpos=btposNew; qcb = QFromBk(btpos.bk, (SHORT)(qbthr->bth.cLevels - 1), qbthr,&errb); qb = qcb->db.rgbBlock + btpos.iKey; pKeyData=qb; cbKey = CbSizeKey((KEY)qb, qbthr, TRUE); qb += cbKey; pRecData=qb; cbRec = CbSizeRec(qb, qbthr); } else break; } rc=S_OK; goto exit1; } /* EOF */