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.

1085 lines
27 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name :
  4. hashtab.cxx
  5. Abstract:
  6. Implements the member functions for Hash table
  7. Author:
  8. Murali R. Krishnan ( MuraliK ) 02-Oct-1996
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Internet Server DLL
  13. Functions Exported:
  14. Revision History:
  15. --*/
  16. /************************************************************
  17. * Include Headers
  18. ************************************************************/
  19. #include "precomp.hxx"
  20. # if !defined(dllexp)
  21. # define dllexp __declspec( dllexport)
  22. # endif
  23. # include <hashtab.hxx>
  24. /*++
  25. Organization of Hash Table
  26. The hash table consists of a set of hash buckets controlled
  27. by the number of buckets specified during creation.
  28. Each bucket consists of a set of bucket chunks. Each bucket
  29. owns a separate critical section to protect the entries in
  30. the bucket itself.
  31. Each bucket chunk consists of an array of MAX_ELEMENTS_PER_BUCKET
  32. HashTableBucketElement Entries (HTBE_ENTRY).
  33. Each HTBE_ENTRY maintains a hash value and pointer to the Hash Element.
  34. --*/
  35. /************************************************************
  36. * HASH_TABLE_BUCKET
  37. ************************************************************/
  38. struct HTBE_ENTRY {
  39. DWORD m_hashValue;
  40. HT_ELEMENT * m_phte;
  41. inline
  42. BOOL IsMatch( DWORD hashValue, LPCSTR pszKey, DWORD cchKey) const
  43. { return ((hashValue == m_hashValue) &&
  44. (NULL != m_phte) &&
  45. m_phte->IsMatch( pszKey, cchKey)
  46. );
  47. }
  48. inline
  49. BOOL IsMatch( IN HT_ELEMENT * phte) const
  50. { return ( phte == m_phte); }
  51. inline BOOL
  52. IsEmpty( VOID) const { return ( NULL == m_phte); }
  53. VOID Print( VOID) const
  54. { m_phte->Print(); }
  55. };
  56. typedef HTBE_ENTRY * PHTBE_ENTRY;
  57. //
  58. // Chunk size should be carefully (empirically) chosen.
  59. // Small Chunk size => large number of chunks
  60. // Large Chunk size => high cost of search on failures.
  61. // For now we choose the chunk size to be 20 entries.
  62. # define MAX_ELEMENTS_PER_BUCKET ( 20 )
  63. struct dllexp HTB_ELEMENT {
  64. HTBE_ENTRY m_rgElements[MAX_ELEMENTS_PER_BUCKET];
  65. DWORD m_nElements;
  66. LIST_ENTRY m_ListEntry;
  67. HTB_ELEMENT(VOID)
  68. : m_nElements ( 0)
  69. {
  70. InitializeListHead( &m_ListEntry);
  71. ZeroMemory( m_rgElements, sizeof( m_rgElements));
  72. }
  73. ~HTB_ELEMENT(VOID)
  74. { Cleanup(); }
  75. VOID Cleanup( VOID);
  76. inline
  77. HT_ELEMENT * Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey);
  78. inline
  79. BOOL Insert( IN DWORD hashVal, IN HT_ELEMENT * phte);
  80. inline
  81. BOOL Delete( IN HT_ELEMENT * phte);
  82. VOID Print( IN DWORD level) const;
  83. HTBE_ENTRY * FirstElement(VOID) { return ( m_rgElements); }
  84. HTBE_ENTRY * LastElement(VOID)
  85. { return ( m_rgElements + MAX_ELEMENTS_PER_BUCKET); }
  86. VOID NextElement( HTBE_ENTRY * & phtbe)
  87. { phtbe++; }
  88. VOID IncrementElements(VOID) { m_nElements++; }
  89. VOID DecrementElements(VOID) { m_nElements--; }
  90. DWORD NumElements( VOID) const { return ( m_nElements); }
  91. BOOL IsSpaceAvailable(VOID) const
  92. { return ( NumElements() < MAX_ELEMENTS_PER_BUCKET); }
  93. DWORD FindNextElement( IN OUT LPDWORD pdwPos,
  94. OUT HT_ELEMENT ** pphte);
  95. };
  96. typedef HTB_ELEMENT * PHTB_ELEMENT;
  97. class dllexp HASH_TABLE_BUCKET {
  98. public:
  99. HASH_TABLE_BUCKET(VOID);
  100. ~HASH_TABLE_BUCKET( VOID);
  101. HT_ELEMENT * Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey);
  102. BOOL Insert( IN DWORD hashVal,
  103. IN HT_ELEMENT * phte,
  104. IN BOOL fCheckForDuplicate);
  105. BOOL Delete( IN HT_ELEMENT * phte);
  106. VOID Print( IN DWORD level);
  107. DWORD NumEntries( VOID);
  108. DWORD InitializeIterator( IN HT_ITERATOR * phti);
  109. DWORD FindNextElement( IN HT_ITERATOR * phti,
  110. OUT HT_ELEMENT ** pphte);
  111. DWORD CloseIterator( IN HT_ITERATOR * phti);
  112. private:
  113. CRITICAL_SECTION m_csLock;
  114. LIST_ENTRY m_lHead;
  115. DWORD m_nEntries;
  116. HTB_ELEMENT m_htbeFirst; // the first bucket chunk
  117. VOID Lock(VOID) { EnterCriticalSection( &m_csLock); }
  118. VOID Unlock( VOID) { LeaveCriticalSection( &m_csLock); }
  119. };
  120. /************************************************************
  121. * Member Functions of HTB_ELEMENT
  122. ************************************************************/
  123. VOID
  124. HTB_ELEMENT::Cleanup( VOID)
  125. {
  126. if ( m_nElements > 0) {
  127. PHTBE_ENTRY phtbeEntry;
  128. // free up all the entries in this bucket.
  129. for (phtbeEntry = FirstElement();
  130. phtbeEntry < (LastElement());
  131. NextElement( phtbeEntry)) {
  132. if ( !phtbeEntry->IsEmpty() ) {
  133. // release the object now.
  134. DecrementElements();
  135. // Assert that ref == 1
  136. DerefAndKillElement( phtbeEntry->m_phte);
  137. phtbeEntry->m_phte = NULL;
  138. phtbeEntry->m_hashValue = 0;
  139. }
  140. } // for
  141. }
  142. DBG_ASSERT( 0 == m_nElements);
  143. return;
  144. } // HTB_ELEMENT::Cleanup()
  145. inline
  146. HT_ELEMENT *
  147. HTB_ELEMENT::Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey)
  148. {
  149. HT_ELEMENT * phte = NULL;
  150. if ( m_nElements > 0) {
  151. PHTBE_ENTRY phtbeEntry;
  152. // find the entry by scanning all entries in this bucket chunk
  153. // if found, increment ref count and return a pointer to the object
  154. for (phtbeEntry = FirstElement();
  155. phtbeEntry < (LastElement());
  156. NextElement( phtbeEntry)) {
  157. //
  158. // If the hash values match and the strings match up, return
  159. // the corresponding hash table entry object
  160. //
  161. if ( phtbeEntry->IsMatch( hashValue, pszKey, cchKey)) {
  162. // we found the entry. return it.
  163. phte = phtbeEntry->m_phte;
  164. DBG_REQUIRE( phte->Reference() > 0);
  165. break;
  166. }
  167. } // for
  168. }
  169. return ( phte);
  170. } // HTB_ELEMENT::Lookup()
  171. inline BOOL
  172. HTB_ELEMENT::Insert( IN DWORD hashVal,
  173. IN HT_ELEMENT * phte
  174. )
  175. {
  176. if ( m_nElements < MAX_ELEMENTS_PER_BUCKET) {
  177. // there is some empty space.
  178. // Find one such a slot and add this new entry
  179. PHTBE_ENTRY phtbeEntry;
  180. for (phtbeEntry = FirstElement();
  181. phtbeEntry < LastElement();
  182. NextElement( phtbeEntry)) {
  183. if ( phtbeEntry->IsEmpty() ) {
  184. DBG_ASSERT( NULL != phte);
  185. // Assume that the object phte already has non-zero ref count
  186. // we found a free entry. insert the new element here.
  187. phtbeEntry->m_hashValue = hashVal;
  188. phtbeEntry->m_phte = phte;
  189. IncrementElements();
  190. return ( TRUE);
  191. }
  192. } // for
  193. // we should not come here. If we do then there is trouble :(
  194. DBG_ASSERT( FALSE);
  195. }
  196. SetLastError( ERROR_INSUFFICIENT_BUFFER);
  197. return ( FALSE);
  198. } // HTB_ELEMENT::Insert()
  199. DWORD
  200. HTB_ELEMENT::FindNextElement( IN OUT LPDWORD pdwPos, OUT HT_ELEMENT ** pphte)
  201. {
  202. DWORD dwErr = ERROR_NO_MORE_ITEMS;
  203. DBG_ASSERT( NULL != pdwPos );
  204. DBG_ASSERT( NULL != pphte );
  205. // Find the first valid element to return back.
  206. //
  207. // Given that deletion might happen any time, we cannot rely on the
  208. // comparison *pdwPos < m_nElements
  209. //
  210. // Do scans with *pdwPos < MAX_ELEMENTS_PER_BUCKET
  211. //
  212. if ( *pdwPos < MAX_ELEMENTS_PER_BUCKET ) {
  213. PHTBE_ENTRY phtbeEntry;
  214. // find the entry by scanning all entries in this bucket chunk
  215. // if found, increment ref count and return a pointer to the object
  216. for (phtbeEntry = m_rgElements + *pdwPos;
  217. phtbeEntry < (LastElement());
  218. NextElement( phtbeEntry)) {
  219. if ( phtbeEntry->m_phte != NULL ) {
  220. //
  221. // Store the element pointer and the offset
  222. // and return after referencing the element
  223. //
  224. *pphte = phtbeEntry->m_phte;
  225. (*pphte)->Reference();
  226. *pdwPos = ( 1 + DIFF(phtbeEntry - FirstElement()));
  227. dwErr = NO_ERROR;
  228. break;
  229. }
  230. } // for
  231. }
  232. return ( dwErr);
  233. } // HTB_ELEMENT::FindNextElement()
  234. inline BOOL
  235. HTB_ELEMENT::Delete( IN HT_ELEMENT * phte)
  236. {
  237. DBG_ASSERT( NULL != phte);
  238. if ( m_nElements > 0) {
  239. PHTBE_ENTRY phtbeEntry;
  240. // find the entry by scanning all entries in this bucket chunk
  241. // if found, increment ref count and return a pointer to the object
  242. for (phtbeEntry = FirstElement();
  243. phtbeEntry < (LastElement());
  244. NextElement( phtbeEntry)) {
  245. //
  246. // If the hash values match and the strings match up,
  247. // decrement ref count and kill the element.
  248. //
  249. if ( phtbeEntry->IsMatch( phte)) {
  250. // We found the entry. Remove it from the table
  251. phtbeEntry->m_phte = NULL;
  252. DecrementElements();
  253. DerefAndKillElement( phte);
  254. return ( TRUE);
  255. }
  256. } // for
  257. }
  258. return ( FALSE);
  259. } // HTB_ELEMENT::Delete()
  260. VOID
  261. HTB_ELEMENT::Print(IN DWORD level) const
  262. {
  263. const HTBE_ENTRY * phtbeEntry;
  264. CHAR rgchBuffer[MAX_ELEMENTS_PER_BUCKET * 22 + 200];
  265. DWORD cch;
  266. DWORD i;
  267. cch = wsprintf( rgchBuffer,
  268. "HTB_ELEMENT(%08x) # Elements %4d; "
  269. "Flink: %08x Blink: %08x\n"
  270. ,
  271. this, m_nElements,
  272. m_ListEntry.Flink, m_ListEntry.Blink);
  273. if ( level > 0) {
  274. // NYI: I need to walk down the entire array.
  275. // Not just the first few entries
  276. for( i = 0; i < m_nElements; i++) {
  277. phtbeEntry = &m_rgElements[i];
  278. cch += wsprintf( rgchBuffer + cch,
  279. " %08x %08x",
  280. phtbeEntry->m_hashValue,
  281. phtbeEntry->m_phte
  282. );
  283. if ( i % 4 == 0) {
  284. rgchBuffer[cch++] = '\n';
  285. rgchBuffer[cch] = '\0';
  286. }
  287. } // for
  288. }
  289. DBGDUMP(( DBG_CONTEXT, rgchBuffer));
  290. return;
  291. } // HTB_ELEMENT::Print()
  292. /************************************************************
  293. * Member Functions of HASH_TABLE_BUCKET
  294. ************************************************************/
  295. HASH_TABLE_BUCKET::HASH_TABLE_BUCKET(VOID)
  296. : m_nEntries ( 0),
  297. m_htbeFirst()
  298. {
  299. InitializeListHead( &m_lHead);
  300. INITIALIZE_CRITICAL_SECTION( & m_csLock);
  301. } // HASH_TABLE_BUCKET::HASH_TABLE_BUCKET()
  302. HASH_TABLE_BUCKET::~HASH_TABLE_BUCKET( VOID)
  303. {
  304. PLIST_ENTRY pl;
  305. PHTB_ELEMENT phtbe;
  306. // Free up the elements in the list
  307. Lock();
  308. while ( !IsListEmpty( &m_lHead)) {
  309. pl = RemoveHeadList( &m_lHead);
  310. phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT, m_ListEntry);
  311. delete phtbe;
  312. } // while
  313. m_htbeFirst.Cleanup();
  314. Unlock();
  315. DeleteCriticalSection( &m_csLock);
  316. } // HASH_TABLE_BUCKET::~HASH_TABLE_BUCKET()
  317. HT_ELEMENT *
  318. HASH_TABLE_BUCKET::Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey)
  319. {
  320. HT_ELEMENT * phte;
  321. Lock();
  322. // 1. search in the first bucket
  323. phte = m_htbeFirst.Lookup( hashValue, pszKey, cchKey);
  324. if ( NULL == phte ) {
  325. // 2. search in the auxiliary buckets
  326. PLIST_ENTRY pl;
  327. for ( pl = m_lHead.Flink; (phte == NULL) && (pl != &m_lHead);
  328. pl = pl->Flink) {
  329. HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl,
  330. HTB_ELEMENT,
  331. m_ListEntry);
  332. phte = phtbe->Lookup( hashValue, pszKey, cchKey);
  333. } // for
  334. }
  335. Unlock();
  336. return (phte);
  337. } // HASH_TABLE_BUCKET::Lookup()
  338. BOOL
  339. HASH_TABLE_BUCKET::Insert( IN DWORD hashValue,
  340. IN HT_ELEMENT * phte,
  341. IN BOOL fCheckForDuplicate)
  342. {
  343. BOOL fReturn = FALSE;
  344. if ( fCheckForDuplicate) {
  345. Lock();
  346. // do a lookup and find out if this data exists.
  347. HT_ELEMENT * phteLookedup = Lookup( hashValue,
  348. phte->QueryKey(),
  349. phte->QueryKeyLen()
  350. );
  351. if ( NULL != phteLookedup) {
  352. // the element is already present - return failure
  353. DerefAndKillElement( phteLookedup);
  354. }
  355. Unlock();
  356. if ( NULL != phteLookedup) {
  357. SetLastError( ERROR_DUP_NAME);
  358. return ( FALSE);
  359. }
  360. }
  361. Lock();
  362. // 1. try inserting in the first bucket chunk, if possible
  363. if ( m_htbeFirst.IsSpaceAvailable()) {
  364. fReturn = m_htbeFirst.Insert( hashValue, phte);
  365. } else {
  366. // 2. Find the first chunk that has space and insert it there.
  367. PLIST_ENTRY pl;
  368. HTB_ELEMENT * phtbe;
  369. for ( pl = m_lHead.Flink; (pl != &m_lHead);
  370. pl = pl->Flink) {
  371. phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT, m_ListEntry);
  372. if ( phtbe->IsSpaceAvailable()) {
  373. fReturn = phtbe->Insert( hashValue, phte);
  374. break;
  375. }
  376. } // for
  377. if ( !fReturn ) {
  378. //
  379. // We ran out of space.
  380. // Allocate a new bucket and insert the new element.
  381. //
  382. phtbe = new HTB_ELEMENT();
  383. if ( NULL != phtbe) {
  384. // add the bucket to the list of buckets and
  385. // then add the element to the bucket
  386. InsertTailList( &m_lHead, &phtbe->m_ListEntry);
  387. fReturn = phtbe->Insert(hashValue, phte);
  388. } else {
  389. IF_DEBUG( ERROR) {
  390. DBGPRINTF(( DBG_CONTEXT,
  391. " HTB(%08x)::Insert: Unable to add a chunk\n",
  392. this));
  393. }
  394. SetLastError( ERROR_NOT_ENOUGH_MEMORY);
  395. }
  396. }
  397. }
  398. Unlock();
  399. return ( fReturn);
  400. } // HASH_TABLE_BUCKET::Insert()
  401. BOOL
  402. HASH_TABLE_BUCKET::Delete( IN HT_ELEMENT * phte)
  403. {
  404. BOOL fReturn = FALSE;
  405. // We do not know which bucket this element belongs to.
  406. // So we should try all chunks to delete this element.
  407. Lock();
  408. // 1. try deleting the element from first bucket chunk, if possible
  409. fReturn = m_htbeFirst.Delete( phte);
  410. if (!fReturn) {
  411. // it was not on the first bucket chunk.
  412. // 2. Find the first chunk that might contain this element
  413. // and delete it.
  414. PLIST_ENTRY pl;
  415. for ( pl = m_lHead.Flink;
  416. !fReturn && (pl != &m_lHead);
  417. pl = pl->Flink) {
  418. HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl,
  419. HTB_ELEMENT,
  420. m_ListEntry);
  421. fReturn = phtbe->Delete( phte);
  422. } // for
  423. // the element should have been in the hash table,
  424. // otherwise the app is calling with wrong entry
  425. DBG_ASSERT( fReturn);
  426. }
  427. Unlock();
  428. return ( fReturn);
  429. } // HASH_TABLE_BUCKET::Delete()
  430. DWORD
  431. HASH_TABLE_BUCKET::NumEntries( VOID)
  432. {
  433. DWORD nEntries;
  434. Lock();
  435. nEntries = m_htbeFirst.NumElements();
  436. PLIST_ENTRY pl;
  437. for ( pl = m_lHead.Flink;
  438. (pl != &m_lHead);
  439. pl = pl->Flink) {
  440. HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
  441. m_ListEntry);
  442. nEntries += phtbe->NumElements();
  443. } // for
  444. Unlock();
  445. return (nEntries);
  446. } // HASH_TABLE_BUCKET::NumEntries()
  447. DWORD
  448. HASH_TABLE_BUCKET::InitializeIterator( IN HT_ITERATOR * phti)
  449. {
  450. DWORD dwErr = ERROR_NO_MORE_ITEMS;
  451. //
  452. // find the first chunk that has a valid element.
  453. // if we find one, leave the lock on for subsequent accesses.
  454. // CloseIterator will shut down the lock
  455. // If we do not find one, we should unlock and return
  456. //
  457. phti->nChunkId = NULL;
  458. phti->nPos = 0;
  459. Lock();
  460. if ( m_htbeFirst.NumElements() > 0) {
  461. phti->nChunkId = (PVOID ) &m_htbeFirst;
  462. dwErr = NO_ERROR;
  463. } else {
  464. // find the first chunk that has an element
  465. PLIST_ENTRY pl;
  466. for ( pl = m_lHead.Flink; (pl != &m_lHead); pl = pl->Flink) {
  467. HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
  468. m_ListEntry);
  469. if ( phtbe->NumElements() > 0) {
  470. phti->nChunkId = (PVOID ) phtbe;
  471. dwErr = NO_ERROR;
  472. break;
  473. }
  474. } // for
  475. }
  476. // if we did not find any elements, then unlock and return
  477. // Otherwise leave the unlocking to the CloseIterator()
  478. if ( dwErr == ERROR_NO_MORE_ITEMS) {
  479. // get out of this bucket completely.
  480. Unlock();
  481. }
  482. return ( dwErr);
  483. } // HASH_TABLE_BUCKET::InitializeIterator()
  484. DWORD
  485. HASH_TABLE_BUCKET::FindNextElement( IN HT_ITERATOR * phti,
  486. OUT HT_ELEMENT ** pphte)
  487. {
  488. // this function should be called only when the bucket is locked.
  489. DWORD dwErr;
  490. HTB_ELEMENT * phtbe = (HTB_ELEMENT * )phti->nChunkId;
  491. //
  492. // phti contains the <chunk, pos> from which we should start scan for
  493. // next element.
  494. //
  495. DBG_ASSERT( NULL != phtbe);
  496. dwErr = phtbe->FindNextElement( &phti->nPos, pphte);
  497. if ( ERROR_NO_MORE_ITEMS == dwErr ) {
  498. // scan the rest of the chunks for next element
  499. PLIST_ENTRY pl = ((phtbe == &m_htbeFirst) ? m_lHead.Flink :
  500. phtbe->m_ListEntry.Flink);
  501. for ( ; (pl != &m_lHead); pl = pl->Flink) {
  502. phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
  503. m_ListEntry);
  504. if ( phtbe->NumElements() > 0) {
  505. phti->nPos = 0;
  506. dwErr = phtbe->FindNextElement( &phti->nPos, pphte);
  507. DBG_ASSERT( NO_ERROR == dwErr);
  508. phti->nChunkId = (PVOID ) phtbe;
  509. break;
  510. }
  511. } // for
  512. }
  513. if ( dwErr == ERROR_NO_MORE_ITEMS) {
  514. phti->nChunkId = NULL;
  515. }
  516. return ( dwErr);
  517. } // HASH_TABLE_BUCKET::FindNextElement()
  518. DWORD
  519. HASH_TABLE_BUCKET::CloseIterator( IN HT_ITERATOR * phti)
  520. {
  521. // just unlock the current bucket.
  522. Unlock();
  523. return ( NO_ERROR);
  524. } // HASH_TABLE_BUCKET::CloseIterator()
  525. VOID
  526. HASH_TABLE_BUCKET::Print( IN DWORD level)
  527. {
  528. Lock();
  529. DBGPRINTF(( DBG_CONTEXT,
  530. "\n\nHASH_TABLE_BUCKET (%08x): Head.Flink=%08x; Head.Blink=%08x\n"
  531. " Bucket Chunk # 0:\n"
  532. ,
  533. this, m_lHead.Flink, m_lHead.Blink
  534. ));
  535. m_htbeFirst.Print( level);
  536. if ( level > 0) {
  537. PLIST_ENTRY pl;
  538. DWORD i;
  539. for ( pl = m_lHead.Flink, i = 1;
  540. (pl != &m_lHead);
  541. pl = pl->Flink, i++) {
  542. HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
  543. m_ListEntry);
  544. DBGPRINTF(( DBG_CONTEXT, "\n Bucket Chunk # %d\n", i));
  545. phtbe->Print( level);
  546. } // for
  547. }
  548. Unlock();
  549. return;
  550. } // HASH_TABLE_BUCKET::Print()
  551. /************************************************************
  552. * Member Functions of HASH_TABLE
  553. ************************************************************/
  554. HASH_TABLE::HASH_TABLE( IN DWORD nBuckets,
  555. IN LPCSTR pszIdentifier,
  556. IN DWORD dwHashTableFlags
  557. )
  558. : m_nBuckets ( nBuckets),
  559. m_dwFlags ( dwHashTableFlags),
  560. m_nEntries ( 0),
  561. m_nLookups ( 0),
  562. m_nHits ( 0),
  563. m_nInserts ( 0),
  564. m_nFlushes ( 0)
  565. {
  566. if ( NULL != pszIdentifier) {
  567. lstrcpynA( m_rgchId, pszIdentifier, sizeof( m_rgchId));
  568. }
  569. m_prgBuckets = new HASH_TABLE_BUCKET[nBuckets];
  570. } // HASH_TABLE::HASH_TABLE()
  571. DWORD
  572. HASH_TABLE::CalculateHash( IN LPCSTR pszKey, DWORD cchKey) const
  573. {
  574. DWORD hash = 0;
  575. DBG_ASSERT( pszKey != NULL );
  576. if ( cchKey > 8) {
  577. //
  578. // hash the last 8 characters
  579. //
  580. pszKey = (pszKey + cchKey - 8);
  581. }
  582. while ( *pszKey != '\0') {
  583. //
  584. // This is an extremely slimey way of getting upper case.
  585. // Kids, don't try this at home
  586. // -johnson
  587. //
  588. DWORD ch = ((*pszKey++) & ~0x20);
  589. // NYI: this is a totally pipe-line unfriendly code. Improve this.
  590. hash <<= 2;
  591. hash ^= ch;
  592. hash += ch;
  593. } // while
  594. //
  595. // Multiply by length (to introduce some randomness. Murali said so.
  596. //
  597. return( hash * cchKey);
  598. } // CalculateHash()
  599. VOID
  600. HASH_TABLE::Cleanup(VOID)
  601. {
  602. if ( NULL != m_prgBuckets ) {
  603. delete [] m_prgBuckets;
  604. m_prgBuckets = NULL;
  605. }
  606. } // HASH_TABLE::Cleanup()
  607. # define INCREMENT_LOOKUPS() \
  608. { InterlockedIncrement( (LPLONG ) &m_nLookups); }
  609. # define INCREMENT_HITS( phte) \
  610. if ( NULL != phte) { InterlockedIncrement( (LPLONG ) &m_nHits); }
  611. # define INCREMENT_INSERTS() \
  612. { InterlockedIncrement( (LPLONG ) &m_nInserts); }
  613. # define INCREMENT_FLUSHES() \
  614. { InterlockedIncrement( (LPLONG ) &m_nFlushes); }
  615. # define INCREMENT_ENTRIES( fRet) \
  616. if ( fRet) { InterlockedIncrement( (LPLONG ) &m_nEntries); }
  617. # define DECREMENT_ENTRIES( fRet) \
  618. if ( fRet) { InterlockedDecrement( (LPLONG ) &m_nEntries); }
  619. HT_ELEMENT *
  620. HASH_TABLE::Lookup( IN LPCSTR pszKey, DWORD cchKey)
  621. {
  622. // 1. Calculate the hash value for pszKey
  623. // 2. Find the bucket for the hash value
  624. // 3. Search for given item in the bucket
  625. // 4. return the result, after updating statistics
  626. DWORD hashVal = CalculateHash( pszKey, cchKey);
  627. HT_ELEMENT * phte;
  628. INCREMENT_LOOKUPS();
  629. DBG_ASSERT( NULL != m_prgBuckets);
  630. phte = m_prgBuckets[hashVal % m_nBuckets].Lookup( hashVal, pszKey, cchKey);
  631. INCREMENT_HITS( phte);
  632. return ( phte);
  633. } // HASH_TABLE::Lookup()
  634. BOOL
  635. HASH_TABLE::Insert( HT_ELEMENT * phte, IN BOOL fCheckBeforeInsert)
  636. {
  637. // 1. Calculate the hash value for key of the HT_ELEMENT object
  638. // 2. Find the bucket for the hash value
  639. // 3. Check if this item is not already present and insert
  640. // it into the hash table.
  641. // (the check can be bypassed if fCheck is set to FALSE)
  642. // 4. return the result, after updating statistics
  643. DWORD hashVal = CalculateHash( phte->QueryKey(),
  644. phte->QueryKeyLen() );
  645. BOOL fRet;
  646. INCREMENT_INSERTS();
  647. DBG_ASSERT( NULL != m_prgBuckets);
  648. fRet = m_prgBuckets[hashVal % m_nBuckets].Insert( hashVal,
  649. phte,
  650. fCheckBeforeInsert);
  651. IF_DEBUG( ERROR) {
  652. if ( !fRet) {
  653. DBGPRINTF(( DBG_CONTEXT,
  654. " Unable to insert %08x into bucket %d."
  655. " Bucket has %d elements. Error = %d\n",
  656. phte, hashVal % m_nBuckets,
  657. m_prgBuckets[hashVal % m_nBuckets].NumEntries(),
  658. GetLastError()
  659. ));
  660. }
  661. }
  662. INCREMENT_ENTRIES( fRet);
  663. return ( fRet);
  664. } // HASH_TABLE::Insert()
  665. BOOL
  666. HASH_TABLE::Delete( HT_ELEMENT * phte)
  667. {
  668. BOOL fRet;
  669. DWORD hashVal = CalculateHash( phte->QueryKey(), phte->QueryKeyLen());
  670. DBG_ASSERT( NULL != m_prgBuckets);
  671. fRet = m_prgBuckets[hashVal % m_nBuckets].Delete( phte);
  672. DECREMENT_ENTRIES( fRet);
  673. return ( fRet);
  674. } // HASH_TABLE::Delete()
  675. VOID
  676. HASH_TABLE::Print( IN DWORD level)
  677. {
  678. DWORD i;
  679. DBGPRINTF(( DBG_CONTEXT,
  680. "HASH_TABLE(%08x) "
  681. "%s: nBuckets = %d; dwFlags = %d;"
  682. " nEntries = %d; nLookups = %d; nHits = %d;"
  683. " nInserts = %d; nFlushes = %d;"
  684. " m_prgBuckets = %d\n",
  685. this, m_rgchId, m_nBuckets, m_dwFlags,
  686. m_nEntries, m_nLookups, m_nHits, m_nInserts,
  687. m_nFlushes, m_prgBuckets));
  688. if ( level == 0 ) {
  689. CHAR rgchBuff[2000];
  690. DWORD cch;
  691. cch = wsprintfA( rgchBuff, "\tBucket NumEntries\n");
  692. DBG_ASSERT( NULL != m_prgBuckets);
  693. for (i = 0; i < m_nBuckets; i++) {
  694. cch += wsprintf( rgchBuff + cch, "\t[%4d] %4d,\n",
  695. i, m_prgBuckets[i].NumEntries());
  696. } // for
  697. DBGDUMP(( DBG_CONTEXT, rgchBuff));
  698. } else {
  699. DBG_ASSERT( NULL != m_prgBuckets);
  700. for (i = 0; i < m_nBuckets; i++) {
  701. m_prgBuckets[i].Print( level);
  702. } // for
  703. }
  704. return;
  705. } // HASH_TABLE::Print()
  706. DWORD
  707. HASH_TABLE::InitializeIterator( IN HT_ITERATOR * phti)
  708. {
  709. DWORD dwErr = ERROR_NO_MORE_ITEMS;
  710. DBG_ASSERT( IsValid());
  711. DBG_ASSERT( NULL != phti);
  712. // initialize the iterator
  713. phti->nBucketNumber = INFINITE;
  714. phti->nChunkId = NULL;
  715. phti->nPos = 0;
  716. if ( m_nEntries > 0) {
  717. // set the iterator to point to the first bucket with some elements.
  718. for ( DWORD i = 0; (i < m_nBuckets); i++) {
  719. dwErr = m_prgBuckets[i].InitializeIterator( phti);
  720. if ( dwErr == NO_ERROR) {
  721. phti->nBucketNumber = i;
  722. break;
  723. }
  724. }
  725. }
  726. return ( dwErr);
  727. } // HASH_TABLE::InitializeIterator()
  728. DWORD
  729. HASH_TABLE::FindNextElement( IN HT_ITERATOR * phti,
  730. OUT HT_ELEMENT ** pphte)
  731. {
  732. DWORD dwErr = ERROR_NO_MORE_ITEMS;
  733. DBG_ASSERT( IsValid());
  734. DBG_ASSERT( NULL != phti);
  735. DBG_ASSERT( NULL != pphte);
  736. if ( INFINITE != phti->nBucketNumber) {
  737. // iterator has some valid state use it.
  738. DBG_ASSERT( phti->nBucketNumber < m_nBuckets);
  739. dwErr =
  740. m_prgBuckets[ phti->nBucketNumber].FindNextElement( phti, pphte);
  741. if ( ERROR_NO_MORE_ITEMS == dwErr) {
  742. DBG_REQUIRE( m_prgBuckets[ phti->nBucketNumber].
  743. CloseIterator( phti)
  744. == NO_ERROR
  745. );
  746. // hunt for the next bucket with an element.
  747. for ( DWORD i = (phti->nBucketNumber + 1); (i < m_nBuckets); i++) {
  748. dwErr = m_prgBuckets[i].InitializeIterator( phti);
  749. if ( dwErr == NO_ERROR) {
  750. phti->nBucketNumber = i;
  751. dwErr = m_prgBuckets[ i].FindNextElement( phti, pphte);
  752. DBG_ASSERT( dwErr == NO_ERROR);
  753. break;
  754. }
  755. } // for
  756. if ( ERROR_NO_MORE_ITEMS == dwErr) {
  757. // reset the bucket number
  758. phti->nBucketNumber = INFINITE;
  759. }
  760. }
  761. }
  762. return ( dwErr);
  763. } // HASH_TABLE::FindNextElement()
  764. DWORD
  765. HASH_TABLE::CloseIterator( IN HT_ITERATOR * phti)
  766. {
  767. DBG_ASSERT( IsValid());
  768. DBG_ASSERT( NULL != phti);
  769. if ( INFINITE != phti->nBucketNumber) {
  770. DBG_ASSERT( phti->nBucketNumber < m_nBuckets);
  771. DBG_REQUIRE( m_prgBuckets[ phti->nBucketNumber].
  772. CloseIterator( phti)
  773. == NO_ERROR
  774. );
  775. phti->nBucketNumber = INFINITE;
  776. }
  777. return ( NO_ERROR);
  778. } // HASH_TABLE::CloseIterator()
  779. /************************ End of File ***********************/