Leaked source code of windows server 2003
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.

3783 lines
103 KiB

  1. //***************************************************************************
  2. //
  3. // BTR.CPP
  4. //
  5. // WMI disk-based B-tree implementation for repository index
  6. //
  7. // raymcc 15-Oct-00 Prepared for Whistler Beta 2 to reduce file count
  8. //
  9. //***************************************************************************
  10. #include "precomp.h"
  11. #include <wbemcomn.h>
  12. #include <reposit.h>
  13. #include <stdlib.h>
  14. #include <math.h>
  15. #include "pagemgr.h"
  16. #include "btr.h"
  17. #include <arena.h>
  18. #define MAX_WORD_VALUE 0xFFFF
  19. #define MAX_TOKENS_PER_KEY 32
  20. #define MAX_FLUSH_INTERVAL 4000
  21. //#define MAX_PAGE_HISTORY 1024
  22. /*
  23. Notes:
  24. (a) Modify allocators to special case for page-size
  25. (b) Modify WriteIdxPage to not rewrite if no deltas
  26. (c) ERROR_PATH_NOT_FOUND if starting enum has no presence; GPF presently
  27. (d) Do a history of page hits and see if caching would be helpful
  28. */
  29. //static WORD History[MAX_PAGE_HISTORY] = {0};
  30. LONG g_lAllocs = 0;
  31. //***************************************************************************
  32. //
  33. // _BtrMemAlloc
  34. //
  35. //***************************************************************************
  36. // ok
  37. LPVOID WINAPI _BtrMemAlloc(
  38. SIZE_T dwBytes // number of bytes to allocate
  39. )
  40. {
  41. // Lookaside for items of page size, default array size, default
  42. // string pool size
  43. g_lAllocs++;
  44. return HeapAlloc(CWin32DefaultArena::GetArenaHeap(), HEAP_ZERO_MEMORY, dwBytes);
  45. }
  46. //***************************************************************************
  47. //
  48. // _BtrMemReAlloc
  49. //
  50. //***************************************************************************
  51. // ok
  52. LPVOID WINAPI _BtrMemReAlloc(
  53. LPVOID pOriginal,
  54. DWORD dwNewBytes
  55. )
  56. {
  57. return HeapReAlloc(CWin32DefaultArena::GetArenaHeap(), HEAP_ZERO_MEMORY, pOriginal, dwNewBytes);
  58. }
  59. //***************************************************************************
  60. //
  61. // _BtrMemFree
  62. //
  63. //***************************************************************************
  64. // ok
  65. BOOL WINAPI _BtrMemFree(LPVOID pMem)
  66. {
  67. if (pMem == 0)
  68. return TRUE;
  69. g_lAllocs--;
  70. return HeapFree(CWin32DefaultArena::GetArenaHeap(), 0, pMem);
  71. }
  72. //***************************************************************************
  73. //
  74. // CBTreeFile::CBTreeFile
  75. //
  76. //***************************************************************************
  77. // ok
  78. CBTreeFile::CBTreeFile()
  79. {
  80. m_dwPageSize = 0;
  81. m_dwLogicalRoot = 0;
  82. }
  83. //***************************************************************************
  84. //
  85. // CBTreeFile::CBTreeFile
  86. //
  87. //***************************************************************************
  88. // ok
  89. CBTreeFile::~CBTreeFile()
  90. {
  91. // if CPageSource__CommitTrans fails, we leak a handle
  92. // since Shutdown is not called
  93. if (m_pFile)
  94. m_pFile->Release();
  95. }
  96. //***************************************************************************
  97. //
  98. // CBTreeFile::Shutdown
  99. //
  100. //***************************************************************************
  101. // ok
  102. DWORD CBTreeFile::Shutdown(DWORD dwShutDownFlags)
  103. {
  104. m_dwPageSize = 0;
  105. m_dwLogicalRoot = 0;
  106. if (m_pFile)
  107. {
  108. m_pFile->Release();
  109. m_pFile = NULL;
  110. }
  111. return NO_ERROR;
  112. }
  113. //***************************************************************************
  114. //
  115. // CBTreeFile::WriteAdminPage
  116. //
  117. // Rewrites the admin page. There is no need to update the pagesize,
  118. // version, etc.
  119. //
  120. //***************************************************************************
  121. // ok
  122. DWORD CBTreeFile::WriteAdminPage()
  123. {
  124. LPDWORD pPageZero = 0;
  125. DWORD dwRes = GetPage(0, (LPVOID *) &pPageZero);
  126. if (dwRes)
  127. return dwRes;
  128. pPageZero[OFFSET_LOGICAL_ROOT] = m_dwLogicalRoot;
  129. dwRes = PutPage(pPageZero, PAGE_TYPE_ADMIN);
  130. _BtrMemFree(pPageZero);
  131. return dwRes;
  132. }
  133. //***************************************************************************
  134. //
  135. // CBTreeFile::SetRootPage
  136. //
  137. //***************************************************************************
  138. //
  139. DWORD CBTreeFile::SetRootPage(DWORD dwNewRoot)
  140. {
  141. m_dwLogicalRoot = dwNewRoot;
  142. return WriteAdminPage();
  143. }
  144. //***************************************************************************
  145. //
  146. // CBTreeFile::Init
  147. //
  148. // The real "constructor" which opens the file
  149. //
  150. //***************************************************************************
  151. // ok
  152. DWORD CBTreeFile::Init(
  153. DWORD dwPageSize,
  154. LPWSTR pszFilename,
  155. CPageSource* pSource
  156. )
  157. {
  158. DWORD dwLastError = 0;
  159. m_pTransactionManager = pSource;
  160. m_dwPageSize = dwPageSize;
  161. long lRes = pSource->GetBTreePageFile(&m_pFile);
  162. if(lRes != ERROR_SUCCESS)
  163. return lRes;
  164. return ReadAdminPage();
  165. }
  166. //***************************************************************************
  167. //
  168. // CBTreeFile::ReadAdminPage
  169. //
  170. //***************************************************************************
  171. // ok
  172. DWORD CBTreeFile::ReadAdminPage()
  173. {
  174. LPDWORD pPageZero = 0;
  175. DWORD dwRes = 0;
  176. dwRes = GetPage(0, (LPVOID *) &pPageZero);
  177. if (dwRes == ERROR_FILE_NOT_FOUND)
  178. {
  179. //First read of admin page fails so we need to set it up
  180. dwRes = Setup();
  181. m_dwLogicalRoot = 0;
  182. }
  183. else if (dwRes == ERROR_SUCCESS)
  184. {
  185. m_dwLogicalRoot = pPageZero[OFFSET_LOGICAL_ROOT];
  186. _BtrMemFree(pPageZero);
  187. }
  188. return dwRes;
  189. }
  190. //***************************************************************************
  191. //
  192. // CBTreeFile::Setup
  193. //
  194. // Sets up the 0th page (Admin page)
  195. //
  196. //***************************************************************************
  197. // ok
  198. DWORD CBTreeFile::Setup()
  199. {
  200. DWORD dwRes;
  201. DWORD dwRoot = 0;
  202. // First two pages, admin & free list root
  203. LPDWORD pPageZero = (LPDWORD) _BtrMemAlloc(m_dwPageSize);
  204. if (pPageZero == 0)
  205. {
  206. dwRes = ERROR_NOT_ENOUGH_MEMORY;
  207. goto Exit;
  208. }
  209. memset(pPageZero, 0, m_dwPageSize);
  210. // Map the page
  211. pPageZero[OFFSET_PAGE_TYPE] = PAGE_TYPE_ADMIN;
  212. pPageZero[OFFSET_PAGE_ID] = 0;
  213. pPageZero[OFFSET_NEXT_PAGE] = 0;
  214. pPageZero[OFFSET_LOGICAL_ROOT] = 0;
  215. dwRes = m_pFile->NewPage(1, 1, &dwRoot);
  216. // Write it out
  217. if (dwRes == ERROR_SUCCESS)
  218. dwRes = PutPage(pPageZero, PAGE_TYPE_ADMIN);
  219. Exit:
  220. _BtrMemFree(pPageZero);
  221. return dwRes;
  222. }
  223. //***************************************************************************
  224. //
  225. // CBTreeFile::Dump
  226. //
  227. // Debug helper
  228. //
  229. //***************************************************************************
  230. // ok
  231. void CBTreeFile::Dump(FILE *f)
  232. {
  233. /*
  234. SetFilePointer(m_hFile, 0, 0, FILE_BEGIN);
  235. LPDWORD pPage = (LPDWORD) new BYTE[m_dwPageSize];
  236. DWORD dwPage = 0;
  237. DWORD dwTotalKeys = 0;
  238. fprintf(f, "---BEGIN PAGE SOURCE DUMP---\n");
  239. fprintf(f, "In memory part:\n");
  240. fprintf(f, " m_dwPageSize = %d (0x%X)\n", m_dwPageSize, m_dwPageSize);
  241. fprintf(f, " m_hFile = 0x%p\n", m_hFile);
  242. fprintf(f, " m_dwNextFreePage = %d\n", m_dwNextFreePage);
  243. fprintf(f, " m_dwTotalPages = %d\n", m_dwTotalPages);
  244. fprintf(f, " m_dwLogicalRoot = %d\n", m_dwLogicalRoot);
  245. fprintf(f, "---\n");
  246. DWORD dwTotalFree = 0;
  247. DWORD dwOffs = 0;
  248. while (1)
  249. {
  250. DWORD dwRead = 0;
  251. BOOL bRes = ReadFile(m_hFile, pPage, m_dwPageSize, &dwRead, 0);
  252. if (dwRead != m_dwPageSize)
  253. break;
  254. fprintf(f, "Dump of page %d:\n", dwPage++);
  255. fprintf(f, " Page type = 0x%X", pPage[OFFSET_PAGE_TYPE]);
  256. if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_IMPOSSIBLE)
  257. fprintf(f, " PAGE_TYPE_IMPOSSIBLE\n");
  258. if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_DELETED)
  259. {
  260. fprintf(f, " PAGE_TYPE_DELETED\n");
  261. fprintf(f, " <page num check = %d>\n", pPage[1]);
  262. fprintf(f, " <next free page = %d>\n", pPage[2]);
  263. dwTotalFree++;
  264. }
  265. if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_ACTIVE)
  266. {
  267. fprintf(f, " PAGE_TYPE_ACTIVE\n");
  268. fprintf(f, " <page num check = %d>\n", pPage[1]);
  269. SIdxKeyTable *pKT = 0;
  270. DWORD dwKeys = 0;
  271. DWORD dwRes = SIdxKeyTable::Create(pPage, &pKT);
  272. if (dwRes == 0)
  273. {
  274. pKT->Dump(f, &dwKeys);
  275. pKT->Release();
  276. dwTotalKeys += dwKeys;
  277. }
  278. else
  279. {
  280. fprintf(f, "<INVALID Page Decode>\n");
  281. }
  282. }
  283. if (pPage[OFFSET_PAGE_TYPE] == PAGE_TYPE_ADMIN)
  284. {
  285. fprintf(f, " PAGE_TYPE_ADMIN\n");
  286. fprintf(f, " Page Num = %d\n", pPage[1]);
  287. fprintf(f, " Next Page = %d\n", pPage[2]);
  288. fprintf(f, " Logical Root = %d\n", pPage[3]);
  289. fprintf(f, " Free List Root = %d\n", pPage[4]);
  290. fprintf(f, " Total Pages = %d\n", pPage[5]);
  291. fprintf(f, " Page Size = %d (0x%X)\n", pPage[6], pPage[6]);
  292. fprintf(f, " Impl Version = 0x%X\n", pPage[7]);
  293. }
  294. }
  295. delete [] pPage;
  296. fprintf(f, "Total free pages detected by scan = %d\n", dwTotalFree);
  297. fprintf(f, "Total active keys = %d\n", dwTotalKeys);
  298. fprintf(f, "---END PAGE DUMP---\n");
  299. */
  300. }
  301. //***************************************************************************
  302. //
  303. // CBTreeFile::GetPage
  304. //
  305. // Reads an existing page; does not support seeking beyond end-of-file
  306. //
  307. //***************************************************************************
  308. // ok
  309. DWORD CBTreeFile::GetPage(
  310. DWORD dwPage,
  311. LPVOID *pPage
  312. )
  313. {
  314. DWORD dwRes;
  315. if (pPage == 0)
  316. return ERROR_INVALID_PARAMETER;
  317. // Allocate some memory
  318. LPVOID pMem = _BtrMemAlloc(m_dwPageSize);
  319. if (!pMem)
  320. return ERROR_NOT_ENOUGH_MEMORY;
  321. long lRes = m_pFile->GetPage(dwPage, 0, pMem);
  322. if (lRes != ERROR_SUCCESS)
  323. {
  324. _BtrMemFree(pMem);
  325. return lRes;
  326. }
  327. *pPage = pMem;
  328. return NO_ERROR;
  329. }
  330. //***************************************************************************
  331. //
  332. // CBTreeFile::PutPage
  333. //
  334. // Always rewrites; the file extent was grown when the page was allocated
  335. // with NewPage, so the page already exists and the write should not fail
  336. //
  337. //***************************************************************************
  338. // ok
  339. DWORD CBTreeFile::PutPage(
  340. LPVOID pPage,
  341. DWORD dwType
  342. )
  343. {
  344. // Force the page to confess its identity
  345. DWORD *pdwHeader = LPDWORD(pPage);
  346. DWORD dwPageId = pdwHeader[OFFSET_PAGE_ID];
  347. pdwHeader[OFFSET_PAGE_TYPE] = dwType;
  348. long lRes = m_pFile->PutPage(dwPageId, 0, pPage);
  349. if(lRes != ERROR_SUCCESS)
  350. return lRes;
  351. return NO_ERROR;
  352. }
  353. //***************************************************************************
  354. //
  355. // CBTreeFile::NewPage
  356. //
  357. // Allocates a new page, preferring the free list
  358. //
  359. //***************************************************************************
  360. // ok
  361. DWORD CBTreeFile::NewPage(LPVOID *pRetPage)
  362. {
  363. DWORD dwRes;
  364. if (pRetPage == 0)
  365. return ERROR_INVALID_PARAMETER;
  366. *pRetPage = 0;
  367. LPDWORD pNewPage = (LPDWORD) _BtrMemAlloc(m_dwPageSize);
  368. if (pNewPage == 0)
  369. return ERROR_NOT_ENOUGH_MEMORY;
  370. DWORD dwPage = 0;
  371. dwRes = m_pFile->NewPage(0, 1, &dwPage);
  372. if (dwRes != ERROR_SUCCESS)
  373. {
  374. _BtrMemFree(pNewPage);
  375. return dwRes;
  376. }
  377. memset(pNewPage, 0, m_dwPageSize);
  378. pNewPage[OFFSET_PAGE_ID] = dwPage;
  379. *pRetPage = pNewPage;
  380. return ERROR_SUCCESS;;
  381. }
  382. //***************************************************************************
  383. //
  384. // CBTreeFile::FreePage
  385. //
  386. // Called to delete or free a page. If the last page is the one
  387. // being freed, then the file is truncated.
  388. //
  389. //***************************************************************************
  390. // ok
  391. DWORD CBTreeFile::FreePage(
  392. LPVOID pPage
  393. )
  394. {
  395. LPDWORD pCast = LPDWORD(pPage);
  396. DWORD dwPageId = pCast[OFFSET_PAGE_ID];
  397. return FreePage(dwPageId);
  398. }
  399. //***************************************************************************
  400. //
  401. // CBTreeFile::FreePage
  402. //
  403. //***************************************************************************
  404. //
  405. DWORD CBTreeFile::FreePage(
  406. DWORD dwId
  407. )
  408. {
  409. return m_pFile->FreePage(0, dwId);
  410. }
  411. //***************************************************************************
  412. //
  413. // SIdxKeyTable::GetRequiredPageMemory
  414. //
  415. // Returns the amount of memory required to store this object in a
  416. // linear page
  417. //
  418. //***************************************************************************
  419. // ok
  420. DWORD SIdxKeyTable::GetRequiredPageMemory()
  421. {
  422. DWORD dwTotal = m_pStrPool->GetRequiredPageMemory();
  423. // Size of the key lookup table & its sizing DWORD, and
  424. // add in the child page & user data
  425. dwTotal += sizeof(DWORD) + sizeof(WORD) * m_dwNumKeys;
  426. dwTotal += sizeof(DWORD) + sizeof(DWORD) * m_dwNumKeys; // User data
  427. dwTotal += sizeof(DWORD) + sizeof(DWORD) * (m_dwNumKeys+1); // Child pages
  428. // Add in the key encoding table
  429. dwTotal += sizeof(WORD) + sizeof(WORD) * m_dwKeyCodesUsed;
  430. // Add in per page overhead
  431. //
  432. // Signature, Page Id, Next Page, Parent Page
  433. dwTotal += sizeof(DWORD) * 4;
  434. // (NOTE A): Add some safety margin...
  435. dwTotal += sizeof(DWORD) * 2;
  436. return dwTotal;
  437. }
  438. //***************************************************************************
  439. //
  440. // SIdxKeyTable::StealKeyFromSibling
  441. //
  442. // Transfers a key from the sibling via the parent in a sort of rotation:
  443. //
  444. // 10
  445. // 1 2 12 13 14
  446. //
  447. // Where <this> is node (1,2) and sibling is (12,13). A single rotation
  448. // moves 10 into (1,2) and grabs 12 from the sibling to replace it,
  449. //
  450. // 12
  451. // 1 2 10 13 14
  452. //
  453. // We repeat this until minimum load of <this> is above const_MinimumLoad.
  454. //
  455. //***************************************************************************
  456. // ok
  457. DWORD SIdxKeyTable::StealKeyFromSibling(
  458. SIdxKeyTable *pParent,
  459. SIdxKeyTable *pSibling
  460. )
  461. {
  462. DWORD dwData, dwChild;
  463. WORD wID;
  464. LPSTR pszKey = 0;
  465. DWORD dwRes = 0;
  466. DWORD dwSiblingId = pSibling->GetPageId();
  467. DWORD dwThisId = GetPageId();
  468. for (WORD i = 0; i < WORD(pParent->GetNumKeys()); i++)
  469. {
  470. DWORD dwChildA = pParent->GetChildPage(i);
  471. DWORD dwChildB = pParent->GetChildPage(i+1);
  472. if (dwChildA == dwThisId && dwChildB == dwSiblingId)
  473. {
  474. dwRes = pParent->GetKeyAt(i, &pszKey);
  475. if (dwRes != 0)
  476. return dwRes;
  477. dwData = pParent->GetUserData(i);
  478. dwRes = FindKey(pszKey, &wID);
  479. if ((dwRes != 0) && (dwRes != ERROR_NOT_FOUND))
  480. {
  481. _BtrMemFree(pszKey);
  482. return dwRes;
  483. }
  484. dwRes = AddKey(pszKey, wID, dwData);
  485. if (dwRes != 0)
  486. {
  487. _BtrMemFree(pszKey);
  488. return dwRes;
  489. }
  490. dwRes = pParent->RemoveKey(i);
  491. if (dwRes != 0)
  492. {
  493. _BtrMemFree(pszKey);
  494. return dwRes;
  495. }
  496. _BtrMemFree(pszKey);
  497. dwRes = pSibling->GetKeyAt(0, &pszKey);
  498. if (dwRes != 0)
  499. return dwRes;
  500. dwData = pSibling->GetUserData(0);
  501. dwChild = pSibling->GetChildPage(0);
  502. dwRes = pSibling->RemoveKey(0);
  503. if (dwRes != 0)
  504. {
  505. _BtrMemFree(pszKey);
  506. return dwRes;
  507. }
  508. SetChildPage(wID+1, dwChild);
  509. dwRes = pParent->AddKey(pszKey, i, dwData);
  510. if (dwRes != 0)
  511. {
  512. _BtrMemFree(pszKey);
  513. return dwRes;
  514. }
  515. pParent->SetChildPage(i, dwThisId);
  516. pParent->SetChildPage(i+1, dwSiblingId);
  517. _BtrMemFree(pszKey);
  518. break;
  519. }
  520. else if (dwChildA == dwSiblingId && dwChildB == dwThisId)
  521. {
  522. dwRes = pParent->GetKeyAt(i, &pszKey);
  523. if (dwRes != 0)
  524. return dwRes;
  525. dwData = pParent->GetUserData(i);
  526. dwRes = FindKey(pszKey, &wID);
  527. if ((dwRes != 0) && (dwRes != ERROR_NOT_FOUND))
  528. {
  529. _BtrMemFree(pszKey);
  530. return dwRes;
  531. }
  532. dwRes = AddKey(pszKey, wID, dwData);
  533. if (dwRes != 0)
  534. {
  535. _BtrMemFree(pszKey);
  536. return dwRes;
  537. }
  538. dwRes = pParent->RemoveKey(i);
  539. if (dwRes != 0)
  540. {
  541. _BtrMemFree(pszKey);
  542. return dwRes;
  543. }
  544. _BtrMemFree(pszKey);
  545. WORD wSibId = (WORD) pSibling->GetNumKeys() - 1;
  546. dwRes = pSibling->GetKeyAt(wSibId, &pszKey);
  547. if (dwRes != 0)
  548. return dwRes;
  549. dwData = pSibling->GetUserData(wSibId);
  550. dwChild = pSibling->GetChildPage(wSibId+1);
  551. dwRes = pSibling->RemoveKey(wSibId);
  552. if (dwRes != 0)
  553. {
  554. _BtrMemFree(pszKey);
  555. return dwRes;
  556. }
  557. SetChildPage(wID, dwChild);
  558. dwRes = pParent->AddKey(pszKey, i, dwData);
  559. if (dwRes != 0)
  560. {
  561. _BtrMemFree(pszKey);
  562. return dwRes;
  563. }
  564. pParent->SetChildPage(i, dwSiblingId);
  565. pParent->SetChildPage(i+1, dwThisId);
  566. _BtrMemFree(pszKey);
  567. break;
  568. }
  569. }
  570. return NO_ERROR;
  571. }
  572. //***************************************************************************
  573. //
  574. // SIdxKeyTable::Collapse
  575. //
  576. // Collapses the contents of a node and its sibling into just one
  577. // node and adjusts the parent.
  578. //
  579. // Precondition: The two siblings can be successfully collapsed
  580. // into a single node, accomodate a key migrated from the parent
  581. // and still safely fit into a single node. Page sizes are not
  582. // checked here.
  583. //
  584. //***************************************************************************
  585. // ok
  586. DWORD SIdxKeyTable::Collapse(
  587. SIdxKeyTable *pParent,
  588. SIdxKeyTable *pDoomedSibling
  589. )
  590. {
  591. WORD wId;
  592. DWORD dwRes;
  593. LPSTR pszKey = 0;
  594. DWORD dwData;
  595. DWORD dwChild;
  596. BOOL bExtra = FALSE;
  597. DWORD dwSiblingId = pDoomedSibling->GetPageId();
  598. DWORD dwThisId = GetPageId();
  599. // Locate the node in the parent which points to the two
  600. // siblings. Since we don't know which sibling this is,
  601. // we have to take into account the two possibilites.
  602. // Is <this> the right side or the left?
  603. //
  604. // 10 20 30 40
  605. // | | | | |
  606. // x Sib This x x
  607. //
  608. // vs.
  609. // 10 20 30 40
  610. // | | | | |
  611. // x This Sib x x
  612. //
  613. // We then migrate the key down into the current node
  614. // and remove it from the parent. We steal the first
  615. //
  616. // ======================================================
  617. for (WORD i = 0; i < WORD(pParent->GetNumKeys()); i++)
  618. {
  619. DWORD dwChildA = pParent->GetChildPage(i);
  620. DWORD dwChildB = pParent->GetChildPage(i+1);
  621. if (dwChildA == dwSiblingId && dwChildB == dwThisId)
  622. {
  623. dwRes = pParent->GetKeyAt(i, &pszKey);
  624. if (dwRes != 0)
  625. return dwRes;
  626. dwData = pParent->GetUserData(i);
  627. dwRes = pParent->RemoveKey(i);
  628. if (dwRes != 0)
  629. {
  630. _BtrMemFree(pszKey);
  631. return dwRes;
  632. }
  633. pParent->SetChildPage(i, dwThisId);
  634. dwChild = pDoomedSibling->GetLastChildPage();
  635. dwRes = AddKey(pszKey, 0, dwData);
  636. if (dwRes != 0)
  637. {
  638. _BtrMemFree(pszKey);
  639. return dwRes;
  640. }
  641. SetChildPage(0, dwChild);
  642. _BtrMemFree(pszKey);
  643. bExtra = FALSE;
  644. break;
  645. }
  646. else if (dwChildA == dwThisId && dwChildB == dwSiblingId)
  647. {
  648. dwRes = pParent->GetKeyAt(i, &pszKey);
  649. if (dwRes != 0)
  650. return dwRes;
  651. dwData = pParent->GetUserData(i);
  652. dwRes = pParent->RemoveKey(i);
  653. if (dwRes != 0)
  654. {
  655. _BtrMemFree(pszKey);
  656. return dwRes;
  657. }
  658. pParent->SetChildPage(i, dwThisId);
  659. dwRes = FindKey(pszKey, &wId);
  660. if ((dwRes != 0) && (dwRes != ERROR_NOT_FOUND))
  661. {
  662. _BtrMemFree(pszKey);
  663. return dwRes;
  664. }
  665. dwRes = AddKey(pszKey, wId, dwData);
  666. if (dwRes != 0)
  667. {
  668. _BtrMemFree(pszKey);
  669. return dwRes;
  670. }
  671. _BtrMemFree(pszKey);
  672. bExtra = TRUE;
  673. break;
  674. }
  675. }
  676. // Move all info from sibling into the current node.
  677. // ==================================================
  678. DWORD dwNumSibKeys = pDoomedSibling->GetNumKeys();
  679. for (WORD i = 0; i < WORD(dwNumSibKeys); i++)
  680. {
  681. LPSTR pKeyStr = 0;
  682. dwRes = pDoomedSibling->GetKeyAt(i, &pKeyStr);
  683. if (dwRes)
  684. return dwRes;
  685. DWORD dwUserData = pDoomedSibling->GetUserData(i);
  686. dwRes = FindKey(pKeyStr, &wId);
  687. if (dwRes != ERROR_NOT_FOUND)
  688. {
  689. _BtrMemFree(pKeyStr);
  690. return ERROR_BAD_FORMAT;
  691. }
  692. dwRes = AddKey(pKeyStr, wId, dwUserData);
  693. if (dwRes != 0)
  694. {
  695. _BtrMemFree(pKeyStr);
  696. return dwRes;
  697. }
  698. dwChild = pDoomedSibling->GetChildPage(i);
  699. SetChildPage(wId, dwChild);
  700. _BtrMemFree(pKeyStr);
  701. }
  702. if (bExtra)
  703. SetChildPage(WORD(GetNumKeys()), pDoomedSibling->GetLastChildPage());
  704. pDoomedSibling->ZapPage();
  705. return NO_ERROR;
  706. }
  707. //***************************************************************************
  708. //
  709. // SIdxKeyTable::GetRightSiblingOf
  710. // SIdxKeyTable::GetRightSiblingOf
  711. //
  712. // Searches the child page pointers and returns the sibling of the
  713. // specified page. A return value of zero indicates there was not
  714. // sibling of the specified value in the direction requested.
  715. //
  716. //***************************************************************************
  717. //
  718. DWORD SIdxKeyTable::GetRightSiblingOf(
  719. DWORD dwId
  720. )
  721. {
  722. for (DWORD i = 0; i < m_dwNumKeys; i++)
  723. {
  724. if (m_pdwChildPageMap[i] == dwId)
  725. return m_pdwChildPageMap[i+1];
  726. }
  727. return 0;
  728. }
  729. DWORD SIdxKeyTable::GetLeftSiblingOf(
  730. DWORD dwId
  731. )
  732. {
  733. for (DWORD i = 1; i < m_dwNumKeys+1; i++)
  734. {
  735. if (m_pdwChildPageMap[i] == dwId)
  736. return m_pdwChildPageMap[i-1];
  737. }
  738. return 0;
  739. }
  740. //***************************************************************************
  741. //
  742. // SIdxKeyTable::Redist
  743. //
  744. // Used when inserting and performing a node split.
  745. // Precondition:
  746. // (a) The current node is oversized
  747. // (b) <pParent> is ready to receive the new median key
  748. // (c) <pNewSibling> is completely empty and refers to the lesser node (left)
  749. // (d) All pages have assigned numbers
  750. //
  751. // We move the nodes from <this> into the <pNewSibling> until both
  752. // are approximately half full. The median key is moved into the parent.
  753. // May fail if <pNewSibling> cannot allocate memory for the new stuff.
  754. //
  755. // If any errors occur, the entire sequence should be considered as failed
  756. // and the pages invalid.
  757. //
  758. //***************************************************************************
  759. //
  760. DWORD SIdxKeyTable::Redist(
  761. SIdxKeyTable *pParent,
  762. SIdxKeyTable *pNewSibling
  763. )
  764. {
  765. DWORD dwRes;
  766. WORD wID;
  767. if (pParent == 0 || pNewSibling == 0)
  768. return ERROR_INVALID_PARAMETER;
  769. if (m_dwNumKeys < 3)
  770. {
  771. return ERROR_INVALID_DATA;
  772. }
  773. // Find median key info and put it into parent.
  774. DWORD dwToTransfer = m_dwNumKeys / 2;
  775. while (dwToTransfer--)
  776. {
  777. // Get 0th key
  778. LPSTR pStr = 0;
  779. dwRes = GetKeyAt(0, &pStr);
  780. if (dwRes)
  781. return dwRes;
  782. DWORD dwUserData = GetUserData(0);
  783. // Move stuff into younger sibling
  784. dwRes = pNewSibling->FindKey(pStr, &wID);
  785. if (dwRes != ERROR_NOT_FOUND)
  786. {
  787. _BtrMemFree(pStr);
  788. return dwRes;
  789. }
  790. dwRes = pNewSibling->AddKey(pStr, wID, dwUserData);
  791. _BtrMemFree(pStr);
  792. if (dwRes)
  793. return dwRes;
  794. DWORD dwChildPage = GetChildPage(0);
  795. pNewSibling->SetChildPage(wID, dwChildPage);
  796. dwRes = RemoveKey(0);
  797. if (dwRes)
  798. return dwRes;
  799. }
  800. pNewSibling->SetChildPage(WORD(pNewSibling->GetNumKeys()), GetChildPage(0));
  801. // Next key is the median key, which migrates to the parent.
  802. LPSTR pStr = 0;
  803. dwRes = GetKeyAt(0, &pStr);
  804. if (dwRes)
  805. return dwRes;
  806. DWORD dwUserData = GetUserData(0);
  807. dwRes = pParent->FindKey(pStr, &wID);
  808. if (dwRes != ERROR_NOT_FOUND)
  809. {
  810. _BtrMemFree(pStr);
  811. return dwRes;
  812. }
  813. dwRes = pParent->AddKey(pStr, wID, dwUserData);
  814. _BtrMemFree(pStr);
  815. if (dwRes)
  816. return dwRes;
  817. dwRes = RemoveKey(0);
  818. if (dwRes != 0)
  819. return dwRes;
  820. // Patch in the various page pointers
  821. pParent->SetChildPage(wID, pNewSibling->GetPageId());
  822. pParent->SetChildPage(wID+1, GetPageId());
  823. // Everything else is already okay
  824. return NO_ERROR;
  825. }
  826. //***************************************************************************
  827. //
  828. // SIdxKeyTable::SIdxKeyTable
  829. //
  830. //***************************************************************************
  831. // ok
  832. SIdxKeyTable::SIdxKeyTable()
  833. {
  834. m_dwRefCount = 0;
  835. m_dwPageId = 0;
  836. m_dwParentPageId = 0;
  837. m_dwNumKeys = 0; // Num keys
  838. m_pwKeyLookup = 0; // Offset of key into key-lookup-table
  839. m_dwKeyLookupTotalSize = 0; // Elements in array
  840. m_pwKeyCodes = 0; // Key encoding table
  841. m_dwKeyCodesTotalSize = 0; // Total elements in array
  842. m_dwKeyCodesUsed = 0; // Elements used
  843. m_pStrPool = 0; // The pool associated with this key table
  844. m_pdwUserData = 0; // Stores user DWORDs for each key
  845. m_pdwChildPageMap = 0; // Stores the child page map (num keys + 1)
  846. }
  847. //***************************************************************************
  848. //
  849. //***************************************************************************
  850. //
  851. DWORD SIdxKeyTable::Clone(
  852. OUT SIdxKeyTable **pRetCopy
  853. )
  854. {
  855. SIdxKeyTable *pCopy = new SIdxKeyTable;
  856. if (!pCopy)
  857. return ERROR_NOT_ENOUGH_MEMORY;
  858. pCopy->m_dwRefCount = 1;
  859. pCopy->m_dwPageId = m_dwPageId;
  860. pCopy->m_dwParentPageId = m_dwParentPageId;
  861. pCopy->m_dwNumKeys = m_dwNumKeys;
  862. pCopy->m_pwKeyLookup = (WORD *)_BtrMemAlloc(sizeof(WORD) * m_dwKeyLookupTotalSize);
  863. if (pCopy->m_pwKeyLookup == 0)
  864. {
  865. delete pCopy;
  866. return ERROR_NOT_ENOUGH_MEMORY;
  867. }
  868. memcpy(pCopy->m_pwKeyLookup, m_pwKeyLookup, sizeof(WORD) * m_dwKeyLookupTotalSize);
  869. pCopy->m_dwKeyLookupTotalSize = m_dwKeyLookupTotalSize;
  870. pCopy->m_pdwUserData = (DWORD *)_BtrMemAlloc(sizeof(DWORD) * m_dwKeyLookupTotalSize);
  871. if (pCopy->m_pdwUserData == 0)
  872. {
  873. delete pCopy;
  874. return ERROR_NOT_ENOUGH_MEMORY;
  875. }
  876. memcpy(pCopy->m_pdwUserData, m_pdwUserData, sizeof(DWORD) * m_dwKeyLookupTotalSize);
  877. pCopy->m_pdwChildPageMap = (DWORD *) _BtrMemAlloc(sizeof(DWORD) * (m_dwKeyLookupTotalSize+1));
  878. if (pCopy->m_pdwChildPageMap == 0)
  879. {
  880. delete pCopy;
  881. return ERROR_NOT_ENOUGH_MEMORY;
  882. }
  883. memcpy(pCopy->m_pdwChildPageMap, m_pdwChildPageMap, sizeof(DWORD) * (m_dwKeyLookupTotalSize+1));
  884. pCopy->m_dwKeyCodesTotalSize = m_dwKeyCodesTotalSize;
  885. pCopy->m_pwKeyCodes = (WORD *) _BtrMemAlloc(sizeof(WORD) * m_dwKeyCodesTotalSize);
  886. if (pCopy->m_pwKeyCodes == 0)
  887. {
  888. delete pCopy;
  889. return ERROR_NOT_ENOUGH_MEMORY;
  890. }
  891. memcpy(pCopy->m_pwKeyCodes, m_pwKeyCodes, sizeof(WORD)* m_dwKeyCodesTotalSize);
  892. pCopy->m_dwKeyCodesUsed = m_dwKeyCodesUsed;
  893. if (m_pStrPool->Clone(&pCopy->m_pStrPool) != 0)
  894. {
  895. delete pCopy;
  896. return ERROR_NOT_ENOUGH_MEMORY;
  897. }
  898. *pRetCopy = pCopy;
  899. return NO_ERROR;
  900. }
  901. //***************************************************************************
  902. //
  903. // SIdxKeyTable::~SIdxKeyTable
  904. //
  905. //***************************************************************************
  906. //
  907. SIdxKeyTable::~SIdxKeyTable()
  908. {
  909. if (m_pwKeyCodes)
  910. _BtrMemFree(m_pwKeyCodes);
  911. if (m_pwKeyLookup)
  912. _BtrMemFree(m_pwKeyLookup);
  913. if (m_pdwUserData)
  914. _BtrMemFree(m_pdwUserData);
  915. if (m_pdwChildPageMap)
  916. _BtrMemFree(m_pdwChildPageMap);
  917. if (m_pStrPool)
  918. delete m_pStrPool;
  919. }
  920. //***************************************************************************
  921. //
  922. // SIdxKeyTable::GetKeyAt
  923. //
  924. // Precondition: <wID> is correct
  925. // The only real case of failure is that the return string cannot be allocated.
  926. //
  927. // Return values:
  928. // NO_ERROR
  929. // ERROR_NOT_ENOUGH_MEMORY
  930. // ERROR_INVALID_PARAMETER
  931. //
  932. //***************************************************************************
  933. // tested
  934. DWORD SIdxKeyTable::GetKeyAt(
  935. WORD wID,
  936. LPSTR *pszKey
  937. )
  938. {
  939. if (wID >= m_dwNumKeys || pszKey == 0)
  940. return ERROR_INVALID_PARAMETER;
  941. WORD wStartOffs = m_pwKeyLookup[wID];
  942. WORD wNumTokens = m_pwKeyCodes[wStartOffs];
  943. LPSTR Strings[MAX_TOKENS_PER_KEY];
  944. DWORD dwTotalLengths = 0;
  945. for (DWORD i = 0; i < DWORD(wNumTokens); i++)
  946. {
  947. Strings[i] = m_pStrPool->GetStrById(m_pwKeyCodes[wStartOffs+1+i]);
  948. dwTotalLengths += strlen(Strings[i]);
  949. }
  950. size_t _len = dwTotalLengths + 1 + wNumTokens;
  951. LPSTR pszFinalStr = (LPSTR) _BtrMemAlloc(_len);
  952. if (!pszFinalStr)
  953. return ERROR_NOT_ENOUGH_MEMORY;
  954. *pszFinalStr = 0;
  955. for (DWORD i = 0; i < DWORD(wNumTokens); i++)
  956. {
  957. if (i > 0)
  958. StringCchCatA(pszFinalStr, _len, "\\");
  959. StringCchCatA(pszFinalStr, _len, Strings[i]);
  960. }
  961. *pszKey = pszFinalStr;
  962. return NO_ERROR;
  963. }
  964. //***************************************************************************
  965. //
  966. // SIdxStringPool::FindStr
  967. //
  968. // Finds a string in the pool, if present and returns the assigned
  969. // offset. Uses a binary search.
  970. //
  971. // Return codes:
  972. // NO_ERROR The string was found
  973. // ERROR_NOT_FOND
  974. //
  975. //***************************************************************************
  976. // tested
  977. DWORD SIdxStringPool::FindStr(
  978. IN LPSTR pszSearchKey,
  979. OUT WORD *pwStringNumber,
  980. OUT WORD *pwPoolOffset
  981. )
  982. {
  983. if (m_dwNumStrings == 0)
  984. {
  985. *pwStringNumber = 0;
  986. return ERROR_NOT_FOUND;
  987. }
  988. // Binary search current node for key match.
  989. // =========================================
  990. int nPosition = 0;
  991. int l = 0, u = int(m_dwNumStrings) - 1;
  992. while (l <= u)
  993. {
  994. int m = (l + u) / 2;
  995. // m is the current key to consider 0...n-1
  996. LPSTR pszCandidateKeyStr = m_pStringPool+m_pwOffsets[m];
  997. int nRes = strcmp(pszSearchKey, pszCandidateKeyStr);
  998. // Decide which way to cut the array in half.
  999. // ==========================================
  1000. if (nRes < 0)
  1001. {
  1002. u = m - 1;
  1003. nPosition = u + 1;
  1004. }
  1005. else if (nRes > 0)
  1006. {
  1007. l = m + 1;
  1008. nPosition = l;
  1009. }
  1010. else
  1011. {
  1012. // If here, we found the darn thing. Life is good.
  1013. // Populate the key unit.
  1014. // ================================================
  1015. if (pwStringNumber)
  1016. *pwStringNumber = WORD(m);
  1017. if (pwPoolOffset)
  1018. *pwPoolOffset = m_pwOffsets[m];
  1019. return NO_ERROR;
  1020. }
  1021. }
  1022. // Not found, if here. We record where the key should have been
  1023. // and tell the user the unhappy news.
  1024. // ==============================================================
  1025. *pwStringNumber = WORD(short(nPosition)); // The key would have been 'here'
  1026. return ERROR_NOT_FOUND;
  1027. }
  1028. //***************************************************************************
  1029. //
  1030. //***************************************************************************
  1031. //
  1032. DWORD SIdxStringPool::Clone(
  1033. SIdxStringPool **pRetCopy
  1034. )
  1035. {
  1036. SIdxStringPool *pCopy = new SIdxStringPool;
  1037. if (pCopy == 0)
  1038. return ERROR_NOT_ENOUGH_MEMORY;
  1039. pCopy->m_dwNumStrings = m_dwNumStrings;
  1040. pCopy->m_pwOffsets = (WORD *) _BtrMemAlloc(sizeof(WORD)*m_dwOffsetsSize);
  1041. if (pCopy->m_pwOffsets == 0)
  1042. {
  1043. delete pCopy;
  1044. return ERROR_NOT_ENOUGH_MEMORY;
  1045. }
  1046. memcpy(pCopy->m_pwOffsets, m_pwOffsets, sizeof(WORD)*m_dwOffsetsSize);
  1047. pCopy->m_dwOffsetsSize = m_dwOffsetsSize;
  1048. pCopy->m_pStringPool = (LPSTR) _BtrMemAlloc(m_dwPoolTotalSize);
  1049. if (pCopy->m_pStringPool == 0)
  1050. {
  1051. delete pCopy;
  1052. return ERROR_NOT_ENOUGH_MEMORY;
  1053. }
  1054. memcpy(pCopy->m_pStringPool, m_pStringPool, m_dwPoolTotalSize);
  1055. pCopy->m_dwPoolTotalSize = m_dwPoolTotalSize;
  1056. pCopy->m_dwPoolUsed = m_dwPoolUsed;
  1057. *pRetCopy = pCopy;
  1058. return NO_ERROR;
  1059. }
  1060. //***************************************************************************
  1061. //
  1062. // SIdxStringPool::DeleteStr
  1063. //
  1064. // Removes a string from the pool and pool index.
  1065. // Precondition: <wStringNum> is known to be valid by virtue of a prior
  1066. // call to <FindStr>.
  1067. //
  1068. // Return values:
  1069. // NO_ERROR <Cannot fail if precondition is met>.
  1070. //
  1071. //***************************************************************************
  1072. //
  1073. DWORD SIdxStringPool::DeleteStr(
  1074. WORD wStringNum,
  1075. int *pnAdjuster
  1076. )
  1077. {
  1078. if (pnAdjuster)
  1079. *pnAdjuster = 0;
  1080. // Find the address of the string to be removed.
  1081. // =============================================
  1082. DWORD dwTargetOffs = m_pwOffsets[wStringNum];
  1083. LPSTR pszDoomed = m_pStringPool+dwTargetOffs;
  1084. DWORD dwDoomedStrLen = strlen(pszDoomed) + 1;
  1085. // Copy all subsequent strings over the top and shorten the heap.
  1086. // Special case if this already the last string
  1087. // ==============================================================
  1088. DWORD dwStrBytesToMove = DWORD(m_pStringPool+m_dwPoolUsed - pszDoomed - dwDoomedStrLen);
  1089. if (dwStrBytesToMove)
  1090. memmove(pszDoomed, pszDoomed+dwDoomedStrLen, dwStrBytesToMove);
  1091. m_dwPoolUsed -= dwDoomedStrLen;
  1092. // Remove this entry from the array.
  1093. // =================================
  1094. DWORD dwArrayElsToMove = m_dwNumStrings - wStringNum - 1;
  1095. if (dwArrayElsToMove)
  1096. {
  1097. memmove(m_pwOffsets+wStringNum, m_pwOffsets+wStringNum+1, dwArrayElsToMove * sizeof(WORD));
  1098. if (pnAdjuster)
  1099. *pnAdjuster = -1;
  1100. }
  1101. m_dwNumStrings--;
  1102. // For all remaining elements, adjust offsets that were affected.
  1103. // ==============================================================
  1104. for (DWORD dwTrace = 0; dwTrace < m_dwNumStrings; dwTrace++)
  1105. {
  1106. if (m_pwOffsets[dwTrace] > dwTargetOffs)
  1107. m_pwOffsets[dwTrace] -= WORD(dwDoomedStrLen);
  1108. }
  1109. // Adjust sizes.
  1110. // =============
  1111. return NO_ERROR;
  1112. }
  1113. //***************************************************************************
  1114. //
  1115. // SIdxStringPool::AddStr
  1116. //
  1117. // Adds a string to the pool. Assumes it is known prior to the call that
  1118. // the string isn't present.
  1119. //
  1120. // Parameters:
  1121. // pszString The string to add
  1122. // pwAssignedOffset Returns the offset code assigned to the string
  1123. // Return values:
  1124. // NO_ERROR
  1125. // ERROR_NOT_ENOUGH_MEMORY
  1126. //
  1127. //***************************************************************************
  1128. // ok
  1129. DWORD SIdxStringPool::AddStr(
  1130. LPSTR pszString,
  1131. WORD wInsertPos,
  1132. int *pnAdjuster
  1133. )
  1134. {
  1135. if (pnAdjuster)
  1136. *pnAdjuster = 0;
  1137. // Precondition: String doesn't exist in the table
  1138. // Determine if the pool is too small for another string.
  1139. // If so, extend it.
  1140. // ======================================================
  1141. DWORD dwRequired = strlen(pszString)+1;
  1142. DWORD dwPoolFree = m_dwPoolTotalSize - m_dwPoolUsed;
  1143. if (m_dwPoolUsed + dwRequired - 1 > MAX_WORD_VALUE)
  1144. {
  1145. return ERROR_INSUFFICIENT_BUFFER;
  1146. }
  1147. if (dwRequired > dwPoolFree)
  1148. {
  1149. // Try to grow the pool
  1150. // ====================
  1151. LPVOID pTemp = _BtrMemReAlloc(m_pStringPool, m_dwPoolTotalSize * 2);
  1152. if (!pTemp) {
  1153. return ERROR_NOT_ENOUGH_MEMORY;
  1154. }
  1155. m_dwPoolTotalSize *= 2;
  1156. m_pStringPool = (LPSTR) pTemp;
  1157. }
  1158. // If array too small, reallocate to larger one
  1159. // ============================================
  1160. if (m_dwNumStrings == m_dwOffsetsSize)
  1161. {
  1162. // Realloc; double current size
  1163. LPVOID pTemp = _BtrMemReAlloc(m_pwOffsets, m_dwOffsetsSize * sizeof(WORD) * 2);
  1164. if (!pTemp)
  1165. return ERROR_NOT_ENOUGH_MEMORY;
  1166. m_dwOffsetsSize *= 2;
  1167. m_pwOffsets = PWORD(pTemp);
  1168. }
  1169. // If here, no problem. We have enough space for everything.
  1170. // =========================================================
  1171. LPSTR pszInsertAddr = m_pStringPool+m_dwPoolUsed;
  1172. DWORD dwInsertOffs = m_dwPoolUsed;
  1173. StringCchCopyA(pszInsertAddr, m_dwPoolTotalSize-m_dwPoolUsed, pszString);
  1174. m_dwPoolUsed += dwRequired;
  1175. // If here, there is enough room.
  1176. // ==============================
  1177. DWORD dwToBeMoved = m_dwNumStrings - wInsertPos;
  1178. if (dwToBeMoved)
  1179. {
  1180. memmove(&m_pwOffsets[wInsertPos+1], &m_pwOffsets[wInsertPos], sizeof(WORD)*dwToBeMoved);
  1181. if (pnAdjuster)
  1182. *pnAdjuster = 1;
  1183. }
  1184. m_pwOffsets[wInsertPos] = WORD(dwInsertOffs);
  1185. m_dwNumStrings++;
  1186. return NO_ERROR;
  1187. }
  1188. //***************************************************************************
  1189. //
  1190. // ParseIntoTokens
  1191. //
  1192. // Parses a slash separated string into separate tokens in preparation
  1193. // for encoding into the string pool. Call FreeStringArray on the output
  1194. // when no longer needed.
  1195. //
  1196. // No more than MAX_TOKEN_PER_KEY are supported. This means that
  1197. // if backslashes are used, no more than MAX_TOKEN_PER_KEY units can
  1198. // be parsed out.
  1199. //
  1200. // Returns:
  1201. // ERROR_INVALID_PARAMETER
  1202. // ERROR_NOT_ENOUGH_MEMORY
  1203. // NO_ERROR
  1204. //
  1205. //***************************************************************************
  1206. // ok
  1207. DWORD ParseIntoTokens(
  1208. IN LPSTR pszSource,
  1209. OUT DWORD *pdwTokenCount,
  1210. OUT LPSTR **pszTokens
  1211. )
  1212. {
  1213. LPSTR Strings[MAX_TOKENS_PER_KEY];
  1214. DWORD dwParseCount = 0, i = 0;
  1215. DWORD dwSourceLen = strlen(pszSource);
  1216. LPSTR *pszRetStr = 0;
  1217. DWORD dwRet;
  1218. if (pszSource == 0 || *pszSource == 0)
  1219. return ERROR_INVALID_PARAMETER;
  1220. LPSTR pszTempBuf = (LPSTR) _BtrMemAlloc(dwSourceLen+1);
  1221. if (!pszTempBuf)
  1222. return ERROR_NOT_ENOUGH_MEMORY;
  1223. LPSTR pszTracer = pszTempBuf;
  1224. for (;;)
  1225. {
  1226. *pszTracer = *pszSource;
  1227. if (*pszTracer == '\\' || *pszTracer == 0)
  1228. {
  1229. *pszTracer = 0; // Replace with null terminator
  1230. size_t _TempBufLen = strlen(pszTempBuf)+1;
  1231. LPSTR pszTemp2 = (LPSTR) _BtrMemAlloc(_TempBufLen);
  1232. if (pszTemp2 == 0)
  1233. {
  1234. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1235. goto Error;
  1236. }
  1237. if (dwParseCount == MAX_TOKENS_PER_KEY)
  1238. {
  1239. _BtrMemFree(pszTemp2);
  1240. dwRet = ERROR_INVALID_DATA;
  1241. goto Error;
  1242. }
  1243. StringCchCopyA(pszTemp2, _TempBufLen, pszTempBuf);
  1244. Strings[dwParseCount++] = pszTemp2;
  1245. pszTracer = pszTempBuf;
  1246. pszTracer--;
  1247. }
  1248. if (*pszSource == 0)
  1249. break;
  1250. pszTracer++;
  1251. pszSource++;
  1252. }
  1253. // If here, we at least parsed one string.
  1254. // =======================================
  1255. pszRetStr = (LPSTR *) _BtrMemAlloc(sizeof(LPSTR) * dwParseCount);
  1256. if (pszRetStr == 0)
  1257. {
  1258. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1259. goto Error;
  1260. }
  1261. memcpy(pszRetStr, Strings, sizeof(LPSTR) * dwParseCount);
  1262. *pdwTokenCount = dwParseCount;
  1263. *pszTokens = pszRetStr;
  1264. _BtrMemFree(pszTempBuf);
  1265. return NO_ERROR;
  1266. Error:
  1267. for (i = 0; i < dwParseCount; i++)
  1268. _BtrMemFree(Strings[i]);
  1269. *pdwTokenCount = 0;
  1270. _BtrMemFree(pszTempBuf);
  1271. return dwRet;
  1272. }
  1273. //***************************************************************************
  1274. //
  1275. // FreeTokenArray
  1276. //
  1277. // Cleans up the array returned by ParseIntoTokens.
  1278. //
  1279. //***************************************************************************
  1280. // ok
  1281. void FreeTokenArray(
  1282. DWORD dwCount,
  1283. LPSTR *pszStrings
  1284. )
  1285. {
  1286. for (DWORD i = 0; i < dwCount; i++)
  1287. _BtrMemFree(pszStrings[i]);
  1288. _BtrMemFree(pszStrings);
  1289. }
  1290. //***************************************************************************
  1291. //
  1292. // SIdxKeyTable::ZapPage
  1293. //
  1294. // Empties the page completely of all keys, codes, strings
  1295. //
  1296. //***************************************************************************
  1297. // ok
  1298. void SIdxKeyTable::ZapPage()
  1299. {
  1300. m_pStrPool->Empty();
  1301. m_dwKeyCodesUsed = 0;
  1302. m_dwNumKeys = 0;
  1303. }
  1304. //***************************************************************************
  1305. //
  1306. // SIdxKeyTable::MapFromPage
  1307. //
  1308. // CAUTION!!!
  1309. // The placement of DWORDs and WORDs is arranged to avoid 64-bit
  1310. // alignment faults.
  1311. //
  1312. //***************************************************************************
  1313. // ok
  1314. DWORD SIdxKeyTable::MapFromPage(LPVOID pSrc)
  1315. {
  1316. if (pSrc == 0)
  1317. return ERROR_INVALID_PARAMETER;
  1318. // Header
  1319. //
  1320. // DWORD[0] Signature
  1321. // DWORD[1] Page number
  1322. // DWORD[2] Next Page (always zero)
  1323. // ==================================\
  1324. LPDWORD pDWCast = (LPDWORD) pSrc;
  1325. if (*pDWCast++ != CBTreeFile::PAGE_TYPE_ACTIVE)
  1326. {
  1327. return ERROR_BAD_FORMAT;
  1328. }
  1329. m_dwPageId = *pDWCast++;
  1330. pDWCast++; // Skip the 'next page' field
  1331. // Key lookup table info
  1332. //
  1333. // DWORD[0] Parent Page
  1334. // DWORD[1] Num Keys = n
  1335. // DWORD[n] User Data
  1336. // DWORD[n+1] Child Page Map
  1337. // WORD[n] Key encoding offsets array
  1338. // ======================================
  1339. m_dwParentPageId = *pDWCast++;
  1340. m_dwNumKeys = *pDWCast++;
  1341. // Decide the allocation sizes and build the arrays
  1342. // ================================================
  1343. if (m_dwNumKeys <= const_DefaultArray)
  1344. m_dwKeyLookupTotalSize = const_DefaultArray;
  1345. else
  1346. m_dwKeyLookupTotalSize = m_dwNumKeys;
  1347. m_pdwUserData = (DWORD*) _BtrMemAlloc(m_dwKeyLookupTotalSize * sizeof(DWORD));
  1348. m_pdwChildPageMap = (DWORD*) _BtrMemAlloc((m_dwKeyLookupTotalSize+1) * sizeof(DWORD));
  1349. m_pwKeyLookup = (WORD*) _BtrMemAlloc(m_dwKeyLookupTotalSize * sizeof(WORD));
  1350. if (m_pdwUserData == 0 || m_pdwChildPageMap == 0 || m_pwKeyLookup == 0)
  1351. {
  1352. return ERROR_NOT_ENOUGH_MEMORY;
  1353. }
  1354. // Copy the page info into the arrays
  1355. // ==================================
  1356. memcpy(m_pdwUserData, pDWCast, sizeof(DWORD) * m_dwNumKeys);
  1357. pDWCast += m_dwNumKeys;
  1358. memcpy(m_pdwChildPageMap, pDWCast, sizeof(DWORD) * (m_dwNumKeys+1));
  1359. pDWCast += m_dwNumKeys + 1;
  1360. memcpy(m_pwKeyLookup, pDWCast, sizeof(WORD) * m_dwNumKeys);
  1361. LPWORD pWCast = LPWORD(pDWCast);
  1362. pWCast += m_dwNumKeys;
  1363. // Key encoding table info
  1364. //
  1365. // WORD[0] Num key codes = n
  1366. // WORD[n] Key codes
  1367. // ===========================
  1368. m_dwKeyCodesUsed = (DWORD) *pWCast++;
  1369. if (m_dwKeyCodesUsed <= const_DefaultKeyCodeArray)
  1370. m_dwKeyCodesTotalSize = const_DefaultKeyCodeArray;
  1371. else
  1372. m_dwKeyCodesTotalSize = m_dwKeyCodesUsed;
  1373. m_pwKeyCodes = (WORD*) _BtrMemAlloc(m_dwKeyCodesTotalSize * sizeof(WORD));
  1374. if (!m_pwKeyCodes)
  1375. {
  1376. return ERROR_NOT_ENOUGH_MEMORY;
  1377. }
  1378. memcpy(m_pwKeyCodes, pWCast, sizeof(WORD) * m_dwKeyCodesUsed);
  1379. pWCast += m_dwKeyCodesUsed;
  1380. // String pool
  1381. //
  1382. // WORD[0] Num strings = n
  1383. // WORD[n] Offsets
  1384. //
  1385. // WORD[0] String pool size = n
  1386. // BYTE[n] String pool
  1387. // =============================
  1388. m_pStrPool = new SIdxStringPool;
  1389. if (!m_pStrPool)
  1390. return ERROR_NOT_ENOUGH_MEMORY;
  1391. m_pStrPool->m_dwNumStrings = (DWORD) *pWCast++;
  1392. if (m_pStrPool->m_dwNumStrings <= const_DefaultArray)
  1393. m_pStrPool->m_dwOffsetsSize = const_DefaultArray;
  1394. else
  1395. m_pStrPool->m_dwOffsetsSize = m_pStrPool->m_dwNumStrings;
  1396. m_pStrPool->m_pwOffsets = (WORD *) _BtrMemAlloc(sizeof(WORD)* m_pStrPool->m_dwOffsetsSize);
  1397. if (m_pStrPool->m_pwOffsets == 0)
  1398. return ERROR_NOT_ENOUGH_MEMORY;
  1399. memcpy(m_pStrPool->m_pwOffsets, pWCast, sizeof(WORD)*m_pStrPool->m_dwNumStrings);
  1400. pWCast += m_pStrPool->m_dwNumStrings;
  1401. // String pool setup
  1402. // =================
  1403. m_pStrPool->m_dwPoolUsed = *pWCast++;
  1404. LPSTR pszCast = LPSTR(pWCast);
  1405. if (m_pStrPool->m_dwPoolUsed <= SIdxStringPool::const_DefaultPoolSize)
  1406. m_pStrPool->m_dwPoolTotalSize = SIdxStringPool::const_DefaultPoolSize;
  1407. else
  1408. m_pStrPool->m_dwPoolTotalSize = m_pStrPool->m_dwPoolUsed;
  1409. m_pStrPool->m_pStringPool = (LPSTR) _BtrMemAlloc(m_pStrPool->m_dwPoolTotalSize);
  1410. if (m_pStrPool->m_pStringPool == 0)
  1411. {
  1412. return ERROR_NOT_ENOUGH_MEMORY;
  1413. }
  1414. memcpy(m_pStrPool->m_pStringPool, pszCast, m_pStrPool->m_dwPoolUsed);
  1415. return NO_ERROR;
  1416. }
  1417. //***************************************************************************
  1418. //
  1419. // SIdxKeyTable::MapToPage
  1420. //
  1421. // Copies the info to a linear page. Precondition: the page must
  1422. // be large enough by validating using a prior test to GetRequiredPageMemory.
  1423. //
  1424. //***************************************************************************
  1425. // ok
  1426. DWORD SIdxKeyTable::MapToPage(LPVOID pDest)
  1427. {
  1428. if (pDest == 0)
  1429. return ERROR_INVALID_PARAMETER;
  1430. // Header
  1431. //
  1432. // DWORD[0] Signature
  1433. // DWORD[1] Page number
  1434. // DWORD[2] Next Page (always zero)
  1435. // ==================================\
  1436. LPDWORD pDWCast = (LPDWORD) pDest;
  1437. *pDWCast++ = CBTreeFile::PAGE_TYPE_ACTIVE;
  1438. *pDWCast++ = m_dwPageId;
  1439. *pDWCast++ = 0; // Unused 'next page' field
  1440. // Key lookup table info
  1441. //
  1442. // DWORD[0] Parent Page
  1443. // DWORD[1] Num Keys = n
  1444. // DWORD[n] User Data
  1445. // DWORD[n+1] Child Page Map
  1446. // WORD[n] Key encoding offsets array
  1447. // ======================================
  1448. *pDWCast++ = m_dwParentPageId;
  1449. *pDWCast++ = m_dwNumKeys;
  1450. // Decide the allocation sizes and build the arrays
  1451. // ================================================
  1452. memcpy(pDWCast, m_pdwUserData, sizeof(DWORD) * m_dwNumKeys);
  1453. pDWCast += m_dwNumKeys;
  1454. memcpy(pDWCast, m_pdwChildPageMap, sizeof(DWORD) * (m_dwNumKeys+1));
  1455. pDWCast += m_dwNumKeys + 1;
  1456. memcpy(pDWCast, m_pwKeyLookup, sizeof(WORD) * m_dwNumKeys);
  1457. LPWORD pWCast = LPWORD(pDWCast);
  1458. pWCast += m_dwNumKeys;
  1459. // Key encoding table info
  1460. //
  1461. // WORD[0] Num key codes = n
  1462. // WORD[n] Key codes
  1463. // ===========================
  1464. *pWCast++ = WORD(m_dwKeyCodesUsed);
  1465. memcpy(pWCast, m_pwKeyCodes, sizeof(WORD) * m_dwKeyCodesUsed);
  1466. pWCast += m_dwKeyCodesUsed;
  1467. // String pool
  1468. //
  1469. // WORD[0] Num strings = n
  1470. // WORD[n] Offsets
  1471. //
  1472. // WORD[0] String pool size = n
  1473. // BYTE[n] String pool
  1474. // =============================
  1475. *pWCast++ = WORD(m_pStrPool->m_dwNumStrings);
  1476. memcpy(pWCast, m_pStrPool->m_pwOffsets, sizeof(WORD)*m_pStrPool->m_dwNumStrings);
  1477. pWCast += m_pStrPool->m_dwNumStrings;
  1478. *pWCast++ = WORD(m_pStrPool->m_dwPoolUsed);
  1479. LPSTR pszCast = LPSTR(pWCast);
  1480. memcpy(pszCast, m_pStrPool->m_pStringPool, m_pStrPool->m_dwPoolUsed);
  1481. return NO_ERROR;
  1482. }
  1483. //***************************************************************************
  1484. //
  1485. // SIdxKeyTable::Create
  1486. //
  1487. // Does a default create
  1488. //
  1489. //***************************************************************************
  1490. // ok
  1491. DWORD SIdxKeyTable::Create(
  1492. DWORD dwPageId,
  1493. OUT SIdxKeyTable **pNewInst
  1494. )
  1495. {
  1496. SIdxKeyTable *p = new SIdxKeyTable;
  1497. if (!p)
  1498. return ERROR_NOT_ENOUGH_MEMORY;
  1499. // Set up default string pool, arrays, etc.
  1500. // ========================================
  1501. p->m_dwPageId = dwPageId;
  1502. p->m_dwNumKeys = 0;
  1503. p->m_pwKeyLookup = (WORD*) _BtrMemAlloc(const_DefaultArray * sizeof(WORD));
  1504. p->m_dwKeyLookupTotalSize = const_DefaultArray;
  1505. p->m_pwKeyCodes = (WORD*) _BtrMemAlloc(const_DefaultArray * sizeof(WORD));
  1506. p->m_dwKeyCodesTotalSize = const_DefaultArray;
  1507. p->m_dwKeyCodesUsed = 0;
  1508. p->m_pdwUserData = (DWORD*) _BtrMemAlloc(const_DefaultArray * sizeof(DWORD));
  1509. p->m_pdwChildPageMap = (DWORD*) _BtrMemAlloc((const_DefaultArray+1) * sizeof(DWORD));
  1510. // Set up string pool.
  1511. // ===================
  1512. p->m_pStrPool = new SIdxStringPool;
  1513. if (p->m_pStrPool != NULL)
  1514. {
  1515. p->m_pStrPool->m_pwOffsets = (WORD*) _BtrMemAlloc(const_DefaultArray * sizeof(WORD));
  1516. p->m_pStrPool->m_dwOffsetsSize = const_DefaultArray;
  1517. p->m_pStrPool->m_pStringPool = (LPSTR) _BtrMemAlloc(SIdxStringPool::const_DefaultPoolSize);
  1518. p->m_pStrPool->m_dwPoolTotalSize = SIdxStringPool::const_DefaultPoolSize;
  1519. }
  1520. // Check all pointers. If any are null, error out.
  1521. // ================================================
  1522. if (
  1523. p->m_pwKeyLookup == NULL ||
  1524. p->m_pwKeyCodes == NULL ||
  1525. p->m_pdwUserData == NULL ||
  1526. p->m_pdwChildPageMap == NULL ||
  1527. p->m_pStrPool == NULL ||
  1528. p->m_pStrPool->m_pwOffsets == NULL ||
  1529. p->m_pStrPool->m_pStringPool == NULL
  1530. )
  1531. {
  1532. delete p;
  1533. *pNewInst = 0;
  1534. return ERROR_NOT_ENOUGH_MEMORY;
  1535. }
  1536. // Return good object to caller.
  1537. // =============================
  1538. p->AddRef();
  1539. *pNewInst = p;
  1540. return NO_ERROR;
  1541. }
  1542. //***************************************************************************
  1543. //
  1544. // SIdxStringPool::~SIdxStringPool
  1545. //
  1546. //***************************************************************************
  1547. // ok
  1548. SIdxStringPool::~SIdxStringPool()
  1549. {
  1550. if (m_pwOffsets)
  1551. _BtrMemFree(m_pwOffsets);
  1552. m_pwOffsets = 0;
  1553. if (m_pStringPool)
  1554. _BtrMemFree(m_pStringPool); // Pointer to string pool
  1555. m_pStringPool = 0;
  1556. }
  1557. //***************************************************************************
  1558. //
  1559. // SIdxKeyTable::Create
  1560. //
  1561. // Does a default create
  1562. //
  1563. //***************************************************************************
  1564. // ok
  1565. DWORD SIdxKeyTable::Create(
  1566. IN LPVOID pPage,
  1567. OUT SIdxKeyTable **pNewInst
  1568. )
  1569. {
  1570. SIdxKeyTable *p = new SIdxKeyTable;
  1571. if (!p)
  1572. return ERROR_NOT_ENOUGH_MEMORY;
  1573. DWORD dwRes = p->MapFromPage(pPage);
  1574. if (dwRes)
  1575. {
  1576. *pNewInst = 0;
  1577. delete p;
  1578. return dwRes;
  1579. }
  1580. p->AddRef();
  1581. *pNewInst = p;
  1582. return NO_ERROR;
  1583. }
  1584. //***************************************************************************
  1585. //
  1586. // SIdxKeyTable::AddRef
  1587. //
  1588. //***************************************************************************
  1589. // ok
  1590. DWORD SIdxKeyTable::AddRef()
  1591. {
  1592. InterlockedIncrement((LONG *) &m_dwRefCount);
  1593. return m_dwRefCount;
  1594. }
  1595. //***************************************************************************
  1596. //
  1597. // SIdxKeyTable::Release
  1598. //
  1599. //***************************************************************************
  1600. // ok
  1601. DWORD SIdxKeyTable::Release()
  1602. {
  1603. DWORD dwNewCount = InterlockedDecrement((LONG *) &m_dwRefCount);
  1604. if (0 != dwNewCount)
  1605. return dwNewCount;
  1606. delete this;
  1607. return 0;
  1608. }
  1609. //***************************************************************************
  1610. //
  1611. // SIdxKeyTable::Cleanup
  1612. //
  1613. // Does a consistency check of the key encoding table and cleans up the
  1614. // string pool if any strings aren't being referenced.
  1615. //
  1616. //***************************************************************************
  1617. // ok
  1618. DWORD SIdxKeyTable::Cleanup()
  1619. {
  1620. // See if all string pool codes are used in key code table.
  1621. // If not, remove the string pool code.
  1622. // =======================================================
  1623. DWORD dwLastId = m_pStrPool->GetLastId();
  1624. BOOL *pCheck = (BOOL*) _BtrMemAlloc(sizeof(BOOL) * dwLastId);
  1625. if (!pCheck)
  1626. return ERROR_NOT_ENOUGH_MEMORY;
  1627. while (1)
  1628. {
  1629. if (m_pStrPool->GetNumStrings() == 0 || m_dwKeyCodesUsed == 0 || m_dwNumKeys == 0)
  1630. {
  1631. ZapPage();
  1632. break;
  1633. }
  1634. dwLastId = m_pStrPool->GetLastId();
  1635. memset(pCheck, 0, sizeof(BOOL)*dwLastId); // Mark all codes as 'unused'
  1636. // Move through all key codes. If we delete a key encoding, there
  1637. // may be a code in the string pool not used by the encoding.
  1638. // What we have to do is set the pCheck array to TRUE for each
  1639. // code encountered. If any have FALSE when we are done, we have
  1640. // an unused code.
  1641. WORD wCurrentSequence = 0;
  1642. for (DWORD i = 0; i < m_dwKeyCodesUsed; i++)
  1643. {
  1644. if (wCurrentSequence == 0) // Skip the length WORD
  1645. {
  1646. wCurrentSequence = m_pwKeyCodes[i];
  1647. continue;
  1648. }
  1649. else // A string pool code
  1650. pCheck[m_pwKeyCodes[i]] = TRUE;
  1651. wCurrentSequence--;
  1652. }
  1653. // Now the pCheck array contains deep within its psyche
  1654. // the knowledge of whether or not all string pool codes
  1655. // were used TRUE for referenced ones, FALSE for those
  1656. // not referenced. Let's look through it and see!
  1657. DWORD dwUsed = 0, dwUnused = 0;
  1658. for (i = 0; i < dwLastId; i++)
  1659. {
  1660. if (pCheck[i] == FALSE)
  1661. {
  1662. dwUnused++;
  1663. // Yikes! A lonely, unused string code. Let's be merciful
  1664. // and zap it before it knows the difference.
  1665. // =======================================================
  1666. int nAdj = 0;
  1667. m_pStrPool->DeleteStr(WORD(i), &nAdj);
  1668. AdjustKeyCodes(WORD(i), nAdj);
  1669. break;
  1670. }
  1671. else
  1672. dwUsed++;
  1673. }
  1674. if (dwUnused == 0)
  1675. break;
  1676. }
  1677. _BtrMemFree(pCheck);
  1678. return NO_ERROR;
  1679. }
  1680. //***************************************************************************
  1681. //
  1682. // SIdxKeyTable::AdjustKeyCodes
  1683. //
  1684. //***************************************************************************
  1685. // ok
  1686. void SIdxKeyTable::AdjustKeyCodes(
  1687. WORD wID,
  1688. int nAdjustment
  1689. )
  1690. {
  1691. // Adjust all key codes starting with wID by the amount of the
  1692. // adjustment, skipping length bytes.
  1693. // =============================================================
  1694. WORD wCurrentSequence = 0;
  1695. for (DWORD i = 0; i < m_dwKeyCodesUsed; i++)
  1696. {
  1697. if (wCurrentSequence == 0)
  1698. {
  1699. wCurrentSequence = m_pwKeyCodes[i];
  1700. continue;
  1701. }
  1702. else
  1703. {
  1704. if (m_pwKeyCodes[i] >= wID)
  1705. m_pwKeyCodes[i] = m_pwKeyCodes[i] + nAdjustment;
  1706. }
  1707. wCurrentSequence--;
  1708. }
  1709. }
  1710. //***************************************************************************
  1711. //
  1712. // SIdxKeyTable::AddKey
  1713. //
  1714. // Adds a string to the table at position <wID>. Assumes FindString
  1715. // was called first to get the correct location.
  1716. //
  1717. // Precondition: <pszStr> is valid, and <wID> is correct.
  1718. //
  1719. // Return codes:
  1720. //
  1721. // ERROR_OUT_OF_MEMORY
  1722. // NO_ERROR
  1723. // ERROR_INVALID_PARAMETER // Too many slashes in key
  1724. //
  1725. //***************************************************************************
  1726. // ok
  1727. DWORD SIdxKeyTable::AddKey(
  1728. LPSTR pszStr,
  1729. WORD wKeyID,
  1730. DWORD dwUserData
  1731. )
  1732. {
  1733. DWORD dwRes, dwRet;
  1734. LPVOID pTemp = 0;
  1735. LPSTR pszTemp = 0;
  1736. DWORD dwLen, i;
  1737. DWORD dwTokenCount = 0;
  1738. WORD *pwTokenIDs = 0;
  1739. DWORD dwNumNewTokens = 0;
  1740. LPSTR *pszStrings = 0;
  1741. DWORD dwToBeMoved;
  1742. DWORD dwStartingOffset;
  1743. // Set up some temp working arrays.
  1744. // ================================
  1745. if (!pszStr)
  1746. return ERROR_INVALID_PARAMETER;
  1747. dwLen = strlen(pszStr);
  1748. if (dwLen == 0)
  1749. return ERROR_INVALID_PARAMETER;
  1750. pszTemp = (LPSTR) _BtrMemAlloc(dwLen+1);
  1751. if (!pszTemp)
  1752. {
  1753. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1754. goto Exit;
  1755. }
  1756. // Ensure there is enough room.
  1757. // ============================
  1758. if (m_dwKeyLookupTotalSize == m_dwNumKeys)
  1759. {
  1760. // Expand the array.
  1761. DWORD dwNewSize = m_dwKeyLookupTotalSize * 2;
  1762. pTemp = _BtrMemReAlloc(m_pwKeyLookup, dwNewSize * sizeof(WORD));
  1763. if (!pTemp)
  1764. {
  1765. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1766. goto Exit;
  1767. }
  1768. m_dwKeyLookupTotalSize = dwNewSize;
  1769. m_pwKeyLookup = PWORD(pTemp);
  1770. // Expand user data.
  1771. pTemp = _BtrMemReAlloc(m_pdwUserData, dwNewSize * sizeof(DWORD));
  1772. if (!pTemp)
  1773. {
  1774. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1775. goto Exit;
  1776. }
  1777. m_pdwUserData = (DWORD *) pTemp;
  1778. // Expand child page map.
  1779. pTemp = _BtrMemReAlloc(m_pdwChildPageMap, (dwNewSize + 1) * sizeof(DWORD));
  1780. if (!pTemp)
  1781. {
  1782. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1783. goto Exit;
  1784. }
  1785. m_pdwChildPageMap = (DWORD *) pTemp;
  1786. }
  1787. // Parse the string into backslash separated tokens.
  1788. // =================================================
  1789. dwRes = ParseIntoTokens(pszStr, &dwTokenCount, &pszStrings);
  1790. if (dwRes)
  1791. {
  1792. dwRet = dwRes;
  1793. goto Exit;
  1794. }
  1795. // Allocate an array to hold the IDs of the tokens in the string.
  1796. // ==============================================================
  1797. pwTokenIDs = (WORD *) _BtrMemAlloc(sizeof(WORD) * dwTokenCount);
  1798. if (pwTokenIDs == 0)
  1799. {
  1800. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1801. goto Exit;
  1802. }
  1803. // Work through the tokens and add them to the pool & key encoding table.
  1804. // =============================================================
  1805. for (i = 0; i < dwTokenCount; i++)
  1806. {
  1807. LPSTR pszTok = pszStrings[i];
  1808. // See if token exists, if not add it.
  1809. // ===================================
  1810. WORD wID = 0;
  1811. dwRes = m_pStrPool->FindStr(pszTok, &wID, 0);
  1812. if (dwRes == NO_ERROR)
  1813. {
  1814. // Found it
  1815. pwTokenIDs[dwNumNewTokens++] = wID;
  1816. }
  1817. else if (dwRes == ERROR_NOT_FOUND)
  1818. {
  1819. int nAdjustment = 0;
  1820. dwRes = m_pStrPool->AddStr(pszTok, wID, &nAdjustment);
  1821. if (dwRes)
  1822. {
  1823. dwRet = dwRes;
  1824. goto Exit;
  1825. }
  1826. // Adjust string IDs because of the addition.
  1827. // All existing ones with the same ID or higher
  1828. // must be adjusted upwards.
  1829. if (nAdjustment)
  1830. {
  1831. AdjustKeyCodes(wID, nAdjustment);
  1832. for (DWORD i2 = 0; i2 < dwNumNewTokens; i2++)
  1833. {
  1834. if (pwTokenIDs[i2] >= wID)
  1835. pwTokenIDs[i2] = pwTokenIDs[i2] + nAdjustment;
  1836. }
  1837. }
  1838. // Adjust current tokens to accomodate new
  1839. pwTokenIDs[dwNumNewTokens++] = wID;
  1840. }
  1841. else
  1842. {
  1843. dwRet = dwRes;
  1844. goto Exit;
  1845. }
  1846. }
  1847. // Now we know the encodings. Add them to the key encoding table.
  1848. // First make sure that there is enough room in the table.
  1849. // ===============================================================
  1850. if (m_dwKeyCodesTotalSize - m_dwKeyCodesUsed < dwNumNewTokens + 1)
  1851. {
  1852. DWORD dwNewSize = m_dwKeyCodesTotalSize * 2 + dwNumNewTokens + 1;
  1853. PWORD pTemp2 = (PWORD) _BtrMemReAlloc(m_pwKeyCodes, dwNewSize * sizeof(WORD));
  1854. if (!pTemp2)
  1855. {
  1856. dwRet = ERROR_NOT_ENOUGH_MEMORY;
  1857. goto Exit;
  1858. }
  1859. m_pwKeyCodes = pTemp2;
  1860. m_dwKeyCodesTotalSize = dwNewSize;
  1861. }
  1862. dwStartingOffset = m_dwKeyCodesUsed;
  1863. m_pwKeyCodes[m_dwKeyCodesUsed++] = (WORD) dwNumNewTokens; // First WORD is count of tokens
  1864. for (i = 0; i < dwNumNewTokens; i++) // Encoded tokens
  1865. m_pwKeyCodes[m_dwKeyCodesUsed++] = pwTokenIDs[i];
  1866. // Now, add in the new key lookup by inserting it into the array.
  1867. // ==============================================================
  1868. dwToBeMoved = m_dwNumKeys - wKeyID;
  1869. if (dwToBeMoved)
  1870. {
  1871. memmove(&m_pwKeyLookup[wKeyID+1], &m_pwKeyLookup[wKeyID], sizeof(WORD)*dwToBeMoved);
  1872. memmove(&m_pdwUserData[wKeyID+1], &m_pdwUserData[wKeyID], sizeof(DWORD)*dwToBeMoved);
  1873. memmove(&m_pdwChildPageMap[wKeyID+1], &m_pdwChildPageMap[wKeyID], (sizeof(DWORD))*(dwToBeMoved+1));
  1874. }
  1875. m_pwKeyLookup[wKeyID] = (WORD) dwStartingOffset;
  1876. m_pdwUserData[wKeyID] = dwUserData;
  1877. m_dwNumKeys++;
  1878. dwRet = NO_ERROR;
  1879. // Cleanup code.
  1880. // =============
  1881. Exit:
  1882. if (pszTemp)
  1883. _BtrMemFree(pszTemp);
  1884. FreeTokenArray(dwTokenCount, pszStrings);
  1885. if (pwTokenIDs)
  1886. _BtrMemFree(pwTokenIDs);
  1887. return dwRet;
  1888. }
  1889. //***************************************************************************
  1890. //
  1891. // SIdxKeyTable::RemoveKey
  1892. //
  1893. // Precondition: <wID> is the valid target
  1894. //
  1895. //***************************************************************************
  1896. // ok
  1897. DWORD SIdxKeyTable::RemoveKey(
  1898. WORD wID
  1899. )
  1900. {
  1901. // Find the key code sequence and remove it.
  1902. // =========================================
  1903. WORD wKeyCodeStart = m_pwKeyLookup[wID];
  1904. DWORD dwToBeMoved = m_dwNumKeys - DWORD(wID) - 1;
  1905. if (dwToBeMoved)
  1906. {
  1907. memmove(&m_pwKeyLookup[wID], &m_pwKeyLookup[wID+1], sizeof(WORD)*dwToBeMoved);
  1908. memmove(&m_pdwUserData[wID], &m_pdwUserData[wID+1], sizeof(DWORD)*dwToBeMoved);
  1909. memmove(&m_pdwChildPageMap[wID], &m_pdwChildPageMap[wID+1], sizeof(DWORD)*(dwToBeMoved+1));
  1910. }
  1911. m_dwNumKeys--;
  1912. // Zap the key encoding table to remove references to this key.
  1913. // ============================================================
  1914. WORD wCount = m_pwKeyCodes[wKeyCodeStart]+1;
  1915. dwToBeMoved = m_dwKeyCodesUsed - (wKeyCodeStart + wCount);
  1916. if (dwToBeMoved)
  1917. memmove(&m_pwKeyCodes[wKeyCodeStart], &m_pwKeyCodes[wKeyCodeStart + wCount], sizeof(WORD)*dwToBeMoved);
  1918. m_dwKeyCodesUsed -= wCount;
  1919. // Adjust all zapped key codes referenced by key lookup table.
  1920. // ===========================================================
  1921. for (DWORD i = 0; i < m_dwNumKeys; i++)
  1922. {
  1923. if (m_pwKeyLookup[i] >= wKeyCodeStart)
  1924. m_pwKeyLookup[i] -= wCount;
  1925. }
  1926. // Now check the string pool & key encoding table for
  1927. // unreferenced strings thanks to the above tricks
  1928. // and clean up the mess left behind!!
  1929. // ==================================================
  1930. return Cleanup();
  1931. }
  1932. //***************************************************************************
  1933. //
  1934. // Compares the literal string in <pszSearchKey> against the encoded
  1935. // string at <nID>. Returns the same value as strcmp().
  1936. //
  1937. // This is done by decoding the compressed string, token by token, and
  1938. // comparing each character to that in the search string.
  1939. //
  1940. //***************************************************************************
  1941. // ok
  1942. int SIdxKeyTable::KeyStrCompare(
  1943. LPSTR pszSearchKey,
  1944. WORD wID
  1945. )
  1946. {
  1947. LPSTR pszTrace = pszSearchKey;
  1948. WORD dwEncodingOffs = m_pwKeyLookup[wID];
  1949. WORD wNumTokens = m_pwKeyCodes[dwEncodingOffs];
  1950. WORD wStrId = m_pwKeyCodes[++dwEncodingOffs];
  1951. LPSTR pszDecoded = m_pStrPool->GetStrById(wStrId);
  1952. wNumTokens--;
  1953. int nRes;
  1954. while (1)
  1955. {
  1956. int nTraceChar = *pszTrace++;
  1957. int nCodedChar = *pszDecoded++;
  1958. if (nCodedChar == 0 && wNumTokens)
  1959. {
  1960. pszDecoded = m_pStrPool->GetStrById(m_pwKeyCodes[++dwEncodingOffs]);
  1961. wNumTokens--;
  1962. nCodedChar = '\\';
  1963. }
  1964. nRes = nTraceChar - nCodedChar;
  1965. if (nRes || (nTraceChar == 0 && nCodedChar == 0))
  1966. return nRes;
  1967. }
  1968. // Identical strings
  1969. return 0;
  1970. }
  1971. //***************************************************************************
  1972. //
  1973. // SIdxKeyTable::FindKey
  1974. //
  1975. // Finds the key in the key table, if present. If not, returns
  1976. // ERROR_NOT_FOUND and <pID> set to the point where it would be if
  1977. // later inserted.
  1978. //
  1979. // Return values:
  1980. // ERROR_NOT_FOUND
  1981. // NO_ERROR
  1982. //
  1983. //***************************************************************************
  1984. // ready for test
  1985. DWORD SIdxKeyTable::FindKey(
  1986. LPSTR pszSearchKey,
  1987. WORD *pID
  1988. )
  1989. {
  1990. if (pszSearchKey == 0 || *pszSearchKey == 0 || pID == 0)
  1991. return ERROR_INVALID_PARAMETER;
  1992. // Binary search the key table.
  1993. // ============================
  1994. if (m_dwNumKeys == 0)
  1995. {
  1996. *pID = 0;
  1997. return ERROR_NOT_FOUND;
  1998. }
  1999. int nPosition = 0;
  2000. int l = 0, u = int(m_dwNumKeys) - 1;
  2001. while (l <= u)
  2002. {
  2003. int m = (l + u) / 2;
  2004. int nRes;
  2005. // m is the current key to consider 0...n-1
  2006. nRes = KeyStrCompare(pszSearchKey, WORD(m));
  2007. // Decide which way to cut the array in half.
  2008. // ==========================================
  2009. if (nRes < 0)
  2010. {
  2011. u = m - 1;
  2012. nPosition = u + 1;
  2013. }
  2014. else if (nRes > 0)
  2015. {
  2016. l = m + 1;
  2017. nPosition = l;
  2018. }
  2019. else
  2020. {
  2021. // If here, we found the darn thing. Life is good.
  2022. // Populate the key unit.
  2023. // ================================================
  2024. *pID = WORD(m);
  2025. return NO_ERROR;
  2026. }
  2027. }
  2028. // Not found, if here. We record where the key should have been
  2029. // and tell the user the unhappy news.
  2030. // ==============================================================
  2031. *pID = WORD(nPosition); // The key would have been 'here'
  2032. return ERROR_NOT_FOUND;
  2033. }
  2034. //***************************************************************************
  2035. //
  2036. //***************************************************************************
  2037. // untested
  2038. DWORD SIdxKeyTable::Dump(FILE *f, DWORD *pdwKeys)
  2039. {
  2040. fprintf(f, "\t|---Begin Key Table Dump---\n");
  2041. fprintf(f, "\t| m_dwPageId = %d (0x%X)\n", m_dwPageId, m_dwPageId);
  2042. fprintf(f, "\t| m_dwParentPageId = %d\n", m_dwParentPageId);
  2043. fprintf(f, "\t| m_dwNumKeys = %d\n", m_dwNumKeys);
  2044. fprintf(f, "\t| m_pwKeyLookup = 0x%p\n", m_pwKeyLookup);
  2045. fprintf(f, "\t| m_dwKeyLookupTotalSize = %d\n", m_dwKeyLookupTotalSize);
  2046. fprintf(f, "\t| m_pwKeyCodes = 0x%p\n", m_pwKeyCodes);
  2047. fprintf(f, "\t| m_dwKeyCodesTotalSize = %d\n", m_dwKeyCodesTotalSize);
  2048. fprintf(f, "\t| m_dwKeyCodesUsed = %d\n", m_dwKeyCodesUsed);
  2049. fprintf(f, "\t| Required Page Memory = %d\n", GetRequiredPageMemory());
  2050. fprintf(f, "\t| --Key Lookup Table\n");
  2051. if (pdwKeys)
  2052. *pdwKeys = m_dwNumKeys;
  2053. for (DWORD i = 0; i < m_dwNumKeys; i++)
  2054. {
  2055. fprintf(f, "\t| * Left Child Page ------------------------> %d\n", m_pdwChildPageMap[i]);
  2056. fprintf(f, "\t| KeyID[%d] = offset %d\n", i, m_pwKeyLookup[i]);
  2057. fprintf(f, "\t| User Data=%d\n", m_pdwUserData[i]);
  2058. WORD wKeyEncodingOffset = m_pwKeyLookup[i];
  2059. WORD wEncodingUnits = m_pwKeyCodes[wKeyEncodingOffset];
  2060. int nPass = 0;
  2061. fprintf(f, "\t | Key=");
  2062. for (DWORD i2 = 0; i2 < DWORD(wEncodingUnits); i2++)
  2063. {
  2064. WORD wCode = m_pwKeyCodes[wKeyEncodingOffset + 1 + i2];
  2065. if (nPass)
  2066. fprintf(f, "\\");
  2067. fprintf(f, "%s", m_pStrPool->GetStrById(wCode));
  2068. nPass++;
  2069. }
  2070. fprintf(f, "\n");
  2071. fprintf(f, "\t| Num encoding units = %d\n", wEncodingUnits);
  2072. for (DWORD i2 = 0; i2 < DWORD(wEncodingUnits); i2++)
  2073. {
  2074. WORD wCode = m_pwKeyCodes[wKeyEncodingOffset + 1 + i2];
  2075. fprintf(f, "\t | KeyCode = %d\n", wCode);
  2076. }
  2077. }
  2078. fprintf(f, "\t| * Rightmost child page -------------------> %d\n", m_pdwChildPageMap[i]);
  2079. fprintf(f, "\t|---\n");
  2080. #ifdef EXTENDED_STRING_TABLE_DUMP
  2081. fprintf(f, "\t|---Key Encoding Table\n");
  2082. WORD wCurrentSequence = 0;
  2083. for (i = 0; i < m_dwKeyCodesUsed; i++)
  2084. {
  2085. if (wCurrentSequence == 0)
  2086. {
  2087. wCurrentSequence = m_pwKeyCodes[i];
  2088. fprintf(f, "\t| KeyCode[%d] = %d <count>\n", i, m_pwKeyCodes[i]);
  2089. continue;
  2090. }
  2091. else
  2092. fprintf(f, "\t| KeyCode[%d] = %d <%s>\n", i, m_pwKeyCodes[i],
  2093. m_pStrPool->GetStrById(m_pwKeyCodes[i]));
  2094. wCurrentSequence--;
  2095. }
  2096. fprintf(f, "\t|---End Key Encoding Table---\n");
  2097. m_pStrPool->Dump(f);
  2098. #endif
  2099. return 0;
  2100. }
  2101. //***************************************************************************
  2102. //
  2103. // SIdxStringPool::Dump
  2104. //
  2105. // Dumps the string pool
  2106. //
  2107. //***************************************************************************
  2108. // tested
  2109. DWORD SIdxStringPool::Dump(FILE *f)
  2110. {
  2111. /*
  2112. try
  2113. {
  2114. fprintf(f, "\t\t|| ---String Pool Dump---\n");
  2115. fprintf(f, "\t\t|| m_dwNumStrings = %d\n", m_dwNumStrings);
  2116. fprintf(f, "\t\t|| m_pwOffsets = 0x%p\n", m_pwOffsets);
  2117. fprintf(f, "\t\t|| m_dwOffsetsSize = %d\n", m_dwOffsetsSize);
  2118. fprintf(f, "\t\t|| m_pStringPool = 0x%p\n", m_pStringPool);
  2119. fprintf(f, "\t\t|| m_dwPoolTotalSize = %d\n", m_dwPoolTotalSize);
  2120. fprintf(f, "\t\t|| m_dwPoolUsed = %d\n", m_dwPoolUsed);
  2121. fprintf(f, "\t\t|| --Contents of offsets array--\n");
  2122. for (DWORD ix = 0; ix < m_dwNumStrings; ix++)
  2123. {
  2124. fprintf(f, "\t\t|| String[%d] = offset %d Value=<%s>\n",
  2125. ix, m_pwOffsets[ix], m_pStringPool+m_pwOffsets[ix]);
  2126. }
  2127. #ifdef EXTENDED_STRING_TABLE_DUMP
  2128. fprintf(f, "\t\t|| --String table--\n");
  2129. for (ix = 0; ix < m_dwPoolTotalSize; ix += 20)
  2130. {
  2131. fprintf(f, "\t\t || %4d ", ix);
  2132. for (int nSubcount = 0; nSubcount < 20; nSubcount++)
  2133. {
  2134. if (nSubcount + ix >= m_dwPoolTotalSize)
  2135. continue;
  2136. char c = m_pStringPool[ix+nSubcount];
  2137. fprintf(f, "%02x ", c);
  2138. }
  2139. for (int nSubcount = 0; nSubcount < 20; nSubcount++)
  2140. {
  2141. if (nSubcount + ix >= m_dwPoolTotalSize)
  2142. continue;
  2143. char c = m_pStringPool[ix+nSubcount];
  2144. if (c < 32)
  2145. {
  2146. c = '.';
  2147. }
  2148. fprintf(f, "%c ", c);
  2149. }
  2150. fprintf(f, "\n");
  2151. }
  2152. #endif
  2153. fprintf(f, "\t\t|| ---End of String Pool Dump\n");
  2154. }
  2155. catch(...)
  2156. {
  2157. printf("Exception during dump\n");
  2158. }
  2159. */
  2160. return 0;
  2161. }
  2162. //***************************************************************************
  2163. //
  2164. // CBTree::Init
  2165. //
  2166. //***************************************************************************
  2167. //
  2168. DWORD CBTree::Init(
  2169. CBTreeFile *pSrc
  2170. )
  2171. {
  2172. DWORD dwRes;
  2173. if (pSrc == 0)
  2174. return ERROR_INVALID_PARAMETER;
  2175. // Read the logical root page, if any. If the index is just
  2176. // being created, create the root index page.
  2177. m_pSrc = pSrc;
  2178. m_pRoot = 0;
  2179. DWORD dwRoot = m_pSrc->GetRootPage();
  2180. if (dwRoot == 0)
  2181. {
  2182. LPDWORD pNewPage = 0;
  2183. dwRes = m_pSrc->NewPage((LPVOID *) &pNewPage);
  2184. if (dwRes)
  2185. return dwRes;
  2186. DWORD dwPageNum = pNewPage[CBTreeFile::OFFSET_PAGE_ID];
  2187. _BtrMemFree(pNewPage);
  2188. dwRes = SIdxKeyTable::Create(dwPageNum, &m_pRoot);
  2189. if (dwRes)
  2190. return dwRes;
  2191. dwRes = m_pSrc->SetRootPage(dwPageNum);
  2192. if (dwRes)
  2193. return dwRes;
  2194. dwRes = WriteIdxPage(m_pRoot);
  2195. if (dwRes)
  2196. return dwRes;
  2197. }
  2198. else
  2199. {
  2200. // Retrieve existing root
  2201. LPVOID pPage = 0;
  2202. dwRes = m_pSrc->GetPage(dwRoot, &pPage);
  2203. if (dwRes)
  2204. return dwRes;
  2205. dwRes = SIdxKeyTable::Create(pPage, &m_pRoot);
  2206. _BtrMemFree(pPage);
  2207. if (dwRes)
  2208. return dwRes;
  2209. }
  2210. return dwRes;
  2211. }
  2212. //***************************************************************************
  2213. //
  2214. // CBTree::CBTree
  2215. //
  2216. //***************************************************************************
  2217. //
  2218. CBTree::CBTree()
  2219. {
  2220. m_pSrc = 0;
  2221. m_pRoot = 0;
  2222. m_lGeneration = 0;
  2223. }
  2224. //***************************************************************************
  2225. //
  2226. // CBTree::~CBTree
  2227. //
  2228. //***************************************************************************
  2229. //
  2230. CBTree::~CBTree()
  2231. {
  2232. if (m_pSrc || m_pRoot)
  2233. {
  2234. Shutdown(WMIDB_SHUTDOWN_NET_STOP);
  2235. }
  2236. }
  2237. //***************************************************************************
  2238. //
  2239. // CBTree::Shutdown
  2240. //
  2241. //***************************************************************************
  2242. //
  2243. DWORD CBTree::Shutdown(DWORD dwShutDownFlags)
  2244. {
  2245. if (m_pRoot)
  2246. {
  2247. m_pRoot->Release();
  2248. m_pRoot = 0;
  2249. }
  2250. return ERROR_SUCCESS;
  2251. }
  2252. //***************************************************************************
  2253. //
  2254. // CBTree::InsertKey
  2255. //
  2256. // Inserts the key+data into the tree. Most of the work is done
  2257. // in InsertPhase2().
  2258. //
  2259. //***************************************************************************
  2260. // ok
  2261. DWORD CBTree::InsertKey(
  2262. IN LPSTR pszKey,
  2263. DWORD dwValue
  2264. )
  2265. {
  2266. DWORD dwRes;
  2267. if (m_pRoot == NULL)
  2268. {
  2269. dwRes = InvalidateCache();
  2270. if (dwRes != ERROR_SUCCESS)
  2271. return dwRes;
  2272. }
  2273. WORD wID;
  2274. SIdxKeyTable *pIdx = 0;
  2275. LONG StackPtr = -1;
  2276. DWORD *Stack = new DWORD[CBTreeIterator::const_MaxStack];
  2277. if (Stack == NULL)
  2278. return ERROR_NOT_ENOUGH_MEMORY;
  2279. std::auto_ptr <DWORD> _autodelete(Stack);
  2280. if (pszKey == 0 || *pszKey == 0)
  2281. return ERROR_INVALID_PARAMETER;
  2282. dwRes = Search(pszKey, &pIdx, &wID, Stack, StackPtr);
  2283. if (dwRes == 0)
  2284. {
  2285. // Ooops. Aleady exists. We can't insert it.
  2286. // ===========================================
  2287. pIdx->Release();
  2288. return ERROR_ALREADY_EXISTS;
  2289. }
  2290. if (dwRes != ERROR_NOT_FOUND)
  2291. return dwRes;
  2292. // If here, we can indeed add it.
  2293. // ==============================
  2294. dwRes = InsertPhase2(pIdx, wID, pszKey, dwValue, Stack, StackPtr);
  2295. ReleaseIfNotNULL(pIdx);
  2296. return dwRes;
  2297. }
  2298. //***************************************************************************
  2299. //
  2300. // CBTree::ComputeLoad
  2301. //
  2302. //***************************************************************************
  2303. //
  2304. DWORD CBTree::ComputeLoad(
  2305. SIdxKeyTable *pKT
  2306. )
  2307. {
  2308. DWORD dwMem = pKT->GetRequiredPageMemory();
  2309. DWORD dwLoad = dwMem * 100 / m_pSrc->GetPageSize();
  2310. return dwLoad;
  2311. }
  2312. //***************************************************************************
  2313. //
  2314. // CBTree::Search
  2315. //
  2316. // The actual search occurs here. Descends through the page mechanism.
  2317. //
  2318. // Returns:
  2319. // NO_ERROR <pPage> is assigned, and <pwID> points to the key.
  2320. //
  2321. // ERROR_NOT_FOUND <pPage> is assigned to where the insert should occur,
  2322. // at <pwID> in that page.
  2323. //
  2324. // Other errors don't assign the OUT parameters.
  2325. //
  2326. // Note: caller must release <pRetIdx> using Release() when it is returned
  2327. // whether with an error code or not.
  2328. //
  2329. //***************************************************************************
  2330. // ok
  2331. DWORD CBTree::Search(
  2332. IN LPSTR pszKey,
  2333. OUT SIdxKeyTable **pRetIdx,
  2334. OUT WORD *pwID,
  2335. DWORD Stack[],
  2336. LONG &StackPtr
  2337. )
  2338. {
  2339. DWORD dwRes, dwChildPage, dwPage;
  2340. if (pszKey == 0 || *pszKey == 0 || pwID == 0 || pRetIdx == 0)
  2341. return ERROR_INVALID_PARAMETER;
  2342. *pRetIdx = 0;
  2343. SIdxKeyTable *pIdx = m_pRoot;
  2344. pIdx->AddRef();
  2345. Stack[++StackPtr] = 0;
  2346. while (1)
  2347. {
  2348. dwRes = pIdx->FindKey(pszKey, pwID);
  2349. if (dwRes == 0)
  2350. {
  2351. // Found it
  2352. *pRetIdx = pIdx;
  2353. return NO_ERROR;
  2354. }
  2355. else if (dwRes != ERROR_NOT_FOUND)
  2356. {
  2357. pIdx->Release();
  2358. pIdx = 0;
  2359. return dwRes;
  2360. }
  2361. // Otherwise, we have to try to descend to a child page.
  2362. // =====================================================
  2363. dwPage = pIdx->GetPageId();
  2364. dwChildPage = pIdx->GetChildPage(*pwID);
  2365. if (dwChildPage == 0)
  2366. break;
  2367. pIdx->Release();
  2368. pIdx = 0;
  2369. Stack[++StackPtr] = dwPage;
  2370. dwRes = ReadIdxPage(dwChildPage, &pIdx);
  2371. if (dwRes)
  2372. return dwRes;
  2373. }
  2374. *pRetIdx = pIdx;
  2375. return ERROR_NOT_FOUND;
  2376. }
  2377. //***************************************************************************
  2378. //
  2379. // CBTree::InsertPhase2
  2380. //
  2381. // On entry, assumes that we have identified the page into which
  2382. // the insert must physically occur. This does the split + migrate
  2383. // logical to keep the tree in balance.
  2384. //
  2385. // Algorithm: Add key to page. If it does not overflow, we are done.
  2386. // If overflow occurs, allocate a new sibling page which will acquire
  2387. // half the keys from the current page. This sibling will be treated
  2388. // as lexically smaller in all cases. The median key is migrated
  2389. // up to the parent with pointers to both the new sibing page and
  2390. // the current page.
  2391. // The parent may also overflow. If so, the algorithm repeats.
  2392. // If an overflow occurs and there is no parent node (we are at the root)
  2393. // a new root node is allocated and the median key migrated into it.
  2394. //
  2395. //***************************************************************************
  2396. // ok
  2397. DWORD CBTree::InsertPhase2(
  2398. SIdxKeyTable *pCurrent,
  2399. WORD wID,
  2400. LPSTR pszKey,
  2401. DWORD dwValue,
  2402. DWORD Stack[],
  2403. LONG &StackPtr
  2404. )
  2405. {
  2406. DWORD dwRes;
  2407. // If non-NULL, used for a primary insert.
  2408. // If NULL, skip this, under the assumption the
  2409. // node is already up-to-date and merely requires
  2410. // the up-recursive split & migrate treatment.
  2411. // ==============================================
  2412. if (pszKey)
  2413. {
  2414. dwRes = pCurrent->AddKey(pszKey, wID, dwValue);
  2415. if (dwRes)
  2416. return dwRes; // Failed
  2417. }
  2418. pCurrent->AddRef(); // Makes following loop consistent
  2419. SIdxKeyTable *pSibling = 0;
  2420. SIdxKeyTable *pParent = 0;
  2421. // The class B-tree split+migration loop.
  2422. // ======================================
  2423. for (;;)
  2424. {
  2425. // Check the current node where we added the key.
  2426. // If it isn't too big, we're done.
  2427. // ==============================================
  2428. dwRes = pCurrent->GetRequiredPageMemory();
  2429. if (dwRes <= m_pSrc->GetPageSize())
  2430. {
  2431. dwRes = WriteIdxPage(pCurrent);
  2432. break;
  2433. }
  2434. // If here, it ain't gonna fit. We have to split the page.
  2435. // Allocate a new page (Sibling) and get the parent page, which
  2436. // will receive the median key.
  2437. // ============================================================
  2438. DWORD dwParent = Stack[StackPtr--];
  2439. if (dwParent == 0)
  2440. {
  2441. // Allocate a new page to become the parent.
  2442. LPDWORD pParentPg = 0;
  2443. dwRes = m_pSrc->NewPage((LPVOID *) &pParentPg);
  2444. if (dwRes)
  2445. break;
  2446. DWORD dwNewParent = pParentPg[CBTreeFile::OFFSET_PAGE_ID];
  2447. _BtrMemFree(pParentPg);
  2448. dwRes = SIdxKeyTable::Create(dwNewParent, &pParent);
  2449. if (dwRes)
  2450. break;
  2451. dwRes = m_pSrc->SetRootPage(dwNewParent);
  2452. if (dwRes)
  2453. break;
  2454. m_pRoot->Release(); // Replace old root
  2455. m_pRoot = pParent;
  2456. m_pRoot->AddRef();
  2457. }
  2458. else
  2459. {
  2460. if (dwParent == m_pRoot->GetPageId())
  2461. {
  2462. pParent = m_pRoot;
  2463. pParent->AddRef();
  2464. }
  2465. else
  2466. {
  2467. dwRes = ReadIdxPage(dwParent, &pParent);
  2468. if (dwRes)
  2469. break;
  2470. }
  2471. }
  2472. // Allocate a new sibling in any case to hold half the keys
  2473. // ========================================================
  2474. LPDWORD pSibPg = 0;
  2475. dwRes = m_pSrc->NewPage((LPVOID *) &pSibPg);
  2476. if (dwRes)
  2477. break;
  2478. DWORD dwNewSib = pSibPg[CBTreeFile::OFFSET_PAGE_ID];
  2479. _BtrMemFree(pSibPg);
  2480. dwRes = SIdxKeyTable::Create(dwNewSib, &pSibling);
  2481. if (dwRes)
  2482. break;
  2483. dwRes = pCurrent->Redist(pParent, pSibling);
  2484. if (dwRes)
  2485. break;
  2486. dwRes = WriteIdxPage(pCurrent);
  2487. if (dwRes)
  2488. break;
  2489. dwRes = WriteIdxPage(pSibling);
  2490. if (dwRes)
  2491. break;
  2492. pCurrent->Release();
  2493. pCurrent = 0;
  2494. pSibling->Release();
  2495. pSibling = 0;
  2496. if (dwRes)
  2497. break;
  2498. pCurrent = pParent;
  2499. pParent = 0;
  2500. }
  2501. ReleaseIfNotNULL(pParent);
  2502. ReleaseIfNotNULL(pCurrent);
  2503. ReleaseIfNotNULL(pSibling);
  2504. return dwRes;
  2505. }
  2506. //***************************************************************************
  2507. //
  2508. // CBTree::WriteIdxPage
  2509. //
  2510. // Writes the object to the physical page it is assigned to.
  2511. // If the page ID is zero, then it is considered invalid. Further,
  2512. // while is it correct to precheck the page size, this function does
  2513. // validate with regard to sizes, etc.
  2514. //
  2515. //***************************************************************************
  2516. //
  2517. DWORD CBTree::WriteIdxPage(
  2518. SIdxKeyTable *pIdx
  2519. )
  2520. {
  2521. DWORD dwRes;
  2522. DWORD dwPageSize = m_pSrc->GetPageSize();
  2523. DWORD dwMem = pIdx->GetRequiredPageMemory();
  2524. if (dwMem > dwPageSize)
  2525. return ERROR_INVALID_PARAMETER;
  2526. LPVOID pMem = _BtrMemAlloc(dwPageSize);
  2527. if (pMem == 0)
  2528. return ERROR_NOT_ENOUGH_MEMORY;
  2529. dwRes = pIdx->MapToPage(pMem);
  2530. if (dwRes)
  2531. {
  2532. _BtrMemFree(pMem);
  2533. return dwRes;
  2534. }
  2535. dwRes = m_pSrc->PutPage(pMem, CBTreeFile::PAGE_TYPE_ACTIVE);
  2536. _BtrMemFree(pMem);
  2537. if (dwRes)
  2538. return dwRes;
  2539. InterlockedIncrement(&m_lGeneration);
  2540. // Check for a root update.
  2541. // ========================
  2542. if (m_pRoot != pIdx && m_pRoot->GetPageId() == pIdx->GetPageId())
  2543. {
  2544. m_pRoot->Release();
  2545. m_pRoot = pIdx;
  2546. m_pRoot->AddRef();
  2547. if (m_pSrc->GetRootPage() != m_pRoot->GetPageId())
  2548. dwRes = m_pSrc->SetRootPage(m_pRoot->GetPageId());
  2549. }
  2550. return dwRes;
  2551. }
  2552. //***************************************************************************
  2553. //
  2554. // CBTree::ReadIdxPage
  2555. //
  2556. //***************************************************************************
  2557. //
  2558. DWORD CBTree::ReadIdxPage(
  2559. DWORD dwPage,
  2560. SIdxKeyTable **pIdx
  2561. )
  2562. {
  2563. DWORD dwRes;
  2564. LPVOID pPage = 0;
  2565. SIdxKeyTable *p = 0;
  2566. if (pIdx == 0)
  2567. return ERROR_INVALID_PARAMETER;
  2568. *pIdx = 0;
  2569. // if (dwPage < MAX_PAGE_HISTORY) // May remove if studies show no caching possible
  2570. // ++History[dwPage];
  2571. dwRes = m_pSrc->GetPage(dwPage, &pPage);
  2572. if (dwRes)
  2573. return dwRes;
  2574. dwRes = SIdxKeyTable::Create(pPage, &p);
  2575. if (dwRes)
  2576. {
  2577. _BtrMemFree(pPage);
  2578. return dwRes;
  2579. }
  2580. _BtrMemFree(pPage);
  2581. if (dwRes)
  2582. return dwRes;
  2583. *pIdx = p;
  2584. return dwRes;
  2585. }
  2586. //***************************************************************************
  2587. //
  2588. // CBTree::FindKey
  2589. //
  2590. // Does a simple search of a key, returning the user data, if requested.
  2591. //
  2592. // Typical Return values
  2593. // NO_ERROR
  2594. // ERROR_NOT_FOUND
  2595. //
  2596. //***************************************************************************
  2597. // ok
  2598. DWORD CBTree::FindKey(
  2599. IN LPSTR pszKey,
  2600. DWORD *pdwData
  2601. )
  2602. {
  2603. DWORD dwRes;
  2604. if (m_pRoot == NULL)
  2605. {
  2606. dwRes = InvalidateCache();
  2607. if (dwRes != ERROR_SUCCESS)
  2608. return dwRes;
  2609. }
  2610. WORD wID;
  2611. SIdxKeyTable *pIdx = 0;
  2612. LONG StackPtr = -1;
  2613. DWORD *Stack = new DWORD[CBTreeIterator::const_MaxStack];
  2614. if (Stack == NULL)
  2615. return ERROR_NOT_ENOUGH_MEMORY;
  2616. CVectorDeleteMe<DWORD> vdm(Stack);
  2617. if (pszKey == 0 || *pszKey == 0)
  2618. return ERROR_INVALID_PARAMETER;
  2619. // Search high and low, hoping against hope...
  2620. // ===========================================
  2621. dwRes = Search(pszKey, &pIdx, &wID, Stack, StackPtr);
  2622. if (dwRes == 0 && pdwData)
  2623. {
  2624. *pdwData = pIdx->GetUserData(wID);
  2625. }
  2626. // If here, we can indeed add it.
  2627. // ==============================
  2628. ReleaseIfNotNULL(pIdx);
  2629. return dwRes;
  2630. }
  2631. //***************************************************************************
  2632. //
  2633. // CBTree::DeleteKey
  2634. //
  2635. //***************************************************************************
  2636. //
  2637. DWORD CBTree::DeleteKey(
  2638. IN LPSTR pszKey
  2639. )
  2640. {
  2641. DWORD dwRes;
  2642. if (m_pRoot == NULL)
  2643. {
  2644. dwRes = InvalidateCache();
  2645. if (dwRes != ERROR_SUCCESS)
  2646. return dwRes;
  2647. }
  2648. LONG StackPtr = -1;
  2649. DWORD *Stack = new DWORD[CBTreeIterator::const_MaxStack];
  2650. if (Stack == NULL)
  2651. return ERROR_NOT_ENOUGH_MEMORY;
  2652. CVectorDeleteMe<DWORD> vdm(Stack);
  2653. SIdxKeyTable *pIdx = 0;
  2654. WORD wId;
  2655. DWORD dwLoad;
  2656. // Find it
  2657. // =======
  2658. dwRes = Search(pszKey, &pIdx, &wId, Stack, StackPtr);
  2659. if (dwRes)
  2660. return dwRes;
  2661. // Delete key from from page
  2662. // ==========================
  2663. if (pIdx->IsLeaf())
  2664. {
  2665. // A leaf node. Remove the key.
  2666. // =============================
  2667. dwRes = pIdx->RemoveKey(wId);
  2668. if (dwRes)
  2669. return dwRes;
  2670. // Now, check the load and see if it has dropped below 30%.
  2671. // Of course, if we are at the root node and it is a leaf,
  2672. // we have to pretty much let it go as is...
  2673. // ========================================================
  2674. dwLoad = ComputeLoad(pIdx);
  2675. if (dwLoad > const_MinimumLoad ||
  2676. pIdx->GetPageId() == m_pRoot->GetPageId())
  2677. {
  2678. dwRes = WriteIdxPage(pIdx);
  2679. pIdx->Release();
  2680. return dwRes;
  2681. }
  2682. }
  2683. else
  2684. {
  2685. // An internal node, so we have to find the successor.
  2686. // Since this call may alter the shape of the tree quite
  2687. // a bit (the successor may overflow the affected node),
  2688. // we have to relocate the successor.
  2689. // ====================================================
  2690. LPSTR pszSuccessor = 0;
  2691. BOOL bUnderflow = FALSE;
  2692. dwRes = ReplaceBySuccessor(pIdx, wId, &pszSuccessor, &bUnderflow, Stack, StackPtr);
  2693. if (dwRes)
  2694. return dwRes;
  2695. dwRes = InsertPhase2(pIdx, 0, 0, 0, Stack, StackPtr);
  2696. if (dwRes)
  2697. return dwRes;
  2698. pIdx->Release();
  2699. pIdx = 0;
  2700. StackPtr = -1;
  2701. if (bUnderflow == FALSE)
  2702. {
  2703. _BtrMemFree(pszSuccessor);
  2704. return NO_ERROR;
  2705. }
  2706. // If here, the node we extracted the successor from was reduced
  2707. // to poverty and underflowed. We have to find it again and
  2708. // execute the underflow repair loop.
  2709. // =============================================================
  2710. dwRes = Search(pszSuccessor, &pIdx, &wId, Stack, StackPtr);
  2711. _BtrMemFree(pszSuccessor);
  2712. if (dwRes)
  2713. return dwRes;
  2714. SIdxKeyTable *pSuccessor = 0;
  2715. dwRes = FindSuccessorNode(pIdx, wId, &pSuccessor, 0, Stack, StackPtr);
  2716. if (dwRes)
  2717. return dwRes;
  2718. pIdx->Release();
  2719. pIdx = pSuccessor;
  2720. }
  2721. // UNDERFLOW REPAIR Loop.
  2722. // At this point <pIdx> points to the deepest affected node.
  2723. // We need to start working back up the tree and repairing
  2724. // the damage. Nodes which have reached zero in size are
  2725. // quite a pain. But they aren't half as bad as nodes which claim
  2726. // they can recombine with a sibling but really can't. So,
  2727. // we either do nothing (the node has enough stuff to be useful),
  2728. // collapse with a sibling node or borrow some keys from a sibling
  2729. // to ensure all nodes meet the minimum load requirement.
  2730. // ===============================================================
  2731. SIdxKeyTable *pSibling = 0;
  2732. SIdxKeyTable *pParent = 0;
  2733. for (;;)
  2734. {
  2735. DWORD dwParentId = Stack[StackPtr--];
  2736. DWORD dwThisId = pIdx->GetPageId();
  2737. dwLoad = ComputeLoad(pIdx);
  2738. if (dwLoad > const_MinimumLoad || dwParentId == 0)
  2739. {
  2740. dwRes = WriteIdxPage(pIdx);
  2741. pIdx->Release();
  2742. if (dwRes != 0)
  2743. return dwRes;
  2744. break;
  2745. }
  2746. // If here the node is getting small. We must collapsed this
  2747. // node with a sibling.
  2748. // collapse this node and a sibling
  2749. dwRes = ReadIdxPage(dwParentId, &pParent);
  2750. if (dwRes != 0)
  2751. return dwRes;
  2752. // Locate a sibling and see if the sibling and the current node
  2753. // can be collapsed with leftover space.
  2754. // =============================================================
  2755. DWORD dwLeftSibling = pParent->GetLeftSiblingOf(pIdx->GetPageId());
  2756. DWORD dwRightSibling = pParent->GetRightSiblingOf(pIdx->GetPageId());
  2757. DWORD dwSiblingId = 0;
  2758. if (dwLeftSibling)
  2759. {
  2760. dwRes = ReadIdxPage(dwLeftSibling, &pSibling);
  2761. if (dwRes != 0)
  2762. return dwRes;
  2763. dwSiblingId = pSibling->GetPageId();
  2764. }
  2765. else
  2766. {
  2767. dwRes = ReadIdxPage(dwRightSibling, &pSibling);
  2768. if (dwRes != 0)
  2769. return dwRes;
  2770. dwSiblingId = pSibling->GetPageId();
  2771. }
  2772. // If here, the node is 'underloaded'. Now we have to
  2773. // get the parent and the sibling and collapsed them.
  2774. // ===================================================
  2775. SIdxKeyTable *pCopy = 0;
  2776. dwRes = pIdx->Clone(&pCopy);
  2777. if (dwRes != 0)
  2778. return dwRes;
  2779. dwRes = pIdx->Collapse(pParent, pSibling);
  2780. if (dwRes != 0)
  2781. {
  2782. pCopy->Release();
  2783. return dwRes;
  2784. }
  2785. // Now we have a different sort of problem, possibly.
  2786. // If the collapsed node is too big, we have to try
  2787. // a different strategy.
  2788. // ===================================================
  2789. if (pIdx->GetRequiredPageMemory() > m_pSrc->GetPageSize())
  2790. {
  2791. pIdx->Release();
  2792. pParent->Release();
  2793. pSibling->Release();
  2794. pIdx = pParent = pSibling = 0;
  2795. // Reread the pages.
  2796. // =================
  2797. pIdx = pCopy;
  2798. dwRes = ReadIdxPage(dwParentId, &pParent);
  2799. if (dwRes != 0)
  2800. return dwRes;
  2801. dwRes = ReadIdxPage(dwSiblingId, &pSibling);
  2802. if (dwRes != 0)
  2803. return dwRes;
  2804. // Transfer a key or two from sibling via parent.
  2805. // This doesn't change the tree shape, but the
  2806. // parent may overflow.
  2807. // ==============================================
  2808. do
  2809. {
  2810. dwRes = pIdx->StealKeyFromSibling(pParent, pSibling);
  2811. if (dwRes != 0)
  2812. return dwRes;
  2813. dwLoad = ComputeLoad(pIdx);
  2814. } while (dwLoad < const_MinimumLoad);
  2815. dwRes = WriteIdxPage(pIdx);
  2816. pIdx->Release();
  2817. if (dwRes != 0)
  2818. return dwRes;
  2819. dwRes = WriteIdxPage(pSibling);
  2820. pSibling->Release();
  2821. if (dwRes != 0)
  2822. return dwRes;
  2823. dwRes = InsertPhase2(pParent, 0, 0, 0, Stack, StackPtr);
  2824. pParent->Release();
  2825. if (dwRes != 0)
  2826. return dwRes;
  2827. break;
  2828. }
  2829. else // The collapse worked; we can free the sibling page
  2830. {
  2831. pCopy->Release();
  2832. dwRes = m_pSrc->FreePage(pSibling->GetPageId());
  2833. if (dwRes != 0)
  2834. return dwRes;
  2835. pSibling->Release();
  2836. }
  2837. // If here, the collapse worked.
  2838. // =============================
  2839. dwRes = WriteIdxPage(pIdx);
  2840. if (dwRes)
  2841. {
  2842. pIdx->Release();
  2843. break;
  2844. }
  2845. if (pParent->GetNumKeys() == 0)
  2846. {
  2847. // We have replaced the root. Note
  2848. // that we transfer the ref count of pIdx to m_pRoot.
  2849. DWORD dwOldRootId = m_pRoot->GetPageId();
  2850. m_pRoot->Release();
  2851. m_pRoot = pIdx;
  2852. // Even though we wrote <pIdx> a few lines back,
  2853. // a rewrite is required to update internal stuff
  2854. // because this has become the new root.
  2855. // ==============================================
  2856. dwRes = m_pSrc->SetRootPage(m_pRoot->GetPageId());
  2857. if (dwRes != 0)
  2858. return dwRes;
  2859. dwRes = WriteIdxPage(m_pRoot);
  2860. if (dwRes != 0)
  2861. return dwRes;
  2862. dwRes = m_pSrc->FreePage(dwOldRootId);
  2863. if (dwRes != 0)
  2864. return dwRes;
  2865. pParent->Release();
  2866. break;
  2867. }
  2868. pIdx->Release();
  2869. pIdx = pParent;
  2870. }
  2871. return dwRes;
  2872. }
  2873. //***************************************************************************
  2874. //
  2875. // CBTree::ReplaceBySuccessor
  2876. //
  2877. // Removes the wId key in the <pIdx> node, and replaces it with the
  2878. // successor.
  2879. //
  2880. // Precondition: <pIdx> is an internal (non-leaf) node.
  2881. //
  2882. // Side-effects: <pIdx> may be overflowed and require the InsertPhase2
  2883. // treatment. The node from which the successor is extracted is
  2884. // written, but may have been reduced to zero keys.
  2885. //
  2886. //***************************************************************************
  2887. //
  2888. DWORD CBTree::ReplaceBySuccessor(
  2889. IN SIdxKeyTable *pIdx,
  2890. IN WORD wId,
  2891. OUT LPSTR *pszSuccessorKey,
  2892. OUT BOOL *pbUnderflowDetected,
  2893. DWORD Stack[],
  2894. LONG &StackPtr
  2895. )
  2896. {
  2897. SIdxKeyTable *pTemp = 0;
  2898. DWORD dwRes;
  2899. DWORD dwPredecessorChild;
  2900. dwRes = FindSuccessorNode(pIdx, wId, &pTemp, &dwPredecessorChild, Stack, StackPtr);
  2901. if (dwRes || pTemp == 0)
  2902. return dwRes;
  2903. LPSTR pszKey = 0;
  2904. dwRes = pTemp->GetKeyAt(0, &pszKey);
  2905. if (dwRes)
  2906. {
  2907. pTemp->Release();
  2908. return dwRes;
  2909. }
  2910. DWORD dwUserData = pTemp->GetUserData(0);
  2911. dwRes = pTemp->RemoveKey(0);
  2912. if (dwRes)
  2913. {
  2914. _BtrMemFree(pszKey);
  2915. pTemp->Release();
  2916. return dwRes;
  2917. }
  2918. if (ComputeLoad(pTemp) < const_MinimumLoad)
  2919. *pbUnderflowDetected = TRUE;
  2920. dwRes = WriteIdxPage(pTemp);
  2921. pTemp->Release();
  2922. if (dwRes)
  2923. {
  2924. _BtrMemFree(pszKey);
  2925. return dwRes;
  2926. }
  2927. pIdx->RemoveKey(wId);
  2928. dwRes = pIdx->AddKey(pszKey, wId, dwUserData);
  2929. if (dwRes)
  2930. {
  2931. _BtrMemFree(pszKey);
  2932. return dwRes;
  2933. }
  2934. pIdx->SetChildPage(wId, dwPredecessorChild);
  2935. *pszSuccessorKey = pszKey;
  2936. StackPtr--;
  2937. return dwRes;
  2938. }
  2939. //***************************************************************************
  2940. //
  2941. // CBTree::FindSuccessorNode
  2942. //
  2943. // Read-only. Finds the node containing the successor to the specified key.
  2944. //
  2945. //***************************************************************************
  2946. //
  2947. DWORD CBTree::FindSuccessorNode(
  2948. IN SIdxKeyTable *pIdx,
  2949. IN WORD wId,
  2950. OUT SIdxKeyTable **pSuccessor,
  2951. OUT DWORD *pdwPredecessorChild,
  2952. DWORD Stack[],
  2953. LONG &StackPtr
  2954. )
  2955. {
  2956. SIdxKeyTable *pTemp = 0;
  2957. DWORD dwRes = 0;
  2958. DWORD dwSuccessorChild, dwPredecessorChild;
  2959. dwPredecessorChild = pIdx->GetChildPage(wId);
  2960. dwSuccessorChild = pIdx->GetChildPage(wId+1);
  2961. Stack[++StackPtr] = pIdx->GetPageId();
  2962. // From this point on, take leftmost children until
  2963. // we reach a leaf node. The leftmost key in the
  2964. // leftmost node is always the successor, thanks to the
  2965. // astonishing properties of the BTree. Nice and easy, huh?
  2966. // =========================================================
  2967. while (dwSuccessorChild)
  2968. {
  2969. Stack[++StackPtr] = dwSuccessorChild;
  2970. if (pTemp)
  2971. pTemp->Release();
  2972. dwRes = ReadIdxPage(dwSuccessorChild, &pTemp);
  2973. if (dwRes)
  2974. {
  2975. //Bail because we have an error!
  2976. return dwRes;
  2977. }
  2978. dwSuccessorChild = pTemp->GetChildPage(0);
  2979. }
  2980. StackPtr--; // Pop the element we are returning in <*pSuccessor>
  2981. *pSuccessor = pTemp;
  2982. if (pdwPredecessorChild)
  2983. *pdwPredecessorChild = dwPredecessorChild;
  2984. return dwRes;
  2985. }
  2986. //***************************************************************************
  2987. //
  2988. // CBTree::BeginEnum
  2989. //
  2990. //***************************************************************************
  2991. //
  2992. DWORD CBTree::BeginEnum(
  2993. LPSTR pszStartKey,
  2994. OUT CBTreeIterator **pIterator
  2995. )
  2996. {
  2997. DWORD dwRes;
  2998. if (m_pRoot == NULL)
  2999. {
  3000. dwRes = InvalidateCache();
  3001. if (dwRes != ERROR_SUCCESS)
  3002. return dwRes;
  3003. }
  3004. CBTreeIterator *pIt = new CBTreeIterator;
  3005. if (pIt == 0)
  3006. return ERROR_NOT_ENOUGH_MEMORY;
  3007. dwRes = pIt->Init(this, pszStartKey);
  3008. if (dwRes)
  3009. {
  3010. pIt->Release();
  3011. return dwRes;
  3012. }
  3013. *pIterator = pIt;
  3014. return NO_ERROR;
  3015. }
  3016. //***************************************************************************
  3017. //
  3018. // CBTree::Dump
  3019. //
  3020. //***************************************************************************
  3021. //
  3022. void CBTree::Dump(FILE *f)
  3023. {
  3024. m_pSrc->Dump(f);
  3025. }
  3026. //***************************************************************************
  3027. //
  3028. //***************************************************************************
  3029. //
  3030. DWORD CBTree::InvalidateCache()
  3031. {
  3032. if (m_pRoot)
  3033. m_pRoot->Release();
  3034. DWORD dwRootPage = m_pSrc->GetRootPage();
  3035. DWORD dwRes = ReadIdxPage(dwRootPage, &m_pRoot);
  3036. return dwRes;
  3037. }
  3038. //***************************************************************************
  3039. //
  3040. // CBTreeIterator::FlushCaches
  3041. //
  3042. //***************************************************************************
  3043. //
  3044. DWORD CBTree::FlushCaches()
  3045. {
  3046. if (m_pRoot)
  3047. {
  3048. m_pRoot->Release();
  3049. m_pRoot = NULL;
  3050. }
  3051. return NO_ERROR;
  3052. }
  3053. //***************************************************************************
  3054. //
  3055. // CBTreeIterator::Init
  3056. //
  3057. //***************************************************************************
  3058. //
  3059. DWORD CBTreeIterator::Init(
  3060. IN CBTree *pTree,
  3061. IN LPSTR pszStartKey
  3062. )
  3063. {
  3064. DWORD dwRes;
  3065. if (pTree == 0)
  3066. return ERROR_INVALID_PARAMETER;
  3067. m_pTree = pTree;
  3068. if (m_pTree->m_pRoot == NULL)
  3069. {
  3070. dwRes = m_pTree->InvalidateCache();
  3071. if (dwRes != ERROR_SUCCESS)
  3072. return dwRes;
  3073. }
  3074. // Special case of enumerating everything. Probably not useful
  3075. // for WMI, but great for testing & debugging (I think).
  3076. // ============================================================
  3077. if (pszStartKey == 0)
  3078. {
  3079. Push(0, 0); // Sentinel value in stack
  3080. SIdxKeyTable *pRoot = pTree->m_pRoot;
  3081. pRoot->AddRef();
  3082. Push(pRoot, 0);
  3083. DWORD dwChildPage = Peek()->GetChildPage(0);
  3084. while (dwChildPage)
  3085. {
  3086. SIdxKeyTable *pIdx = 0;
  3087. dwRes = m_pTree->ReadIdxPage(dwChildPage, &pIdx);
  3088. if (dwRes)
  3089. return dwRes;
  3090. if (StackFull())
  3091. {
  3092. pIdx->Release();
  3093. return ERROR_INSUFFICIENT_BUFFER;
  3094. }
  3095. Push(pIdx, 0);
  3096. dwChildPage = pIdx->GetChildPage(0);
  3097. }
  3098. return NO_ERROR;
  3099. }
  3100. // If here, a matching string was specified.
  3101. // This is the typical case.
  3102. // =========================================
  3103. Push(0, 0); // Sentinel value in stack
  3104. WORD wId = 0;
  3105. DWORD dwChildPage;
  3106. SIdxKeyTable *pIdx = pTree->m_pRoot;
  3107. pIdx->AddRef();
  3108. while (1)
  3109. {
  3110. dwRes = pIdx->FindKey(pszStartKey, &wId);
  3111. if (dwRes == 0)
  3112. {
  3113. // Found it
  3114. Push(pIdx, wId);
  3115. return NO_ERROR;
  3116. }
  3117. else if (dwRes != ERROR_NOT_FOUND)
  3118. return dwRes;
  3119. // Otherwise, we have to try to descend to a child page.
  3120. // =====================================================
  3121. dwChildPage = pIdx->GetChildPage(wId);
  3122. if (dwChildPage == 0)
  3123. break;
  3124. Push(pIdx, wId);
  3125. pIdx = 0;
  3126. dwRes = pTree->ReadIdxPage(dwChildPage, &pIdx);
  3127. if (dwRes)
  3128. return dwRes;
  3129. }
  3130. Push(pIdx, wId);
  3131. while (Peek() && PeekId() == WORD(Peek()->GetNumKeys()))
  3132. Pop();
  3133. return NO_ERROR;
  3134. }
  3135. //***************************************************************************
  3136. //
  3137. // CBTreeIterator::Next
  3138. //
  3139. // On entry:
  3140. // <wID> is the key to visit in the current node (top-of-stack).
  3141. // The call sets up the successor before leaving. If there is no successor,
  3142. // the top of stack is left at NULL and ERROR_NO_MORE_ITEMS is returned.
  3143. //
  3144. // Returns ERROR_NO_MORE_ITEMS when the iteration is complete.
  3145. //
  3146. //***************************************************************************
  3147. //
  3148. DWORD CBTreeIterator::Next(
  3149. LPSTR *ppszStr,
  3150. DWORD *pdwData
  3151. )
  3152. {
  3153. DWORD dwRes;
  3154. if (ppszStr == 0)
  3155. return ERROR_INVALID_PARAMETER;
  3156. *ppszStr = 0;
  3157. if (Peek() == 0)
  3158. return ERROR_NO_MORE_ITEMS;
  3159. // Get the item for the caller.
  3160. // ============================
  3161. dwRes = Peek()->GetKeyAt(PeekId(), ppszStr);
  3162. if (dwRes)
  3163. return dwRes;
  3164. if (pdwData)
  3165. *pdwData = Peek()->GetUserData(PeekId());
  3166. IncStackId();
  3167. // Now find the successor.
  3168. // =======================
  3169. DWORD dwChildPage = Peek()->GetChildPage(PeekId());
  3170. while (dwChildPage)
  3171. {
  3172. SIdxKeyTable *pIdx = 0;
  3173. dwRes = m_pTree->ReadIdxPage(dwChildPage, &pIdx);
  3174. if (dwRes)
  3175. {
  3176. _BtrMemFree(*ppszStr);
  3177. *ppszStr = NULL;
  3178. return dwRes;
  3179. }
  3180. if (StackFull())
  3181. {
  3182. _BtrMemFree(*ppszStr);
  3183. *ppszStr = NULL;
  3184. pIdx->Release();
  3185. return ERROR_INSUFFICIENT_BUFFER;
  3186. }
  3187. Push(pIdx, 0);
  3188. dwChildPage = pIdx->GetChildPage(0);
  3189. }
  3190. // If here, we are at a leaf node.
  3191. // ===============================
  3192. while (Peek() && PeekId() == WORD(Peek()->GetNumKeys()))
  3193. Pop();
  3194. return NO_ERROR;
  3195. }
  3196. //***************************************************************************
  3197. //
  3198. // CBTreeIterator::Release
  3199. //
  3200. //***************************************************************************
  3201. //
  3202. DWORD CBTreeIterator::Release()
  3203. {
  3204. delete this;
  3205. return 0;
  3206. }
  3207. //***************************************************************************
  3208. //
  3209. // CBTreeIterator::~CBTreeIterator
  3210. //
  3211. //***************************************************************************
  3212. //
  3213. CBTreeIterator::~CBTreeIterator()
  3214. {
  3215. // Cleanup any leftover stack
  3216. while (m_lStackPointer > -1)
  3217. Pop();
  3218. }