Source code of Windows XP (NT5)
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.

495 lines
13 KiB

  1. /*****************************************************************************
  2. * *
  3. * BTFILL.C *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1990 - 1994. *
  6. * All Rights reserved. *
  7. * *
  8. ******************************************************************************
  9. * *
  10. * Module Intent *
  11. * *
  12. * Functions for creating a btree by adding keys in order. This is faster *
  13. * and the resulting btree is more compact and has adjacent leaf nodes. *
  14. * *
  15. ******************************************************************************
  16. * *
  17. * Current Owner: Binhn *
  18. * *
  19. *****************************************************************************/
  20. /*****************************************************************************
  21. *
  22. * Revision History: Created 08/17/90 by JohnSc
  23. *
  24. * 11/12/90 JohnSc RcFillHbt() wasn't setting rcBtreeError to S_OK
  25. * 11/29/90 RobertBu #ifdef'ed out routines that are not used under
  26. * windows.
  27. *
  28. *****************************************************************************/
  29. static char s_aszModule[]= __FILE__; /* For error report */
  30. #include <mvopsys.h>
  31. #include <orkin.h>
  32. #include <iterror.h>
  33. #include <misc.h>
  34. #include <wrapstor.h>
  35. #include <_mvutil.h>
  36. /*************************************************************************
  37. *
  38. * INTERNAL PRIVATE FUNCTIONS
  39. *
  40. * All of them should be declared near
  41. *
  42. *************************************************************************/
  43. PRIVATE HRESULT NEAR PASCAL RcGrowCache(QBTHR);
  44. PRIVATE KEY NEAR PASCAL KeyLeastInSubtree(QBTHR, BK, SHORT, LPVOID);
  45. /***************************************************************************
  46. *
  47. * @doc INTERNAL
  48. *
  49. * @func HRESULT NEAR PASCAL | RcGrowCache |
  50. * Grow the cache by one level.
  51. *
  52. * @parm QBTHR | qbthr |
  53. * Pointer to B-tree
  54. *
  55. * @rdesc rc
  56. * args OUT: qbthr->bth.cLevels - incremented
  57. * qbthr->bth.ghCache - locked
  58. * qbthr->bth.qCache - points to locked ghCache
  59. *
  60. * Note: Root is at level 0, leaves at level qbthr->bth.cLevels - 1.
  61. *
  62. ***************************************************************************/
  63. PRIVATE HRESULT NEAR PASCAL RcGrowCache(QBTHR qbthr)
  64. {
  65. HANDLE gh;
  66. QB qb;
  67. SHORT cbcb = CbCacheBlock(qbthr);
  68. qbthr->bth.cLevels++;
  69. /* Allocate a new cache block
  70. */
  71. if ((gh = _GLOBALALLOC(GMEM_SHARE| GMEM_MOVEABLE | GMEM_ZEROINIT,
  72. (LONG)cbcb * qbthr->bth.cLevels)) == NULL) {
  73. return E_OUTOFMEMORY;
  74. }
  75. qb = (QB)_GLOBALLOCK(gh);
  76. /* Copy the old data */
  77. QVCOPY(qb + cbcb, qbthr->qCache,
  78. (LONG)cbcb * (qbthr->bth.cLevels - 1));
  79. /* Remove the old cache */
  80. _GLOBALUNLOCK(qbthr->ghCache);
  81. _GLOBALFREE(qbthr->ghCache);
  82. /* Update pointer to the new block */
  83. qbthr->ghCache = gh;
  84. qbthr->qCache = qb;
  85. return S_OK;
  86. }
  87. /***************************************************************************
  88. *
  89. * @doc INTERNAL
  90. *
  91. * @func KEY NEAR PASCAL | KeyLeastInSubtree |
  92. * Return the least key in the subtree speced by bk and icbLevel.
  93. *
  94. * @parm QBTHR | qbthr |
  95. * Pointer to B-tree
  96. *
  97. * @parm BK | bk |
  98. * bk at root of subtree
  99. *
  100. * @parm SHORT | icbLevel |
  101. * level of subtree root
  102. *
  103. * @rdesc key - the smallest key in the subtree
  104. * -1 if error
  105. *
  106. * @comm qbthr->ghCache, ->qCache - contents of cache may change
  107. *
  108. ***************************************************************************/
  109. PRIVATE KEY NEAR PASCAL KeyLeastInSubtree(QBTHR qbthr, BK bk,
  110. SHORT icbLevel, PHRESULT phr)
  111. {
  112. QCB qcb;
  113. SHORT icbMost = qbthr->bth.cLevels - 1;
  114. while (icbLevel < icbMost)
  115. {
  116. if ((qcb = QFromBk(bk, icbLevel, qbthr, phr)) == NULL)
  117. return (KEY)-1;
  118. bk = *(BK UNALIGNED *UNALIGNED)qcb->db.rgbBlock;
  119. ++icbLevel;
  120. }
  121. if ((qcb = QFromBk(bk, icbLevel, qbthr, phr)) == NULL)
  122. return ((KEY)-1);
  123. return (KEY)qcb->db.rgbBlock + 2 * sizeof(BK);
  124. }
  125. /***************************************************************************
  126. *
  127. * @doc PUBLIC API
  128. *
  129. * @func HBT PASCAL FAR | HbtInitFill |
  130. * Start the btree fill process. Note that the HBT returned
  131. * is NOT a valid btree handle.
  132. *
  133. * @parm LPSTR | sz |
  134. * btree name
  135. *
  136. * @parm BTREE_PARAMS FAR * | qbtp |
  137. * btree creation parameters
  138. *
  139. * @rdesc an HBT that isn't a valid btree handle until RcFiniFillHbt()
  140. * is called on it (with intervening RcFillHbt()'s)
  141. * The only valid operations on this HBT are
  142. * RcFillHbt() - add keys in order one at a time
  143. * RcAbandonHbt() - junk the hbt
  144. * RcFiniFillHbt() - finish adding keys. After this, the
  145. * hbt is a normal btree handle.
  146. *
  147. * @comm Method:
  148. * Create a btree. Create a single-block cache.
  149. *
  150. ***************************************************************************/
  151. PUBLIC HBT PASCAL FAR EXPORT_API HbtInitFill(LPSTR sz,
  152. BTREE_PARAMS FAR *qbtp, PHRESULT phr)
  153. {
  154. HBT hbt;
  155. QBTHR qbthr;
  156. QCB qcb;
  157. // Get a btree handle
  158. if ((hbt = HbtCreateBtreeSz(sz, qbtp, phr)) == 0)
  159. return 0;
  160. qbthr = (QBTHR)_GLOBALLOCK(hbt);
  161. // make a one-block cache
  162. if ((qbthr->ghCache = _GLOBALALLOC(GMEM_ZEROINIT|GMEM_SHARE| GMEM_MOVEABLE,
  163. (LONG)CbCacheBlock(qbthr))) == NULL)
  164. {
  165. SetErrCode (phr, E_OUTOFMEMORY);
  166. _GLOBALUNLOCK(hbt);
  167. RcAbandonHbt(hbt);
  168. return 0;
  169. }
  170. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  171. qcb = (QCB)qbthr->qCache;
  172. qbthr->bth.cLevels = 1;
  173. qbthr->bth.bkFirst = qbthr->bth.bkLast = qcb->bk = BkAlloc(qbthr, phr);
  174. qcb->bFlags = fCacheDirty | fCacheValid;
  175. qcb->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  176. - 2 * sizeof(BK);
  177. qcb->db.cKeys = 0;
  178. SetBkPrev(qcb, bkNil);
  179. _GLOBALUNLOCK(qbthr->ghCache);
  180. _GLOBALUNLOCK(hbt);
  181. return hbt;
  182. }
  183. /***************************************************************************
  184. *
  185. * @doc PUBLIC API
  186. *
  187. * @func HRESULT PASCAL FAR | RcFillHbt |
  188. * Add a key and record (in order) to the "HBT" given.
  189. *
  190. * @parm HBT | hbt |
  191. * NOT a valid hbt: it was produced with HbtInitFill().
  192. *
  193. * @parm KEY | key |
  194. * key to add. Must be greater than all keys previously added.
  195. *
  196. * qvRec- record associated with key
  197. *
  198. * PROMISES
  199. * returns: error code
  200. * args OUT: hbt - key, record added
  201. * +++
  202. *
  203. * Method: If key and record don't fit in current leaf, allocate a
  204. * new one and make it the current one.
  205. * Add key and record to current block.
  206. *
  207. ***************************************************************************/
  208. HRESULT PASCAL FAR EXPORT_API RcFillHbt(HBT hbt, KEY key, QV qvRec)
  209. {
  210. QBTHR qbthr;
  211. QCB qcb;
  212. SHORT cbRec, cbKey;
  213. QB qb;
  214. HRESULT rc;
  215. /* Sanity check */
  216. if (hbt == 0 || key == 0 || qvRec == NULL)
  217. return E_INVALIDARG;
  218. qbthr = (QBTHR)_GLOBALLOCK(hbt);
  219. qcb = (QCB)_GLOBALLOCK(qbthr->ghCache);
  220. cbRec = CbSizeRec(qvRec, qbthr);
  221. cbKey = CbSizeKey(key, qbthr, FALSE);
  222. // Make sure key and record aren't too big for even an empty block.
  223. if (cbRec + cbKey > (qbthr->bth.cbBlock / 2))
  224. {
  225. _GLOBALUNLOCK(qbthr->ghCache);
  226. _GLOBALUNLOCK(hbt);
  227. return E_INVALIDARG;
  228. }
  229. if (cbRec + cbKey > qcb->db.cbSlack) {
  230. // key and rec don't fit in this block: write it out
  231. SetBkNext(qcb, BkAlloc(qbthr, NULL));
  232. if ((rc = RcWriteBlock(qcb, qbthr)) != S_OK)
  233. {
  234. _GLOBALUNLOCK(qbthr->ghCache);
  235. _GLOBALFREE(qbthr->ghCache);
  236. RcAbandonHf(qbthr->hf);
  237. _GLOBALUNLOCK(hbt);
  238. _GLOBALFREE(hbt);
  239. return rc;
  240. }
  241. // recycle the block
  242. SetBkPrev(qcb, qcb->bk);
  243. qcb->bk = BkNext(qcb);
  244. qcb->bFlags = fCacheDirty | fCacheValid;
  245. qcb->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  246. - 2 * sizeof(BK);
  247. qcb->db.cKeys = 0;
  248. }
  249. // add key and rec to the current block;
  250. qb = (QB)&(qcb->db) + qbthr->bth.cbBlock - qcb->db.cbSlack;
  251. QVCOPY(qb, (QV)key, (LONG)cbKey);
  252. QVCOPY(qb + cbKey, qvRec, (LONG)cbRec);
  253. qcb->db.cKeys++;
  254. qcb->db.cbSlack -= (cbKey + cbRec);
  255. qbthr->bth.lcEntries++;
  256. _GLOBALUNLOCK(qbthr->ghCache);
  257. _GLOBALUNLOCK(hbt);
  258. return S_OK;
  259. }
  260. /***************************************************************************
  261. *
  262. * @doc PUBLIC API
  263. *
  264. * @func HRESULT PASCAL FAR | RcFiniFillHbt |
  265. * Complete filling of the hbt. After this call, the hbt is a valid
  266. * btree handle.
  267. *
  268. * @parm HBT | hbt |
  269. * NOT a valid hbt: created with RcInitFillHbt()
  270. * and filled with keys & records by RcFillHbt().
  271. *
  272. * @rdesc error code
  273. * hbt - a valid hbt (on S_OK)
  274. *
  275. * @comm Take the first key of each leaf block, creating a layer
  276. * of internal nodes.
  277. * Take the first key in each node in this layer to create
  278. * another layer of internal nodes. Repeat until we get
  279. * we get a layer with only one node. That's the root.
  280. *
  281. ***************************************************************************/
  282. PUBLIC HRESULT PASCAL FAR EXPORT_API RcFiniFillHbt(HBT hbt)
  283. {
  284. BK bkThisMin, bkThisMost, bkThisCur, // level being scanned
  285. bkTopMin, bkTopMost; // level being created
  286. QBTHR qbthr;
  287. QCB qcbThis, qcbTop;
  288. SHORT cbKey;
  289. KEY key;
  290. QB qbDst;
  291. HRESULT rc = S_OK;
  292. if ((qbthr = (QBTHR)_GLOBALLOCK(hbt)) == NULL)
  293. return E_INVALIDARG;
  294. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache); // we know cache is valid
  295. qcbThis = QCacheBlock(qbthr, 0);
  296. SetBkNext(qcbThis, bkNil);
  297. bkThisMin = qbthr->bth.bkFirst;
  298. bkThisMost = qbthr->bth.bkLast = qcbThis->bk;
  299. if (bkThisMin == bkThisMost)
  300. { // only one leaf
  301. qbthr->bth.bkRoot = bkThisMin;
  302. goto normal_return;
  303. }
  304. if ((rc = RcGrowCache( qbthr)) != S_OK)
  305. {
  306. goto error_return;
  307. }
  308. qcbTop = QCacheBlock(qbthr, 0);
  309. qcbTop->bk = bkTopMin = bkTopMost = BkAlloc(qbthr, NULL);
  310. qcbTop->bFlags = fCacheDirty | fCacheValid;
  311. qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  312. - sizeof(BK);
  313. qcbTop->db.cKeys = 0;
  314. // Get first key from each leaf node and build a layer of internal nodes.
  315. // add bk of first leaf to the node
  316. qbDst = qcbTop->db.rgbBlock;
  317. *(BK UNALIGNED *UNALIGNED)qbDst = bkThisMin;
  318. qbDst += sizeof(BK);
  319. for (bkThisCur = bkThisMin + 1; bkThisCur <= bkThisMost; ++bkThisCur)
  320. {
  321. qcbThis = QFromBk(bkThisCur, 1, qbthr, NULL);
  322. key = (KEY)(qcbThis->db.rgbBlock + 2 * sizeof( BK));
  323. cbKey = CbSizeKey(key, qbthr, FALSE);
  324. if ((SHORT)(cbKey + sizeof( BK)) > qcbTop->db.cbSlack)
  325. {
  326. // key and bk don't fit in this block: write it out
  327. rc = RcWriteBlock(qcbTop, qbthr);
  328. // recycle the block
  329. qcbTop->bk = bkTopMost = BkAlloc(qbthr, NULL);
  330. qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  331. - sizeof(BK); // (bk added below)
  332. qcbTop->db.cKeys = 0;
  333. qbDst = qcbTop->db.rgbBlock;
  334. }
  335. else {
  336. qcbTop->db.cbSlack -= cbKey + sizeof(BK);
  337. QVCOPY(qbDst, (QB)key, cbKey);
  338. qbDst += cbKey;
  339. qcbTop->db.cKeys++;
  340. }
  341. *(BK UNALIGNED *UNALIGNED)qbDst = bkThisCur;
  342. qbDst += sizeof(BK);
  343. }
  344. // Keep adding layers of internal nodes until we have a root.
  345. while (bkTopMost > bkTopMin)
  346. {
  347. bkThisMin = bkTopMin;
  348. bkThisMost = bkTopMost;
  349. bkTopMin = bkTopMost = BkAlloc(qbthr, NULL);
  350. _GLOBALUNLOCK(qbthr->ghCache);
  351. rc = RcGrowCache(qbthr);
  352. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  353. if (rc != S_OK)
  354. {
  355. goto error_return;
  356. }
  357. qcbTop = QCacheBlock(qbthr, 0);
  358. qcbTop->bk = bkTopMin;
  359. qcbTop->bFlags = fCacheDirty | fCacheValid;
  360. qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  361. - sizeof(BK);
  362. qcbTop->db.cKeys = 0;
  363. // add bk of first node of this level to current node of top level;
  364. qbDst = qcbTop->db.rgbBlock;
  365. *(BK UNALIGNED *UNALIGNED)qbDst = bkThisMin;
  366. qbDst += sizeof(BK);
  367. // for (each internal node in this level after first)
  368. for (bkThisCur = bkThisMin + 1;
  369. bkThisCur <= bkThisMost; ++bkThisCur) {
  370. key = KeyLeastInSubtree(qbthr, bkThisCur, 1, NULL);
  371. cbKey = CbSizeKey(key, qbthr, FALSE);
  372. if ((SHORT)(cbKey + sizeof( BK)) > qcbTop->db.cbSlack) {
  373. // key and bk don't fit in this block: write it out
  374. rc = RcWriteBlock(qcbTop, qbthr);
  375. // recycle the block
  376. qcbTop->bk = bkTopMost = BkAlloc(qbthr, NULL);
  377. qcbTop->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  378. - sizeof(BK); // (bk added below)
  379. qcbTop->db.cKeys = 0;
  380. qbDst = qcbTop->db.rgbBlock;
  381. }
  382. else {
  383. qcbTop->db.cbSlack -= cbKey + sizeof(BK);
  384. QVCOPY(qbDst, (QB)key, cbKey);
  385. qbDst += cbKey;
  386. qcbTop->db.cKeys++;
  387. }
  388. *(BK UNALIGNED *UNALIGNED)qbDst = bkThisCur;
  389. qbDst += sizeof(BK);
  390. }
  391. }
  392. assert(bkTopMin == bkTopMost);
  393. qbthr->bth.bkRoot = bkTopMin;
  394. qbthr->bth.bkEOF = bkTopMin + 1;
  395. normal_return:
  396. _GLOBALUNLOCK(qbthr->ghCache);
  397. _GLOBALUNLOCK(hbt);
  398. return rc;
  399. error_return:
  400. _GLOBALUNLOCK(qbthr->ghCache);
  401. _GLOBALUNLOCK(hbt);
  402. RcAbandonHbt(hbt);
  403. return (rc);
  404. }
  405. /* EOF */