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.

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