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.

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