Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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