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.

2006 lines
51 KiB

  1. #include "precomp.h"
  2. //
  3. // CH.CPP
  4. // Cache Handler
  5. //
  6. // Copyright(c) Microsoft 1997-
  7. //
  8. #define MLZ_FILE_ZONE ZONE_CORE
  9. //
  10. // CACHE HANDLER
  11. //
  12. // The Cache Handler is a generic cache manager that handles blocks of
  13. // memory supplied by the calling component.
  14. //
  15. // Once a cache of a particular size has been created, blocks of memory can
  16. // be added to it (CH_CacheData). The cache can then be searched
  17. // (CH_SearchCache) to try and match the contents of a given block of
  18. // memory with the blocks in the cache.
  19. //
  20. // When a block is added to the cache and the cache is full, one of the
  21. // blocks currently in the cache is discarded on a Least-Recently Used
  22. // (LRU) basis.
  23. //
  24. // The component that creates the cache specifies a callback function which
  25. // is called every time a block is removed from the cache. This allows the
  26. // caller to free up memory blocks when they are no longer in use.
  27. //
  28. //
  29. // FUNCTION: CH_CreateCache
  30. //
  31. BOOL ASHost::CH_CreateCache
  32. (
  33. PCHCACHE * ppCache,
  34. UINT cCacheEntries,
  35. UINT cNumEvictionCategories,
  36. UINT cbNotHashed,
  37. PFNCACHEDEL pfnCacheDel
  38. )
  39. {
  40. UINT cbCacheSize;
  41. UINT i;
  42. PCHCACHE pCache;
  43. DebugEntry(ASHost::CH_CreateCache);
  44. //
  45. // Initialize return value
  46. //
  47. pCache = NULL;
  48. //
  49. // Do a few parameter validation checks.
  50. //
  51. ASSERT((cCacheEntries > 0));
  52. ASSERT((cCacheEntries < CH_MAX_CACHE_ENTRIES));
  53. ASSERT(cNumEvictionCategories > 0);
  54. ASSERT(cNumEvictionCategories <= CH_NUM_EVICTION_CATEGORIES);
  55. //
  56. // Calculate the amount of memory required.
  57. // NOTE that the CHCACHE definition includes one cache entry
  58. //
  59. cbCacheSize = sizeof(CHCACHE) + ((cCacheEntries-1) * sizeof(CHENTRY));
  60. //
  61. // Allocate memory for the cache.
  62. //
  63. pCache = (PCHCACHE)new BYTE[cbCacheSize];
  64. if (pCache == NULL)
  65. {
  66. ERROR_OUT(("Failed to alloc cache"));
  67. DC_QUIT;
  68. }
  69. SET_STAMP(pCache, CHCACHE);
  70. pCache->pRoot = NULL;
  71. pCache->pFirst = NULL;
  72. pCache->pLast= NULL;
  73. pCache->free = 0;
  74. pCache->cEntries = cCacheEntries;
  75. pCache->cNumEvictionCategories = cNumEvictionCategories;
  76. pCache->cbNotHashed = cbNotHashed;
  77. pCache->pfnCacheDel = pfnCacheDel;
  78. //
  79. // Initialize the cache entries
  80. //
  81. for (i = 0; i < cCacheEntries; i++)
  82. {
  83. CHInitEntry(&pCache->Entry[i]);
  84. pCache->Entry[i].free = (WORD)(i+1);
  85. }
  86. pCache->Entry[cCacheEntries-1].free = CH_MAX_CACHE_ENTRIES;
  87. //
  88. // Set up the default eviction category limits. Default is to balance
  89. // at 75% to the high category, 75% of the remainder to the next lower
  90. // and so on
  91. //
  92. for (i = cNumEvictionCategories; i > 0; i--)
  93. {
  94. pCache->iMRUHead[i-1] = CH_MAX_CACHE_ENTRIES;
  95. pCache->iMRUTail[i-1] = CH_MAX_CACHE_ENTRIES;
  96. pCache->cEvictThreshold[i-1] = (WORD)((cCacheEntries*3)/4);
  97. }
  98. DC_EXIT_POINT:
  99. *ppCache = pCache;
  100. DebugExitBOOL(ASHost::CH_CreateCache, (pCache != NULL));
  101. return(pCache != NULL);
  102. }
  103. //
  104. // CH_DestroyCache
  105. // Destroys a created cache, if it is valid.
  106. //
  107. void ASHost::CH_DestroyCache(PCHCACHE pCache)
  108. {
  109. DebugEntry(ASHost::CH_DestroyCache);
  110. ASSERT(IsValidCache(pCache));
  111. //
  112. // Clear the entries in the cache
  113. //
  114. CH_ClearCache(pCache);
  115. //
  116. // Free the memory
  117. //
  118. delete pCache;
  119. DebugExitVOID(ASHost::CH_DestroyCache);
  120. }
  121. //
  122. // FUNCTION: CH_SearchCache
  123. //
  124. BOOL ASHost::CH_SearchCache
  125. (
  126. PCHCACHE pCache,
  127. LPBYTE pData,
  128. UINT cbDataSize,
  129. UINT evictionCategory,
  130. UINT * piCacheEntry
  131. )
  132. {
  133. BOOL rc = FALSE;
  134. UINT checkSum;
  135. DebugEntry(ASHost::CH_SearchCache);
  136. ASSERT(IsValidCache(pCache));
  137. checkSum = CHCheckSum(pData + pCache->cbNotHashed,
  138. cbDataSize - pCache->cbNotHashed);
  139. *piCacheEntry = CHTreeSearch(pCache, checkSum, cbDataSize, pData);
  140. if ( *piCacheEntry != CH_MAX_CACHE_ENTRIES )
  141. {
  142. //
  143. // Found a match
  144. //
  145. CHUpdateMRUList(pCache, *piCacheEntry, evictionCategory);
  146. rc = TRUE;
  147. }
  148. DebugExitBOOL(ASHost::CH_SearchCache, rc);
  149. return(rc);
  150. }
  151. //
  152. // FUNCTION: CH_CacheData
  153. //
  154. UINT ASHost::CH_CacheData
  155. (
  156. PCHCACHE pCache,
  157. LPBYTE pData,
  158. UINT cbDataSize,
  159. UINT evictionCategory
  160. )
  161. {
  162. UINT evictionCount;
  163. UINT iEntry = CH_MAX_CACHE_ENTRIES;
  164. PCHENTRY pEntry;
  165. DebugEntry(ASHost::CH_CacheData);
  166. ASSERT(IsValidCache(pCache));
  167. ASSERT((evictionCategory < pCache->cNumEvictionCategories));
  168. if (!CHFindFreeCacheEntry(pCache, &iEntry, &evictionCount))
  169. {
  170. iEntry = CHEvictLRUCacheEntry(pCache, evictionCategory, evictionCount);
  171. //
  172. // MNM1422: Ideally we would now call CHFindFreeCacheEntry again to
  173. // get the entry freed up by the eviction process - but since we
  174. // have just been returned that entry, we may as well use it to
  175. // improve performance.
  176. //
  177. // However, the processing has left pTreeCacheData->tree.free
  178. // pointing to the entry we have just evicted - which we are about
  179. // to use. So we need to perform the same processing on the free
  180. // list as CHFindFreeCacheEntry would have done, or next time
  181. // through, the first 'free' entry will really be in use, and the
  182. // insert code will assert!
  183. //
  184. ASSERT(pCache->free == iEntry);
  185. pCache->free = pCache->Entry[iEntry].free;
  186. }
  187. pEntry = &pCache->Entry[iEntry];
  188. pEntry->pData = pData;
  189. pEntry->cbData = cbDataSize;
  190. pEntry->checkSum = CHCheckSum(pData + pCache->cbNotHashed,
  191. cbDataSize - pCache->cbNotHashed);
  192. pEntry->evictionCategory = (WORD)evictionCategory;
  193. CHAvlInsert(pCache, pEntry);
  194. TRACE_OUT(( "Cache 0x%08x entry %d checksum 0x%08x data 0x%08x",
  195. pCache, iEntry, pEntry->checkSum, pEntry->pData));
  196. CHUpdateMRUList(pCache, iEntry, evictionCategory);
  197. DebugExitDWORD(ASHost::CH_CacheData, iEntry);
  198. return(iEntry);
  199. }
  200. //
  201. // FUNCTION: CH_SearchAndCacheData
  202. //
  203. BOOL ASHost::CH_SearchAndCacheData
  204. (
  205. PCHCACHE pCache,
  206. LPBYTE pData,
  207. UINT cbDataSize,
  208. UINT evictionCategory,
  209. UINT * piCacheEntry
  210. )
  211. {
  212. UINT checkSum;
  213. UINT i;
  214. BOOL preExisting;
  215. UINT iEntry = CH_MAX_CACHE_ENTRIES;
  216. UINT evictionCount = 0;
  217. PCHENTRY pEntry;
  218. DebugEntry(ASHost::CH_SearchAndCacheData);
  219. ASSERT(IsValidCache(pCache));
  220. ASSERT(evictionCategory < pCache->cNumEvictionCategories);
  221. //
  222. // Does this entry exist?
  223. //
  224. checkSum = CHCheckSum(pData + pCache->cbNotHashed,
  225. cbDataSize - pCache->cbNotHashed);
  226. iEntry = CHTreeSearch(pCache, checkSum, cbDataSize, pData);
  227. if ( iEntry == CH_MAX_CACHE_ENTRIES)
  228. {
  229. preExisting = FALSE;
  230. //
  231. // We didn't find the entry--can we add it?
  232. //
  233. TRACE_OUT(("CACHE: entry not found in cache 0x%08x csum 0x%08x",
  234. pCache, checkSum));
  235. if (!CHFindFreeCacheEntry(pCache, &iEntry, &evictionCount))
  236. {
  237. //
  238. // Nope. Evict an entry
  239. //
  240. iEntry = CHEvictLRUCacheEntry(pCache, evictionCategory, evictionCount);
  241. ASSERT(iEntry != CH_MAX_CACHE_ENTRIES);
  242. TRACE_OUT(("CACHE: no free entries so evicted cache 0x%08x entry %d",
  243. pCache, iEntry));
  244. //
  245. // Ideally we would now call CHFindFreeCacheEntry again to
  246. // get the entry freed up via the eviction process, but since
  247. // we just returned that entry use to to improve perf.
  248. //
  249. // However, the processing has left pCache->free pointing
  250. // to the entry we just evicted and are about to use. So
  251. // we need to fix it up.
  252. //
  253. ASSERT(pCache->free == iEntry);
  254. pCache->free = pCache->Entry[iEntry].free;
  255. }
  256. //
  257. // Fill in this entry's data
  258. //
  259. pEntry = &pCache->Entry[iEntry];
  260. pEntry->pData = pData;
  261. pEntry->cbData = cbDataSize;
  262. pEntry->checkSum = checkSum;
  263. pEntry->evictionCategory = (WORD)evictionCategory;
  264. CHAvlInsert(pCache, pEntry);
  265. TRACE_OUT(( "CACHE: NEW ENTRY cache 0x%08x entry %d csum 0x%08x pdata 0x%08x",
  266. pCache, iEntry, checkSum, pEntry->pData));
  267. }
  268. else
  269. {
  270. //
  271. // We found the entry
  272. //
  273. preExisting = TRUE;
  274. TRACE_OUT(( "CACHE: entry found in cache 0x%08x entry %d csum 0x%08x",
  275. pCache, iEntry, checkSum));
  276. }
  277. CHUpdateMRUList(pCache, iEntry, evictionCategory);
  278. *piCacheEntry = iEntry;
  279. DebugExitBOOL(ASHost::CH_SearchAndCacheData, preExisting);
  280. return(preExisting);
  281. }
  282. //
  283. // FUNCTION: CH_RemoveCacheEntry
  284. //
  285. void ASHost::CH_RemoveCacheEntry
  286. (
  287. PCHCACHE pCache,
  288. UINT iCacheEntry
  289. )
  290. {
  291. DebugEntry(ASHost::CH_RemoveCacheEntry);
  292. ASSERT(IsValidCache(pCache));
  293. // ASSERT(IsValidCacheIndex(pCache, iCacheEntry)); Always True
  294. CHEvictCacheEntry(pCache, iCacheEntry, pCache->Entry[iCacheEntry].evictionCategory);
  295. DebugExitVOID(ASHost::CH_RemoveCacheEntry);
  296. }
  297. //
  298. // FUNCTION: CH_ClearCache
  299. //
  300. void ASHost::CH_ClearCache
  301. (
  302. PCHCACHE pCache
  303. )
  304. {
  305. UINT i;
  306. DebugEntry(ASHost::CH_ClearCache);
  307. ASSERT(IsValidCache(pCache));
  308. //
  309. // Remove the cache entries
  310. //
  311. for (i = 0; i < pCache->cEntries; i++)
  312. {
  313. if (pCache->Entry[i].pData != NULL)
  314. {
  315. CHRemoveEntry(pCache, i);
  316. }
  317. }
  318. DebugExitVOID(ASHost::CH_ClearCache);
  319. }
  320. //
  321. // CH_TouchCacheEntry() - see ch.h
  322. //
  323. void ASHost::CH_TouchCacheEntry
  324. (
  325. PCHCACHE pCache,
  326. UINT iCacheEntry
  327. )
  328. {
  329. DebugEntry(ASHost::CH_TouchCacheEntry);
  330. ASSERT(IsValidCache(pCache));
  331. // ASSERT(IsValidCacheIndex(pCache, iCacheEntry)); Always True
  332. TRACE_OUT(( "Touching cache entry 0x%08x %d", pCache, iCacheEntry));
  333. CHUpdateMRUList(pCache, iCacheEntry, 0);
  334. DebugExitVOID(ASHost::CH_TouchCacheEntry);
  335. }
  336. //
  337. // CHInitEntry
  338. // Initializes a cache entry
  339. //
  340. //
  341. void ASHost::CHInitEntry(PCHENTRY pEntry)
  342. {
  343. pEntry->pParent = NULL;
  344. pEntry->pLeft = NULL;
  345. pEntry->pRight = NULL;
  346. pEntry->pData = NULL;
  347. pEntry->checkSum = 0;
  348. pEntry->lHeight = 0xFFFF;
  349. pEntry->rHeight = 0xFFFF;
  350. pEntry->chain.next = CH_MAX_CACHE_ENTRIES;
  351. pEntry->chain.prev = CH_MAX_CACHE_ENTRIES;
  352. pEntry->cbData = 0;
  353. }
  354. //
  355. // FUNCTION: CHUpdateMRUList
  356. //
  357. void ASHost::CHUpdateMRUList
  358. (
  359. PCHCACHE pCache,
  360. UINT iEntry,
  361. UINT evictionCategory
  362. )
  363. {
  364. WORD inext;
  365. WORD iprev;
  366. DebugEntry(ASHost::CHUpdateMRUList);
  367. //
  368. // Move the given entry to the head of the MRU if isn't there already
  369. //
  370. if (pCache->iMRUHead[evictionCategory] != iEntry)
  371. {
  372. //
  373. // Remove the supplied entry from the MRU list, if it is currently
  374. // chained. Since we never do this if the entry is already in the
  375. // front, an iprev of CH_MAX_CACHE_ENTRIES indicates that we are
  376. // updated an unchained entry.
  377. //
  378. iprev = pCache->Entry[iEntry].chain.prev;
  379. inext = pCache->Entry[iEntry].chain.next;
  380. TRACE_OUT(("Add/promote entry %u which was chained off %hu to %hu",
  381. iEntry,iprev,inext));
  382. if (iprev != CH_MAX_CACHE_ENTRIES)
  383. {
  384. pCache->Entry[iprev].chain.next = inext;
  385. if (inext != CH_MAX_CACHE_ENTRIES)
  386. {
  387. pCache->Entry[inext].chain.prev = iprev;
  388. }
  389. else
  390. {
  391. TRACE_OUT(("Removing final entry(%u) from MRU chain leaving %hu at tail",
  392. iEntry, iprev));
  393. pCache->iMRUTail[evictionCategory] = iprev;
  394. }
  395. }
  396. //
  397. // Now add this entry to the head of the MRU list
  398. //
  399. inext = pCache->iMRUHead[evictionCategory];
  400. pCache->Entry[iEntry].chain.next = inext;
  401. pCache->Entry[iEntry].chain.prev = CH_MAX_CACHE_ENTRIES;
  402. pCache->iMRUHead[evictionCategory] = (WORD)iEntry;
  403. if (inext != CH_MAX_CACHE_ENTRIES)
  404. {
  405. pCache->Entry[inext].chain.prev = (WORD)iEntry;
  406. }
  407. else
  408. {
  409. //
  410. // If the MRU chain is currently empty, then we must first add
  411. // the entry to the tail of the chain.
  412. //
  413. pCache->iMRUTail[evictionCategory] = (WORD)iEntry;
  414. TRACE_OUT(("Cache 0x%08x entry %u is first so add to MRU %u tail",
  415. pCache, iEntry, evictionCategory));
  416. }
  417. TRACE_OUT(( "Cache 0x%08x entry %u to head of MRU category %u",
  418. pCache, iEntry, evictionCategory));
  419. }
  420. else
  421. {
  422. TRACE_OUT(("Cache 0x%08x entry %u already at head of eviction category %u",
  423. pCache, iEntry, evictionCategory));
  424. }
  425. DebugExitVOID(ASHost::CHUpateMRUList);
  426. }
  427. //
  428. // FUNCTION: CHFindFreeCacheEntry
  429. //
  430. BOOL ASHost::CHFindFreeCacheEntry
  431. (
  432. PCHCACHE pCache,
  433. UINT* piEntry,
  434. UINT* pEvictionCount
  435. )
  436. {
  437. UINT iEntry;
  438. BOOL rc = FALSE;
  439. DebugEntry(ASHost::CHFindFreeCacheEntry);
  440. ASSERT(IsValidCache(pCache));
  441. iEntry = pCache->free;
  442. if (iEntry == CH_MAX_CACHE_ENTRIES)
  443. {
  444. TRACE_OUT(( "Cache 0x%08x is full", pCache));
  445. *pEvictionCount = pCache->cEntries;
  446. rc = FALSE;
  447. }
  448. else
  449. {
  450. TRACE_OUT(( "Free entry at %u",iEntry));
  451. *piEntry = iEntry;
  452. pCache->free = pCache->Entry[iEntry].free;
  453. *pEvictionCount = 0;
  454. rc = TRUE;
  455. }
  456. DebugExitBOOL(ASHost::CHFindFreeCacheEntry, rc);
  457. return(rc);
  458. }
  459. //
  460. // FUNCTION: CHEvictCacheEntry
  461. //
  462. UINT ASHost::CHEvictCacheEntry
  463. (
  464. PCHCACHE pCache,
  465. UINT iEntry,
  466. UINT evictionCategory
  467. )
  468. {
  469. WORD inext;
  470. WORD iprev;
  471. DebugEntry(ASHost::CHEvictCacheEntry);
  472. //
  473. // Evict the specified entry by removing it from the MRU chain, and
  474. // then resetting it. If it is in the cache, it must be in an MRU
  475. // cache.
  476. //
  477. inext = pCache->Entry[iEntry].chain.next;
  478. iprev = pCache->Entry[iEntry].chain.prev;
  479. TRACE_OUT(( "Evicting entry %u which was chained off %hu to %hu",
  480. iEntry, iprev, inext));
  481. if (iprev < CH_MAX_CACHE_ENTRIES)
  482. {
  483. pCache->Entry[iprev].chain.next = inext;
  484. }
  485. else
  486. {
  487. TRACE_OUT(("Removing head entry(%u) from MRU chain leaving %hu at head",
  488. iEntry, inext));
  489. pCache->iMRUHead[evictionCategory] = inext;
  490. }
  491. if (inext < CH_MAX_CACHE_ENTRIES)
  492. {
  493. pCache->Entry[inext].chain.prev = iprev;
  494. }
  495. else
  496. {
  497. TRACE_OUT(("Removing tail entry(%u) from MRU chain leaving %hu at tail",
  498. iEntry, iprev));
  499. pCache->iMRUTail[evictionCategory] = iprev;
  500. }
  501. CHRemoveEntry(pCache, iEntry);
  502. DebugExitDWORD(ASHost::CHEvictCacheEntry, iEntry);
  503. return(iEntry);
  504. }
  505. //
  506. // FUNCTION: CHEvictLRUCacheEntry
  507. //
  508. UINT ASHost::CHEvictLRUCacheEntry
  509. (
  510. PCHCACHE pCache,
  511. UINT evictionCategory,
  512. UINT evictionCount
  513. )
  514. {
  515. UINT iEntry;
  516. UINT i;
  517. DebugEntry(ASHost::CHEvictLRUCacheEntry);
  518. TRACE_OUT(("0x%08x LRU eviction requested, category %u, count %u",
  519. pCache, evictionCategory, evictionCount));
  520. //
  521. // Evict from the same eviction category provided the number cached
  522. // is above the threshold. Otherwise, take from the category one above.
  523. // This will allow the system to eventually stabilize at the correct
  524. // thresholds as all cache entries get used up.
  525. //
  526. if (evictionCount < pCache->cEvictThreshold[evictionCategory])
  527. {
  528. for (i = 0; i < pCache->cNumEvictionCategories; i++)
  529. {
  530. evictionCategory = (evictionCategory + 1) %
  531. pCache->cNumEvictionCategories;
  532. if (pCache->iMRUTail[evictionCategory] != CH_MAX_CACHE_ENTRIES)
  533. break;
  534. }
  535. WARNING_OUT(( "Threshold %u, count %u so set eviction category to %u",
  536. pCache->cEvictThreshold[evictionCategory],
  537. evictionCount,
  538. evictionCategory));
  539. }
  540. //
  541. // Evict the lasat entry in the MRU chain
  542. //
  543. iEntry = pCache->iMRUTail[evictionCategory];
  544. TRACE_OUT(( "Selected %u for eviction",iEntry));
  545. ASSERT((iEntry != CH_MAX_CACHE_ENTRIES));
  546. CHEvictCacheEntry(pCache, iEntry, evictionCategory);
  547. DebugExitDWORD(ASHost::CHEvictLRUCacheEntry, iEntry);
  548. return(iEntry);
  549. }
  550. //
  551. // FUNCTION: CHRemoveEntry
  552. //
  553. void ASHost::CHRemoveEntry
  554. (
  555. PCHCACHE pCache,
  556. UINT iCacheEntry
  557. )
  558. {
  559. DebugEntry(ASHost::CHRemoveEntry);
  560. ASSERT(IsValidCache(pCache));
  561. // ASSERT(IsValidCacheIndex(pCache, iCacheEntry)); Always True
  562. if (pCache->Entry[iCacheEntry].pData != NULL)
  563. {
  564. if (pCache->pfnCacheDel)
  565. {
  566. (pCache->pfnCacheDel)(this, pCache, iCacheEntry,
  567. pCache->Entry[iCacheEntry].pData);
  568. }
  569. else
  570. {
  571. // Simple deletion -- just free memory
  572. delete[] pCache->Entry[iCacheEntry].pData;
  573. }
  574. }
  575. CHAvlDelete(pCache, &pCache->Entry[iCacheEntry], iCacheEntry);
  576. DebugExitVOID(ASHost::CHRemoveEntry);
  577. }
  578. //
  579. // FUNCTION: CHCheckSum
  580. //
  581. // For processing speed we calculate the checksum based on whole multiples
  582. // of 4 bytes followed by a final addition of the last few bytes
  583. //
  584. UINT ASHost::CHCheckSum
  585. (
  586. LPBYTE pData,
  587. UINT cbDataSize
  588. )
  589. {
  590. UINT cSum = 0;
  591. UINT * pCh;
  592. UINT * pEnd;
  593. LPBYTE pCh8;
  594. DebugEntry(ASHost::CHCheckSum);
  595. ASSERT(cbDataSize > 3);
  596. pCh = (UINT *)pData;
  597. pEnd = (UINT *)(pData + cbDataSize - 4);
  598. //
  599. // Get the DWORD-aligned checksum
  600. //
  601. while (pCh <= pEnd)
  602. {
  603. cSum = (cSum << 1) + *pCh++ + ((cSum & 0x80000000) != 0);
  604. }
  605. //
  606. // Get the rest past the last DWORD boundaray
  607. //
  608. pEnd = (UINT *)(pData + cbDataSize);
  609. for (pCh8 = (LPBYTE)pCh; pCh8 < (LPBYTE)pEnd; pCh8++)
  610. {
  611. cSum = cSum + *pCh8;
  612. }
  613. DebugExitDWORD(ASHost::CHCheckSum, cSum);
  614. return(cSum);
  615. }
  616. //
  617. // FUNCTION: CHTreeSearch
  618. //
  619. // Finds a node in the cache tree which matches size, checksum and data.
  620. //
  621. UINT ASHost::CHTreeSearch
  622. (
  623. PCHCACHE pCache,
  624. UINT checkSum,
  625. UINT cbDataSize,
  626. LPBYTE pData
  627. )
  628. {
  629. PCHENTRY pEntry;
  630. UINT iCacheEntry = CH_MAX_CACHE_ENTRIES;
  631. DebugEntry(ASHost::CHTreeSearch);
  632. pEntry = CHAvlFind(pCache, checkSum, cbDataSize);
  633. while (pEntry != NULL)
  634. {
  635. ASSERT(IsValidCacheEntry(pEntry));
  636. //
  637. // Found a match based on the checksum. Now see if the data
  638. // also matches.
  639. //
  640. if (!memcmp(pEntry->pData + pCache->cbNotHashed,
  641. pData + pCache->cbNotHashed,
  642. cbDataSize - pCache->cbNotHashed))
  643. {
  644. //
  645. // Data also matches. Get an index into the memory block
  646. // of the cache.
  647. //
  648. iCacheEntry = (UINT)(pEntry - pCache->Entry);
  649. TRACE_OUT(( "Cache 0x%08x entry %d match-csum 0x%08x",
  650. pCache, iCacheEntry, checkSum));
  651. break;
  652. }
  653. else
  654. {
  655. TRACE_OUT(( "Checksum 0x%08x size %u matched, data didn't",
  656. checkSum, cbDataSize));
  657. pEntry = CHAvlFindEqual(pCache, pEntry);
  658. }
  659. }
  660. DebugExitDWORD(ASHost::CHTreeSearch, iCacheEntry);
  661. return(iCacheEntry);
  662. }
  663. //
  664. // Name: CHAvlInsert
  665. //
  666. // Purpose: Insert the supplied node into the specified AVL tree
  667. //
  668. // Returns: Nothing
  669. //
  670. // Params: IN pTree - a pointer to the AVL tree
  671. // IN pEntry - a pointer to the node to insert
  672. //
  673. // Operation: Scan down the tree looking for the insert point, going left
  674. // if the insert key is less than or equal to the key in the tree
  675. // and right if it is greater. When the insert point is found
  676. // insert the new node and rebalance the tree if necessary.
  677. //
  678. //
  679. void ASHost::CHAvlInsert
  680. (
  681. PCHCACHE pCache,
  682. PCHENTRY pEntry
  683. )
  684. {
  685. PCHENTRY pParentEntry;
  686. int result;
  687. DebugEntry(ASHost::CHAvlInsert);
  688. ASSERT(IsValidCacheEntry(pEntry));
  689. ASSERT(!IsCacheEntryInTree(pEntry));
  690. pEntry->rHeight = 0;
  691. pEntry->lHeight = 0;
  692. if (pCache->pRoot == NULL)
  693. {
  694. //
  695. // tree is empty, so insert at root
  696. //
  697. TRACE_OUT(( "tree is empty, so insert at root" ));
  698. pCache->pRoot = pEntry;
  699. pCache->pFirst = pEntry;
  700. pCache->pLast = pEntry;
  701. DC_QUIT;
  702. }
  703. //
  704. // scan down the tree looking for the appropriate insert point
  705. //
  706. TRACE_OUT(( "scan for insert point" ));
  707. pParentEntry = pCache->pRoot;
  708. while (pParentEntry != NULL)
  709. {
  710. //
  711. // go left or right, depending on comparison
  712. //
  713. result = CHCompare(pEntry->checkSum, pEntry->cbData, pParentEntry);
  714. if (result > 0)
  715. {
  716. //
  717. // new key is greater than this node's key, so move down right
  718. // subtree
  719. //
  720. TRACE_OUT(( "move down right subtree" ));
  721. if (pParentEntry->pRight == NULL)
  722. {
  723. //
  724. // right subtree is empty, so insert here
  725. //
  726. TRACE_OUT(( "right subtree empty, insert here" ));
  727. pEntry->pParent = pParentEntry;
  728. ASSERT((pParentEntry != pEntry));
  729. pParentEntry->pRight = pEntry;
  730. pParentEntry->rHeight = 1;
  731. if (pParentEntry == pCache->pLast)
  732. {
  733. //
  734. // parent was the right-most node in the tree, so new
  735. // node is now right-most
  736. //
  737. TRACE_OUT(( "new last node" ));
  738. pCache->pLast = pEntry;
  739. }
  740. break;
  741. }
  742. else
  743. {
  744. //
  745. // right subtree is not empty
  746. //
  747. TRACE_OUT(( "right subtree not empty" ));
  748. pParentEntry = pParentEntry->pRight;
  749. }
  750. }
  751. else
  752. {
  753. //
  754. // New key is less than or equal to this node's key, so move
  755. // down left subtree. The new node could be inserted before
  756. // the current node when equal, but this happens so rarely
  757. // that it's not worth special casing.
  758. //
  759. TRACE_OUT(( "move down left subtree" ));
  760. if (pParentEntry->pLeft == NULL)
  761. {
  762. //
  763. // left subtree is empty, so insert here
  764. //
  765. TRACE_OUT(( "left subtree empty, insert here" ));
  766. pEntry->pParent = pParentEntry;
  767. ASSERT((pParentEntry != pEntry));
  768. pParentEntry->pLeft = pEntry;
  769. pParentEntry->lHeight = 1;
  770. if (pParentEntry == pCache->pFirst)
  771. {
  772. //
  773. // parent was the left-most node in the tree, so new
  774. // node is now left-most
  775. //
  776. TRACE_OUT(( "new first node" ));
  777. pCache->pFirst = pEntry;
  778. }
  779. break;
  780. }
  781. else
  782. {
  783. //
  784. // left subtree is not empty
  785. //
  786. TRACE_OUT(( "left subtree not empty" ));
  787. pParentEntry = pParentEntry->pLeft;
  788. }
  789. }
  790. }
  791. //
  792. // now rebalance the tree if necessary
  793. //
  794. CHAvlBalanceTree(pCache, pParentEntry);
  795. DC_EXIT_POINT:
  796. DebugExitVOID(ASHost::CHAvlInsert);
  797. }
  798. //
  799. // Name: CHAvlDelete
  800. //
  801. // Purpose: Delete the specified node from the specified AVL tree
  802. //
  803. // Returns: Nothing
  804. //
  805. // Params: IN pCache - a pointer to the AVL tree
  806. // IN pEntry - a pointer to the node to delete
  807. //
  808. //
  809. void ASHost::CHAvlDelete
  810. (
  811. PCHCACHE pCache,
  812. PCHENTRY pEntry,
  813. UINT iCacheEntry
  814. )
  815. {
  816. PCHENTRY pReplaceEntry;
  817. PCHENTRY pParentEntry;
  818. WORD newHeight;
  819. DebugEntry(ASHost::CHAvlDelete);
  820. ASSERT(IsValidCacheEntry(pEntry));
  821. ASSERT(IsCacheEntryInTree(pEntry));
  822. if ((pEntry->pLeft == NULL) && (pEntry->pRight == NULL))
  823. {
  824. //
  825. // Barren node (no children). Update all references to it with
  826. // our parent.
  827. //
  828. TRACE_OUT(( "delete barren node" ));
  829. pReplaceEntry = NULL;
  830. if (pCache->pFirst == pEntry)
  831. {
  832. //
  833. // We are the first in the b-tree
  834. //
  835. TRACE_OUT(( "replace first node in tree" ));
  836. pCache->pFirst = pEntry->pParent;
  837. }
  838. if (pCache->pLast == pEntry)
  839. {
  840. //
  841. // We are the last in the b-tree
  842. //
  843. TRACE_OUT(( "replace last node in tree" ));
  844. pCache->pLast = pEntry->pParent;
  845. }
  846. }
  847. else if (pEntry->pLeft == NULL)
  848. {
  849. //
  850. // This node has no left child, so update references to it with
  851. // pointer to right child.
  852. //
  853. TRACE_OUT(( "node has no left child, replace with right child" ));
  854. pReplaceEntry = pEntry->pRight;
  855. if (pCache->pFirst == pEntry)
  856. {
  857. //
  858. // We are the first in the b-tree
  859. //
  860. TRACE_OUT(( "replace first node in tree" ));
  861. pCache->pFirst = pReplaceEntry;
  862. }
  863. // WE CAN'T BE THE LAST IN THE B-TREE SINCE WE HAVE A RIGHT CHILD
  864. ASSERT(pCache->pLast != pEntry);
  865. }
  866. else if (pEntry->pRight == NULL)
  867. {
  868. //
  869. // This node has no right child, so update references to it with
  870. // pointer to left child.
  871. //
  872. TRACE_OUT(( "node has no right son, replace with left son" ));
  873. pReplaceEntry = pEntry->pLeft;
  874. // WE CAN'T BE THE FIRST IN THE B-TREE SINCE WE HAVE A LEFT CHILD
  875. ASSERT(pCache->pFirst != pEntry);
  876. if (pCache->pLast == pEntry)
  877. {
  878. //
  879. // We are the last in the b-tree
  880. //
  881. TRACE_OUT(( "replace last node in tree" ));
  882. pCache->pLast = pReplaceEntry;
  883. }
  884. }
  885. else
  886. {
  887. //
  888. // HARDEST CASE. WE HAVE LEFT AND RIGHT CHILDREN
  889. TRACE_OUT(( "node has two sons" ));
  890. if (pEntry->rHeight > pEntry->lHeight)
  891. {
  892. //
  893. // Right subtree is bigger than left subtree
  894. //
  895. TRACE_OUT(( "right subtree is higher" ));
  896. if (pEntry->pRight->pLeft == NULL)
  897. {
  898. //
  899. // Replace references to entry with right child since it
  900. // has no left child (left grandchild of us)
  901. //
  902. TRACE_OUT(( "replace node with right son" ));
  903. pReplaceEntry = pEntry->pRight;
  904. pReplaceEntry->pLeft = pEntry->pLeft;
  905. pReplaceEntry->pLeft->pParent = pReplaceEntry;
  906. pReplaceEntry->lHeight = pEntry->lHeight;
  907. }
  908. else
  909. {
  910. //
  911. // Swap with leftmost descendent of the right subtree
  912. //
  913. TRACE_OUT(( "swap with left-most right descendent" ));
  914. CHAvlSwapLeftmost(pCache, pEntry->pRight, pEntry);
  915. pReplaceEntry = pEntry->pRight;
  916. }
  917. }
  918. else
  919. {
  920. //
  921. // Left subtree is bigger than or equal to right subtree
  922. //
  923. TRACE_OUT(( "left subtree is higher" ));
  924. TRACE_OUT(( "(or both subtrees are of equal height)" ));
  925. if (pEntry->pLeft->pRight == NULL)
  926. {
  927. //
  928. // Replace references to entry with left child since it
  929. // no right child (right grandchild of us)
  930. //
  931. TRACE_OUT(( "replace node with left son" ));
  932. pReplaceEntry = pEntry->pLeft;
  933. pReplaceEntry->pRight = pEntry->pRight;
  934. pReplaceEntry->pRight->pParent = pReplaceEntry;
  935. pReplaceEntry->rHeight = pEntry->rHeight;
  936. }
  937. else
  938. {
  939. //
  940. // Swap with rightmost descendent of the left subtree
  941. //
  942. TRACE_OUT(( "swap with right-most left descendent" ));
  943. CHAvlSwapRightmost(pCache, pEntry->pLeft, pEntry);
  944. pReplaceEntry = pEntry->pLeft;
  945. }
  946. }
  947. }
  948. //
  949. // NOTE: We can not save parent entry above because some code might
  950. // swap the tree around. In which case, our parenty entry will change
  951. // out from underneath us.
  952. //
  953. pParentEntry = pEntry->pParent;
  954. //
  955. // Clear out the about-to-be-deleted cache entry
  956. //
  957. TRACE_OUT(( "reset deleted node" ));
  958. CHInitEntry(pEntry);
  959. if (pReplaceEntry != NULL)
  960. {
  961. //
  962. // Fix up parent pointers, and calculate new heights of subtree
  963. //
  964. TRACE_OUT(( "fixup parent pointer of replacement node" ));
  965. pReplaceEntry->pParent = pParentEntry;
  966. newHeight = (WORD)(1 + max(pReplaceEntry->lHeight, pReplaceEntry->rHeight));
  967. }
  968. else
  969. {
  970. newHeight = 0;
  971. }
  972. TRACE_OUT(( "new height of parent is %d", newHeight ));
  973. if (pParentEntry != NULL)
  974. {
  975. //
  976. // Fixup parent entry pointers
  977. //
  978. TRACE_OUT(( "fix-up parent node" ));
  979. if (pParentEntry->pRight == pEntry)
  980. {
  981. //
  982. // Entry is right child of parent
  983. //
  984. TRACE_OUT(( "replacement node is right son" ));
  985. pParentEntry->pRight = pReplaceEntry;
  986. pParentEntry->rHeight = newHeight;
  987. }
  988. else
  989. {
  990. //
  991. // Entry is left child of parent
  992. //
  993. TRACE_OUT(( "replacement node is left son" ));
  994. pParentEntry->pLeft = pReplaceEntry;
  995. pParentEntry->lHeight = newHeight;
  996. }
  997. //
  998. // Now rebalance the tree if necessary
  999. //
  1000. CHAvlBalanceTree(pCache, pParentEntry);
  1001. }
  1002. else
  1003. {
  1004. //
  1005. // Replacement is now root of tree
  1006. //
  1007. TRACE_OUT(( "replacement node is now root of tree" ));
  1008. pCache->pRoot = pReplaceEntry;
  1009. }
  1010. //
  1011. // Put entry back into free list.
  1012. //
  1013. pEntry->free = pCache->free;
  1014. pCache->free = (WORD)iCacheEntry;
  1015. DebugExitVOID(ASHost::CHAvlDelete);
  1016. }
  1017. //
  1018. // Name: CHAvlNext
  1019. //
  1020. // Purpose: Find next node in the AVL tree
  1021. //
  1022. // Returns: A pointer to the next node's data
  1023. //
  1024. // Params: IN pEntry - a pointer to the current node in
  1025. // the tree
  1026. //
  1027. // Operation: If the specified node has a right-son then return the left-
  1028. // most son of this. Otherwise search back up until we find a
  1029. // node of which we are in the left sub-tree and return that.
  1030. //
  1031. //
  1032. LPBYTE ASHost::CHAvlNext
  1033. (
  1034. PCHENTRY pEntry
  1035. )
  1036. {
  1037. //
  1038. // find next node in tree
  1039. //
  1040. DebugEntry(ASHost::CHAvlNext);
  1041. ASSERT(IsValidCacheEntry(pEntry));
  1042. ASSERT(IsCacheEntryInTree(pEntry));
  1043. if (pEntry->pRight != NULL)
  1044. {
  1045. //
  1046. // Next entry is the left-most in the right-subtree
  1047. //
  1048. TRACE_OUT(( "next node is left-most right descendent" ));
  1049. pEntry = pEntry->pRight;
  1050. ASSERT(IsValidCacheEntry(pEntry));
  1051. while (pEntry->pLeft != NULL)
  1052. {
  1053. ASSERT(IsValidCacheEntry(pEntry->pLeft));
  1054. pEntry = pEntry->pLeft;
  1055. }
  1056. }
  1057. else
  1058. {
  1059. //
  1060. // No right child. So find an entry for which we are in its left
  1061. // subtree.
  1062. //
  1063. TRACE_OUT(( "find node which this is in left subtree of" ));
  1064. while (pEntry != NULL)
  1065. {
  1066. ASSERT(IsValidCacheEntry(pEntry));
  1067. if ((pEntry->pParent == NULL) ||
  1068. (pEntry->pParent->pLeft == pEntry))
  1069. {
  1070. pEntry = pEntry->pParent;
  1071. break;
  1072. }
  1073. pEntry = pEntry->pParent;
  1074. }
  1075. }
  1076. DebugExitVOID(ASHost::CHAvlNext);
  1077. return((pEntry != NULL) ? pEntry->pData : NULL);
  1078. }
  1079. //
  1080. // Name: CHAvlPrev
  1081. //
  1082. // Purpose: Find previous node in the AVL tree
  1083. //
  1084. // Returns: A pointer to the previous node's data in the tree
  1085. //
  1086. // Params: IN PNode - a pointer to the current node in
  1087. // the tree
  1088. //
  1089. // Operation: If we have a left-son then the previous node is the right-most
  1090. // son of this. Otherwise, look for a node of whom we are in the
  1091. // left subtree and return that.
  1092. //
  1093. //
  1094. LPBYTE ASHost::CHAvlPrev(PCHENTRY pEntry)
  1095. {
  1096. //
  1097. // find previous node in tree
  1098. //
  1099. DebugEntry(ASHost::CHAvlPrev);
  1100. ASSERT(IsValidCacheEntry(pEntry));
  1101. ASSERT(IsCacheEntryInTree(pEntry));
  1102. if (pEntry->pLeft != NULL)
  1103. {
  1104. //
  1105. // Previous entry is right-most in left-subtree
  1106. //
  1107. TRACE_OUT(( "previous node is right-most left descendent" ));
  1108. pEntry = pEntry->pLeft;
  1109. ASSERT(IsValidCacheEntry(pEntry));
  1110. while (pEntry->pRight != NULL)
  1111. {
  1112. ASSERT(IsValidCacheEntry(pEntry->pRight));
  1113. pEntry = pEntry->pRight;
  1114. }
  1115. }
  1116. else
  1117. {
  1118. //
  1119. // No left child. So find an entry for which we are in the right
  1120. // subtree.
  1121. //
  1122. TRACE_OUT(( "find node which this is in right subtree of"));
  1123. while (pEntry != NULL)
  1124. {
  1125. ASSERT(IsValidCacheEntry(pEntry));
  1126. if ((pEntry->pParent == NULL) ||
  1127. (pEntry->pParent->pRight == pEntry))
  1128. {
  1129. pEntry = pEntry->pParent;
  1130. break;
  1131. }
  1132. pEntry = pEntry->pParent;
  1133. }
  1134. }
  1135. DebugExitVOID(ASHost::CHAvlPrev);
  1136. return((pEntry != NULL) ? pEntry->pData : NULL);
  1137. }
  1138. //
  1139. // Name: CHAvlFindEqual
  1140. //
  1141. // Purpose: Find the node in the AVL tree with the same key and size as
  1142. // the supplied node
  1143. //
  1144. // Returns: A pointer to the node
  1145. // NULL if no node is found with the specified key and size
  1146. //
  1147. // Params: IN pCache - a pointer to the AVL tree
  1148. // IN pEntry - a pointer to the node to test
  1149. //
  1150. // Operation: Check if the left node has the same key and size, returning
  1151. // a pointer to its data if it does.
  1152. //
  1153. //
  1154. PCHENTRY ASHost::CHAvlFindEqual
  1155. (
  1156. PCHCACHE pCache,
  1157. PCHENTRY pEntry
  1158. )
  1159. {
  1160. int result;
  1161. PCHENTRY pReturn = NULL;
  1162. DebugEntry(ASHost::CHAvlFindEqual);
  1163. ASSERT(IsValidCacheEntry(pEntry));
  1164. if (pEntry->pLeft)
  1165. {
  1166. ASSERT(IsValidCacheEntry(pEntry->pLeft));
  1167. result = CHCompare(pEntry->pLeft->checkSum, pEntry->cbData, pEntry);
  1168. if (result < 0)
  1169. {
  1170. //
  1171. // specified key is less than key of this node - this is what
  1172. // will normally occur
  1173. //
  1174. TRACE_OUT(( "left node size %u csum 0x%08x",
  1175. pEntry->pLeft->cbData,
  1176. pEntry->pLeft->checkSum));
  1177. }
  1178. else if (result == 0)
  1179. {
  1180. //
  1181. // Found a match on size and key.
  1182. //
  1183. TRACE_OUT(( "left node dups size and key" ));
  1184. pReturn = pEntry->pLeft;
  1185. }
  1186. else
  1187. {
  1188. //
  1189. // This is an error (left node should never be greater)
  1190. //
  1191. ERROR_OUT(( "left node csum %#lx, supplied %#lx",
  1192. pEntry->pLeft->checkSum,
  1193. pEntry->checkSum));
  1194. }
  1195. }
  1196. DebugExitPVOID(ASHost::CHAvlFindEqual, pReturn);
  1197. return(pReturn);
  1198. }
  1199. //
  1200. // Name: CHAvlFind
  1201. //
  1202. // Purpose: Find the node in the AVL tree with the supplied key and size
  1203. //
  1204. // Returns: A pointer to the node
  1205. // NULL if no node is found with the specified key and size
  1206. //
  1207. // Params: IN pCache - a pointer to the AVL tree
  1208. // IN checkSum - a pointer to the key
  1209. // IN cbSize - number of node data bytes
  1210. //
  1211. // Operation: Search down the tree going left if the search key is less than
  1212. // the node in the tree and right if the search key is greater.
  1213. // When we run out of tree to search through either we've found
  1214. // it or the node is not in the tree.
  1215. //
  1216. //
  1217. PCHENTRY ASHost::CHAvlFind
  1218. (
  1219. PCHCACHE pCache,
  1220. UINT checkSum,
  1221. UINT cbSize
  1222. )
  1223. {
  1224. PCHENTRY pEntry;
  1225. int result;
  1226. // DebugEntry(ASHost::CHAvlFind);
  1227. pEntry = pCache->pRoot;
  1228. while (pEntry != NULL)
  1229. {
  1230. ASSERT(IsValidCacheEntry(pEntry));
  1231. //
  1232. // Compare the supplied key (checksum) with that of the current node
  1233. //
  1234. result = CHCompare(checkSum, cbSize, pEntry);
  1235. if (result > 0)
  1236. {
  1237. //
  1238. // Supplied key is greater than that of this entry, so look in
  1239. // the right subtree
  1240. //
  1241. pEntry = pEntry->pRight;
  1242. TRACE_OUT(( "move down right subtree to node 0x%08x", pEntry));
  1243. }
  1244. else if (result < 0)
  1245. {
  1246. //
  1247. // Supplied key is lesser than that of this entry, so look in
  1248. // the left subtree
  1249. //
  1250. pEntry = pEntry->pLeft;
  1251. TRACE_OUT(( "move down left subtree to node 0x%08x", pEntry));
  1252. }
  1253. else
  1254. {
  1255. //
  1256. // We found the FIRST entry with an identical key (checksum).
  1257. //
  1258. TRACE_OUT(( "found requested node" ));
  1259. break;
  1260. }
  1261. }
  1262. // DebugExitPVOID(ASHost::CHAvlFind, pEntry);
  1263. return(pEntry);
  1264. }
  1265. //
  1266. // Name: CHAvlBalanceTree
  1267. //
  1268. // Purpose: Reblance the tree starting at the supplied node and ending at
  1269. // the root of the tree
  1270. //
  1271. // Returns: Nothing
  1272. //
  1273. // Params: IN pCache - a pointer to the AVL tree
  1274. // IN pEntry - a pointer to the node to start
  1275. // balancing from
  1276. //
  1277. //
  1278. void ASHost::CHAvlBalanceTree
  1279. (
  1280. PCHCACHE pCache,
  1281. PCHENTRY pEntry
  1282. )
  1283. {
  1284. //
  1285. // Balance the tree starting at the given entry, ending with the root
  1286. // of the tree
  1287. //
  1288. DebugEntry(ASHost::CHAvlBalanceTree);
  1289. ASSERT(IsValidCacheEntry(pEntry));
  1290. while (pEntry->pParent != NULL)
  1291. {
  1292. ASSERT(IsValidCacheEntry(pEntry->pParent));
  1293. //
  1294. // node has uneven balance, so may need to rebalance it
  1295. //
  1296. TRACE_OUT(( "check node balance" ));
  1297. TRACE_OUT(( " rHeight = %hd", pEntry->rHeight ));
  1298. TRACE_OUT(( " lHeight = %hd", pEntry->lHeight ));
  1299. if (pEntry->pParent->pRight == pEntry)
  1300. {
  1301. //
  1302. // node is right-son of its parent
  1303. //
  1304. TRACE_OUT(( "node is right-son" ));
  1305. pEntry = pEntry->pParent;
  1306. CHAvlRebalance(&pEntry->pRight);
  1307. //
  1308. // now update the right height of the parent
  1309. //
  1310. pEntry->rHeight = (WORD)
  1311. (1 + max(pEntry->pRight->rHeight, pEntry->pRight->lHeight));
  1312. TRACE_OUT(( "new rHeight = %d", pEntry->rHeight ));
  1313. }
  1314. else
  1315. {
  1316. //
  1317. // node is left-son of its parent
  1318. //
  1319. TRACE_OUT(( "node is left-son" ));
  1320. pEntry = pEntry->pParent;
  1321. CHAvlRebalance(&pEntry->pLeft);
  1322. //
  1323. // now update the left height of the parent
  1324. //
  1325. pEntry->lHeight = (WORD)
  1326. (1 + max(pEntry->pLeft->rHeight, pEntry->pLeft->lHeight));
  1327. TRACE_OUT(( "new lHeight = %d", pEntry->lHeight ));
  1328. }
  1329. ASSERT(IsValidCacheEntry(pEntry));
  1330. }
  1331. if (pEntry->lHeight != pEntry->rHeight)
  1332. {
  1333. //
  1334. // rebalance root node
  1335. //
  1336. TRACE_OUT(( "rebalance root node"));
  1337. CHAvlRebalance(&pCache->pRoot);
  1338. }
  1339. DebugExitVOID(ASHost::CHAvlBalanceTree);
  1340. }
  1341. //
  1342. // Name: CHAvlRebalance
  1343. //
  1344. // Purpose: Reblance a subtree of the AVL tree (if necessary)
  1345. //
  1346. // Returns: Nothing
  1347. //
  1348. // Params: IN/OUT ppSubtree - a pointer to the subtree to
  1349. // rebalance
  1350. //
  1351. //
  1352. void ASHost::CHAvlRebalance
  1353. (
  1354. PCHENTRY * ppSubtree
  1355. )
  1356. {
  1357. int moment;
  1358. DebugEntry(ASHost::CHAvlRebalance);
  1359. ASSERT(IsValidCacheEntry(*ppSubtree));
  1360. TRACE_OUT(( "rebalance subtree" ));
  1361. TRACE_OUT(( " rHeight = %hd", (*ppSubtree)->rHeight ));
  1362. TRACE_OUT(( " lHeight = %hd", (*ppSubtree)->lHeight ));
  1363. //
  1364. // How unbalanced - don't want to recalculate
  1365. //
  1366. moment = (*ppSubtree)->rHeight - (*ppSubtree)->lHeight;
  1367. if (moment > 1)
  1368. {
  1369. //
  1370. // subtree is heavy on the right side
  1371. //
  1372. TRACE_OUT(( "subtree is heavy on right side" ));
  1373. TRACE_OUT(( "right subtree" ));
  1374. TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pRight->rHeight ));
  1375. TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pRight->lHeight ));
  1376. if ((*ppSubtree)->pRight->lHeight > (*ppSubtree)->pRight->rHeight)
  1377. {
  1378. //
  1379. // right subtree is heavier on left side, so must perform right
  1380. // rotation on this subtree to make it heavier on the right
  1381. // side
  1382. //
  1383. TRACE_OUT(( "right subtree is heavier on left side ..." ));
  1384. TRACE_OUT(( "... so rotate it right" ));
  1385. CHAvlRotateRight(&(*ppSubtree)->pRight);
  1386. TRACE_OUT(( "right subtree" ));
  1387. TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pRight->rHeight ));
  1388. TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pRight->lHeight ));
  1389. }
  1390. //
  1391. // now rotate the subtree left
  1392. //
  1393. TRACE_OUT(( "rotate subtree left" ));
  1394. CHAvlRotateLeft(ppSubtree);
  1395. }
  1396. else if (moment < -1)
  1397. {
  1398. //
  1399. // subtree is heavy on the left side
  1400. //
  1401. TRACE_OUT(( "subtree is heavy on left side" ));
  1402. TRACE_OUT(( "left subtree" ));
  1403. TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pLeft->rHeight ));
  1404. TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pLeft->lHeight ));
  1405. if ((*ppSubtree)->pLeft->rHeight > (*ppSubtree)->pLeft->lHeight)
  1406. {
  1407. //
  1408. // left subtree is heavier on right side, so must perform left
  1409. // rotation on this subtree to make it heavier on the left side
  1410. //
  1411. TRACE_OUT(( "left subtree is heavier on right side ..." ));
  1412. TRACE_OUT(( "... so rotate it left" ));
  1413. CHAvlRotateLeft(&(*ppSubtree)->pLeft);
  1414. TRACE_OUT(( "left subtree" ));
  1415. TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pLeft->rHeight ));
  1416. TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pLeft->lHeight ));
  1417. }
  1418. //
  1419. // now rotate the subtree right
  1420. //
  1421. TRACE_OUT(( "rotate subtree right" ));
  1422. CHAvlRotateRight(ppSubtree);
  1423. }
  1424. TRACE_OUT(( "balanced subtree" ));
  1425. TRACE_OUT(( " rHeight = %d", (*ppSubtree)->rHeight ));
  1426. TRACE_OUT(( " lHeight = %d", (*ppSubtree)->lHeight ));
  1427. DebugExitVOID(ASHost::CHAvlRebalance);
  1428. }
  1429. //
  1430. // Name: CHAvlRotateRight
  1431. //
  1432. // Purpose: Rotate a subtree of the AVL tree right
  1433. //
  1434. // Returns: Nothing
  1435. //
  1436. // Params: IN/OUT ppSubtree - a pointer to the subtree to rotate
  1437. //
  1438. //
  1439. void ASHost::CHAvlRotateRight
  1440. (
  1441. PCHENTRY * ppSubtree
  1442. )
  1443. {
  1444. PCHENTRY pLeftSon;
  1445. DebugEntry(ASHost::CHAvlRotateRight);
  1446. ASSERT(IsValidCacheEntry(*ppSubtree));
  1447. pLeftSon = (*ppSubtree)->pLeft;
  1448. ASSERT(IsValidCacheEntry(pLeftSon));
  1449. (*ppSubtree)->pLeft = pLeftSon->pRight;
  1450. if ((*ppSubtree)->pLeft != NULL)
  1451. {
  1452. (*ppSubtree)->pLeft->pParent = (*ppSubtree);
  1453. }
  1454. (*ppSubtree)->lHeight = pLeftSon->rHeight;
  1455. pLeftSon->pParent = (*ppSubtree)->pParent;
  1456. pLeftSon->pRight = *ppSubtree;
  1457. pLeftSon->pRight->pParent = pLeftSon;
  1458. pLeftSon->rHeight = (WORD)
  1459. (1 + max((*ppSubtree)->rHeight, (*ppSubtree)->lHeight));
  1460. *ppSubtree = pLeftSon;
  1461. DebugExitVOID(ASHost::CHAvlRotateRight);
  1462. }
  1463. //
  1464. // Name: CHAvlRotateLeft
  1465. //
  1466. // Purpose: Rotate a subtree of the AVL tree left
  1467. //
  1468. // Returns: Nothing
  1469. //
  1470. // Params: IN/OUT ppSubtree - a pointer to the subtree to rotate
  1471. //
  1472. //
  1473. void ASHost::CHAvlRotateLeft
  1474. (
  1475. PCHENTRY * ppSubtree
  1476. )
  1477. {
  1478. PCHENTRY pRightSon;
  1479. DebugEntry(ASHost::CHAvlRotateLeft);
  1480. ASSERT(IsValidCacheEntry(*ppSubtree));
  1481. pRightSon = (*ppSubtree)->pRight;
  1482. ASSERT(IsValidCacheEntry(pRightSon));
  1483. (*ppSubtree)->pRight = pRightSon->pLeft;
  1484. if ((*ppSubtree)->pRight != NULL)
  1485. {
  1486. (*ppSubtree)->pRight->pParent = (*ppSubtree);
  1487. }
  1488. (*ppSubtree)->rHeight = pRightSon->lHeight;
  1489. pRightSon->pParent = (*ppSubtree)->pParent;
  1490. pRightSon->pLeft = *ppSubtree;
  1491. pRightSon->pLeft->pParent = pRightSon;
  1492. pRightSon->lHeight = (WORD)
  1493. (1 + max((*ppSubtree)->rHeight, (*ppSubtree)->lHeight));
  1494. *ppSubtree = pRightSon;
  1495. DebugExitVOID(ASHost::CHAvlRotateLeft);
  1496. }
  1497. //
  1498. // Name: CHAvlSwapRightmost
  1499. //
  1500. // Purpose: Swap node with right-most descendent of subtree
  1501. //
  1502. // Returns: Nothing
  1503. //
  1504. // Params: IN pCache - a pointer to the tree
  1505. // IN pSubtree - a pointer to the subtree
  1506. // IN pEntry - a pointer to the node to swap
  1507. //
  1508. //
  1509. void ASHost::CHAvlSwapRightmost
  1510. (
  1511. PCHCACHE pCache,
  1512. PCHENTRY pSubtree,
  1513. PCHENTRY pEntry
  1514. )
  1515. {
  1516. PCHENTRY pSwapEntry;
  1517. PCHENTRY pSwapParent;
  1518. PCHENTRY pSwapLeft;
  1519. DebugEntry(ASHost::CHAvlSwapRightmost);
  1520. ASSERT(IsValidCacheEntry(pEntry));
  1521. ASSERT((pEntry->pRight != NULL));
  1522. ASSERT((pEntry->pLeft != NULL));
  1523. //
  1524. // find right-most descendent of subtree
  1525. //
  1526. ASSERT(IsValidCacheEntry(pSubtree));
  1527. pSwapEntry = pSubtree;
  1528. while (pSwapEntry->pRight != NULL)
  1529. {
  1530. pSwapEntry = pSwapEntry->pRight;
  1531. ASSERT(IsValidCacheEntry(pSwapEntry));
  1532. }
  1533. ASSERT((pSwapEntry->rHeight == 0));
  1534. ASSERT((pSwapEntry->lHeight <= 1));
  1535. //
  1536. // save parent and left-son of right-most descendent
  1537. //
  1538. pSwapParent = pSwapEntry->pParent;
  1539. pSwapLeft = pSwapEntry->pLeft;
  1540. //
  1541. // move swap node to its new position
  1542. //
  1543. pSwapEntry->pParent = pEntry->pParent;
  1544. pSwapEntry->pRight = pEntry->pRight;
  1545. pSwapEntry->pLeft = pEntry->pLeft;
  1546. pSwapEntry->rHeight = pEntry->rHeight;
  1547. pSwapEntry->lHeight = pEntry->lHeight;
  1548. pSwapEntry->pRight->pParent = pSwapEntry;
  1549. pSwapEntry->pLeft->pParent = pSwapEntry;
  1550. if (pEntry->pParent == NULL)
  1551. {
  1552. //
  1553. // node is at root of tree
  1554. //
  1555. pCache->pRoot = pSwapEntry;
  1556. }
  1557. else if (pEntry->pParent->pRight == pEntry)
  1558. {
  1559. //
  1560. // node is right-son of parent
  1561. //
  1562. pSwapEntry->pParent->pRight = pSwapEntry;
  1563. }
  1564. else
  1565. {
  1566. //
  1567. // node is left-son of parent
  1568. //
  1569. pSwapEntry->pParent->pLeft = pSwapEntry;
  1570. }
  1571. //
  1572. // move node to its new position
  1573. //
  1574. pEntry->pParent = pSwapParent;
  1575. pEntry->pRight = NULL;
  1576. pEntry->pLeft = pSwapLeft;
  1577. if (pEntry->pLeft != NULL)
  1578. {
  1579. pEntry->pLeft->pParent = pEntry;
  1580. pEntry->lHeight = 1;
  1581. }
  1582. else
  1583. {
  1584. pEntry->lHeight = 0;
  1585. }
  1586. pEntry->rHeight = 0;
  1587. pEntry->pParent->pRight = pEntry;
  1588. DebugExitVOID(ASHost::CHAvlSwapRightmost);
  1589. }
  1590. //
  1591. // Name: CHAvlSwapLeftmost
  1592. //
  1593. // Purpose: Swap node with left-most descendent of subtree
  1594. //
  1595. // Returns: Nothing
  1596. //
  1597. // Params: IN pCache - a pointer to the tree
  1598. // IN pSubtree - a pointer to the subtree
  1599. // IN pEntry - a pointer to the node to swap
  1600. //
  1601. //
  1602. void ASHost::CHAvlSwapLeftmost
  1603. (
  1604. PCHCACHE pCache,
  1605. PCHENTRY pSubtree,
  1606. PCHENTRY pEntry
  1607. )
  1608. {
  1609. PCHENTRY pSwapEntry;
  1610. PCHENTRY pSwapParent;
  1611. PCHENTRY pSwapRight;
  1612. DebugEntry(ASHost::CHAvlSwapLeftmost);
  1613. ASSERT(IsValidCacheEntry(pEntry));
  1614. ASSERT((pEntry->pRight != NULL));
  1615. ASSERT((pEntry->pLeft != NULL));
  1616. //
  1617. // find left-most descendent of pSubtree
  1618. //
  1619. ASSERT(IsValidCacheEntry(pSubtree));
  1620. pSwapEntry = pSubtree;
  1621. while (pSwapEntry->pLeft != NULL)
  1622. {
  1623. pSwapEntry = pSwapEntry->pLeft;
  1624. ASSERT(IsValidCacheEntry(pSwapEntry));
  1625. }
  1626. ASSERT((pSwapEntry->lHeight == 0));
  1627. ASSERT((pSwapEntry->rHeight <= 1));
  1628. //
  1629. // save parent and right-son of left-most descendent
  1630. //
  1631. pSwapParent = pSwapEntry->pParent;
  1632. pSwapRight = pSwapEntry->pRight;
  1633. //
  1634. // move swap node to its new position
  1635. //
  1636. pSwapEntry->pParent = pEntry->pParent;
  1637. pSwapEntry->pRight = pEntry->pRight;
  1638. pSwapEntry->pLeft = pEntry->pLeft;
  1639. pSwapEntry->rHeight = pEntry->rHeight;
  1640. pSwapEntry->lHeight = pEntry->lHeight;
  1641. pSwapEntry->pRight->pParent = pSwapEntry;
  1642. pSwapEntry->pLeft->pParent = pSwapEntry;
  1643. if (pEntry->pParent == NULL)
  1644. {
  1645. //
  1646. // node is at root of tree
  1647. //
  1648. pCache->pRoot = pSwapEntry;
  1649. }
  1650. else if (pEntry->pParent->pRight == pEntry)
  1651. {
  1652. //
  1653. // node is right-son of parent
  1654. //
  1655. pSwapEntry->pParent->pRight = pSwapEntry;
  1656. }
  1657. else
  1658. {
  1659. //
  1660. // node is left-son of parent
  1661. //
  1662. pSwapEntry->pParent->pLeft = pSwapEntry;
  1663. }
  1664. //
  1665. // move node to its new position
  1666. //
  1667. pEntry->pParent = pSwapParent;
  1668. pEntry->pRight = pSwapRight;
  1669. pEntry->pLeft = NULL;
  1670. if (pEntry->pRight != NULL)
  1671. {
  1672. pEntry->pRight->pParent = pEntry;
  1673. pEntry->rHeight = 1;
  1674. }
  1675. else
  1676. {
  1677. pEntry->rHeight = 0;
  1678. }
  1679. pEntry->lHeight = 0;
  1680. pEntry->pParent->pLeft = pEntry;
  1681. DebugExitVOID(ASHost::CHAvlSwapLeftmost);
  1682. }
  1683. //
  1684. // Name: CHCompare
  1685. //
  1686. // Purpose: Standard function for comparing UINTs
  1687. //
  1688. // Returns: -1 if key < pEntry->checksum
  1689. // -1 if key = pEntry->checksum AND sizes do not match
  1690. // 0 if key = pEntry->checksum AND sizes match
  1691. // 1 if key > pEntry->checksum
  1692. //
  1693. // Params: IN key - a pointer to the comparison key
  1694. // IN cbSize - number of comparison data bytes
  1695. // IN pEntry - a pointer to the node to compare
  1696. //
  1697. //
  1698. int ASHost::CHCompare
  1699. (
  1700. UINT key,
  1701. UINT cbSize,
  1702. PCHENTRY pEntry
  1703. )
  1704. {
  1705. int ret_val;
  1706. DebugEntry(ASHost::CHCompare);
  1707. ASSERT(IsValidCacheEntry(pEntry));
  1708. if (key < pEntry->checkSum)
  1709. {
  1710. ret_val = -1;
  1711. TRACE_OUT(( "Key is less (-1)"));
  1712. }
  1713. else if (key > pEntry->checkSum)
  1714. {
  1715. ret_val = 1;
  1716. TRACE_OUT(( "Key is more (+1)"));
  1717. }
  1718. else
  1719. {
  1720. if (cbSize == pEntry->cbData)
  1721. {
  1722. ret_val = 0;
  1723. TRACE_OUT(( "Key and size match"));
  1724. }
  1725. else
  1726. {
  1727. ret_val = -1;
  1728. TRACE_OUT(( "Key match, size mismatch (-1)"));
  1729. }
  1730. }
  1731. DebugExitDWORD(ASHost::CHCompare, ret_val);
  1732. return(ret_val);
  1733. }