Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1067 lines
26 KiB

  1. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. //
  3. // GENCACHE.H
  4. //
  5. // Header for generic cache classes.
  6. //
  7. // Copyright 1997-1998 Microsoft Corporation, All Rights Reserved
  8. //
  9. // Documenting my dependencies
  10. // These must be included BEFORE this file is included.
  11. //#include "caldbg.h"
  12. //#include "autoptr.h"
  13. #ifndef _EX_GENCACHE_H_
  14. #define _EX_GENCACHE_H_
  15. #pragma warning(disable:4200) // zero-sized array
  16. // Exdav-safe allocators --------------------------------------------------
  17. //
  18. // The classed declared here use the EXDAV-safe allocators (ExAlloc, ExFree)
  19. // for all allocations.
  20. // NOTE: These allocators can FAIL. You must check for failure on
  21. // ExAlloc and ExRealloc!
  22. //
  23. #include <ex\exmem.h>
  24. #include <align.h>
  25. // ========================================================================
  26. //
  27. // TEMPLATE CLASS CPoolAllocator
  28. //
  29. // A generic type-specific pool allocator template. Items in the pool
  30. // are allocated in chunks and handed out upon request.
  31. // Items are recycled on a free chain.
  32. // All items are the same size, so reuse is relatively easy.
  33. //
  34. // NOTE: I have NOT optimized the heck out of this thing. To really
  35. // optimize locality of mem usage, we'd want to always grow & shrink
  36. // "at the tail". To that end, I always check the freechain first --
  37. // reuse an item before using a new one from the current buffer.
  38. // More optimization would require sorting the freechain & stuff.
  39. //
  40. template<class T>
  41. class CPoolAllocator
  42. {
  43. // CHAINBUFHDR ----------------------------------------
  44. // Header struct for chaining together pool buffers.
  45. //
  46. struct CHAINBUFHDR
  47. {
  48. CHAINBUFHDR * phbNext;
  49. int cItems;
  50. int cItemsUsed;
  51. // Remainder of buffer is set of items of type T.
  52. T rgtPool[0];
  53. // Just to quiet our noisy compiler.
  54. CHAINBUFHDR() {};
  55. ~CHAINBUFHDR() {};
  56. };
  57. // CHAINITEM ------------------------------------------
  58. // Struct "applied over" free items to chain them together.
  59. // The actual items MUST be large enough to accomodate this.
  60. //
  61. struct CHAINITEM
  62. {
  63. CHAINITEM * piNext;
  64. };
  65. // Chain of buffers.
  66. CHAINBUFHDR * m_phbCurrent;
  67. // Chain of free'd items to reuse.
  68. CHAINITEM * m_piFreeChain;
  69. // Size of chunk to alloc.
  70. int m_ciChunkSize;
  71. // Constant (enum) for our default starting chunk size (in items).
  72. //
  73. enum { CHUNKSIZE_START = 20 };
  74. public:
  75. // Constructor takes count of items for initial chunk size.
  76. //
  77. CPoolAllocator (int ciChunkSize = CHUNKSIZE_START) :
  78. m_phbCurrent(NULL),
  79. m_piFreeChain(NULL),
  80. m_ciChunkSize(ciChunkSize)
  81. {
  82. // A CHAINITEM struct will be "applied over" free items to chain
  83. // them together.
  84. // The actual items MUST be large enough to accomodate this.
  85. //
  86. Assert (sizeof(T) >= sizeof(CHAINITEM));
  87. };
  88. ~CPoolAllocator()
  89. {
  90. // Walk the list of blocks we allocated and free them.
  91. //
  92. while (m_phbCurrent)
  93. {
  94. CHAINBUFHDR * phbTemp = m_phbCurrent->phbNext;
  95. ExFree (m_phbCurrent);
  96. m_phbCurrent = phbTemp;
  97. }
  98. }
  99. // ------------------------------------------------------------------------
  100. // GetItem
  101. // Return an item from the pool to our caller.
  102. // We get the item from the free chain, or from the next block of items.
  103. //
  104. T * GetItem()
  105. {
  106. T * ptToReturn;
  107. if (m_piFreeChain)
  108. {
  109. // The free chain is non-empty. Return the first item here.
  110. //
  111. ptToReturn = reinterpret_cast<T *>(m_piFreeChain);
  112. m_piFreeChain = m_piFreeChain->piNext;
  113. }
  114. else
  115. {
  116. // The free chain is empty. We must grab a never-used item from
  117. // the current block.
  118. //
  119. if (!m_phbCurrent ||
  120. (m_phbCurrent->cItemsUsed == m_phbCurrent->cItems))
  121. {
  122. // There are no more items in the current block.
  123. // Allocate a whole new block of items.
  124. //
  125. CHAINBUFHDR * phbNew = static_cast<CHAINBUFHDR *>(
  126. ExAlloc (sizeof(CHAINBUFHDR) +
  127. (m_ciChunkSize * sizeof(T))));
  128. // The allocators CAN FAIL. Handle this case!
  129. if (!phbNew)
  130. return NULL;
  131. phbNew->cItems = m_ciChunkSize;
  132. phbNew->cItemsUsed = 0;
  133. phbNew->phbNext = m_phbCurrent;
  134. m_phbCurrent = phbNew;
  135. }
  136. // Now we should have a block with an unused item for us to return.
  137. //
  138. Assert (m_phbCurrent &&
  139. (m_phbCurrent->cItemsUsed < m_phbCurrent->cItems));
  140. ptToReturn = & m_phbCurrent->rgtPool[ m_phbCurrent->cItemsUsed++ ];
  141. }
  142. Assert (ptToReturn);
  143. return ptToReturn;
  144. }
  145. // ------------------------------------------------------------------------
  146. // FreeItem
  147. // The caller is done with this item. Add it to our free chain.
  148. //
  149. void FreeItem (T * pi)
  150. {
  151. // Add the item to the free chain.
  152. // To do this without allocating more memory, we use the item's
  153. // storage to hold our next-pointer.
  154. // The actual items MUST be large enough to accomodate this.
  155. //
  156. reinterpret_cast<CHAINITEM *>(pi)->piNext = m_piFreeChain;
  157. m_piFreeChain = reinterpret_cast<CHAINITEM *>(pi);
  158. }
  159. };
  160. // ========================================================================
  161. //
  162. // TEMPLATE CLASS CCache
  163. //
  164. // A generic hash cache template. Items in the cache uniquely map keys of
  165. // type _K to values of type _Ty. Keys and values are copied when
  166. // they are added to the cache; there is no "ownership".
  167. //
  168. // The key (type _K) must provide methods hash and isequal. These methods
  169. // will be used to hash and compare the keys.
  170. //
  171. //
  172. // Add()
  173. // Adds an item (key/value pair) to the cache. Returns a reference
  174. // to the added item's value.
  175. //
  176. // Set()
  177. // Sets an item's value, adding the item if it doesn't already exist.
  178. // Returns a reference to the added item's value.
  179. //
  180. // Lookup()
  181. // Looks for an item with the specified key. If the item exists,
  182. // returns a pointer to its value, otherwise returns NULL.
  183. //
  184. // FFetch()
  185. // Boolean version of Lookup().
  186. //
  187. // Remove()
  188. // Removes the item associated with a particular key.
  189. // Does nothing if there is no item with that key.
  190. //
  191. // Clear()
  192. // Removes all items from the cache.
  193. //
  194. // ForEach()
  195. // Applies an operation, specified by an operator object passed in
  196. // as a parameter, to all of the items in the cache.
  197. //
  198. // ForEachMatch()
  199. // Applies an operation, specified by an operator object passed in
  200. // as a parameter, to each item in the cache that matches the provided key.
  201. //
  202. // Additional functions proposed
  203. // Rehash - currently ITP only
  204. // Resize the table & re-add all items.
  205. // DumpCacheUsage() - NYI
  206. // Dump the bookkeeping data about the cache.
  207. //
  208. template<class _K, class _Ty>
  209. class CCache
  210. {
  211. // ---------------------------------------------------------------------
  212. // Cache Entry structures
  213. //
  214. struct Entry
  215. {
  216. struct Entry * peNext;
  217. _K key;
  218. _Ty data;
  219. #ifdef DBG
  220. BOOL fValid; // Is this entry valid?
  221. #endif // DBG
  222. // CONSTRUCTORS
  223. Entry (const _K& k, const _Ty& d) :
  224. key(k),
  225. data(d)
  226. {
  227. };
  228. //
  229. // The following is to get around the fact that the store has
  230. // defined a "new" macro which makes using the new operator to
  231. // do in place initialization very difficult
  232. //
  233. void EntryInit (const _K& k, const _Ty& d) {
  234. key = k;
  235. data = d;
  236. };
  237. };
  238. struct TableEntry //HashLine
  239. {
  240. BOOL fLineValid; // Is this cache line valid?
  241. Entry * peChain;
  242. #ifdef DBG
  243. int cEntries; // Number of entries in this line in the cache.
  244. mutable BYTE cAccesses; // Bookkeeping
  245. #endif // DBG
  246. };
  247. // ---------------------------------------------------------------------
  248. // The hash table data
  249. //
  250. int m_cSize; // Size of the hash table.
  251. auto_heap_ptr<TableEntry> m_argTable; // The hash table.
  252. int m_cItems; // Current number of items in the cache.
  253. // ---------------------------------------------------------------------
  254. // Pool allocator to alloc nodes
  255. //
  256. CPoolAllocator<Entry> m_poolalloc;
  257. // ---------------------------------------------------------------------
  258. // Constant (enum) for our default initial count of lines in the cache.
  259. // NOTE: Callers should really pick their own best size.
  260. // This size is prime to try to force fewer collisions.
  261. //
  262. enum { CACHESIZE_START = 37 };
  263. #ifdef DBG
  264. // ---------------------------------------------------------------------
  265. // Bookkeeping bits
  266. //
  267. int m_cCollisions; // Adds that hit the same chain
  268. mutable int m_cHits; // Lookup/Set hits
  269. mutable int m_cMisses; // Lookup/Set misses
  270. #endif // DBG
  271. // ---------------------------------------------------------------------
  272. // Helper function to build the table
  273. //
  274. BOOL FBuildTable()
  275. {
  276. Assert (!m_argTable.get());
  277. // Allocate space for the number of cache lines we need (m_cSize).
  278. //
  279. m_argTable = reinterpret_cast<TableEntry *>(ExAlloc (
  280. m_cSize * sizeof(TableEntry)));
  281. // The allocators CAN FAIL. Handle this case!
  282. if (!m_argTable.get())
  283. return FALSE;
  284. ZeroMemory (m_argTable.get(), m_cSize * sizeof(TableEntry));
  285. return TRUE;
  286. }
  287. // ---------------------------------------------------------------------
  288. // CreateEntry
  289. // Create a new entry to add to the cache.
  290. //
  291. Entry * CreateEntry(const _K& k, const _Ty& d)
  292. {
  293. Entry * peNew = m_poolalloc.GetItem();
  294. // The allocators CAN FAIL. Handle this case!
  295. if (!peNew)
  296. return NULL;
  297. ZeroMemory (peNew, sizeof(Entry));
  298. // peNew = new (peNew) Entry(k,d);
  299. peNew->EntryInit (k,d);
  300. #ifdef DBG
  301. peNew->fValid = TRUE;
  302. #endif // DBG
  303. return peNew;
  304. }
  305. void DeleteEntry(Entry * pe)
  306. {
  307. pe->~Entry();
  308. #ifdef DBG
  309. pe->fValid = FALSE;
  310. #endif // DBG
  311. m_poolalloc.FreeItem (pe);
  312. }
  313. // NOT IMPLEMENTED
  314. //
  315. CCache (const CCache&);
  316. CCache& operator= (const CCache&);
  317. public:
  318. // =====================================================================
  319. //
  320. // TEMPLATE CLASS IOp
  321. //
  322. // Operator base class interface used in ForEach() operations
  323. // on the cache.
  324. // The operator can return FALSE to cancel iteration, or TRUE to
  325. // continue walking the cache.
  326. //
  327. class IOp
  328. {
  329. // NOT IMPLEMENTED
  330. //
  331. IOp& operator= (const IOp&);
  332. public:
  333. virtual BOOL operator() (const _K& key,
  334. const _Ty& value) = 0;
  335. };
  336. // =====================================================================
  337. //
  338. // CREATORS
  339. //
  340. CCache (int cSize = CACHESIZE_START) :
  341. m_cSize(cSize),
  342. m_cItems(0)
  343. {
  344. Assert (m_cSize); // table size of zero is invalid!
  345. // (and would cause div-by-zero errors later!)
  346. #ifdef DBG
  347. m_cCollisions = 0;
  348. m_cHits = 0;
  349. m_cMisses = 0;
  350. #endif // DBG
  351. };
  352. ~CCache()
  353. {
  354. // If we have a table (FInit was successfully called), clear it.
  355. //
  356. if (m_argTable.get())
  357. Clear();
  358. // Auto-pointer will clean up the table.
  359. };
  360. BOOL FInit()
  361. {
  362. // Set up the cache with the provided initial size.
  363. // When running under the store (exdav.dll) THIS CAN FAIL!
  364. //
  365. return FBuildTable();
  366. }
  367. // =====================================================================
  368. //
  369. // ACCESSORS
  370. //
  371. // --------------------------------------------------------------------
  372. // CItems
  373. // Returns the number of items in the cache.
  374. //
  375. int CItems() const
  376. {
  377. return m_cItems;
  378. }
  379. // --------------------------------------------------------------------
  380. // Lookup
  381. // Find the first item in the cache that matches this key.
  382. // key.hash is used to find the correct line of the cache.
  383. // key.isequal is called on each item in the collision chain until a
  384. // match is found.
  385. //
  386. _Ty * Lookup (const _K& key) const
  387. {
  388. // Find the index of the correct cache line for this key.
  389. //
  390. int iHash = key.hash(m_cSize);
  391. Assert (iHash < m_cSize);
  392. Assert (m_argTable.get());
  393. #ifdef DBG
  394. TableEntry * pte = &m_argTable[iHash];
  395. pte->cAccesses++;
  396. #endif // DBG
  397. // Do we have any entries in this cache line?
  398. // If this cache line is not valid, we have no entries -- NOT found.
  399. //
  400. if (m_argTable[iHash].fLineValid)
  401. {
  402. Entry * pe = m_argTable[iHash].peChain;
  403. while (pe)
  404. {
  405. Assert (pe->fValid);
  406. if (key.isequal (pe->key))
  407. {
  408. #ifdef DBG
  409. m_cHits++;
  410. #endif // DBG
  411. return &pe->data;
  412. }
  413. pe = pe->peNext;
  414. }
  415. }
  416. #ifdef DBG
  417. m_cMisses++;
  418. #endif // DBG
  419. return NULL;
  420. }
  421. // --------------------------------------------------------------------
  422. // FFetch
  423. // Boolean-returning wrapper for Lookup.
  424. //
  425. BOOL FFetch (const _K& key, _Ty * pValueRet) const
  426. {
  427. _Ty * pValueFound = Lookup (key);
  428. if (pValueFound)
  429. {
  430. *pValueRet = *pValueFound;
  431. return TRUE;
  432. }
  433. return FALSE;
  434. }
  435. // --------------------------------------------------------------------
  436. // ForEach
  437. // Seek through the cache, calling the provided operator on each item.
  438. // The operator can return FALSE to cancel iteration, or TRUE to continue
  439. // walking the cache.
  440. //
  441. // NOTE: This function is built to allow deletion of the item being
  442. // visited (see the comment inside the while loop -- fetch a pointer
  443. // to the next item BEFORE calling the visitor op), BUT other
  444. // deletes and adds are not "supported" and will have undefined results.
  445. // Two specific disaster scenarios: delete of some other item could
  446. // actually delete the item we pre-fetched, and we will crash on the
  447. // next time around the loop. add of any item during the op callback
  448. // could end up adding the item either before or after our current loop
  449. // position, and thus might get visited, or might not.
  450. //
  451. void ForEach (IOp& op) const
  452. {
  453. // If we don't have any items, quit now!
  454. //
  455. if (!m_cItems)
  456. return;
  457. Assert (m_argTable.get());
  458. // Loop through all items in the cache, calling the
  459. // provided operator on each item.
  460. //
  461. for (int iHash = 0; iHash < m_cSize; iHash++)
  462. {
  463. // Look for valid cache entries.
  464. //
  465. if (m_argTable[iHash].fLineValid)
  466. {
  467. Entry * pe = m_argTable[iHash].peChain;
  468. while (pe)
  469. {
  470. // To support deleting inside the op,
  471. // fetch the next item BEFORE calling the op.
  472. //
  473. Entry * peNext = pe->peNext;
  474. Assert (pe->fValid);
  475. // Found a valid entry. Call the operator.
  476. // If the operator returns TRUE, keep looping.
  477. // If he returns FALSE, quit the loop.
  478. //
  479. if (!op (pe->key, pe->data))
  480. return;
  481. pe = peNext;
  482. }
  483. }
  484. }
  485. }
  486. // --------------------------------------------------------------------
  487. // ForEachMatch
  488. // Seek through the cache, calling the provided operator on each item
  489. // that has a matching key. This is meant to be used with a cache
  490. // that may have duplicate items.
  491. // The operator can return FALSE to cancel iteration, or TRUE to continue
  492. // walking the cache.
  493. //
  494. void ForEachMatch (const _K& key, IOp& op) const
  495. {
  496. // If we don't have any items, quit now!
  497. //
  498. if (!m_cItems)
  499. return;
  500. // Find the index of the correct cache line for this key.
  501. //
  502. int iHash = key.hash(m_cSize);
  503. Assert (iHash < m_cSize);
  504. Assert (m_argTable.get());
  505. #ifdef DBG
  506. TableEntry * pte = &m_argTable[iHash];
  507. pte->cAccesses++;
  508. #endif // DBG
  509. // Only process if this row of the cache is valid.
  510. //
  511. if (m_argTable[iHash].fLineValid)
  512. {
  513. // Loop through all items in this row of the cache, calling the
  514. // provided operator on each item.
  515. //
  516. Entry * pe = m_argTable[iHash].peChain;
  517. while (pe)
  518. {
  519. // To support deleting inside the op,
  520. // fetch the next item BEFORE calling the op.
  521. //
  522. Entry * peNext = pe->peNext;
  523. Assert (pe->fValid);
  524. if (key.isequal (pe->key))
  525. {
  526. // Found a matching entry. Call the operator.
  527. // If the operator returns TRUE, keep looping.
  528. // If he returns FALSE, quit the loop.
  529. //
  530. if (!op (pe->key, pe->data))
  531. return;
  532. }
  533. pe = peNext;
  534. }
  535. }
  536. }
  537. // =====================================================================
  538. //
  539. // MANIPULATORS
  540. //
  541. // --------------------------------------------------------------------
  542. // FSet
  543. // Reset the value of an item in the cache, adding it if the item
  544. // does not yet exist.
  545. //
  546. BOOL FSet (const _K& key, const _Ty& value)
  547. {
  548. // Find the index of the correct cache line for this key.
  549. //
  550. int iHash = key.hash (m_cSize);
  551. Assert (iHash < m_cSize);
  552. Assert (m_argTable.get());
  553. #ifdef DBG
  554. TableEntry * pte = &m_argTable[iHash];
  555. pte->cAccesses++;
  556. #endif // DBG
  557. // Look for valid cache entries.
  558. //
  559. if (m_argTable[iHash].fLineValid)
  560. {
  561. Entry * pe = m_argTable[iHash].peChain;
  562. while (pe)
  563. {
  564. Assert (pe->fValid);
  565. if (key.isequal (pe->key))
  566. {
  567. #ifdef DBG
  568. m_cHits++;
  569. #endif // DBG
  570. pe->data = value;
  571. return TRUE;
  572. }
  573. pe = pe->peNext;
  574. }
  575. }
  576. #ifdef DBG
  577. m_cMisses++;
  578. #endif // DBG
  579. // The items does NOT exist in the cache. Add it now.
  580. //
  581. return FAdd (key, value);
  582. }
  583. // --------------------------------------------------------------------
  584. // FAdd
  585. // Add an item to the cache.
  586. // WARNING: Duplicate keys will be blindly added here! Use FSet()
  587. // if you want to change the value for an existing item. Use Lookup()
  588. // to check if a matching item already exists.
  589. //$LATER: On DBG, scan the list for duplicate keys.
  590. //
  591. BOOL FAdd (const _K& key, const _Ty& value)
  592. {
  593. // Create a new element to add to the chain.
  594. // NOTE: This calls the copy constructors for the key & value!
  595. //
  596. Entry * peNew = CreateEntry (key, value);
  597. // The allocators CAN FAIL. Handle this case!
  598. if (!peNew)
  599. return FALSE;
  600. // Find the index of the correct cache line for this key.
  601. //
  602. int iHash = key.hash (m_cSize);
  603. Assert (iHash < m_cSize);
  604. Assert (m_argTable.get());
  605. #ifdef DBG
  606. TableEntry * pte = &m_argTable[iHash];
  607. pte->cEntries++;
  608. if (m_argTable[iHash].peChain)
  609. m_cCollisions++;
  610. else
  611. pte->cAccesses = 0;
  612. #endif // DBG
  613. // Link this new element into the chain.
  614. //
  615. peNew->peNext = m_argTable[iHash].peChain;
  616. m_argTable[iHash].peChain = peNew;
  617. m_argTable[iHash].fLineValid = TRUE;
  618. m_cItems++;
  619. return TRUE;
  620. }
  621. // --------------------------------------------------------------------
  622. // Remove
  623. // Remove an item from the cache.
  624. //$REVIEW: Does this need to return a "found" boolean??
  625. //
  626. void Remove (const _K& key)
  627. {
  628. // Find the index of the correct cache line for this key.
  629. //
  630. int iHash = key.hash (m_cSize);
  631. Assert (iHash < m_cSize);
  632. Assert (m_argTable.get());
  633. #ifdef DBG
  634. TableEntry * pte = &m_argTable[iHash];
  635. pte->cAccesses++;
  636. #endif // DBG
  637. // If this cache line is not valid, we have no entries --
  638. // nothing to remove.
  639. //
  640. if (m_argTable[iHash].fLineValid)
  641. {
  642. Entry * pe = m_argTable[iHash].peChain;
  643. Entry * peNext = pe->peNext;
  644. Assert (pe->fValid);
  645. // Delete first item in chain.
  646. //
  647. if (key.isequal (pe->key))
  648. {
  649. // Snip the item to delete (pe) out of the chain.
  650. m_argTable[iHash].peChain = peNext;
  651. if (!peNext)
  652. {
  653. // We deleted the last item. This line is empty.
  654. //
  655. m_argTable[iHash].fLineValid = FALSE;
  656. }
  657. // Delete entry to destroy the copied data (value) object.
  658. DeleteEntry (pe);
  659. m_cItems--;
  660. #ifdef DBG
  661. pte->cEntries--;
  662. #endif // DBG
  663. }
  664. else
  665. {
  666. // Lookahead compare & delete.
  667. //
  668. while (peNext)
  669. {
  670. Assert (peNext->fValid);
  671. if (key.isequal (peNext->key))
  672. {
  673. // Snip peNext out of the chain.
  674. pe->peNext = peNext->peNext;
  675. // Delete entry to destroy the copied data (value) object.
  676. DeleteEntry (peNext);
  677. m_cItems--;
  678. #ifdef DBG
  679. pte->cEntries--;
  680. #endif // DBG
  681. break;
  682. }
  683. // Advance
  684. pe = peNext;
  685. peNext = pe->peNext;
  686. }
  687. }
  688. }
  689. }
  690. // --------------------------------------------------------------------
  691. // Clear
  692. // Clear all items from the cache.
  693. // NOTE: This does not destroy the table -- the cache is still usable
  694. // after this call.
  695. //
  696. void Clear()
  697. {
  698. if (m_argTable.get())
  699. {
  700. // Walk the cache, checking for valid lines.
  701. //
  702. for (int iHash = 0; iHash < m_cSize; iHash++)
  703. {
  704. // If the line if valid, look for items to clear out.
  705. //
  706. if (m_argTable[iHash].fLineValid)
  707. {
  708. Entry * pe = m_argTable[iHash].peChain;
  709. // The cache line was marked as valid. There should be
  710. // at least one item here.
  711. Assert (pe);
  712. // Walk the chain of items in this cache line.
  713. //
  714. while (pe)
  715. {
  716. Entry * peTemp = pe->peNext;
  717. Assert (pe->fValid);
  718. // Delete entry to destroy the copied data (value) object.
  719. DeleteEntry (pe);
  720. pe = peTemp;
  721. }
  722. }
  723. // Clear out our cache line.
  724. //
  725. m_argTable[iHash].peChain = NULL;
  726. m_argTable[iHash].fLineValid = FALSE;
  727. #ifdef DBG
  728. // Clear out the bookkeeping bits in the cache line.
  729. //
  730. m_argTable[iHash].cEntries = 0;
  731. m_argTable[iHash].cAccesses = 0;
  732. #endif // DBG
  733. }
  734. // We have no more items.
  735. //
  736. m_cItems = 0;
  737. }
  738. }
  739. #ifdef ITP_USE_ONLY
  740. // ---------------------------------------------------------------------
  741. // Rehash
  742. // Re-allocates the cache's hash table and re-hashes all items.
  743. // NOTE: If this call fails (due to memory failure), the old hash table
  744. // is restored so that we don't lose any the items.
  745. // **RA** This call has NOT been tested in production (shipping) code!
  746. // **RA** It is provided here for ITP use only!!!
  747. //
  748. BOOL FRehash (int cNewSize)
  749. {
  750. Assert (m_argTable.get());
  751. // Swap out the old table and build a new one.
  752. //
  753. auto_heap_ptr<TableEntry> pOldTable ( m_argTable.relinquish() );
  754. int cOldSize = m_cSize;
  755. Assert (pOldTable.get());
  756. m_cSize = cNewSize;
  757. if (!FBuildTable())
  758. {
  759. Assert (pOldTable.get());
  760. Assert (!m_argTable.get());
  761. // Restore the old table.
  762. //
  763. m_cSize = cOldSize;
  764. m_argTable = pOldTable.relinquish();
  765. return FALSE;
  766. }
  767. // If no items in the cache, we're done!
  768. //
  769. if (!m_cItems)
  770. {
  771. return TRUE;
  772. }
  773. // Loop through all items in the cache (old table), placing them
  774. // into the new table.
  775. //
  776. for ( int iHash = 0; iHash < cOldSize; iHash++ )
  777. {
  778. // Look for valid cache entries.
  779. //
  780. if (pOldTable[iHash].fLineValid)
  781. {
  782. Entry * pe = pOldTable[iHash].peChain;
  783. while (pe)
  784. {
  785. // Keep track of next item.
  786. Entry * peNext = pe->peNext;
  787. Assert (pe->fValid);
  788. // Found a valid entry. Place it in the new hash table.
  789. //
  790. int iHashNew = pe->key.hash (m_cSize);
  791. pe->peNext = m_argTable[iHashNew].peChain;
  792. m_argTable[iHashNew].peChain = pe;
  793. m_argTable[iHashNew].fLineValid = TRUE;
  794. #ifdef DBG
  795. m_argTable[iHashNew].cEntries++;
  796. #endif // DBG
  797. pe = peNext;
  798. }
  799. }
  800. }
  801. // We're done re-filling the cache.
  802. //
  803. return TRUE;
  804. }
  805. #endif // ITP_USE_ONLY
  806. };
  807. // ========================================================================
  808. // COMMON KEY CLASSES
  809. // ========================================================================
  810. // ========================================================================
  811. // class DwordKey
  812. // Key class for any dword data that can be compared with ==.
  813. //
  814. class DwordKey
  815. {
  816. private:
  817. DWORD m_dw;
  818. public:
  819. DwordKey (DWORD dw) : m_dw(dw) {}
  820. DWORD Dw() const
  821. {
  822. return m_dw;
  823. }
  824. int DwordKey::hash (const int rhs) const
  825. {
  826. return (m_dw % rhs);
  827. }
  828. bool DwordKey::isequal (const DwordKey& rhs) const
  829. {
  830. return (rhs.m_dw == m_dw);
  831. }
  832. };
  833. // ========================================================================
  834. // class PvoidKey
  835. // Key class for any pointer data that can be compared with ==.
  836. //
  837. class PvoidKey
  838. {
  839. private:
  840. PVOID m_pv;
  841. public:
  842. PvoidKey (PVOID pv) : m_pv(pv) {}
  843. // operators for use with the hash cache
  844. //
  845. int PvoidKey::hash (const int rhs) const
  846. {
  847. // Since we are talking about ptrs, we want
  848. // to shift the pointer such that the hash
  849. // values don't tend to overlap due to alignment
  850. // NOTE: This shouldn't matter if you choose your hash table size
  851. // (rhs) well.... but it also doesn't hurt.
  852. //
  853. return (int)((reinterpret_cast<UINT_PTR>(m_pv) >> ALIGN_NATURAL) % rhs);
  854. }
  855. bool PvoidKey::isequal (const PvoidKey& rhs) const
  856. {
  857. // Just check if the values are equal.
  858. //
  859. return (rhs.m_pv == m_pv);
  860. }
  861. };
  862. // ========================================================================
  863. //
  864. // CLASS Int64Key
  865. //
  866. // __int64-based key class for use with the CCache (hashcache).
  867. //
  868. class Int64Key
  869. {
  870. private:
  871. __int64 m_i64;
  872. // NOT IMPLEMENTED
  873. //
  874. bool operator< (const Int64Key& rhs) const;
  875. public:
  876. Int64Key (__int64 i64) :
  877. m_i64(i64)
  878. {
  879. };
  880. // operators for use with the hash cache
  881. //
  882. int hash (const int rhs) const
  883. {
  884. // Don't even bother with the high part of the int64.
  885. // The mod operation would lose that part anyway....
  886. //
  887. return ( static_cast<UINT>(m_i64) % rhs );
  888. }
  889. BOOL isequal (const Int64Key& rhs) const
  890. {
  891. // Just check if the ids are equal.
  892. //
  893. return ( m_i64 == rhs.m_i64 );
  894. }
  895. };
  896. // ========================================================================
  897. // CLASS GuidKey
  898. // Key class for per-MDB cache of prop-mapping tables.
  899. //
  900. class GuidKey
  901. {
  902. private:
  903. const GUID * m_pguid;
  904. public:
  905. GuidKey (const GUID * pguid) :
  906. m_pguid(pguid)
  907. {
  908. }
  909. // operators for use with the hash cache
  910. //
  911. int hash (const int rhs) const
  912. {
  913. return (m_pguid->Data1 % rhs);
  914. }
  915. bool isequal (const GuidKey& rhs) const
  916. {
  917. return (!!IsEqualGUID (*m_pguid, *rhs.m_pguid));
  918. }
  919. };
  920. // ========================================================================
  921. // CLASS StoredGuidKey
  922. // Key class for per-MDB cache of prop-mapping tables.
  923. //
  924. class StoredGuidKey
  925. {
  926. private:
  927. GUID m_guid;
  928. public:
  929. StoredGuidKey (const GUID guid)
  930. {
  931. CopyMemory(&m_guid, &guid, sizeof(GUID));
  932. }
  933. // operators for use with the hash cache
  934. //
  935. int hash (const int rhs) const
  936. {
  937. return (m_guid.Data1 % rhs);
  938. }
  939. bool isequal (const StoredGuidKey& rhs) const
  940. {
  941. return (!!IsEqualGUID (m_guid, rhs.m_guid));
  942. }
  943. };
  944. #endif // !_EX_GENCACHE_H_