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.

1120 lines
26 KiB

  1. /*****************************************************************************
  2. * *
  3. * BTLOOKUP.C *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1989 - 1994. *
  6. * All Rights reserved. *
  7. * *
  8. ******************************************************************************
  9. * *
  10. * Module Intent *
  11. * Btree lookup and helper functions. *
  12. * *
  13. ******************************************************************************
  14. * *
  15. * Current Owner: UNDONE *
  16. * *
  17. *****************************************************************************/
  18. static char s_aszModule[] = __FILE__; /* For error report */
  19. #include <mvopsys.h>
  20. #include <orkin.h>
  21. #include <misc.h>
  22. #include <iterror.h>
  23. #include <wrapstor.h>
  24. #include <_mvutil.h>
  25. /***************************************************************************
  26. *
  27. * Private Functions
  28. *
  29. ***************************************************************************/
  30. /***************************************************************************
  31. *
  32. * @doc INTERNAL
  33. *
  34. * @func SHORT PASCAL FAR | CbSizeRec |
  35. * Get the size of a record.
  36. *
  37. * @parm QV | qRec |
  38. * the record to be sized
  39. *
  40. * @parm QBTHR | qbthr |
  41. * btree header containing record format string
  42. *
  43. * @rdesc size of the record in bytes
  44. * If we've never computed the size before, we do so by looking
  45. * at the record format string in the btree header. If the
  46. * record is fixed size, we store the size in the header for
  47. * next time. If it isn't fixed size, we have to look at the
  48. * actual record to determine its size.
  49. *
  50. ***************************************************************************/
  51. PUBLIC SHORT PASCAL FAR CbSizeRec(QV qRec, QBTHR qbthr)
  52. {
  53. CHAR ch;
  54. QCH qchFormat = qbthr->bth.rgchFormat;
  55. SHORT cb = 0;
  56. BOOL fFixedSize;
  57. LPBYTE lpb;
  58. if (qbthr->cbRecordSize)
  59. return qbthr->cbRecordSize;
  60. fFixedSize = TRUE;
  61. for (qchFormat++; ch = *qchFormat; qchFormat++)
  62. {
  63. switch (ch)
  64. {
  65. case '0': case '1': case '2': case '3': case '4':
  66. case '5': case '6': case '7': case '8': case '9':
  67. cb += ch - '0';
  68. break;
  69. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  70. cb += ch + 10 - 'a';
  71. break;
  72. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  73. cb += ch + 10 - 'A';
  74. break;
  75. // Variable length File Offset value (minimum 3 bytes)
  76. case FMT_VNUM_FO:
  77. lpb=((LPBYTE)qRec)+cb;
  78. cb++;
  79. while ((*(lpb++))&0x80)
  80. cb++;
  81. fFixedSize = FALSE;
  82. break;
  83. case FMT_BYTE_PREFIX:
  84. cb += sizeof(BYTE) + *((QB)qRec + cb);
  85. fFixedSize = FALSE;
  86. break;
  87. case FMT_WORD_PREFIX:
  88. cb += sizeof(SHORT) + *((QW)qRec + cb);
  89. fFixedSize = FALSE;
  90. break;
  91. case FMT_SZ:
  92. cb += (SHORT) STRLEN((QB)qRec + cb) + 1;
  93. fFixedSize = FALSE;
  94. break;
  95. default:
  96. /* error */
  97. assert(FALSE);
  98. break;
  99. }
  100. }
  101. if (fFixedSize)
  102. {
  103. qbthr->cbRecordSize = cb;
  104. }
  105. return cb;
  106. }
  107. /***************************************************************************
  108. *
  109. * @doc INTERNAL
  110. *
  111. * @func BOOL PASCAL FAR | FReadBlock |
  112. * Read a block from the btree file into the cache block.
  113. *
  114. * @parm QCB | qcb |
  115. * qcb->bk - bk of block to read
  116. * qcb->db - receives block read in from file
  117. * qcb->bFlags - fCacheValid flag set, all others cleared
  118. *
  119. * @parm QBTHR | qbthr |
  120. * qbthr->cbBlock - size of disk block to read
  121. *
  122. * @rdesc S_OK or other errors
  123. *
  124. * Notes: Doesn't know about real cache, just this block
  125. *
  126. ***************************************************************************/
  127. PUBLIC HRESULT PASCAL FAR FReadBlock(QCB qcb, QBTHR qbthr)
  128. {
  129. LONG l;
  130. HRESULT errb;
  131. FILEOFFSET foSeek;
  132. if (qcb->bk >= qbthr->bth.bkEOF)
  133. {
  134. return E_ASSERT;
  135. }
  136. foSeek=FoFromBk(qcb->bk, qbthr);
  137. if (!FoEquals(FoSeekHf(qbthr->hf, foSeek, wFSSeekSet, &errb),foSeek))
  138. {
  139. return (E_FILESEEK);
  140. }
  141. l = qbthr->bth.cbBlock;
  142. errb = S_OK;
  143. if (LcbReadHf(qbthr->hf, &(qcb->db), (LONG)qbthr->bth.cbBlock,
  144. &errb) != l)
  145. {
  146. if (errb == S_OK)
  147. return (E_FILEINVALID);
  148. return errb;
  149. }
  150. qcb->bFlags = fCacheValid;
  151. qcb->db.cbSlack = SWAPWORD(qcb->db.cbSlack);
  152. qcb->db.cKeys = SWAPWORD(qcb->db.cKeys);
  153. return (S_OK);
  154. }
  155. /***************************************************************************
  156. *
  157. * @doc INTERNAL
  158. *
  159. * @func HRESULT PASCAL FAR | RcWriteBlock |
  160. * Write a cached block to a file.
  161. *
  162. * @parm QCB | qcb |
  163. * qcb->db the block to write
  164. * qcb->bk bk of block to write
  165. *
  166. * @parm QBTHR | qbthr |
  167. * qbthr->hf we write to this file
  168. *
  169. * @rdesc S_OK or other errors
  170. * Side Effects: Fatal exit on read or seek failure.
  171. *
  172. * Note: Don't reset dirty flag, because everyone who wants
  173. * that done does it themselves. (?)
  174. *
  175. ***************************************************************************/
  176. PUBLIC HRESULT PASCAL FAR RcWriteBlock(QCB qcb, QBTHR qbthr)
  177. {
  178. #ifdef MOSMAP // {
  179. // Disable function
  180. return ERR_NOTSUPPORTED;
  181. #else // } {
  182. HRESULT errb;
  183. FILEOFFSET foSeek;
  184. if (qcb->bk >= qbthr->bth.bkEOF)
  185. return E_ASSERT;
  186. if ((qcb->db.cbSlack > qbthr->bth.cbBlock) || (qcb->db.cbSlack<0))
  187. return E_ASSERT;
  188. #if 0
  189. if (qcb->db.cKeys*8+qcb->db.cbSlack > qbthr->bth.cbBlock)
  190. return E_ASSERT;
  191. #endif
  192. foSeek=FoFromBk(qcb->bk, qbthr);
  193. errb = S_OK;
  194. if (!FoEquals(FoSeekHf(qbthr->hf, foSeek, wFSSeekSet, &errb),foSeek) )
  195. {
  196. return(errb);
  197. }
  198. LcbWriteHf(qbthr->hf, &(qcb->db), (LONG)qbthr->bth.cbBlock, &errb);
  199. return errb;
  200. #endif //}
  201. }
  202. /***************************************************************************
  203. *
  204. * @doc INTERNAL
  205. *
  206. * @func QCB PASCAL FAR | QFromBk |
  207. * Convert a BK into a pointer to a cache block. Cache the
  208. * block at the given level, if it isn't there already.
  209. *
  210. * @parm BK | bk |
  211. * BK to convert
  212. *
  213. * @parm SHORT | wLevel |
  214. * btree level
  215. * @parm QBTHR | qbthr |
  216. * Ptr to B-tree struct. State in: btree cache is locked
  217. *
  218. * @rdesc pointer to the cache block, with all fields up to date
  219. * or NULL on I/O error
  220. * state OUT: block will be in cache at specified level; cache locked
  221. *
  222. ***************************************************************************/
  223. PUBLIC QCB PASCAL FAR QFromBk(BK bk, SHORT wLevel, QBTHR qbthr, PHRESULT phr)
  224. {
  225. QCB qcb;
  226. HRESULT fRet;
  227. if (wLevel < 0 || wLevel >= qbthr->bth.cLevels || bk >= qbthr->bth.bkEOF)
  228. {
  229. SetErrCode (phr, E_ASSERT);
  230. return(NULL);
  231. }
  232. qcb = QCacheBlock(qbthr, wLevel);
  233. if (!(qcb->bFlags & fCacheValid) || bk != qcb->bk)
  234. {
  235. /* requested block is not cached */
  236. if ((qcb->bFlags & fCacheDirty) && (qcb->bFlags & fCacheValid))
  237. {
  238. if ((fRet = RcWriteBlock(qcb, qbthr)) != S_OK)
  239. {
  240. SetErrCode (phr, fRet);
  241. return NULL;
  242. }
  243. }
  244. qcb->bk = bk;
  245. if ((fRet = FReadBlock(qcb, qbthr)) != S_OK)
  246. {
  247. SetErrCode (phr, fRet);
  248. return NULL;
  249. }
  250. }
  251. return qcb;
  252. }
  253. /***************************************************************************
  254. *
  255. * @doc INTERNAL
  256. *
  257. * @func HRESULT PASCAL FAR | RcFlushCache |
  258. * Write out dirty cache blocks
  259. *
  260. * @parm QBTHR | qbthr |
  261. * qCache is locked
  262. *
  263. * @rdesc rc
  264. * state OUT: btree file is up to date. cache block dirty flags reset
  265. *
  266. ***************************************************************************/
  267. PUBLIC HRESULT PASCAL FAR RcFlushCache(QBTHR qbthr)
  268. {
  269. #ifdef MOSMAP // {
  270. // Disable function
  271. return ERR_SUCCESSS;
  272. #else // } {
  273. SHORT i;
  274. QB qb;
  275. HRESULT rc = S_OK;
  276. const SHORT iMax = qbthr->bth.cLevels;
  277. // We need to traverse this list in reverse order so the
  278. // nodes are actually written in numeric order
  279. qb = qbthr->qCache + CbCacheBlock(qbthr) * (iMax - 1);
  280. for (i = 0; i < iMax; i++, qb -= CbCacheBlock(qbthr))
  281. {
  282. if ((((QCB)qb)->bFlags & (fCacheDirty | fCacheValid)) ==
  283. (fCacheValid | fCacheDirty))
  284. {
  285. if ((rc = RcWriteBlock((QCB)qb, qbthr)) != S_OK)
  286. break;
  287. ((QCB)qb)->bFlags &= ~fCacheDirty;
  288. }
  289. }
  290. return rc;
  291. #endif //}
  292. }
  293. /***************************************************************************
  294. *
  295. * Public Functions
  296. *
  297. ***************************************************************************/
  298. /***************************************************************************
  299. *
  300. * @doc INTERNAL
  301. *
  302. * @func HRESULT PASCAL FAR | RcLookupByKeyAux |
  303. * Look up a key in a btree and retrieve the data.
  304. * state IN: cache is unlocked
  305. *
  306. * @parm HBT | hbt |
  307. * btree handle
  308. *
  309. * @parm KEY | key |
  310. * key we are looking up
  311. *
  312. * @parm QBTPOS | qbtpos |
  313. * pointer to buffer for pos; use NULL if not wanted
  314. *
  315. * @parm QV | qData |
  316. * pointer to buffer for record; NULL if not wanted
  317. *
  318. * @parm BOOL | fInsert |
  319. * TRUE: if key would lie between two blocks, pos refers to proper
  320. * place to insert it
  321. * FALSE: pos returned will be valid unless key > all keys in btree
  322. *
  323. * @rdesc S_OK if found, E_NOTEXIST if not found;
  324. * other errors like ERR_MEMORY
  325. * key found:
  326. * qbtpos - btpos for this key
  327. * qData - record for this key
  328. *
  329. * key not found:
  330. * qbtpos - btpos for first key > this key
  331. * qData - record for first key > this key
  332. *
  333. * key not found, no keys in btree > key:
  334. * qbtpos - invalid (qbtpos->bk == bkNil)
  335. * qData - undefined
  336. * state OUT: All ancestor blocks back to root are cached
  337. *
  338. ***************************************************************************/
  339. PUBLIC HRESULT PASCAL FAR RcLookupByKeyAux(HBT hbt, KEY key,
  340. QBTPOS qbtpos, QV qData, BOOL fInsert)
  341. {
  342. QBTHR qbthr;
  343. SHORT wLevel;
  344. BK bk;
  345. HRESULT rc;
  346. HRESULT errb;
  347. if (hbt == NULL)
  348. return E_INVALIDARG;
  349. qbthr = _GLOBALLOCK(hbt);
  350. if (qbthr->bth.cLevels <= 0)
  351. {
  352. rc = E_NOTEXIST;
  353. exit0:
  354. _GLOBALUNLOCK(hbt);
  355. return rc;
  356. }
  357. if (qbthr->ghCache == NULL)
  358. {
  359. if ((rc = RcMakeCache(qbthr)) != S_OK)
  360. goto exit0;
  361. }
  362. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  363. /* Look in the top level */
  364. for (wLevel = 0, bk = qbthr->bth.bkRoot;
  365. bk != bkNil && wLevel < qbthr->bth.cLevels - 1; wLevel++)
  366. {
  367. bk = qbthr->BkScanInternal(bk, key, wLevel, qbthr, NULL, &errb);
  368. }
  369. if (bk == bkNil)
  370. {
  371. rc = E_NOTEXIST;
  372. exit1:
  373. _GLOBALUNLOCK(qbthr->ghCache);
  374. goto exit0;
  375. }
  376. if (((rc = qbthr->RcScanLeaf(bk, key, wLevel, qbthr,
  377. qData, qbtpos)) == E_NOTEXIST) &&
  378. qbtpos != NULL && !fInsert)
  379. {
  380. QCB qcb;
  381. errb = S_OK;
  382. qcb = QFromBk(qbtpos->bk, (SHORT)(qbthr->bth.cLevels - 1),
  383. qbthr, &errb);
  384. if (errb != S_OK)
  385. rc = errb;
  386. if (qcb != NULL)
  387. {
  388. if (qcb != NULL && qbtpos->cKey == qcb->db.cKeys)
  389. {
  390. if (qbtpos->bk == qbthr->bth.bkLast)
  391. {
  392. qbtpos->bk = bkNil;
  393. }
  394. else {
  395. qbtpos->bk = SWAPLONG(BkNext(qcb));
  396. qbtpos->cKey = 0;
  397. qbtpos->iKey = 2 * sizeof(BK);
  398. }
  399. }
  400. }
  401. }
  402. goto exit1;
  403. }
  404. /***************************************************************************
  405. *
  406. * @doc INTERNAL
  407. *
  408. * @func WORD PASCAL FAR | WGetNextNEntries |
  409. * Get a series of keys, data, or both from btree
  410. *
  411. * @parm HBT | hbt | B-tree handle
  412. *
  413. * @parm WORD | wFlags |
  414. * Any one of the following flags (they may be combined, in which case
  415. * the data will be of the form [Key Flag]* (keys and flags interleaved).
  416. *
  417. * @flag GETNEXT_KEYS | Key fields will be returned
  418. * @flag GETNEXT_RECS | Data records will be returned
  419. * @flag GETNEXT_RESET | Ignore value in qbtpos and start from first entry
  420. * @parm WORD | wNumEntries | Max number of entries to retrieve. It may
  421. * be the case that fewer entries are returned.
  422. *
  423. * @parm QBTPOS | qbtpos |
  424. * pointer to buffer containing starting point for retrieval, or NULL
  425. * to start from the beginning and not care where we left off. Call
  426. * <f WGetNextN> multiple times to get the next items in the series
  427. * if <P qbtpos> is valid.
  428. *
  429. * @parm QV | qvBuffer
  430. * Pointer to buffer for record retrieved data.
  431. *
  432. * @parm LONG | lBufSize
  433. * Maximum number of bytes contained in buffer.
  434. *
  435. * @parm PHRESULT | lpErrb
  436. * Error return code. Valid if returned value not equal to <P wNumEntries>.
  437. *
  438. * @rdesc Number of entries returned in buffer.
  439. *
  440. ***************************************************************************/
  441. WORD PASCAL FAR wGetNextNEntries(HBT hbt, WORD wFlags, WORD wNumEntries, QBTPOS qbtpos,
  442. QV qvBuffer, LONG lBufSize, PHRESULT phr)
  443. {
  444. QBTHR qbthr;
  445. BK bk;
  446. QCB qcb;
  447. SHORT cbKey, cbRec;
  448. QB qb;
  449. QB qbBuffer=(QB)qvBuffer;
  450. HRESULT rc;
  451. int iKeyCurrent;
  452. int cKey;
  453. WORD wEntriesFilled=0;
  454. if (hbt == NULL)
  455. { SetErrCode(phr,E_INVALIDARG);
  456. return wEntriesFilled;
  457. }
  458. qbthr = _GLOBALLOCK(hbt);
  459. if (qbthr->bth.lcEntries == (LONG)0)
  460. {
  461. SetErrCode(phr, E_NOTEXIST);
  462. exit0:
  463. _GLOBALUNLOCK(hbt);
  464. return wEntriesFilled;
  465. }
  466. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  467. if (qbthr->ghCache == NULL)
  468. {
  469. if ((rc = RcMakeCache(qbthr)) != S_OK)
  470. {
  471. SetErrCode(phr, rc);
  472. goto exit0;
  473. }
  474. }
  475. if ((!qbtpos) || (wFlags&GETNEXT_RESET)) // Start from first
  476. { if ((bk = qbthr->bth.bkFirst) == bkNil)
  477. {
  478. SetErrCode(phr, E_ASSERT);
  479. exit1:
  480. _GLOBALUNLOCK(qbthr->ghCache);
  481. goto exit0;
  482. }
  483. if ((qcb = QFromBk(bk, (SHORT)(qbthr->bth.cLevels - 1), qbthr,
  484. phr)) == NULL)
  485. {
  486. goto exit1;
  487. }
  488. iKeyCurrent=2 * sizeof(BK);
  489. qb = qcb->db.rgbBlock + iKeyCurrent;
  490. cKey=0;
  491. }
  492. else // Start from qbtpos
  493. {
  494. if (!FValidPos(qbtpos))
  495. {
  496. SetErrCode(phr,E_ASSERT);
  497. goto exit0;
  498. }
  499. bk=qbtpos->bk;
  500. if ((qcb = QFromBk(bk, (SHORT)(qbthr->bth.cLevels - 1),
  501. qbthr, phr)) == NULL)
  502. {
  503. goto exit1;
  504. }
  505. if (qbtpos->cKey==qcb->db.cKeys)
  506. {
  507. SetErrCode(phr, E_NOTEXIST);
  508. goto exit1;
  509. }
  510. assert(qbtpos->cKey < qcb->db.cKeys);
  511. assert(qbtpos->cKey >= 0);
  512. assert(qbtpos->iKey >= 2 * sizeof(BK));
  513. assert(qbtpos->iKey <= (SHORT)(qbthr->bth.cbBlock - sizeof(DISK_BLOCK)));
  514. cKey=qbtpos->cKey;
  515. iKeyCurrent=qbtpos->iKey;
  516. qb = qcb->db.rgbBlock + iKeyCurrent;
  517. }
  518. // keep getting data and filling, and fix qbtpos to next valid spot
  519. while (wNumEntries--)
  520. {
  521. cbKey = CbSizeKey((KEY)qb, qbthr, TRUE);
  522. if (wFlags&GETNEXT_KEYS)
  523. {
  524. if (cbKey>lBufSize)
  525. {
  526. break;
  527. }
  528. QVCOPY(qbBuffer, qb,(LONG)cbKey);
  529. qbBuffer+=cbKey;
  530. lBufSize-=cbKey;
  531. }
  532. iKeyCurrent+=cbKey;
  533. qb+=cbKey;
  534. cbRec = CbSizeRec(qb, qbthr);
  535. if (wFlags&GETNEXT_RECS)
  536. {
  537. if (cbRec>lBufSize)
  538. { iKeyCurrent-=cbKey;
  539. lBufSize-=cbKey;
  540. break;
  541. }
  542. QVCOPY(qbBuffer,qb,(LONG)cbRec);
  543. qbBuffer+=cbRec;
  544. lBufSize-=cbRec;
  545. }
  546. iKeyCurrent+=cbRec;
  547. qb+=cbRec;
  548. wEntriesFilled++;
  549. cKey++;
  550. if (cKey>=qcb->db.cKeys) // Must advance to next block!
  551. {
  552. BK bkNew = SWAPLONG(BkNext(qcb));
  553. if (bkNew == bkNil) // Back up to last key in btree
  554. { //cKey--;
  555. //iKeyCurrent-=cbKey+cbRec;
  556. break;
  557. }
  558. if ((qcb = QFromBk(bkNew, (SHORT)(qbthr->bth.cLevels - 1),
  559. qbthr, phr)) == NULL)
  560. {
  561. goto exit1;
  562. }
  563. iKeyCurrent=2 * sizeof(BK);
  564. qb = qcb->db.rgbBlock + iKeyCurrent;
  565. cKey=0;
  566. bk=bkNew;
  567. }
  568. }
  569. if (qbtpos != NULL)
  570. {
  571. qbtpos->bk = bk;
  572. qbtpos->iKey = iKeyCurrent;
  573. qbtpos->cKey = cKey;
  574. }
  575. goto exit1;
  576. }
  577. /***************************************************************************
  578. *
  579. * @doc INTERNAL
  580. *
  581. * @func HRESULT PASCAL FAR | RcFirstHbt |
  582. * Get first key and record from btree.
  583. *
  584. * @parm HBT | hbt | B-tree handle
  585. *
  586. * @parm KEY | key |
  587. * points to buffer big enough to hold a key (256 bytes is more
  588. * than enough)
  589. *
  590. * @parm QV | qvRec
  591. * pointer to buffer for record or NULL if not wanted
  592. *
  593. * @parm QBTPOS | qbtpos |
  594. * pointer to buffer for btpos or NULL if not wanted
  595. *
  596. * @rdesc S_OK if anything found, else error code.
  597. * key - key copied here
  598. * qvRec - record copied here
  599. * qbtpos- btpos of first entry copied here
  600. *
  601. ***************************************************************************/
  602. PUBLIC HRESULT PASCAL FAR RcFirstHbt(HBT hbt, KEY key, QV qvRec, QBTPOS qbtpos)
  603. {
  604. QBTHR qbthr;
  605. BK bk;
  606. QCB qcb;
  607. SHORT cbKey, cbRec;
  608. QB qb;
  609. HRESULT rc;
  610. HRESULT errb;
  611. if (hbt == NULL)
  612. return E_INVALIDARG;
  613. qbthr = _GLOBALLOCK(hbt);
  614. if (qbthr->bth.lcEntries == (LONG)0)
  615. {
  616. rc = E_NOTEXIST;
  617. exit0:
  618. _GLOBALUNLOCK(hbt);
  619. return rc;
  620. }
  621. if ((bk = qbthr->bth.bkFirst) == bkNil)
  622. {
  623. rc = E_ASSERT;
  624. goto exit0;
  625. }
  626. if (qbthr->ghCache == NULL)
  627. {
  628. if ((rc = RcMakeCache(qbthr)) != S_OK)
  629. goto exit0;
  630. }
  631. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  632. if ((qcb = QFromBk(bk, (SHORT)(qbthr->bth.cLevels - 1), qbthr,
  633. &errb)) == NULL)
  634. {
  635. rc = errb;
  636. exit1:
  637. _GLOBALUNLOCK(qbthr->ghCache);
  638. goto exit0;
  639. }
  640. qb = qcb->db.rgbBlock + 2 * sizeof(BK);
  641. cbKey = CbSizeKey((KEY)qb, qbthr, TRUE);
  642. if ((QV)key != NULL) QVCOPY((QV)key, qb, (LONG)cbKey);
  643. qb += cbKey;
  644. cbRec = CbSizeRec(qb, qbthr);
  645. if (qvRec != NULL)
  646. QVCOPY(qvRec, qb, (LONG)cbRec);
  647. if (qbtpos != NULL)
  648. {
  649. qbtpos->bk = bk;
  650. qbtpos->iKey = 2 * sizeof(BK);
  651. qbtpos->cKey = 0;
  652. }
  653. rc = S_OK;
  654. goto exit1;
  655. }
  656. /***************************************************************************
  657. *
  658. * @doc INTERNAL
  659. *
  660. * @func HRESULT PASCAL FAR | RcLastHbt |
  661. * Get last key and record from btree.
  662. *
  663. * @parm HBT | hbt | B-tree handle
  664. *
  665. * @parm KEY | key |
  666. * points to buffer big enough to hold a key (256 bytes
  667. * is more than enough)
  668. *
  669. * @parm QV | qvRec |
  670. * points to buffer big enough for record
  671. *
  672. * @rdesc S_OK if anything found, else error code.
  673. * key - key copied here
  674. * qvRec - record copied here
  675. *
  676. ***************************************************************************/
  677. PUBLIC HRESULT PASCAL FAR RcLastHbt(HBT hbt, KEY key, QV qvRec, QBTPOS qbtpos)
  678. {
  679. QBTHR qbthr;
  680. BK bk;
  681. QCB qcb;
  682. SHORT cbKey, cbRec, cKey;
  683. QB qb;
  684. HRESULT rc;
  685. HRESULT errb;
  686. if (hbt == NULL)
  687. return E_INVALIDARG;
  688. qbthr = _GLOBALLOCK(hbt);
  689. if (qbthr->bth.lcEntries == (LONG)0)
  690. {
  691. rc = E_NOTEXIST;
  692. exit0:
  693. _GLOBALUNLOCK(hbt);
  694. return rc;
  695. }
  696. if ((bk = qbthr->bth.bkLast) ==bkNil)
  697. {
  698. rc = E_ASSERT;
  699. goto exit0;
  700. }
  701. if (qbthr->ghCache == NULL)
  702. {
  703. if ((rc = RcMakeCache(qbthr)) != S_OK)
  704. goto exit0;
  705. }
  706. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  707. if ((qcb = QFromBk(bk, (SHORT)(qbthr->bth.cLevels - 1),
  708. qbthr, &errb)) == NULL)
  709. {
  710. rc = errb;
  711. exit1:
  712. _GLOBALUNLOCK(qbthr->ghCache);
  713. goto exit0;
  714. }
  715. qb = qcb->db.rgbBlock + 2 * sizeof(BK);
  716. for (cKey = 0; cKey < qcb->db.cKeys - 1; cKey++)
  717. {
  718. qb += CbSizeKey((KEY)qb, qbthr, TRUE);
  719. qb += CbSizeRec(qb, qbthr);
  720. }
  721. cbKey = CbSizeKey((KEY)qb, qbthr, FALSE);
  722. if ((QV)key != NULL)
  723. QVCOPY((QV)key, qb, (LONG)cbKey); // decompress
  724. cbRec = CbSizeRec(qb + cbKey, qbthr);
  725. if (qvRec != NULL)
  726. QVCOPY(qvRec, qb + cbKey, (LONG)cbRec);
  727. if (qbtpos != NULL)
  728. {
  729. qbtpos->bk = bk;
  730. qbtpos->iKey = (int)(qb - (QB)qcb->db.rgbBlock);
  731. qbtpos->cKey = cKey;
  732. }
  733. rc = S_OK;
  734. goto exit1;
  735. }
  736. /***************************************************************************
  737. *
  738. * @doc INTERNAL
  739. *
  740. * @func HRESULT FAR PASCAL | RcLookupByPos |
  741. * Map a pos into a key and rec (both optional).
  742. *
  743. * @parm HBT | hbt |
  744. * the btree
  745. *
  746. * @parm QBTPOS | qbtpos
  747. * pointer to pos
  748. *
  749. * @rdesc S_OK or errors
  750. * key - if not (KEY)NULL, key copied here, not to exceed iLen
  751. * qRec - if not NULL, record copied here
  752. *
  753. ***************************************************************************/
  754. PUBLIC HRESULT FAR PASCAL RcLookupByPos(HBT hbt, QBTPOS qbtpos,
  755. KEY key, int iLen, QV qRec)
  756. {
  757. QBTHR qbthr;
  758. QCB qcbLeaf;
  759. QB qb;
  760. HRESULT rc;
  761. HRESULT errb;
  762. /* Sanity check */
  763. if (!FValidPos(qbtpos))
  764. return E_ASSERT;
  765. if (hbt == NULL)
  766. return E_INVALIDARG;
  767. qbthr = _GLOBALLOCK(hbt);
  768. if (qbthr->bth.cLevels <= 0)
  769. {
  770. rc = E_NOTEXIST;
  771. exit0:
  772. _GLOBALUNLOCK(hbt);
  773. return rc;
  774. }
  775. if (qbthr->ghCache == NULL)
  776. {
  777. if ((rc = RcMakeCache(qbthr)) != S_OK)
  778. goto exit0;
  779. }
  780. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  781. if ((qcbLeaf = QFromBk(qbtpos->bk, (SHORT)(qbthr->bth.cLevels - 1),
  782. qbthr, &errb)) == NULL)
  783. {
  784. rc = errb;
  785. exit1:
  786. _GLOBALUNLOCK(qbthr->ghCache);
  787. goto exit0;
  788. }
  789. assert(qbtpos->cKey < qcbLeaf->db.cKeys);
  790. assert(qbtpos->cKey >= 0);
  791. assert(qbtpos->iKey >= 2 * sizeof(BK));
  792. assert(qbtpos->iKey <= (SHORT)(qbthr->bth.cbBlock - sizeof(DISK_BLOCK)));
  793. qb = qcbLeaf->db.rgbBlock + qbtpos->iKey;
  794. if (key != (KEY)NULL)
  795. {
  796. QVCOPY((QV)key, qb, (LONG)min(iLen,CbSizeKey((KEY)qb, qbthr, FALSE))); /* need to decompress */
  797. qb += CbSizeKey(key, qbthr, TRUE);
  798. }
  799. if (qRec != NULL)
  800. {
  801. QVCOPY(qRec, qb, (LONG)CbSizeRec(qb, qbthr));
  802. }
  803. rc = S_OK;
  804. goto exit1;
  805. }
  806. /***************************************************************************
  807. *
  808. * @doc INTERNAL
  809. *
  810. * @func HRESULT PASCAL FAR | RcNextPos |
  811. * get the next record from the btree
  812. * Next means the next key lexicographically from the key
  813. * most recently inserted or looked up
  814. * Won't work if we looked up a key and it wasn't there
  815. *
  816. * STATE IN: a record has been read from or written to the file
  817. * since the last deletion
  818. *
  819. *
  820. * @parm HBT | hbt | B-tree handle
  821. *
  822. * @rdesc S_OK; E_NOTEXIST if no successor record
  823. * args OUT: key - next key copied to here
  824. * qvRec - record gets copied here
  825. *
  826. ***************************************************************************/
  827. PUBLIC HRESULT PASCAL FAR RcNextPos(HBT hbt, QBTPOS qbtposIn, QBTPOS qbtposOut)
  828. {
  829. LONG l;
  830. return RcOffsetPos(hbt, qbtposIn, (LONG)1, &l, qbtposOut);
  831. }
  832. /***************************************************************************
  833. *
  834. * @dos INTERNAL
  835. *
  836. * @func HRESULT PASCAL FAR | RcOffsetPos |
  837. * pos, offset from the previous pos by specified amount.
  838. * If not possible (i.e. prev of first) return real amount offset
  839. * and a pos.
  840. *
  841. * @parm HBT | hbt |
  842. * handle to btree
  843. *
  844. * @parm QBTPOS | qbtposIn |
  845. * position we want an offset from
  846. *
  847. * @parm LONGD | lcOffset |
  848. * amount to offset (+ or - OK)
  849. *
  850. * @rdesc rc
  851. * args OUT: qbtposOut - new position offset by *qcRealOffset
  852. * *qlcRealOffset - equal to lcOffset if legal, otherwise
  853. * as close as is legal
  854. *
  855. ***************************************************************************/
  856. PUBLIC HRESULT PASCAL FAR RcOffsetPos(HBT hbt, QBTPOS qbtposIn,
  857. LONG lcOffset, QL qlcRealOffset, QBTPOS qbtposOut)
  858. {
  859. QBTHR qbthr;
  860. HRESULT rc = S_OK;
  861. SHORT c;
  862. LONG lcKey, lcDelta = (LONG)0;
  863. QCB qcb;
  864. BK bk;
  865. QB qb;
  866. HRESULT errb;
  867. if (!FValidPos(qbtposIn))
  868. return E_ASSERT;
  869. if (hbt == NULL || qlcRealOffset == NULL)
  870. return E_INVALIDARG;
  871. bk = qbtposIn->bk;
  872. qbthr = _GLOBALLOCK(hbt);
  873. if (qbthr->bth.cLevels <= 0)
  874. {
  875. rc = E_NOTEXIST;
  876. exit0:
  877. _GLOBALUNLOCK(hbt);
  878. return rc;
  879. }
  880. if (qbthr->ghCache == NULL)
  881. {
  882. if ((rc = RcMakeCache(qbthr)) != S_OK)
  883. {
  884. goto exit0;
  885. }
  886. }
  887. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache); // >>>>what if no entries??
  888. if ((qcb = QFromBk(qbtposIn->bk,
  889. (SHORT)(qbthr->bth.cLevels - 1), qbthr, &errb)) == NULL)
  890. {
  891. rc = errb;
  892. exit1:
  893. _GLOBALUNLOCK(qbthr->ghCache);
  894. goto exit0;
  895. }
  896. lcKey = qbtposIn->cKey + lcOffset;
  897. /* chase prev to find the right block */
  898. while (lcKey < 0)
  899. {
  900. bk = SWAPLONG(BkPrev(qcb));
  901. if (bk == bkNil)
  902. {
  903. bk = qcb->bk;
  904. lcDelta = lcKey;
  905. lcKey = 0;
  906. break;
  907. }
  908. if ((qcb = QFromBk(bk, (SHORT)(qbthr->bth.cLevels - 1),
  909. qbthr, &errb)) == NULL)
  910. {
  911. rc = errb;
  912. goto exit1;
  913. }
  914. lcKey += qcb->db.cKeys;
  915. }
  916. /* chase next to find the right block */
  917. while (lcKey >= qcb->db.cKeys)
  918. {
  919. lcKey -= qcb->db.cKeys;
  920. bk = SWAPLONG(BkNext(qcb));
  921. if (bk == bkNil)
  922. {
  923. bk = qcb->bk;
  924. lcDelta = lcKey + 1;
  925. lcKey = qcb->db.cKeys - 1;
  926. break;
  927. }
  928. if ((qcb = QFromBk(bk, (SHORT)(qbthr->bth.cLevels - 1),
  929. qbthr, &errb)) == NULL)
  930. {
  931. rc = errb;
  932. goto exit1;
  933. }
  934. }
  935. if (bk == qbtposIn->bk && lcKey >= qbtposIn->cKey)
  936. {
  937. c = (SHORT) qbtposIn->cKey;
  938. qb = qcb->db.rgbBlock + qbtposIn->iKey;
  939. }
  940. else
  941. {
  942. c = 0;
  943. qb = qcb->db.rgbBlock + 2 * sizeof(BK);
  944. }
  945. while ((LONG)c < lcKey)
  946. {
  947. qb += CbSizeKey((KEY)qb, qbthr, TRUE);
  948. qb += CbSizeRec(qb, qbthr);
  949. c++;
  950. }
  951. if (qbtposOut != NULL)
  952. {
  953. qbtposOut->bk = bk;
  954. qbtposOut->iKey = (int)(qb - (QB)qcb->db.rgbBlock);
  955. qbtposOut->cKey = c;
  956. }
  957. *qlcRealOffset = lcOffset - lcDelta;
  958. rc = (lcDelta ? E_NOTEXIST: S_OK);
  959. goto exit1;
  960. }
  961. /* EOF */