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.

758 lines
15 KiB

  1. /*++
  2. FDLHash.h
  3. This file contains a template class for a hash table.
  4. The templates used in here build off the templates in tfdlist.h for
  5. doubly linked lists. The bucket chains implemented in this version
  6. of the hash table are doubly linked lists.
  7. The Data type must support the following :
  8. class Data {
  9. DLSIT_ENTRY m_list;
  10. KEYREF GetKey( ) ;
  11. } ;
  12. int MatchKey( KEYREF otherkey, KEYREF otherkey) ; /* NOTE : MatchKey returns non-zero on equality
  13. DWORD (* m_pfnReHash)(Data* p ) ;
  14. DWORD (* m_pfnHash)( KEYREF k ) ;
  15. --*/
  16. #ifndef _FDLHASH_H_
  17. #define _FDLHASH_H_
  18. #include "tfdlist.h"
  19. class CHashStats {
  20. public :
  21. enum COUNTER {
  22. HASHITEMS = 0, // Number of items in the hash table
  23. INSERTS, // Number of times Insert has been called
  24. SPLITINSERTS, // Number of inserts until the next split !
  25. DELETES, // Number of times Delete has been called
  26. SEARCHES, // Number of times Search has been called
  27. SEARCHHITS, // Number of times we Search and find something !
  28. SPLITS, // Number of times we've split the table on an insert !
  29. REALLOCS, // Number of times we've reallocated memory for a split
  30. DEEPBUCKET, // The deepest bucket we have !
  31. AVERAGEBUCKET, // The average depth of the buckets
  32. EMPTYBUCKET, // The number of Empty buckets !
  33. ALLOCBUCKETS, // Number of buckets we've allocated
  34. ACTIVEBUCKETS, // Number of Active buckets
  35. AVERAGESEARCH, // Average number of buckets we examine each search
  36. DEEPSEARCH, // Longest walk we do on a search
  37. SEARCHCOST, // Sum of the number of items we've visited for all search hits !
  38. SEARCHCOSTMISS, // Sum of the number of items we've visited for search misses !
  39. MAX_HASH_STATS // Number of statistics we report !
  40. } ;
  41. long m_cHashCounters[MAX_HASH_STATS] ;
  42. CHashStats() {
  43. ZeroMemory( m_cHashCounters, sizeof( m_cHashCounters ) ) ;
  44. //m_cHashCounters[SMALLSEARCH] = 0x7FFF ;
  45. }
  46. static inline void
  47. IncrementStat( CHashStats* p, CHashStats::COUNTER c ) {
  48. _ASSERT( c < CHashStats::MAX_HASH_STATS ) ;
  49. if( p != 0 ) {
  50. InterlockedIncrement( &p->m_cHashCounters[c] ) ;
  51. }
  52. }
  53. static inline void
  54. AddStat( CHashStats*p, CHashStats::COUNTER c, long l ) {
  55. _ASSERT( c < CHashStats::MAX_HASH_STATS ) ;
  56. if( p != 0 ) {
  57. InterlockedExchangeAdd( &p->m_cHashCounters[c], l ) ;
  58. }
  59. }
  60. static inline void
  61. DecrementStat( CHashStats* p, CHashStats::COUNTER c ) {
  62. _ASSERT( c < CHashStats::MAX_HASH_STATS ) ;
  63. if( p != 0 ) {
  64. InterlockedDecrement( &p->m_cHashCounters[c] ) ;
  65. }
  66. }
  67. static inline void
  68. SetStat( CHashStats* p, CHashStats::COUNTER c, long l ) {
  69. _ASSERT( c < CHashStats::MAX_HASH_STATS ) ;
  70. if( p != 0 ) {
  71. p->m_cHashCounters[c] = l ;
  72. }
  73. }
  74. } ;
  75. #ifdef METER
  76. #define INCREMENTSTAT( s ) CHashStats::IncrementStat( m_pStat, CHashStats::##s )
  77. #define DECREMENTSTAT( s ) CHashStats::DecrementStat( m_pStat, CHashStats::##s )
  78. #define ADDSTAT( s, a ) CHashStats::AddStat( m_pStat, CHashStats::##s, a )
  79. #define SETSTAT( s, a ) CHashStats::SetStat( m_pStat, CHashStats::##s, a )
  80. //#if 0
  81. #define MAXBUCKET( i ) MaxBucket( i )
  82. #define AVERAGEBUCKET() AverageBucket()
  83. //#else
  84. //#define MAXBUCKET( i )
  85. //#define AVERAGEBUCKET()
  86. //#endif
  87. #else // METER
  88. #define INCREMENTSTAT( s )
  89. #define DECREMENTSTAT( s )
  90. #define ADDSTAT( s, a )
  91. #define SETSTAT( s, a )
  92. #define MAXBUCKET( i )
  93. #define AVERAGEBUCKET()
  94. #endif // METER
  95. template< class HASHTABLE >
  96. class TFDLHashIterator {
  97. private :
  98. //
  99. // The hash table that the item is in !
  100. //
  101. HASHTABLE* m_pTable ;
  102. //
  103. // The bucket we are walking on !
  104. //
  105. int m_iBucket ;
  106. //
  107. // Keep track of our position in a list !
  108. //
  109. HASHTABLE::ITER m_Iter ;
  110. //
  111. // Move between hash table buckets as necessary !
  112. //
  113. void
  114. PrevBucket() {
  115. _ASSERT( m_iBucket >= 0 && m_iBucket < m_pTable->m_cActiveBuckets ) ;
  116. _ASSERT( m_Iter.AtEnd() ) ;
  117. if( m_iBucket > 0 ) {
  118. do {
  119. m_Iter.ReBind( &m_pTable->m_pBucket[--m_iBucket] ) ;
  120. } while( m_Iter.AtEnd() && m_iBucket > 0 ) ;
  121. }
  122. _ASSERT( m_iBucket >= 0 && m_iBucket < m_pTable->m_cActiveBuckets ) ;
  123. }
  124. //
  125. // Move between hash table buckets as necessary !
  126. //
  127. void
  128. NextBucket() {
  129. _ASSERT( m_iBucket >= 0 && m_iBucket < m_pTable->m_cActiveBuckets ) ;
  130. _ASSERT( m_Iter.AtEnd() ) ;
  131. if( m_iBucket < m_pTable->m_cActiveBuckets-1 ) {
  132. do {
  133. m_Iter.ReBind( &m_pTable->m_pBucket[++m_iBucket] ) ;
  134. } while( m_Iter.AtEnd() && m_iBucket < m_pTable->m_cActiveBuckets-1 ) ;
  135. }
  136. _ASSERT( m_iBucket >= 0 && m_iBucket < m_pTable->m_cActiveBuckets ) ;
  137. }
  138. public :
  139. typedef HASHTABLE::DATA DATA ;
  140. TFDLHashIterator( HASHTABLE& ref, BOOL fForward = TRUE ) :
  141. m_pTable( &ref ),
  142. m_iBucket( fForward ? 0 : ref.m_cActiveBuckets-1 ),
  143. m_Iter( ref.m_pBucket[m_iBucket] ) {
  144. if( m_Iter.AtEnd() ) {
  145. if( fForward ) {
  146. NextBucket() ;
  147. } else {
  148. PrevBucket() ;
  149. }
  150. }
  151. }
  152. void
  153. Prev() {
  154. /*++
  155. Routine Description :
  156. This function moves the iterator back one slot.
  157. Arguments :
  158. None.
  159. Return Value :
  160. None.
  161. --*/
  162. m_Iter.Prev() ;
  163. if( m_Iter.AtEnd() ) {
  164. PrevBucket() ;
  165. }
  166. }
  167. void
  168. Next() {
  169. /*++
  170. Routine Description :
  171. This function moves the iterator forward one slot.
  172. Arguments :
  173. None.
  174. Return Value :
  175. None.
  176. --*/
  177. m_Iter.Next() ;
  178. if( m_Iter.AtEnd() ) {
  179. NextBucket() ;
  180. }
  181. }
  182. void
  183. Front() {
  184. /*++
  185. Routine Description :
  186. Reset the iterator to reference the first item of the list !
  187. Arguments :
  188. None.
  189. Return Value :
  190. None.
  191. --*/
  192. m_Iter.ReBind( &m_pTable->m_pBucket[0], TRUE ) ;
  193. m_iBucket = 0 ;
  194. if( m_Iter.AtEnd() ) {
  195. NextBucket() ;
  196. }
  197. }
  198. void
  199. Back() {
  200. /*++
  201. Routine Description :
  202. Reset the iterator to reference the last item of the list !
  203. Arguments :
  204. None.
  205. Return Value :
  206. None.
  207. --*/
  208. m_Iter.ReBind( &m_pTable->m_pBucket[m_pTable->m_cActiveBuckets-1], FALSE ) ;
  209. m_iBucket = m_pTable->m_cActiveBuckets-1 ;
  210. if( m_Iter.AtEnd() ) {
  211. PrevBucket() ;
  212. }
  213. }
  214. BOOL
  215. AtEnd() {
  216. /*++
  217. Routine Description :
  218. Return TRUE if we are at the end of the list !
  219. This is a little more complicated to compute -
  220. depends on which way we are going !
  221. Arguments :
  222. None.
  223. Return Value :
  224. None.
  225. --*/
  226. return m_Iter.AtEnd() ;
  227. }
  228. DATA*
  229. CurrentEntry() {
  230. return m_Iter.Current() ;
  231. }
  232. DATA*
  233. RemoveItem() {
  234. /*++
  235. Routine Description :
  236. Remove the item that the iterator currently
  237. references from the list.
  238. If we are going forward then the iterator
  239. will be setting on the previous element,
  240. otherwise the iterator is left on the next element.
  241. We have to take care that we don't leave the iterator
  242. sitting on an invalid element.
  243. Arguments :
  244. None.
  245. Return Value :
  246. Pointer to the removed item.
  247. --*/
  248. DATA* pData = m_Iter.RemoveItem() ;
  249. if( pData ) {
  250. m_pTable->NotifyOfRemoval() ;
  251. }
  252. if( m_Iter.AtEnd() ) {
  253. if( m_Iter.m_fForward ) {
  254. NextBucket() ;
  255. } else {
  256. PrevBucket() ;
  257. }
  258. }
  259. return pData ;
  260. }
  261. inline DATA*
  262. Current( ) {
  263. return m_Iter.Current() ;
  264. }
  265. inline void
  266. InsertBefore( DATA* p ) {
  267. m_Iter.InsertBefore( p ) ;
  268. }
  269. inline void
  270. InsertAfter( DATA* p ) {
  271. m_Iter.InsertAfter( p ) ;
  272. }
  273. } ;
  274. //------------------------------------------------------------
  275. template< class Data, /* This is the item that resides in the hashtable */
  276. class KEYREF, /* This is the type used to point or reference items in the cache*/
  277. Data::PFNDLIST pfnDlist,
  278. BOOL fOrdered = TRUE
  279. >
  280. class TFDLHash {
  281. //
  282. // This class defines a Hash table which can grow dynamically to
  283. // accomodate insertions into the table. The table only grows, and
  284. // does not shrink.
  285. //
  286. public :
  287. //
  288. // This is the iterator object that can walk the hash table !
  289. //
  290. friend class TFDLHashIterator< TFDLHash< Data, KEYREF, pfnDlist > > ;
  291. //
  292. // This is the type of the Data item !
  293. //
  294. //typedef DATAHELPER Data ;
  295. typedef KEYREF (Data::*GETKEY)() ;
  296. //
  297. // This is the type that we use to maintain doubly linked lists of
  298. // hash table items !
  299. //
  300. typedef TDListHead< Data, pfnDlist > DLIST ;
  301. //
  302. // This is the type we use to make iterators over the bucket chains !
  303. //
  304. typedef TDListIterator< DLIST > ITER ;
  305. //
  306. // Define this type for our iterators !
  307. //
  308. typedef Data DATA ;
  309. //
  310. // This is a member function pointer to a function which
  311. // will retrieve the key we are to use !
  312. //
  313. //typedef KEYREF (Data::*GETKEY)( ) ;
  314. //typedef Data::GETKEY GETKEY ;
  315. //
  316. // This is the type of function that computes the hash value !
  317. //
  318. typedef DWORD (*PFNHASH)( KEYREF ) ;
  319. //
  320. // This is the type of function that can recompute the hash value when
  321. // we are splitting up the hash table !
  322. //
  323. typedef DWORD (*PFNREHASH)( Data* ) ;
  324. //
  325. // This is a member function pointer of the type that will
  326. // compare keys for us !
  327. //
  328. typedef int (*MATCHKEY)( KEYREF key1, KEYREF key2 ) ;
  329. private :
  330. //
  331. // An array of buckets !
  332. //
  333. DLIST* m_pBucket ;
  334. //
  335. // Member Pointer - will get the key out of the object for us !
  336. //
  337. GETKEY m_pGetKey ;
  338. //
  339. // Member Pointer - will compare the key in the item for us !
  340. //
  341. MATCHKEY m_pMatchKey ;
  342. //
  343. // A counter that we use to determine when to grow the
  344. // hash table. Each time we grow the table we set this
  345. // to a large positive value, and decrement as we insert
  346. // elements. When this hits 0 its time to grow the table !
  347. //
  348. long m_cInserts ;
  349. //
  350. // The function we use to compute hash values.
  351. // (Provided by the Caller of Init())
  352. //
  353. PFNHASH m_pfnHash ;
  354. //
  355. // The function we call when we are growing the hash table
  356. // and splitting bucket chains and we need to rehash an element !
  357. //
  358. PFNREHASH m_pfnReHash ;
  359. //
  360. // Number of Buckets used in index computation
  361. //
  362. int m_cBuckets ;
  363. //
  364. // Number of Buckets we are actually using
  365. // Assert( m_cBuckets >= m_cActiveBuckets ) always true.
  366. //
  367. int m_cActiveBuckets ;
  368. //
  369. // Number of Buckets we have allocated
  370. // Assert( m_cNumAlloced >= m_cActiveBuckets ) must
  371. // always be true.
  372. //
  373. int m_cNumAlloced ;
  374. //
  375. // The amount we should grow the hash table when we
  376. // decide to grow it.
  377. //
  378. int m_cIncrement ;
  379. //
  380. // The number of CBuckets we should allow in each
  381. // collision chain (on average).
  382. //
  383. int m_load ;
  384. #ifdef METER
  385. //
  386. // The structure for collecting our performance data !
  387. //
  388. CHashStats* m_pStat ;
  389. //
  390. // Compute the depth of a bucket !
  391. //
  392. long
  393. BucketDepth( DWORD index ) ;
  394. //
  395. // set the statistics for the deepest bucket !
  396. //
  397. void
  398. MaxBucket( DWORD index ) ;
  399. //
  400. // Compute the average Search depth !
  401. //
  402. void
  403. AverageSearch( BOOL fHit, long lDepth ) ;
  404. //
  405. // Compute the average Bucket depth !
  406. //
  407. void
  408. AverageBucket( ) ;
  409. #endif
  410. //
  411. // The function we use to compute the
  412. // position of an element in the hash table given its
  413. // Hash Value.
  414. //
  415. DWORD
  416. ComputeIndex( DWORD dw ) ;
  417. DWORD
  418. ReHash( Data* p ) {
  419. if( m_pfnReHash )
  420. return m_pfnReHash( p ) ;
  421. return m_pfnHash( (p->*m_pGetKey)() ) ;
  422. }
  423. public :
  424. TFDLHash( ) ;
  425. ~TFDLHash( ) ;
  426. BOOL
  427. Init( int cInitial,
  428. int cIncrement,
  429. int load,
  430. PFNHASH pfnHash,
  431. GETKEY pGetKey,
  432. MATCHKEY pMatchKey,
  433. PFNREHASH pfnReHash = 0,
  434. CHashStats* pStats = 0
  435. ) ;
  436. //
  437. // Check that the hash table is in a valid state
  438. // if fCheckHash == TRUE we will walk all the buckets and check that
  439. // the data hashes to the correct value !
  440. //
  441. BOOL
  442. IsValid( BOOL fCheckHash = FALSE ) ;
  443. //
  444. // Check that the Bucket is valid - everything contains
  445. // proper hash value and is in order !
  446. //
  447. BOOL
  448. IsValidBucket( int i ) ;
  449. //
  450. // This function grows the number of hash buckets as the
  451. // total number of items in the table grows !
  452. //
  453. BOOL
  454. Split() ;
  455. //
  456. // Insert a piece of Data into the Hash Table
  457. // We take a pointer to the Data object.
  458. //
  459. BOOL
  460. InsertDataHash( DWORD dw,
  461. KEYREF k,
  462. Data* pd
  463. ) ;
  464. //
  465. // Insert a piece of Data into the Hash Table
  466. //
  467. // We take an iterator that is already position in the
  468. // correct location for inserting the item !
  469. //
  470. BOOL
  471. InsertDataHashIter( ITER& iter,
  472. DWORD dw,
  473. KEYREF k,
  474. Data* pd
  475. ) ;
  476. //
  477. // Insert a piece of Data into the Hash Table
  478. //
  479. BOOL
  480. InsertData( Data* pd ) {
  481. KEYREF keyref = (pd->*m_pGetKey)() ;
  482. return InsertDataHash( m_pfnHash(keyref), keyref, pd ) ;
  483. }
  484. //
  485. // Insert a piece of Data into the Hash table
  486. // given an iterator that should be at the right location !
  487. //
  488. BOOL
  489. InsertDataIter( ITER& iter,
  490. Data* pd ) {
  491. KEYREF keyref = (pd->*m_pGetKey)() ;
  492. return InsertDataHashIter( iter, m_pfnHash(keyref), keyref, pd ) ;
  493. }
  494. //
  495. // Search for an item in the cache - if we don't find
  496. // it we return an ITERATOR that the user can use to insert
  497. // the item by calling ITER.InsertBefore() ;
  498. //
  499. // If the item is found, we'll return the item, as well
  500. // as returning an iterator who's current element
  501. // points at the data item !
  502. //
  503. ITER
  504. SearchKeyHashIter(
  505. DWORD dw,
  506. KEYREF k,
  507. Data* &pd
  508. ) ;
  509. //
  510. // Search for a given Key in the Hash Table - return a pointer
  511. // to the Data within our Bucket object
  512. //
  513. void
  514. SearchKeyHash( DWORD dw,
  515. KEYREF k,
  516. Data* &pd
  517. ) ;
  518. //
  519. // Search for a given Key in the Hash Table - return a pointer
  520. // to the Data within our Bucket object
  521. //
  522. Data*
  523. SearchKey( KEYREF k ) {
  524. Data* p ;
  525. SearchKeyHash( m_pfnHash( k ), k, p ) ;
  526. return p ;
  527. }
  528. //
  529. // Search for the given item and return a good iterator !
  530. //
  531. ITER
  532. SearchKeyIter( KEYREF k,
  533. Data* &pd ) {
  534. pd = 0 ;
  535. return SearchKeyHashIter( m_pfnHash( k ), k, pd ) ;
  536. }
  537. Data*
  538. SearchKey( DWORD dw,
  539. KEYREF k
  540. ) {
  541. Data* p = 0 ;
  542. _ASSERT( dw == m_pfnHash( k ) ) ;
  543. SearchKeyHash( dw, k, p ) ;
  544. return p ;
  545. }
  546. //
  547. // Given an item in the hash table - remove it !
  548. //
  549. void
  550. Delete( Data* pd ) ;
  551. //
  552. // Find an element in the hash table - and remove it !
  553. // (Confirm that the found item matches the Key!)
  554. //
  555. void
  556. DeleteData( KEYREF k,
  557. Data* pd
  558. ) ;
  559. //
  560. // Remove an item from the hash table - and return it !
  561. //
  562. Data*
  563. DeleteData( KEYREF k ) {
  564. Data* p ;
  565. //
  566. // Find the item
  567. //
  568. SearchKeyHash( m_pfnHash( k ), k, p ) ;
  569. //
  570. // Remove from Hash Table
  571. //
  572. if( p )
  573. Delete( p ) ;
  574. return p ;
  575. }
  576. //
  577. // Delete the key and associated data from the table.
  578. //
  579. BOOL
  580. Destroy( KEYREF k ) {
  581. Data* p = DeleteData( k ) ;
  582. if( p ) {
  583. delete p ;
  584. return TRUE ;
  585. }
  586. return FALSE ;
  587. }
  588. //
  589. // Discards any memory we have allocated - after this, you must
  590. // call Init() again!
  591. //
  592. void Clear( ) ;
  593. //
  594. // Removes all of the items in the hash table. Does not call "delete"
  595. // on them.
  596. //
  597. void Empty( ) ;
  598. //
  599. // Called by Iterators that want to let us know that items have been
  600. // removed from the cache so we can do our splits correctly etc... !
  601. //
  602. void NotifyOfRemoval() ;
  603. //
  604. // Function to compute hash value of a key for callers
  605. // who don't keep track of the hash function
  606. //
  607. DWORD
  608. ComputeHash( KEYREF k ) ;
  609. } ;
  610. #include "fdlhash.inl"
  611. #endif // _FDLHASH_H_