Windows NT 4.0 source code leak
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.

916 lines
23 KiB

4 years ago
  1. /*****************************************************************************
  2. * *
  3. * BTLOOKUP.C *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1989, 1990. *
  6. * All Rights reserved. *
  7. * *
  8. ******************************************************************************
  9. * *
  10. * Module Intent *
  11. * Btree lookup and helper functions. *
  12. * *
  13. ******************************************************************************
  14. * *
  15. * Testing Notes *
  16. * *
  17. ******************************************************************************
  18. * *
  19. * Current Owner: JohnSc *
  20. * *
  21. ******************************************************************************
  22. * *
  23. * Released by Development: long, long ago *
  24. * *
  25. *****************************************************************************/
  26. /*****************************************************************************
  27. *
  28. * Revision History: Created 04/20/89 by JohnSc
  29. *
  30. * 08/21/90 JohnSc autodocified
  31. * 11/29/90 RobertBu #ifdef'ed out routines that are not used in the
  32. * WINHELP runtime.
  33. * 05-Feb-1991 JohnSc QFromBk() wasn't returning NULL on read failure
  34. *
  35. *****************************************************************************/
  36. #include <windows.h>
  37. #include <orkin.h>
  38. #include "_mvfs.h"
  39. #include "imvfs.h"
  40. #include "btpriv.h"
  41. // _subsystem( btree );
  42. /***************************************************************************\
  43. *
  44. * Private Functions
  45. *
  46. \***************************************************************************/
  47. /***************************************************************************\
  48. *
  49. - Function: CbSizeRec( qRec, qbthr )
  50. -
  51. * Purpose: Get the size of a record.
  52. *
  53. * ASSUMES
  54. * args IN: qRec - the record to be sized
  55. * qbthr - btree header containing record format string
  56. *
  57. * PROMISES
  58. * returns: size of the record in bytes
  59. * +++
  60. * Method: If we've never computed the size before, we do so by looking
  61. * at the record format string in the btree header. If the
  62. * record is fixed size, we store the size in the header for
  63. * next time. If it isn't fixed size, we have to look at the
  64. * actual record to determine its size.
  65. *
  66. \***************************************************************************/
  67. _private INT
  68. CbSizeRec( qRec, qbthr )
  69. QV qRec;
  70. QBTHR qbthr;
  71. {
  72. CHAR ch;
  73. QCH qchFormat = qbthr->bth.rgchFormat;
  74. INT cb = 0;
  75. BOOL fFixedSize;
  76. if ( qbthr->cbRecordSize )
  77. return qbthr->cbRecordSize;
  78. fFixedSize = TRUE;
  79. for ( qchFormat++; ch = *qchFormat; qchFormat++ )
  80. {
  81. switch ( ch )
  82. {
  83. case '0': case '1': case '2': case '3': case '4':
  84. case '5': case '6': case '7': case '8': case '9':
  85. cb += ch - '0';
  86. break;
  87. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  88. cb += ch + 10 - 'a';
  89. break;
  90. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  91. cb += ch + 10 - 'A';
  92. break;
  93. case FMT_BYTE_PREFIX:
  94. cb += sizeof( BYTE ) + *( (QB)qRec + cb );
  95. fFixedSize = FALSE;
  96. break;
  97. case FMT_WORD_PREFIX:
  98. cb += sizeof( INT ) + *( (QW)qRec + cb );
  99. fFixedSize = FALSE;
  100. break;
  101. case FMT_SZ:
  102. cb += lstrlen( (QB)qRec + cb ) + 1;
  103. fFixedSize = FALSE;
  104. break;
  105. default:
  106. /* error */
  107. assert( FALSE );
  108. break;
  109. }
  110. }
  111. if ( fFixedSize )
  112. {
  113. qbthr->cbRecordSize = cb;
  114. }
  115. return cb;
  116. }
  117. /***************************************************************************\
  118. *
  119. - Function: FReadBlock( qcb, qbthr )
  120. -
  121. * Purpose: Read a block from the btree file into the cache block.
  122. *
  123. * ASSUMES
  124. * args IN: qcb->bk - bk of block to read
  125. * qbthr->cbBlock - size of disk block to read
  126. *
  127. * PROMISES
  128. * returns: fTruth of success
  129. * args OUT: qcb->db - receives block read in from file
  130. * qcb->bFlags - fCacheValid flag set, all others cleared
  131. *
  132. * Side Effects: Fatal exit on read or seek failure (corrupted file or qbthr)
  133. *
  134. * Notes: Doesn't know about real cache, just this block
  135. *
  136. \***************************************************************************/
  137. _private BOOL
  138. FReadBlock( qcb, qbthr )
  139. QCB qcb;
  140. QBTHR qbthr;
  141. {
  142. LONG l;
  143. assert( qcb->bk < qbthr->bth.bkEOF );
  144. Ensure( LSeekHf( qbthr->hf, LifFromBk( qcb->bk, qbthr ), wFSSeekSet ),
  145. LifFromBk( qcb->bk, qbthr ) );
  146. l = qbthr->bth.cbBlock;
  147. if ( LcbReadHf( qbthr->hf, &(qcb->db), (LONG)qbthr->bth.cbBlock ) != l )
  148. {
  149. SetBtreeErrorRc(RcGetFSError()) == rcSuccess ? rcInvalid : RcGetFSError();
  150. return FALSE;
  151. }
  152. else
  153. {
  154. qcb->bFlags = fCacheValid;
  155. SetBtreeErrorRc(rcSuccess);
  156. return TRUE;
  157. }
  158. }
  159. /***************************************************************************\
  160. *
  161. - Function: RcWriteBlock( qcb, qbthr )
  162. -
  163. * Purpose: Write a cached block to a file.
  164. *
  165. * ASSUMES
  166. * args IN: qcb->db - the block to write
  167. * qcb->bk - bk of block to write
  168. *
  169. * PROMISES
  170. * returns: rcSuccess; rcFailure; rcDiskFull (when we can detect it)
  171. * args OUT: qbthr->hf - we write to this file
  172. *
  173. * Side Effects: Fatal exit on read or seek failure.
  174. *
  175. * Note: Don't reset dirty flag, because everyone who wants
  176. * that done does it themselves. (?)
  177. *
  178. \***************************************************************************/
  179. _private RC
  180. RcWriteBlock( qcb, qbthr )
  181. QCB qcb;
  182. QBTHR qbthr;
  183. {
  184. assert( qcb->bk < qbthr->bth.bkEOF );
  185. Ensure( LSeekHf( qbthr->hf, LifFromBk( qcb->bk, qbthr ), wFSSeekSet ),
  186. LifFromBk( qcb->bk, qbthr ) );
  187. LcbWriteHf( qbthr->hf, &(qcb->db), (LONG)qbthr->bth.cbBlock );
  188. return SetBtreeErrorRc(RcGetFSError());
  189. }
  190. /***************************************************************************\
  191. *
  192. - Function: QFromBk( bk, wLevel, qbthr )
  193. -
  194. * Purpose: Convert a BK into a pointer to a cache block. Cache the
  195. * block at the given level, if it isn't there already.
  196. *
  197. * ASSUMES
  198. * args IN: bk - BK to convert
  199. * wLevel - btree level
  200. * qbthr -
  201. * state IN: btree cache is locked
  202. *
  203. * PROMISES
  204. * returns: pointer to the cache block, with all fields up to date
  205. * or NULL on I/O error
  206. * state OUT: block will be in cache at specified level; cache locked
  207. *
  208. \***************************************************************************/
  209. _private QCB
  210. QFromBk(
  211. BK bk,
  212. INT wLevel,
  213. QBTHR qbthr)
  214. {
  215. QCB qcb;
  216. assert( wLevel >= 0 && wLevel < qbthr->bth.cLevels );
  217. assert( bk < qbthr->bth.bkEOF );
  218. qcb = QCacheBlock( qbthr, wLevel );
  219. if ( !( qcb->bFlags & fCacheValid ) || bk != qcb->bk )
  220. {
  221. /* requested block is not cached */
  222. if ( ( qcb->bFlags & fCacheDirty ) && ( qcb->bFlags & fCacheValid ) )
  223. {
  224. if ( RcWriteBlock( qcb, qbthr ) != rcSuccess )
  225. {
  226. return NULL;
  227. }
  228. }
  229. qcb->bk = bk;
  230. if ( !FReadBlock( qcb, qbthr ) )
  231. {
  232. return NULL;
  233. }
  234. }
  235. else
  236. {
  237. SetBtreeErrorRc(rcSuccess);
  238. }
  239. return qcb;
  240. }
  241. /***************************************************************************\
  242. *
  243. - Function: RcFlushCache( qbthr )
  244. -
  245. * Purpose: Write out dirty cache blocks
  246. *
  247. * ASSUMES
  248. * args IN: qbthr - qCache is locked
  249. *
  250. * PROMISES
  251. * returns: rc
  252. * globals OUT rcBtreeError
  253. * state OUT: btree file is up to date. cache block dirty flags reset
  254. *
  255. \***************************************************************************/
  256. _private RC FAR PASCAL
  257. RcFlushCache( qbthr )
  258. QBTHR qbthr;
  259. {
  260. INT i;
  261. QB qb;
  262. SetBtreeErrorRc(rcSuccess);
  263. for ( i = qbthr->bth.cLevels, qb = qbthr->qCache;
  264. i > 0;
  265. i--, qb += CbCacheBlock( qbthr ) )
  266. {
  267. if ( ( ((QCB)qb)->bFlags & ( fCacheDirty | fCacheValid ) )
  268. ==
  269. ( fCacheValid | fCacheDirty ) )
  270. {
  271. if ( RcWriteBlock( (QCB)qb, qbthr ) != rcSuccess )
  272. {
  273. break;
  274. }
  275. ((QCB)qb)->bFlags &= ~fCacheDirty;
  276. }
  277. }
  278. return RcGetBtreeError();
  279. }
  280. /***************************************************************************\
  281. *
  282. * Public Functions
  283. *
  284. \***************************************************************************/
  285. /***************************************************************************\
  286. *
  287. - Function: RcLookupByKeyAux( hbt, key, qbtpos, qData, fNormal )
  288. -
  289. * Purpose: Look up a key in a btree and retrieve the data.
  290. *
  291. * ASSUMES
  292. * args IN: hbt - btree handle
  293. * key - key we are looking up
  294. * qbtpos - pointer to buffer for pos; use NULL if not wanted
  295. * qData - pointer to buffer for record; NULL if not wanted
  296. * fInsert - TRUE: if key would lie between two blocks, pos
  297. * refers to proper place to insert it
  298. * FALSE: pos returned will be valid unless
  299. * key > all keys in btree
  300. * state IN: cache is unlocked
  301. *
  302. * PROMISES
  303. * returns: rcSuccess if found, rcNoExists if not found;
  304. * other errors like rcOutOfMemory
  305. * args OUT: key found:
  306. * qbtpos - btpos for this key
  307. * qData - record for this key
  308. *
  309. * key not found:
  310. * qbtpos - btpos for first key > this key
  311. * qData - record for first key > this key
  312. *
  313. * key not found, no keys in btree > key:
  314. * qbtpos - invalid (qbtpos->bk == bkNil)
  315. * qData - undefined
  316. * globals OUT rcBtreeError
  317. * state OUT: All ancestor blocks back to root are cached
  318. *
  319. \***************************************************************************/
  320. _public RC
  321. RcLookupByKeyAux( hbt, key, qbtpos, qData, fInsert )
  322. HBT hbt;
  323. KEY key;
  324. QBTPOS qbtpos;
  325. QV qData;
  326. BOOL fInsert;
  327. {
  328. QBTHR qbthr;
  329. INT wLevel;
  330. BK bk;
  331. assert( hbt != NULL );
  332. qbthr = QLockGh( hbt );
  333. assert( qbthr != NULL );
  334. if ( qbthr->bth.cLevels <= 0 )
  335. {
  336. UnlockGh( hbt );
  337. if ( qbtpos != NULL )
  338. {
  339. qbtpos->bk = bkNil;
  340. }
  341. return SetBtreeErrorRc(rcNoExists);
  342. }
  343. if ( qbthr->ghCache == NULL )
  344. {
  345. if ( RcMakeCache( qbthr ) != rcSuccess )
  346. {
  347. UnlockGh( hbt );
  348. if ( qbtpos != NULL )
  349. {
  350. qbtpos->bk = bkNil;
  351. }
  352. return RcGetBtreeError();
  353. }
  354. }
  355. qbthr->qCache = QLockGh( qbthr->ghCache );
  356. for ( wLevel = 0, bk = qbthr->bth.bkRoot;
  357. bk != bkNil && wLevel < qbthr->bth.cLevels - 1;
  358. wLevel++ )
  359. {
  360. bk = qbthr->BkScanInternal( bk, key, wLevel, qbthr, NULL );
  361. }
  362. if ( bk == bkNil )
  363. {
  364. UnlockGh( qbthr->ghCache );
  365. UnlockGh( hbt );
  366. return RcGetBtreeError();
  367. }
  368. if ( qbthr->RcScanLeaf( bk, key, wLevel, qbthr, qData, qbtpos ) == rcNoExists
  369. &&
  370. qbtpos != NULL
  371. &&
  372. !fInsert )
  373. {
  374. QCB qcb = QFromBk( qbtpos->bk, qbthr->bth.cLevels - 1, qbthr );
  375. if ( qcb != NULL )
  376. {
  377. /* error code was clobbered by QFromBk: restore it */
  378. SetBtreeErrorRc(rcNoExists);
  379. if ( qcb != NULL && qbtpos->cKey == qcb->db.cKeys )
  380. {
  381. if ( qbtpos->bk == qbthr->bth.bkLast )
  382. {
  383. qbtpos->bk = bkNil;
  384. }
  385. else
  386. {
  387. qbtpos->bk = BkNext( qcb );
  388. qbtpos->cKey = 0;
  389. qbtpos->iKey = 2 * sizeof( BK );
  390. }
  391. }
  392. }
  393. }
  394. UnlockGh( qbthr->ghCache );
  395. UnlockGh( hbt );
  396. return RcGetBtreeError();
  397. }
  398. /***************************************************************************\
  399. *
  400. - Function: RcFirstHbt( hbt, key, qvRec, qbtpos )
  401. -
  402. * Purpose: Get first key and record from btree.
  403. *
  404. * ASSUMES
  405. * args IN: hbt
  406. * key - points to buffer big enough to hold a key
  407. * (256 bytes is more than enough)
  408. * qvRec - pointer to buffer for record or NULL if not wanted
  409. * qbtpos- pointer to buffer for btpos or NULL if not wanted
  410. *
  411. * PROMISES
  412. * returns: rcSuccess if anything found, else error code.
  413. * args OUT: key - key copied here
  414. * qvRec - record copied here
  415. * qbtpos- btpos of first entry copied here
  416. *
  417. \***************************************************************************/
  418. _public RC PASCAL
  419. RcFirstHbt( hbt, key, qvRec, qbtpos )
  420. HBT hbt;
  421. KEY key;
  422. QV qvRec;
  423. QBTPOS qbtpos;
  424. {
  425. QBTHR qbthr;
  426. BK bk;
  427. QCB qcb;
  428. INT cbKey, cbRec;
  429. QB qb;
  430. assert( hbt != NULL );
  431. qbthr = QLockGh( hbt );
  432. assert( qbthr != NULL );
  433. if ( qbthr->bth.lcEntries == (LONG)0 )
  434. {
  435. UnlockGh( hbt );
  436. if ( qbtpos != NULL )
  437. {
  438. qbtpos->bk = bkNil;
  439. qbtpos->iKey = 0;
  440. qbtpos->cKey = 0;
  441. }
  442. return SetBtreeErrorRc(rcNoExists);
  443. }
  444. bk = qbthr->bth.bkFirst;
  445. assert( bk != bkNil );
  446. if ( qbthr->ghCache == NULL )
  447. {
  448. if ( RcMakeCache( qbthr ) != rcSuccess )
  449. {
  450. UnlockGh( hbt );
  451. if ( qbtpos != NULL )
  452. {
  453. qbtpos->bk = bkNil;
  454. }
  455. return RcGetBtreeError();
  456. }
  457. }
  458. qbthr->qCache = QLockGh( qbthr->ghCache );
  459. if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
  460. {
  461. UnlockGh( qbthr->ghCache );
  462. UnlockGh( hbt );
  463. return RcGetBtreeError();
  464. }
  465. qb = qcb->db.rgbBlock + 2 * sizeof( BK );
  466. cbKey = CbSizeKey( (KEY)qb, qbthr, TRUE );
  467. if ( (QV)key != NULL ) QvCopy( (QV)key, qb, (LONG)cbKey );
  468. qb += cbKey;
  469. cbRec = CbSizeRec( qb, qbthr );
  470. if ( qvRec != NULL ) QvCopy( qvRec, qb, (LONG)cbRec );
  471. if ( qbtpos != NULL )
  472. {
  473. qbtpos->bk = bk;
  474. qbtpos->iKey = 2 * sizeof( BK );
  475. qbtpos->cKey = 0;
  476. }
  477. UnlockGh( qbthr->ghCache );
  478. UnlockGh( hbt );
  479. return SetBtreeErrorRc(rcSuccess);
  480. }
  481. /***************************************************************************\
  482. *
  483. - Function: RcLastHbt( hbt, key, qvRec, qbtpos )
  484. -
  485. * Purpose: Get last key and record from btree.
  486. *
  487. * ASSUMES
  488. * args IN: hbt
  489. * key - points to buffer big enough to hold a key
  490. * (256 bytes is more than enough)
  491. * qvRec - points to buffer big enough for record
  492. *
  493. * PROMISES
  494. * returns: rcSuccess if anything found, else error code.
  495. * args OUT: key - key copied here
  496. * qvRec - record copied here
  497. *
  498. \***************************************************************************/
  499. _public RC PASCAL
  500. RcLastHbt( hbt, key, qvRec, qbtpos )
  501. HBT hbt;
  502. KEY key;
  503. QV qvRec;
  504. QBTPOS qbtpos;
  505. {
  506. QBTHR qbthr;
  507. BK bk;
  508. QCB qcb;
  509. INT cbKey, cbRec, cKey;
  510. QB qb;
  511. assert( hbt != NULL );
  512. qbthr = QLockGh( hbt );
  513. assert( qbthr != NULL );
  514. if ( qbthr->bth.lcEntries == (LONG)0 )
  515. {
  516. UnlockGh( hbt );
  517. if ( qbtpos != NULL )
  518. {
  519. qbtpos->bk = bkNil;
  520. qbtpos->iKey = 0;
  521. qbtpos->cKey = 0;
  522. }
  523. return SetBtreeErrorRc(rcNoExists);
  524. }
  525. bk = qbthr->bth.bkLast;
  526. assert( bk != bkNil );
  527. if ( qbthr->ghCache == NULL )
  528. {
  529. if ( RcMakeCache( qbthr ) != rcSuccess )
  530. {
  531. UnlockGh( hbt );
  532. if ( qbtpos != NULL )
  533. {
  534. qbtpos->bk = bkNil;
  535. }
  536. return RcGetBtreeError();
  537. }
  538. }
  539. qbthr->qCache = QLockGh( qbthr->ghCache );
  540. if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
  541. {
  542. UnlockGh( qbthr->ghCache );
  543. UnlockGh( hbt );
  544. return RcGetBtreeError();
  545. }
  546. qb = qcb->db.rgbBlock + 2 * sizeof( BK );
  547. for ( cKey = 0; cKey < qcb->db.cKeys - 1; cKey++ )
  548. {
  549. qb += CbSizeKey( (KEY)qb, qbthr, TRUE );
  550. qb += CbSizeRec( qb, qbthr );
  551. }
  552. cbKey = CbSizeKey( (KEY)qb, qbthr, FALSE );
  553. if ( (QV)key != NULL ) QvCopy( (QV)key, qb, (LONG)cbKey ); // decompress
  554. cbRec = CbSizeRec( qb + cbKey, qbthr );
  555. if ( qvRec != NULL ) QvCopy( qvRec, qb + cbKey, (LONG)cbRec );
  556. if ( qbtpos != NULL )
  557. {
  558. qbtpos->bk = bk;
  559. qbtpos->iKey = qb - (QB)qcb->db.rgbBlock;
  560. qbtpos->cKey = cKey;
  561. }
  562. UnlockGh( qbthr->ghCache );
  563. UnlockGh( hbt );
  564. return SetBtreeErrorRc(rcSuccess);
  565. }
  566. /***************************************************************************\
  567. *
  568. - Function: RcLookupByPos( hbt, qbtpos, key, qRec )
  569. -
  570. * Purpose: Map a pos into a key and rec (both optional).
  571. *
  572. * ASSUMES
  573. * args IN: hbt - the btree
  574. * qbtpos - pointer to pos
  575. *
  576. * PROMISES
  577. * returns: rcSuccess, rcOutOfMemory
  578. * Note: we assert() if the pos is invalid
  579. * args OUT: key - if not (KEY)NULL, key copied here, not to exceed iLen
  580. * qRec - if not NULL, record copied here
  581. *
  582. \***************************************************************************/
  583. _public RC FAR PASCAL
  584. RcLookupByPos(
  585. HBT hbt,
  586. QBTPOS qbtpos,
  587. KEY key,
  588. INT iLen, // length of key buffer8
  589. QV qRec) {
  590. QBTHR qbthr;
  591. QCB qcbLeaf;
  592. QB qb;
  593. assert( FValidPos( qbtpos ) );
  594. assert( hbt != NULL );
  595. qbthr = QLockGh( hbt );
  596. assert( qbthr != NULL );
  597. if ( qbthr->bth.cLevels <= 0 )
  598. {
  599. UnlockGh( hbt );
  600. return rcNoExists;
  601. }
  602. if ( qbthr->ghCache == NULL )
  603. {
  604. if ( RcMakeCache( qbthr ) != rcSuccess )
  605. {
  606. UnlockGh( hbt );
  607. return RcGetBtreeError();
  608. }
  609. }
  610. qbthr->qCache = QLockGh( qbthr->ghCache );
  611. if ( ( qcbLeaf = QFromBk( qbtpos->bk, qbthr->bth.cLevels - 1, qbthr ) )
  612. ==
  613. NULL )
  614. {
  615. UnlockGh( qbthr->ghCache );
  616. UnlockGh( hbt );
  617. return RcGetBtreeError();
  618. }
  619. assert( qbtpos->cKey < qcbLeaf->db.cKeys
  620. &&
  621. qbtpos->cKey >= 0
  622. &&
  623. qbtpos->iKey >= 2 * sizeof( BK )
  624. &&
  625. qbtpos->iKey <= (INT)(qbthr->bth.cbBlock - sizeof( DISK_BLOCK )) );
  626. qb = qcbLeaf->db.rgbBlock + qbtpos->iKey;
  627. if ( key != (KEY)NULL )
  628. {
  629. QvCopy( (QV)key, qb, (LONG)MIN(iLen,CbSizeKey( (KEY)qb, qbthr, FALSE )) ); /* need to decompress */
  630. qb += CbSizeKey( key, qbthr, TRUE );
  631. }
  632. if ( qRec != NULL )
  633. {
  634. QvCopy( qRec, qb, (LONG)CbSizeRec( qb, qbthr ) );
  635. }
  636. UnlockGh( qbthr->ghCache );
  637. UnlockGh( hbt );
  638. return SetBtreeErrorRc(rcSuccess);
  639. }
  640. /***************************************************************************\
  641. *
  642. - Function: RcNextPos( hbt, qbtposIn, qbtposOut )
  643. -
  644. * Purpose: get the next record from the btree
  645. * Next means the next key lexicographically from the key
  646. * most recently inserted or looked up
  647. * Won't work if we looked up a key and it wasn't there
  648. *
  649. * ASSUMES
  650. * args IN: hbt -
  651. * state IN: a record has been read from or written to the file
  652. * since the last deletion
  653. *
  654. * PROMISES
  655. * returns: rcSuccess; rcNoExists if no successor record
  656. * args OUT: key - next key copied to here
  657. * qvRec - record gets copied here
  658. *
  659. \***************************************************************************/
  660. _public RC PASCAL
  661. RcNextPos( hbt, qbtposIn, qbtposOut )
  662. HBT hbt;
  663. QBTPOS qbtposIn, qbtposOut;
  664. {
  665. LONG l;
  666. return RcOffsetPos( hbt, qbtposIn, (LONG)1, &l, qbtposOut );
  667. }
  668. /***************************************************************************\
  669. *
  670. - Function: RcOffsetPos( hbt, qbtposIn, lcOffset, qlcRealOffset, qbtposOut )
  671. -
  672. * Purpose: Take a pos and an offset and return a new pos, offset from
  673. * the previous pos by specified amount. If not possible
  674. * (i.e. prev of first) return real amount offset and a pos.
  675. *
  676. * ASSUMES
  677. * args IN: hbt - handle to btree
  678. * qbtposIn - position we want an offset from
  679. * lcOffset - amount to offset (+ or - OK)
  680. *
  681. * PROMISES
  682. * returns: rc
  683. * args OUT: qbtposOut - new position offset by *qcRealOffset
  684. * *qlcRealOffset - equal to lcOffset if legal, otherwise
  685. * as close as is legal
  686. *
  687. \***************************************************************************/
  688. _public RC PASCAL
  689. RcOffsetPos( hbt, qbtposIn, lcOffset, qlcRealOffset, qbtposOut )
  690. HBT hbt;
  691. QBTPOS qbtposIn;
  692. LONG lcOffset;
  693. QL qlcRealOffset;
  694. QBTPOS qbtposOut;
  695. {
  696. QBTHR qbthr;
  697. RC rc = rcSuccess;
  698. INT c;
  699. LONG lcKey, lcDelta = (LONG)0;
  700. QCB qcb;
  701. BK bk;
  702. QB qb;
  703. assert( FValidPos( qbtposIn ) );
  704. bk = qbtposIn->bk;
  705. assert( hbt != NULL );
  706. qbthr = QLockGh( hbt );
  707. assert( qbthr != NULL );
  708. assert( qlcRealOffset != NULL );
  709. if ( qbthr->bth.cLevels <= 0 )
  710. {
  711. UnlockGh( hbt );
  712. return SetBtreeErrorRc(rcNoExists);
  713. }
  714. if ( qbthr->ghCache == NULL )
  715. {
  716. if ( rc = RcMakeCache( qbthr ) != rcSuccess )
  717. {
  718. UnlockGh( hbt );
  719. if ( qbtposOut != NULL )
  720. {
  721. qbtposOut->bk = bkNil;
  722. }
  723. return RcGetBtreeError();
  724. }
  725. }
  726. qbthr->qCache = QLockGh( qbthr->ghCache ); // >>>>what if no entries??
  727. assert( qbthr->qCache != NULL );
  728. if ( ( qcb = QFromBk( qbtposIn->bk, qbthr->bth.cLevels - 1, qbthr ) )
  729. ==
  730. NULL )
  731. {
  732. UnlockGh( qbthr->ghCache );
  733. UnlockGh( hbt );
  734. return RcGetBtreeError();
  735. }
  736. lcKey = qbtposIn->cKey + lcOffset;
  737. /* chase prev to find the right block */
  738. while ( lcKey < 0 )
  739. {
  740. bk = BkPrev( qcb );
  741. if ( bk == bkNil )
  742. {
  743. bk = qcb->bk;
  744. lcDelta = lcKey;
  745. lcKey = 0;
  746. break;
  747. }
  748. if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
  749. {
  750. UnlockGh( qbthr->ghCache );
  751. UnlockGh( hbt );
  752. return RcGetBtreeError();
  753. }
  754. lcKey += qcb->db.cKeys;
  755. }
  756. /* chase next to find the right block */
  757. while ( lcKey >= qcb->db.cKeys )
  758. {
  759. lcKey -= qcb->db.cKeys;
  760. bk = BkNext( qcb );
  761. if ( bk == bkNil )
  762. {
  763. bk = qcb->bk;
  764. lcDelta = lcKey + 1;
  765. lcKey = qcb->db.cKeys - 1;
  766. break;
  767. }
  768. if ( ( qcb = QFromBk( bk, qbthr->bth.cLevels - 1, qbthr ) ) == NULL )
  769. {
  770. UnlockGh( qbthr->ghCache );
  771. UnlockGh( hbt );
  772. return RcGetBtreeError();
  773. }
  774. }
  775. if ( bk == qbtposIn->bk && lcKey >= qbtposIn->cKey )
  776. {
  777. c = qbtposIn->cKey;
  778. qb = qcb->db.rgbBlock + qbtposIn->iKey;
  779. }
  780. else
  781. {
  782. c = 0;
  783. qb = qcb->db.rgbBlock + 2 * sizeof( BK );
  784. }
  785. while ( (LONG)c < lcKey )
  786. {
  787. qb += CbSizeKey( (KEY)qb, qbthr, TRUE );
  788. qb += CbSizeRec( qb, qbthr );
  789. c++;
  790. }
  791. if ( qbtposOut != NULL )
  792. {
  793. qbtposOut->bk = bk;
  794. qbtposOut->iKey = qb - (QB)qcb->db.rgbBlock;
  795. qbtposOut->cKey = c;
  796. }
  797. *qlcRealOffset = lcOffset - lcDelta;
  798. UnlockGh( qbthr->ghCache );
  799. UnlockGh( hbt );
  800. return SetBtreeErrorRc(lcDelta ? rcNoExists : rcSuccess);
  801. }
  802. /* EOF */