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.

639 lines
18 KiB

  1. //
  2. // Copyright (c) Microsoft Corporation 1991-1993
  3. //
  4. // File: Hash.c
  5. //
  6. // Comments:
  7. // This file contains functions that are roughly equivelent to the
  8. // kernel atom function. There are two main differences. The first
  9. // is that in 32 bit land the tables are maintined in our shared heap,
  10. // which makes it shared between all of our apps. The second is that
  11. // we can assocate a long pointer with each of the items, which in many
  12. // cases allows us to keep from having to do a secondary lookup from
  13. // a different table
  14. //
  15. // History:
  16. // 09/08/93 - Created KurtE
  17. // ??/??/94 - ported for unicode (anonymous)
  18. // 10/26/95 - rearranged hashitems for perf, alignment FrancisH
  19. //
  20. //---------------------------------------------------------------------------
  21. #include "shellprv.h"
  22. #pragma hdrstop
  23. #include "fstreex.h" // for SHCF_ICON_INDEX
  24. #define DM_PERF 0 // perf stats
  25. //--------------------------------------------------------------------------
  26. // First define a data structure to use to maintain the list
  27. #define DEF_HASH_BUCKET_COUNT 71
  28. // NOTE a PHASHITEM is defined as a LPCSTR externaly (for old code to work)
  29. #undef PHASHITEM
  30. typedef struct _HashItem * PHASHITEM;
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Hash item layout:
  34. //
  35. // [extra data][_HashItem struct][item text]
  36. //
  37. //-----------------------------------------------------------------------------
  38. typedef struct _HashItem
  39. {
  40. //
  41. // this part of the struct is aligned
  42. //
  43. PHASHITEM phiNext; //
  44. WORD wCount; // Usage count
  45. WORD cchLen; // Length of name in characters.
  46. //
  47. // this member is just a placeholder
  48. //
  49. TCHAR szName[1]; // name
  50. } HASHITEM;
  51. #pragma warning(disable:4200) // Zero-sized array in struct
  52. typedef struct _HashTable
  53. {
  54. UINT uBuckets; // Number of buckets
  55. UINT uItems; // Number of items
  56. UINT cbExtra; // Extra bytes per item
  57. LPCTSTR pszHTCache; // MRU ptr for last lookup/add/etc.
  58. PHASHITEM ahiBuckets[0]; // Set of buckets for the table
  59. } HASHTABLE, * PHASHTABLE;
  60. #define HIFROMSZ(sz) ((PHASHITEM)((BYTE*)(sz) - FIELD_OFFSET(HASHITEM, szName)))
  61. #define HIDATAPTR(pht, sz) ((void *)(((BYTE *)HIFROMSZ(sz)) - (pht? pht->cbExtra : 0)))
  62. #define HIDATAARRAY(pht, sz) ((DWORD_PTR *)HIDATAPTR(pht, sz))
  63. #define LOOKUPHASHITEM 0
  64. #define ADDHASHITEM 1
  65. #define DELETEHASHITEM 2
  66. #define PURGEHASHITEM 3 // DANGER: EVIL!
  67. static HHASHTABLE g_hHashTable = NULL;
  68. HHASHTABLE GetGlobalHashTable();
  69. PHASHTABLE _CreateHashTable(UINT uBuckets, UINT cbExtra);
  70. //--------------------------------------------------------------------------
  71. // This function allocs a hashitem.
  72. //
  73. PHASHITEM _AllocHashItem(PHASHTABLE pht, DWORD cchName)
  74. {
  75. BYTE *mem;
  76. ASSERT(pht);
  77. mem = (BYTE *)LocalAlloc(LPTR, SIZEOF(HASHITEM) + (cchName * SIZEOF(TCHAR)) + pht->cbExtra);
  78. if (mem)
  79. mem += pht->cbExtra;
  80. return (PHASHITEM)mem;
  81. }
  82. //--------------------------------------------------------------------------
  83. // This function frees a hashitem.
  84. //
  85. __inline void _FreeHashItem(PHASHTABLE pht, PHASHITEM phi)
  86. {
  87. ASSERT(pht && phi);
  88. LocalFree((BYTE *)phi - pht->cbExtra);
  89. }
  90. // PERF_CACHE
  91. //*** c_szHTNil -- 1-element MRU for hashtable
  92. // DESCRIPTION
  93. // it turns out we have long runs of duplicate lookups (e.g. "Directory"
  94. // and ".lnk"). a 1-element MRU is a v. cheap way of speeding things up.
  95. // rather than check for the (rare) special case of NULL each time we
  96. // check our cache, we pt at at this guy. then iff we think it's a
  97. // cache hit, we make sure it's not pting at this special guy.
  98. const TCHAR c_szHTNil[] = TEXT(""); // arbitrary value, unique-&
  99. #ifdef DEBUG
  100. int g_cHTTot, g_cHTHit;
  101. int g_cHTMod = 100;
  102. #endif
  103. // --------------------------------------------------------
  104. // Compute a hash value from an input string of any type, i.e.
  105. // the input is just treated as a sequence of bytes.
  106. // Based on a hash function originally proposed by J. Zobel.
  107. // Author: Paul Larson, 1999, [email protected]
  108. // --------------------------------------------------------
  109. ULONG _CalculateHashKey(LPCTSTR pszName, WORD *pcch)
  110. {
  111. // initialize HashKey to a reasonably large constant so very
  112. // short keys won't get mapped to small values. Virtually any
  113. // large odd constant will do.
  114. unsigned int HashKey = 314159269 ;
  115. TUCHAR *pC = (TUCHAR *)pszName;
  116. for(; *pC; pC++){
  117. HashKey ^= (HashKey<<11) + (HashKey<<5) + (HashKey>>2) + (unsigned int) *pC ;
  118. }
  119. if (pcch)
  120. *pcch = (WORD)(pC - pszName);
  121. return (HashKey & 0x7FFFFFFF) ;
  122. }
  123. void _GrowTable(HHASHTABLE hht)
  124. {
  125. // hht can't be NULL here
  126. PHASHTABLE pht = *hht;
  127. PHASHTABLE phtNew = _CreateHashTable((pht->uBuckets * 2) -1, pht->cbExtra);
  128. if (phtNew)
  129. {
  130. int i;
  131. for (i=0; i<(int)pht->uBuckets; i++)
  132. {
  133. PHASHITEM phi;
  134. PHASHITEM phiNext;
  135. for (phi=pht->ahiBuckets[i]; phi; phi=phiNext)
  136. {
  137. // We always use case sensitive hash here since the case has already been fixed when adding the key.
  138. ULONG uBucket = _CalculateHashKey(phi->szName, NULL) % phtNew->uBuckets;
  139. phiNext = phi->phiNext;
  140. // And link it in to the right bucket
  141. phi->phiNext = phtNew->ahiBuckets[uBucket];
  142. phtNew->ahiBuckets[uBucket] = phi;
  143. phtNew->uItems++; // One more item in the table
  144. }
  145. }
  146. ASSERT(phtNew->uItems == pht->uItems);
  147. // Now switch the 2 tables
  148. LocalFree(pht);
  149. *hht = phtNew;
  150. }
  151. }
  152. //--------------------------------------------------------------------------
  153. // This function looks up the name in the hash table and optionally does
  154. // things like add it, or delete it.
  155. //
  156. LPCTSTR LookupItemInHashTable(HHASHTABLE hht, LPCTSTR pszName, int iOp)
  157. {
  158. // First thing to do is calculate the hash value for the item
  159. UINT uBucket;
  160. WORD cchName;
  161. PHASHITEM phi, phiPrev;
  162. PHASHTABLE pht;
  163. ENTERCRITICAL;
  164. pht = hht ? *hht : NULL;
  165. ASSERT(!hht || pht); // If hht is not NULL, then pht can't be NULL either
  166. if (pht == NULL)
  167. {
  168. hht = GetGlobalHashTable();
  169. if (hht)
  170. {
  171. pht = *hht;
  172. }
  173. if (pht == NULL) {
  174. TraceMsg(TF_WARNING, "LookupItemInHashTable() - Can't get global hash table!");
  175. LEAVECRITICAL;
  176. return NULL;
  177. }
  178. }
  179. #ifdef DEBUG
  180. if ((g_cHTTot % g_cHTMod) == 0)
  181. TraceMsg(DM_PERF, "ht: tot=%d hit=%d", g_cHTTot, g_cHTHit);
  182. #endif
  183. DBEXEC(TRUE, g_cHTTot++);
  184. if (*pszName == *pht->pszHTCache && iOp == LOOKUPHASHITEM) {
  185. // StrCmpC is a fast ansi strcmp, good enough for a quick/approx check
  186. if (StrCmpC(pszName, pht->pszHTCache) == 0 && pht->pszHTCache != c_szHTNil) {
  187. DBEXEC(TRUE, g_cHTHit++);
  188. LEAVECRITICAL; // see 'semi-race' comment below
  189. return (LPCTSTR)pht->pszHTCache;
  190. #if 0 // currently not worth it (very few ADDHASHITEMs of existing)
  191. // careful! this is o.k. for ADDHASHITEM but not for (e.g.)
  192. // DELETEHASHITEM (which needs phiPrev)
  193. phi = HIFROMSZ(pht->pszHTCache);
  194. goto Ldoop; // warning: pending ENTERCRITICAL
  195. #endif
  196. }
  197. }
  198. uBucket = _CalculateHashKey(pszName, &cchName) % pht->uBuckets;
  199. // now search for the item in the buckets.
  200. phiPrev = NULL;
  201. phi = pht->ahiBuckets[uBucket];
  202. while (phi)
  203. {
  204. if (phi->cchLen == cchName)
  205. {
  206. if (!lstrcmp(pszName, phi->szName))
  207. break; // Found match
  208. }
  209. phiPrev = phi; // Keep the previous item
  210. phi = phi->phiNext;
  211. }
  212. //
  213. // Sortof gross, but do the work here
  214. //
  215. switch (iOp)
  216. {
  217. case ADDHASHITEM:
  218. if (phi)
  219. {
  220. // Simply increment the reference count
  221. DebugMsg(TF_HASH, TEXT("Add Hit on '%s'"), pszName);
  222. phi->wCount++;
  223. }
  224. else
  225. {
  226. DebugMsg(TF_HASH, TEXT("Add MISS on '%s'"), pszName);
  227. // Not Found, try to allocate it out of the heap
  228. if ((phi = _AllocHashItem(pht, cchName)) != NULL)
  229. {
  230. // Initialize it
  231. phi->wCount = 1; // One use of it
  232. phi->cchLen = cchName; // The length of it;
  233. lstrcpy(phi->szName, pszName);
  234. // And link it in to the right bucket
  235. phi->phiNext = pht->ahiBuckets[uBucket];
  236. pht->ahiBuckets[uBucket] = phi;
  237. pht->uItems++; // One more item in the table
  238. if (pht->uItems > pht->uBuckets)
  239. {
  240. _GrowTable(hht);
  241. pht = *hht;
  242. }
  243. TraceMsg(TF_HASH, "Added new hash item %x(phiNext=%x,szName=\"%s\") for hash table %x at bucket %x",
  244. phi, phi->phiNext, phi->szName, pht, uBucket);
  245. }
  246. }
  247. break;
  248. case PURGEHASHITEM:
  249. case DELETEHASHITEM:
  250. if (phi && ((iOp == PURGEHASHITEM) || (!--phi->wCount)))
  251. {
  252. // Useage count went to zero so unlink it and delete it
  253. if (phiPrev != NULL)
  254. phiPrev->phiNext = phi->phiNext;
  255. else
  256. pht->ahiBuckets[uBucket] = phi->phiNext;
  257. // And delete it
  258. TraceMsg(TF_HASH, "Free hash item %x(szName=\"%s\") from hash table %x at bucket %x",
  259. phi, phi->szName, pht, uBucket);
  260. _FreeHashItem(pht, phi);
  261. phi = NULL;
  262. pht->uItems--; // One less item in the table
  263. }
  264. }
  265. // kill cache if this was a PURGE/DELETEHASHITEM, o.w. cache it.
  266. // note that there's a semi-race on pht->pszHTCache ops, viz. that
  267. // we LEAVECRITICAL but then return a ptr into our table. however
  268. // it's 'no worse' than the existing races. so i guess the caller
  269. // is supposed to avoid a concurrent lookup/delete.
  270. pht->pszHTCache = phi ? phi->szName : c_szHTNil;
  271. LEAVECRITICAL;
  272. // If find was passed in simply return it.
  273. if (phi)
  274. return (LPCTSTR)phi->szName;
  275. else
  276. return NULL;
  277. }
  278. //--------------------------------------------------------------------------
  279. LPCTSTR WINAPI FindHashItem(HHASHTABLE hht, LPCTSTR lpszStr)
  280. {
  281. return LookupItemInHashTable(hht, lpszStr, LOOKUPHASHITEM);
  282. }
  283. //--------------------------------------------------------------------------
  284. LPCTSTR WINAPI AddHashItem(HHASHTABLE hht, LPCTSTR lpszStr)
  285. {
  286. return LookupItemInHashTable(hht, lpszStr, ADDHASHITEM);
  287. }
  288. //--------------------------------------------------------------------------
  289. LPCTSTR WINAPI DeleteHashItem(HHASHTABLE hht, LPCTSTR lpszStr)
  290. {
  291. return LookupItemInHashTable(hht, lpszStr, DELETEHASHITEM);
  292. }
  293. //--------------------------------------------------------------------------
  294. LPCTSTR WINAPI PurgeHashItem(HHASHTABLE hht, LPCTSTR lpszStr)
  295. {
  296. return LookupItemInHashTable(hht, lpszStr, PURGEHASHITEM);
  297. }
  298. //--------------------------------------------------------------------------
  299. // this sets the extra data in an HashItem
  300. void WINAPI SetHashItemData(HHASHTABLE hht, LPCTSTR sz, int n, DWORD_PTR dwData)
  301. {
  302. PHASHTABLE pht;
  303. ENTERCRITICAL;
  304. pht = hht ? *hht : NULL;
  305. ASSERT(!hht || pht); // If hht is not NULL, then pht can't be NULL either
  306. // string must be from the hash table
  307. ASSERT(FindHashItem(hht, sz) == sz);
  308. // the default hash table does not have extra data!
  309. if ((pht != NULL) && (n >= 0) && (n < (int)(pht->cbExtra/SIZEOF(DWORD_PTR))))
  310. HIDATAARRAY(pht, sz)[n] = dwData;
  311. LEAVECRITICAL;
  312. }
  313. //======================================================================
  314. // this is like SetHashItemData, except it gets the HashItem data...
  315. DWORD_PTR WINAPI GetHashItemData(HHASHTABLE hht, LPCTSTR sz, int n)
  316. {
  317. DWORD_PTR dwpRet;
  318. PHASHTABLE pht;
  319. ENTERCRITICAL;
  320. pht = hht ? *hht : NULL;
  321. ASSERT(!hht || pht); // If hht is not NULL, then pht can't be NULL either
  322. // string must be from the hash table
  323. ASSERT(FindHashItem(hht, sz) == sz);
  324. // the default hash table does not have extra data!
  325. if (pht != NULL && n <= (int)(pht->cbExtra/SIZEOF(DWORD_PTR)))
  326. dwpRet = HIDATAARRAY(pht, sz)[n];
  327. else
  328. dwpRet = 0;
  329. LEAVECRITICAL;
  330. return dwpRet;
  331. }
  332. //======================================================================
  333. // like GetHashItemData, except it just gets a pointer to the buffer...
  334. void * WINAPI GetHashItemDataPtr(HHASHTABLE hht, LPCTSTR sz)
  335. {
  336. void *pvRet;
  337. PHASHTABLE pht;
  338. ENTERCRITICAL;
  339. pht = hht ? *hht : NULL;
  340. ASSERT(!hht || pht); // If hht is not NULL, then pht can't be NULL either
  341. // string must be from the hash table
  342. ASSERT(FindHashItem(hht, sz) == sz);
  343. // the default hash table does not have extra data!
  344. pvRet = (pht? HIDATAPTR(pht, sz) : NULL);
  345. LEAVECRITICAL;
  346. return pvRet;
  347. }
  348. //======================================================================
  349. PHASHTABLE _CreateHashTable(UINT uBuckets, UINT cbExtra)
  350. {
  351. PHASHTABLE pht;
  352. if (uBuckets == 0)
  353. uBuckets = DEF_HASH_BUCKET_COUNT;
  354. pht = (PHASHTABLE)LocalAlloc(LPTR, SIZEOF(HASHTABLE) + uBuckets * SIZEOF(PHASHITEM));
  355. if (pht)
  356. {
  357. pht->uBuckets = uBuckets;
  358. pht->cbExtra = (cbExtra + sizeof(DWORD_PTR) - 1) & ~(sizeof(DWORD_PTR)-1); // rounding to the next DWORD_PTR size
  359. pht->pszHTCache = c_szHTNil;
  360. }
  361. return pht;
  362. }
  363. HHASHTABLE WINAPI CreateHashItemTable(UINT uBuckets, UINT cbExtra)
  364. {
  365. PHASHTABLE *hht = NULL;
  366. PHASHTABLE pht;
  367. pht = _CreateHashTable(uBuckets, cbExtra);
  368. if (pht)
  369. {
  370. hht = (PHASHTABLE *)LocalAlloc(LPTR, sizeof(PHASHTABLE));
  371. if (hht)
  372. {
  373. *hht = pht;
  374. }
  375. else
  376. {
  377. LocalFree(pht);
  378. }
  379. }
  380. TraceMsg(TF_HASH, "Created hash table %x(uBuckets=%x, cbExtra=%x)",
  381. pht, pht->uBuckets, pht->cbExtra);
  382. return hht;
  383. }
  384. //======================================================================
  385. void WINAPI EnumHashItems(HHASHTABLE hht, HASHITEMCALLBACK callback, DWORD_PTR dwParam)
  386. {
  387. PHASHTABLE pht;
  388. ENTERCRITICAL;
  389. pht = hht ? *hht : NULL;
  390. ASSERT(!hht || pht); // If hht is not NULL, then pht can't be NULL either
  391. if (!pht && g_hHashTable)
  392. pht = *g_hHashTable;
  393. if (pht)
  394. {
  395. int i;
  396. PHASHITEM phi;
  397. PHASHITEM phiNext;
  398. #ifdef DEBUG
  399. ULONG uCount = 0;
  400. #endif
  401. for (i=0; i<(int)pht->uBuckets; i++)
  402. {
  403. for (phi=pht->ahiBuckets[i]; phi; phi=phiNext)
  404. {
  405. phiNext = phi->phiNext;
  406. (*callback)(hht, phi->szName, phi->wCount, dwParam);
  407. #ifdef DEBUG
  408. uCount++;
  409. #endif
  410. }
  411. }
  412. ASSERT(uCount == pht->uItems);
  413. }
  414. LEAVECRITICAL;
  415. }
  416. //======================================================================
  417. void _DeleteHashItem(HHASHTABLE hht, LPCTSTR sz, UINT usage, DWORD_PTR param)
  418. {
  419. PHASHTABLE pht;
  420. ENTERCRITICAL;
  421. pht = hht ? *hht : NULL;
  422. _FreeHashItem(pht, HIFROMSZ(sz));
  423. LEAVECRITICAL;
  424. }
  425. //======================================================================
  426. void WINAPI DestroyHashItemTable(HHASHTABLE hht)
  427. {
  428. PHASHTABLE pht;
  429. ENTERCRITICAL;
  430. pht = hht ? *hht : NULL;
  431. ASSERT(!hht || pht); // If hht is not NULL, then pht can't be NULL either
  432. TraceMsg(TF_HASH, "DestroyHashItemTable(pht=%x)", pht);
  433. if (pht == NULL)
  434. {
  435. if (g_hHashTable)
  436. {
  437. pht = *g_hHashTable;
  438. hht = g_hHashTable;
  439. g_hHashTable = NULL;
  440. }
  441. }
  442. if (pht)
  443. {
  444. EnumHashItems(hht, _DeleteHashItem, 0);
  445. LocalFree(pht);
  446. }
  447. if (hht)
  448. {
  449. LocalFree(hht);
  450. }
  451. LEAVECRITICAL;
  452. }
  453. //======================================================================
  454. HHASHTABLE GetGlobalHashTable()
  455. {
  456. if (!g_hHashTable)
  457. {
  458. ENTERCRITICAL;
  459. g_hHashTable = CreateHashItemTable(0, 0);
  460. LEAVECRITICAL;
  461. }
  462. return g_hHashTable;
  463. }
  464. //======================================================================
  465. #ifdef DEBUG
  466. static int TotalBytes;
  467. void CALLBACK _DumpHashItem(HHASHTABLE hht, LPCTSTR sz, UINT usage, DWORD_PTR param)
  468. {
  469. PHASHTABLE pht;
  470. ENTERCRITICAL;
  471. pht = hht ? *hht : NULL;
  472. DebugMsg(TF_ALWAYS, TEXT(" %08x %5ld \"%s\""), HIFROMSZ(sz), usage, sz);
  473. TotalBytes += (HIFROMSZ(sz)->cchLen * SIZEOF(TCHAR)) + SIZEOF(HASHITEM);
  474. LEAVECRITICAL;
  475. }
  476. void CALLBACK _DumpHashItemWithData(HHASHTABLE hht, LPCTSTR sz, UINT usage, DWORD_PTR param)
  477. {
  478. PHASHTABLE pht;
  479. ENTERCRITICAL;
  480. pht = hht ? *hht : NULL;
  481. DebugMsg(TF_ALWAYS, TEXT(" %08x %5ld %08x \"%s\""), HIFROMSZ(sz), usage, HIDATAARRAY(pht, sz)[0], sz);
  482. TotalBytes += (HIFROMSZ(sz)->cchLen * SIZEOF(TCHAR)) + SIZEOF(HASHITEM) + (pht? pht->cbExtra : 0);
  483. LEAVECRITICAL;
  484. }
  485. void WINAPI DumpHashItemTable(HHASHTABLE hht)
  486. {
  487. PHASHTABLE pht;
  488. ENTERCRITICAL;
  489. pht = hht ? *hht : NULL;
  490. TotalBytes = 0;
  491. if (IsFlagSet(g_dwDumpFlags, DF_HASH))
  492. {
  493. DebugMsg(TF_ALWAYS, TEXT("Hash Table: %08x"), pht);
  494. if (pht && (pht->cbExtra > 0)) {
  495. DebugMsg(TF_ALWAYS, TEXT(" Hash Usage dwEx[0] String"));
  496. DebugMsg(TF_ALWAYS, TEXT(" -------- ----- -------- ------------------------------"));
  497. EnumHashItems(hht, _DumpHashItemWithData, 0);
  498. }
  499. else {
  500. DebugMsg(TF_ALWAYS, TEXT(" Hash Usage String"));
  501. DebugMsg(TF_ALWAYS, TEXT(" -------- ----- --------------------------------"));
  502. EnumHashItems(hht, _DumpHashItem, 0);
  503. }
  504. DebugMsg(TF_ALWAYS, TEXT("Total Bytes: %d"), TotalBytes);
  505. }
  506. LEAVECRITICAL;
  507. }
  508. #endif