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.

1041 lines
26 KiB

  1. /*===================================================================
  2. Microsoft Denali
  3. Microsoft Confidential.
  4. Copyright 1996 Microsoft Corporation. All Rights Reserved.
  5. Component: Link list and Hash table
  6. File: Hashing.cpp
  7. Owner: PramodD
  8. This is the Link list and Hash table source file.
  9. ===================================================================*/
  10. #include "denpre.h"
  11. #pragma hdrstop
  12. #include "memchk.h"
  13. /*===================================================================
  14. ::DefaultHash
  15. this is a hash algorithm that is highly recommended by Aho,
  16. Seth, and Ulman from the dragon book. (THE compiler reference)
  17. Parameters:
  18. BYTE * pbKey
  19. int cbKey
  20. Returns:
  21. Hashed DWORD value.
  22. ===================================================================*/
  23. DWORD DefaultHash(const BYTE *pbKey, int cbKey)
  24. {
  25. const unsigned WORD_BITS = CHAR_BIT * sizeof(unsigned);
  26. const unsigned SEVENTY_FIVE_PERCENT = WORD_BITS * 3 / 4;
  27. const unsigned ONE_EIGHTH = WORD_BITS / 8;
  28. const unsigned HIGH_BITS = ~(unsigned(~0) >> ONE_EIGHTH);
  29. register unsigned uT, uResult = 0;
  30. register const BYTE *pb = pbKey;
  31. while (cbKey-- > 0)
  32. {
  33. uResult = (uResult << ONE_EIGHTH) + *pb++;
  34. if ((uT = uResult & HIGH_BITS) != 0)
  35. uResult = (uResult ^ (uT >> SEVENTY_FIVE_PERCENT)) & ~HIGH_BITS;
  36. }
  37. return uResult;
  38. }
  39. /*===================================================================
  40. ::UnicodeUpcaseHash
  41. This is Aho, Seth, and Ulman's hash algorithm adapted for wide
  42. character strings. Their algorithm was not designed for cases
  43. where every other character is 0 (which is how a unicode string
  44. looks if you pretend it's ascii) Therefore, performance
  45. qualities are unknown for that case.
  46. NOTE: for real Unicode, (not unicode that is merely ANSI converted)
  47. I have no idea how good a distribution this algorithm will
  48. produce. (since we are shifting in values > 8 bits now)
  49. Parameters:
  50. BYTE * pbKey
  51. int cbKey
  52. Returns:
  53. Hashed DWORD value.
  54. ===================================================================*/
  55. #define toupper(x) WORD(CharUpper(LPSTR(WORD(x))))
  56. DWORD UnicodeUpcaseHash(const BYTE *pbKey, int cbKey)
  57. {
  58. // PERF hash on last CCH_HASH chars only
  59. const unsigned WORD_BITS = CHAR_BIT * sizeof(unsigned);
  60. const unsigned SEVENTY_FIVE_PERCENT = WORD_BITS * 3 / 4;
  61. const unsigned ONE_EIGHTH = WORD_BITS / 8;
  62. const unsigned HIGH_BITS = ~(unsigned(~0) >> ONE_EIGHTH);
  63. const unsigned CCH_HASH = 8;
  64. register unsigned uT, uResult = 0;
  65. Assert ((cbKey & 1) == 0); // cbKey better be even!
  66. int cwKey = unsigned(cbKey) >> 1;
  67. register const WORD *pw = reinterpret_cast<const WORD *>(pbKey) + cwKey;
  68. cwKey = min(cwKey, CCH_HASH);
  69. if (FIsWinNT())
  70. {
  71. WCHAR awcTemp[CCH_HASH];
  72. // copy last cwKey WCHARs of pbKey to last cwKey WCHARs of awcTemp
  73. wcsncpy(awcTemp + CCH_HASH - cwKey, pw - cwKey, cwKey);
  74. CharUpperBuffW(awcTemp + CCH_HASH - cwKey, cwKey);
  75. pw = awcTemp + CCH_HASH;
  76. while (cwKey-- > 0)
  77. {
  78. uResult = (uResult << ONE_EIGHTH) + *--pw;
  79. if ((uT = uResult & HIGH_BITS) != 0)
  80. uResult = (uResult ^ (uT >> SEVENTY_FIVE_PERCENT)) & ~HIGH_BITS;
  81. }
  82. }
  83. else
  84. {
  85. // CharUpperBuffW does nothing on Windows 95, so we use CharUpperBuffA
  86. // instead. Chances are that the top eight bits are zero anyway,
  87. // and even if they aren't, this will still give us a halfway-decent
  88. // distribution.
  89. CHAR achTemp[CCH_HASH];
  90. for (int i = 0; i < cwKey; ++i)
  91. achTemp[i] = (CHAR) *(pw - cwKey + i);
  92. CharUpperBuffA(achTemp, cwKey);
  93. while (cwKey-- > 0)
  94. {
  95. uResult = (uResult << ONE_EIGHTH) + achTemp[cwKey];
  96. if ((uT = uResult & HIGH_BITS) != 0)
  97. uResult = (uResult ^ (uT >> SEVENTY_FIVE_PERCENT)) & ~HIGH_BITS;
  98. }
  99. }
  100. return uResult;
  101. }
  102. DWORD MultiByteUpcaseHash(const BYTE *pbKey, int cbKey)
  103. {
  104. // PERF hash on first CCH_HASH chars only
  105. const unsigned WORD_BITS = CHAR_BIT * sizeof(unsigned);
  106. const unsigned SEVENTY_FIVE_PERCENT = WORD_BITS * 3 / 4;
  107. const unsigned ONE_EIGHTH = WORD_BITS / 8;
  108. const unsigned HIGH_BITS = ~(unsigned(~0) >> ONE_EIGHTH);
  109. const unsigned CCH_HASH = 8;
  110. register unsigned uT, uResult = 0;
  111. unsigned char achTemp[CCH_HASH + 1];
  112. // For performance we only HASH on at most CCH_HASH characters.
  113. cbKey = min(cbKey, CCH_HASH);
  114. // Copy cbKey chacters into temporary buffer
  115. memcpy(achTemp, pbKey, cbKey);
  116. // Add terminating null character
  117. achTemp[cbKey] = 0;
  118. // Convert to upper case
  119. _mbsupr(achTemp);
  120. while (cbKey-- > 0)
  121. {
  122. uResult = (uResult << ONE_EIGHTH) + achTemp[cbKey];
  123. if ((uT = uResult & HIGH_BITS) != 0)
  124. uResult = (uResult ^ (uT >> SEVENTY_FIVE_PERCENT)) & ~HIGH_BITS;
  125. }
  126. return uResult;
  127. }
  128. /*===================================================================
  129. ::PtrHash
  130. Hash function that returns the pointer itself as the
  131. DWORD hash value
  132. Parameters:
  133. BYTE * pbKey
  134. int cbKey (not used)
  135. Returns:
  136. Hashed DWORD value.
  137. ===================================================================*/
  138. DWORD PtrHash
  139. (
  140. const BYTE *pbKey,
  141. int /* cbKey */
  142. )
  143. {
  144. return *(reinterpret_cast<DWORD *>(&pbKey));
  145. }
  146. /*===================================================================
  147. CLSIDHash
  148. CLSID hash. Uses xor of the first and last DWORD
  149. Parameters:
  150. BYTE * pbKey
  151. int cbKey
  152. Returns:
  153. Hashed DWORD value.
  154. ===================================================================*/
  155. DWORD CLSIDHash
  156. (
  157. const BYTE *pbKey,
  158. int cbKey
  159. )
  160. {
  161. Assert(cbKey == 16);
  162. DWORD *pdwKey = (DWORD *)pbKey;
  163. return (pdwKey[0] ^ pdwKey[3]);
  164. }
  165. /*===================================================================
  166. CLinkElem::CLinkElem
  167. The Constructor.
  168. Parameters:
  169. NONE
  170. Returns:
  171. NONE
  172. ===================================================================*/
  173. CLinkElem::CLinkElem(void)
  174. : m_pKey(NULL),
  175. m_cbKey(0),
  176. m_Info(0),
  177. m_pPrev(NULL),
  178. m_pNext(NULL)
  179. {
  180. }
  181. /*===================================================================
  182. HRESULT CLinkElem::Init
  183. Initializes class members
  184. Parameters:
  185. void * pKey
  186. int cbKey
  187. Returns:
  188. S_OK Success
  189. E_FAIL Error
  190. ===================================================================*/
  191. HRESULT CLinkElem::Init( void *pKey, int cbKey )
  192. {
  193. m_pPrev = NULL;
  194. m_pNext = NULL;
  195. m_Info = 0;
  196. if ( pKey == NULL || cbKey == 0 )
  197. return E_FAIL;
  198. m_pKey = static_cast<BYTE *>(pKey);
  199. m_cbKey = (short)cbKey;
  200. return S_OK;
  201. }
  202. /*===================================================================
  203. CHashTable::CHashTable
  204. Constructor for CHashTable
  205. Parameters:
  206. NONE
  207. Returns:
  208. NONE
  209. ===================================================================*/
  210. CHashTable::CHashTable( HashFunction pfnHash )
  211. : m_fInited(FALSE),
  212. m_fBucketsAllocated(FALSE),
  213. m_pHead(NULL),
  214. m_pTail(NULL),
  215. m_rgpBuckets(NULL),
  216. m_pfnHash(pfnHash),
  217. m_cBuckets(0),
  218. m_Count(0)
  219. {
  220. }
  221. /*===================================================================
  222. CHashTable::~CHashTable
  223. Destructor for CHashTable. Frees allocated bucket array.
  224. Parameters:
  225. NONE
  226. Returns:
  227. NONE
  228. ===================================================================*/
  229. CHashTable::~CHashTable( void )
  230. {
  231. if (m_fBucketsAllocated)
  232. {
  233. Assert(m_rgpBuckets);
  234. delete [] m_rgpBuckets;
  235. }
  236. }
  237. /*===================================================================
  238. HRESULT CHashTable::UnInit
  239. Frees allocated bucket array.
  240. Parameters:
  241. NONE
  242. Returns:
  243. S_OK Always
  244. ===================================================================*/
  245. HRESULT CHashTable::UnInit( void )
  246. {
  247. if (m_fBucketsAllocated)
  248. {
  249. Assert(m_rgpBuckets);
  250. delete [] m_rgpBuckets;
  251. m_fBucketsAllocated = FALSE;
  252. }
  253. m_rgpBuckets = NULL;
  254. m_pHead = NULL;
  255. m_pTail = NULL;
  256. m_cBuckets = 0;
  257. m_Count = 0;
  258. m_fInited = FALSE;
  259. return S_OK;
  260. }
  261. /*===================================================================
  262. void CHashTable::AssertValid
  263. Verify integrity of the data structure.
  264. NOTE: This function does very deep integrity checks and thus is
  265. very slow.
  266. Checks performed:
  267. verify that m_Count is valid
  268. verify that each element is in the right bucket
  269. verify prev, next links and info fields
  270. ===================================================================*/
  271. #ifdef DBG
  272. void CHashTable::AssertValid() const
  273. {
  274. CLinkElem *pElem; // pointer to current link element
  275. unsigned i; // index into current bucket
  276. unsigned cItems = 0; // actual number of items in the table
  277. Assert(m_fInited);
  278. if (m_Count == 0)
  279. {
  280. if (m_rgpBuckets)
  281. {
  282. BOOL fAllNulls = TRUE;
  283. // empty hash table - make sure that everything reflects this
  284. Assert(m_pHead == NULL);
  285. Assert (m_pTail == NULL);
  286. for (i = 0; i < m_cBuckets; i++)
  287. {
  288. if (m_rgpBuckets[i] != NULL)
  289. {
  290. fAllNulls = FALSE;
  291. break;
  292. }
  293. }
  294. Assert(fAllNulls);
  295. }
  296. return;
  297. }
  298. // If m_Count > 0
  299. Assert(m_pHead);
  300. Assert(m_pHead->m_pPrev == NULL);
  301. Assert(m_pTail != NULL && m_pTail->m_pNext == NULL);
  302. Assert(m_rgpBuckets);
  303. // Now verify each entry
  304. for (i = 0; i < m_cBuckets; ++i)
  305. {
  306. pElem = m_rgpBuckets[i];
  307. while (pElem != NULL)
  308. {
  309. // Verify hashing
  310. Assert ((m_pfnHash(pElem->m_pKey, pElem->m_cbKey) % m_cBuckets) == i);
  311. // Verify links
  312. if (pElem->m_pPrev)
  313. {
  314. Assert (pElem->m_pPrev->m_pNext == pElem);
  315. }
  316. else
  317. {
  318. Assert (m_pHead == pElem);
  319. }
  320. if (pElem->m_pNext)
  321. {
  322. Assert (pElem->m_pNext->m_pPrev == pElem);
  323. }
  324. else
  325. {
  326. Assert (m_pTail == pElem);
  327. }
  328. // Verify info fields
  329. Assert (pElem->m_Info >= 0);
  330. if (pElem != m_rgpBuckets[i])
  331. {
  332. Assert (pElem->m_Info == pElem->m_pPrev->m_Info - 1);
  333. }
  334. // Prepare for next iteration, stopping when m_Info is zero.
  335. ++cItems;
  336. if (pElem->m_Info == 0)
  337. break;
  338. pElem = pElem->m_pNext;
  339. }
  340. }
  341. // Verify count
  342. Assert (m_Count == cItems);
  343. }
  344. #endif
  345. /*===================================================================
  346. HRESULT CHashTable::Init
  347. Initialize CHashTable by allocating the bucket array and
  348. and initializing the bucket link lists.
  349. Parameters:
  350. UINT cBuckets Number of buckets
  351. Returns:
  352. HRESULT S_OK
  353. E_OUTOFMEMORY
  354. ===================================================================*/
  355. HRESULT CHashTable::Init( UINT cBuckets )
  356. {
  357. m_cBuckets = cBuckets;
  358. m_Count = 0;
  359. m_rgpBuckets = NULL; // created on demand
  360. m_fInited = TRUE;
  361. return S_OK;
  362. }
  363. /*===================================================================
  364. HRESULT CHashTable::ReInit
  365. Reinitialize CHashTable by deleting everything in it. - client
  366. is responsible for making the hashtable empty first
  367. Parameters:
  368. None
  369. Returns:
  370. None
  371. ===================================================================*/
  372. void CHashTable::ReInit()
  373. {
  374. Assert( m_fInited );
  375. if (m_rgpBuckets)
  376. memset(m_rgpBuckets, 0, m_cBuckets * sizeof(CLinkElem *));
  377. m_Count = 0;
  378. m_pHead = NULL;
  379. m_pTail = NULL;
  380. }
  381. /*===================================================================
  382. HRESULT CHashTable::AllocateBuckets()
  383. Allocates hash table buckets on demand
  384. Parameters:
  385. Returns:
  386. HRESULT
  387. ===================================================================*/
  388. HRESULT CHashTable::AllocateBuckets()
  389. {
  390. Assert(m_rgpBuckets == NULL);
  391. Assert(m_fInited);
  392. Assert(m_cBuckets > 0);
  393. if (m_cBuckets <= PREALLOCATED_BUCKETS_MAX)
  394. {
  395. m_rgpBuckets = m_rgpBucketsBuffer;
  396. }
  397. else
  398. {
  399. m_rgpBuckets = new CLinkElem * [m_cBuckets];
  400. if (m_rgpBuckets == NULL)
  401. return E_OUTOFMEMORY;
  402. m_fBucketsAllocated = TRUE;
  403. }
  404. memset(m_rgpBuckets, 0, m_cBuckets * sizeof(CLinkElem *));
  405. return S_OK;
  406. }
  407. /*===================================================================
  408. BOOL CHashTable::FIsEqual
  409. compare two keys using their lengths and memcmp()
  410. Parameters:
  411. const void *pKey1 first key
  412. int cbKey1 length of the first key
  413. const void *pKey2 second key
  414. int cbKey2 length of second key
  415. Returns:
  416. Pointer to element added/found.
  417. ===================================================================*/
  418. BOOL CHashTable::FIsEqual( const void * pKey1,
  419. int cbKey1,
  420. const void * pKey2,
  421. int cbKey2 )
  422. {
  423. if (cbKey1 != cbKey2)
  424. return FALSE;
  425. return memcmp(pKey1, pKey2, cbKey1) == 0;
  426. }
  427. #pragma optimize("g", off)
  428. /*===================================================================
  429. CHashTable::AddElem
  430. Adds a CLinkElem to Hash table.
  431. User is responsible for allocating the Element to be added.
  432. Parameters:
  433. CLinkElem * pElem Object to be added
  434. BOOL fTestDups Look for duplicates if true
  435. Returns:
  436. Pointer to element added/found.
  437. ===================================================================*/
  438. CLinkElem *CHashTable::AddElem( CLinkElem *pElem, BOOL fTestDups )
  439. {
  440. if (m_rgpBuckets == NULL)
  441. {
  442. if (FAILED(AllocateBuckets()))
  443. return NULL;
  444. }
  445. if (pElem == NULL)
  446. return NULL;
  447. BOOL fNew = TRUE;
  448. DWORD iT = m_pfnHash( pElem->m_pKey, pElem->m_cbKey ) % m_cBuckets;
  449. CLinkElem * pT = m_rgpBuckets[iT];
  450. BOOL fDebugTestDups = FALSE;
  451. #ifdef DBG
  452. // In retail, if fTestDups is false, it means that
  453. // there shouldnt be any dups, so dont bother testing. Under debug, however
  454. // we want to be able to assert that there isnt a dup (since there isnt supposed to be one).
  455. fDebugTestDups = !fTestDups;
  456. #endif
  457. if (fTestDups || fDebugTestDups)
  458. {
  459. while ( pT && fNew )
  460. {
  461. if ( FIsEqual( pT->m_pKey, pT->m_cbKey, pElem->m_pKey, pElem->m_cbKey ) )
  462. fNew = FALSE;
  463. else if ( pT->m_Info > 0 )
  464. pT = pT->m_pNext;
  465. else
  466. break;
  467. }
  468. }
  469. #ifdef DBG
  470. // If there arent supposed to be any dups, then make sure this element is seen as "new"
  471. if (fDebugTestDups)
  472. Assert(fNew);
  473. #endif
  474. #ifdef DUMP_HASHING_INFO
  475. static DWORD cAdds = 0;
  476. FILE *logfile = NULL;
  477. if (cAdds++ > 1000000 && m_Count > 100)
  478. {
  479. cAdds = 0;
  480. if (logfile = fopen("C:\\Temp\\hashdump.Log", "a+"))
  481. {
  482. DWORD cZero = 0;
  483. short iMax = 0;
  484. DWORD cGte3 = 0;
  485. DWORD cGte5 = 0;
  486. DWORD cGte10 = 0;
  487. fprintf(logfile, "Hash dump: # elements = %d\n", m_Count);
  488. for (UINT iBucket = 0; iBucket < m_cBuckets; iBucket++)
  489. {
  490. if (m_rgpBuckets[iBucket] == NULL)
  491. cZero++;
  492. else
  493. {
  494. short Info = m_rgpBuckets[iBucket]->m_Info;
  495. if (Info > iMax)
  496. iMax = Info;
  497. if (Info >= 10) cGte10++;
  498. else if (Info >= 5) cGte5++;
  499. else if (Info >= 3) cGte3++;
  500. }
  501. }
  502. fprintf(logfile, "Max chain = %d, # 0 chains = %d, # >= 3 = %d, # >= 5 = %d, # >= 10 = %d\n",
  503. (DWORD)iMax, cZero, cGte3, cGte5, cGte10);
  504. fflush(logfile);
  505. fclose(logfile);
  506. }
  507. }
  508. #endif
  509. if ( fNew )
  510. {
  511. if ( pT )
  512. {
  513. // There are other elements in bucket
  514. pT = m_rgpBuckets[iT];
  515. m_rgpBuckets[iT] = pElem;
  516. pElem->m_Info = pT->m_Info + 1;
  517. pElem->m_pNext = pT;
  518. pElem->m_pPrev = pT->m_pPrev;
  519. pT->m_pPrev = pElem;
  520. if ( pElem->m_pPrev == NULL )
  521. m_pHead = pElem;
  522. else
  523. pElem->m_pPrev->m_pNext = pElem;
  524. }
  525. else
  526. {
  527. // This is the first element in the bucket
  528. m_rgpBuckets[iT] = pElem;
  529. pElem->m_pPrev = NULL;
  530. pElem->m_pNext = m_pHead;
  531. pElem->m_Info = 0;
  532. if ( m_pHead )
  533. m_pHead->m_pPrev = pElem;
  534. else
  535. m_pTail = pElem;
  536. m_pHead = pElem;
  537. }
  538. m_Count++;
  539. AssertValid();
  540. return pElem;
  541. }
  542. AssertValid();
  543. return pT;
  544. }
  545. #pragma optimize("g", on)
  546. #pragma optimize("g", off)
  547. /*===================================================================
  548. CLinkElem * CHashTable::FindElem
  549. Finds an object in the hash table based on the name.
  550. Parameters:
  551. void * pKey
  552. int cbKey
  553. Returns:
  554. Pointer to CLinkElem if found, otherwise NULL.
  555. ===================================================================*/
  556. CLinkElem * CHashTable::FindElem( const void *pKey, int cbKey )
  557. {
  558. AssertValid();
  559. if ( m_rgpBuckets == NULL || pKey == NULL )
  560. return NULL;
  561. DWORD iT = m_pfnHash( static_cast<const BYTE *>(pKey), cbKey ) % m_cBuckets;
  562. CLinkElem * pT = m_rgpBuckets[iT];
  563. CLinkElem * pRet = NULL;
  564. while ( pT && pRet == NULL )
  565. {
  566. if ( FIsEqual( pT->m_pKey, pT->m_cbKey, pKey, cbKey ) )
  567. pRet = pT;
  568. else if ( pT->m_Info > 0 )
  569. pT = pT->m_pNext;
  570. else
  571. break;
  572. }
  573. return pRet;
  574. }
  575. #pragma optimize("g", on)
  576. #pragma optimize("g", off)
  577. /*===================================================================
  578. CHashTable::DeleteElem
  579. Removes a CLinkElem from Hash table.
  580. The user should delete the freed link list element.
  581. Parameters:
  582. void * pbKey key
  583. int cbKey length of key
  584. Returns:
  585. Pointer to element removed, NULL if not found
  586. ===================================================================*/
  587. CLinkElem * CHashTable::DeleteElem( const void *pKey, int cbKey )
  588. {
  589. if ( m_rgpBuckets == NULL || pKey == NULL )
  590. return NULL;
  591. CLinkElem * pRet = NULL;
  592. DWORD iT = m_pfnHash( static_cast<const BYTE *>(pKey), cbKey ) % m_cBuckets;
  593. CLinkElem * pT = m_rgpBuckets[iT];
  594. while ( pT && pRet == NULL )// Find it !
  595. {
  596. if ( FIsEqual( pT->m_pKey, pT->m_cbKey, pKey, cbKey ) )
  597. pRet = pT;
  598. else if ( pT->m_Info > 0 )
  599. pT = pT->m_pNext;
  600. else
  601. break;
  602. }
  603. if ( pRet )
  604. {
  605. pT = m_rgpBuckets[iT];
  606. if ( pRet == pT )
  607. {
  608. // Update bucket head
  609. if ( pRet->m_Info > 0 )
  610. m_rgpBuckets[iT] = pRet->m_pNext;
  611. else
  612. m_rgpBuckets[iT] = NULL;
  613. }
  614. // Update counts in bucket link list
  615. while ( pT != pRet )
  616. {
  617. pT->m_Info--;
  618. pT = pT->m_pNext;
  619. }
  620. // Update link list
  621. if ( pT = pRet->m_pPrev )
  622. {
  623. // Not the Head of the link list
  624. if ( pT->m_pNext = pRet->m_pNext )
  625. pT->m_pNext->m_pPrev = pT;
  626. else
  627. m_pTail = pT;
  628. }
  629. else
  630. {
  631. // Head of the link list
  632. if ( m_pHead = pRet->m_pNext )
  633. m_pHead->m_pPrev = NULL;
  634. else
  635. m_pTail = NULL;
  636. }
  637. m_Count--;
  638. }
  639. AssertValid();
  640. return pRet;
  641. }
  642. #pragma optimize("g", on)
  643. /*===================================================================
  644. CHashTable::RemoveElem
  645. Removes a given CLinkElem from Hash table.
  646. The user should delete the freed link list element.
  647. Parameters:
  648. CLinkElem * pLE Element to remove
  649. Returns:
  650. Pointer to element removed
  651. ===================================================================*/
  652. CLinkElem * CHashTable::RemoveElem( CLinkElem *pLE )
  653. {
  654. CLinkElem *pLET;
  655. if ( m_rgpBuckets == NULL || pLE == NULL )
  656. return NULL;
  657. // Remove this item from the linked list
  658. pLET = pLE->m_pPrev;
  659. if (pLET)
  660. pLET->m_pNext = pLE->m_pNext;
  661. pLET = pLE->m_pNext;
  662. if (pLET)
  663. pLET->m_pPrev = pLE->m_pPrev;
  664. if (m_pHead == pLE)
  665. m_pHead = pLE->m_pNext;
  666. if (m_pTail == pLE)
  667. m_pTail = pLE->m_pPrev;
  668. /*
  669. * If this was the first item in a bucket, then fix up the bucket.
  670. * Otherwise, decrement the count of items in the bucket for each item
  671. * in the bucket prior to this item
  672. */
  673. if (pLE->m_pPrev == NULL || pLE->m_pPrev->m_Info == 0)
  674. {
  675. UINT iBucket;
  676. // This item is head of a bucket. Need to find out which bucket!
  677. for (iBucket = 0; iBucket < m_cBuckets; iBucket++)
  678. if (m_rgpBuckets[iBucket] == pLE)
  679. break;
  680. Assert(iBucket < m_cBuckets && m_rgpBuckets[iBucket] == pLE);
  681. if (pLE->m_Info == 0)
  682. m_rgpBuckets[iBucket] = NULL;
  683. else
  684. m_rgpBuckets[iBucket] = pLE->m_pNext;
  685. }
  686. else
  687. {
  688. // This item is in the middle of a bucket chain. Update counts in preceeding items
  689. pLET = pLE->m_pPrev;
  690. while (pLET != NULL && pLET->m_Info != 0)
  691. {
  692. pLET->m_Info--;
  693. pLET = pLET->m_pPrev;
  694. }
  695. }
  696. // Decrement count of total number of items
  697. m_Count--;
  698. AssertValid();
  699. return pLE;
  700. }
  701. /*===================================================================
  702. CHashTableStr::CHashTableStr
  703. Constructor for CHashTableStr
  704. Parameters:
  705. NONE
  706. Returns:
  707. NONE
  708. ===================================================================*/
  709. CHashTableStr::CHashTableStr( HashFunction pfnHash )
  710. : CHashTable( pfnHash )
  711. {
  712. }
  713. /*===================================================================
  714. BOOL CHashTableStr::FIsEqual
  715. compare two keys using their lengths, treating the keys
  716. as Unicode and doing case insensitive compare.
  717. Parameters:
  718. const void *pKey1 first key
  719. int cbKey1 length of the first key
  720. const void *pKey2 second key
  721. int cbKey2 length of second key
  722. Returns:
  723. Pointer to element added/found.
  724. ===================================================================*/
  725. BOOL CHashTableStr::FIsEqual( const void * pKey1,
  726. int cbKey1,
  727. const void * pKey2,
  728. int cbKey2 )
  729. {
  730. if ( cbKey1 != cbKey2 )
  731. return FALSE;
  732. return _wcsnicmp(static_cast<const wchar_t *>(pKey1), static_cast<const wchar_t *>(pKey2), cbKey1) == 0;
  733. }
  734. /*===================================================================
  735. CHashTableMBStr::CHashTableMBStr
  736. Constructor for CHashTableMBStr
  737. Parameters:
  738. NONE
  739. Returns:
  740. NONE
  741. ===================================================================*/
  742. CHashTableMBStr::CHashTableMBStr( HashFunction pfnHash )
  743. : CHashTable( pfnHash )
  744. {
  745. }
  746. /*===================================================================
  747. BOOL CHashTableMBStr::FIsEqual
  748. compare two keys using their lengths, treating the keys
  749. as multi-byte strings and doing case insensitive compare.
  750. Parameters:
  751. const void *pKey1 first key
  752. int cbKey1 length of the first key
  753. const void *pKey2 second key
  754. int cbKey2 length of second key
  755. Returns:
  756. Pointer to element added/found.
  757. ===================================================================*/
  758. BOOL CHashTableMBStr::FIsEqual( const void * pKey1,
  759. int cbKey1,
  760. const void * pKey2,
  761. int cbKey2 )
  762. {
  763. if ( cbKey1 != cbKey2 )
  764. return FALSE;
  765. return _mbsnicmp(static_cast<const unsigned char *>(pKey1), static_cast<const unsigned char *>(pKey2), cbKey1) == 0;
  766. }
  767. /*===================================================================
  768. CHashTablePtr::CHashTablePtr
  769. Constructor for CHashTableStr
  770. Parameters:
  771. HashFunction pfnHash has function (PtrHash is default)
  772. Returns:
  773. NONE
  774. ===================================================================*/
  775. CHashTablePtr::CHashTablePtr
  776. (
  777. HashFunction pfnHash
  778. )
  779. : CHashTable(pfnHash)
  780. {
  781. }
  782. /*===================================================================
  783. BOOL CHashTablePtr::FIsEqual
  784. Compare two pointers.
  785. Used by CHashTable to find elements
  786. Parameters:
  787. const void *pKey1 first key
  788. int cbKey1 length of the first key (unused)
  789. const void *pKey2 second key
  790. int cbKey2 length of second key (unused)
  791. Returns:
  792. BOOL (true when equal)
  793. ===================================================================*/
  794. BOOL CHashTablePtr::FIsEqual
  795. (
  796. const void *pKey1,
  797. int /* cbKey1 */,
  798. const void *pKey2,
  799. int /* cbKey2 */
  800. )
  801. {
  802. return (pKey1 == pKey2);
  803. }
  804. /*===================================================================
  805. CHashTableCLSID::CHashTableCLSID
  806. Constructor for CHashTableCLSID
  807. Parameters:
  808. HashFunction pfnHash has function (CLSIDHash is default)
  809. Returns:
  810. NONE
  811. ===================================================================*/
  812. CHashTableCLSID::CHashTableCLSID
  813. (
  814. HashFunction pfnHash
  815. )
  816. : CHashTable(pfnHash)
  817. {
  818. }
  819. /*===================================================================
  820. BOOL CHashTableCLSID::FIsEqual
  821. Compare two CLSIDs.
  822. Parameters:
  823. const void *pKey1 first key
  824. int cbKey1 length of the first key
  825. const void *pKey2 second key
  826. int cbKey2 length of second key
  827. Returns:
  828. BOOL (true when equal)
  829. ===================================================================*/
  830. BOOL CHashTableCLSID::FIsEqual
  831. (
  832. const void *pKey1,
  833. int cbKey1,
  834. const void *pKey2,
  835. int cbKey2
  836. )
  837. {
  838. Assert(cbKey1 == sizeof(CLSID) && cbKey2 == sizeof(CLSID));
  839. return IsEqualCLSID(*((CLSID *)pKey1), *((CLSID *)pKey2));
  840. }