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.

627 lines
14 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. hash.c
  5. Abstract:
  6. Hashing routines used to speed lookup of memdb keys.
  7. Author:
  8. Jim Schmidt (jimschm) 8-Aug-1996
  9. Revision History:
  10. Jim Schmidt (jimschm) 21-Oct-1997 Split from memdb.c
  11. --*/
  12. #include "pch.h"
  13. #include "memdbp.h"
  14. #define DBG_MEMDB "MemDb"
  15. //
  16. // #defines
  17. //
  18. #define HASH_BUCKETS 7001//4099
  19. #define HASH_BLOCK_SIZE (HASH_BUCKETS * sizeof (BUCKETSTRUCT))
  20. #define HASHBUFPTR(offset) ((PBUCKETSTRUCT) (pHashTable->Buf + offset))
  21. typedef struct _tagHASHSTRUCT {
  22. UINT Offset;
  23. UINT NextItem;
  24. } BUCKETSTRUCT, *PBUCKETSTRUCT;
  25. typedef struct {
  26. PBUCKETSTRUCT BucketPtr;
  27. PBUCKETSTRUCT PrevBucketPtr;
  28. UINT Bucket;
  29. UINT LastOffset;
  30. } HASHENUM, *PHASHENUM;
  31. BOOL
  32. EnumFirstHashEntry (
  33. IN PMEMDBHASH pHashTable,
  34. OUT PHASHENUM HashEnum
  35. );
  36. BOOL
  37. EnumNextHashEntry (
  38. IN PMEMDBHASH pHashTable,
  39. IN OUT PHASHENUM HashEnum
  40. );
  41. //
  42. // Local privates
  43. //
  44. VOID
  45. pResetHashBlock (
  46. IN PMEMDBHASH pHashTable
  47. );
  48. //
  49. // Implementation
  50. //
  51. PMEMDBHASH
  52. CreateHashBlock (
  53. VOID
  54. )
  55. {
  56. PMEMDBHASH pHashTable;
  57. pHashTable = (PMEMDBHASH) MemAlloc (g_hHeap, 0, sizeof(MEMDBHASH));
  58. if (!pHashTable) {
  59. DEBUGMSG ((DBG_ERROR, "Could not allocate hash table!\n"));
  60. return NULL;
  61. }
  62. pHashTable->Size = HASH_BLOCK_SIZE * 2;
  63. pHashTable->Buf = (PBYTE) MemAlloc (g_hHeap, 0, pHashTable->Size);
  64. if (!pHashTable->Buf) {
  65. DEBUGMSG ((DBG_ERROR, "Could not allocate hash buffer!\n"));
  66. MemFree (g_hHeap, 0, pHashTable);
  67. return NULL;
  68. }
  69. pResetHashBlock(pHashTable);
  70. return pHashTable;
  71. }
  72. VOID
  73. pResetHashBlock (
  74. IN PMEMDBHASH pHashTable
  75. )
  76. {
  77. PBUCKETSTRUCT BucketPtr;
  78. INT i;
  79. pHashTable->End = HASH_BLOCK_SIZE;
  80. pHashTable->FreeHead = INVALID_OFFSET;
  81. BucketPtr = (PBUCKETSTRUCT) pHashTable->Buf;
  82. for (i = 0 ; i < HASH_BUCKETS ; i++) {
  83. BucketPtr->Offset = INVALID_OFFSET;
  84. BucketPtr->NextItem = INVALID_OFFSET;
  85. BucketPtr++;
  86. }
  87. }
  88. VOID
  89. FreeHashBlock (
  90. IN PMEMDBHASH pHashTable
  91. )
  92. {
  93. if (pHashTable->Buf) {
  94. MemFree (g_hHeap, 0, pHashTable->Buf);
  95. }
  96. MemFree (g_hHeap, 0, pHashTable);
  97. }
  98. BOOL
  99. EnumFirstHashEntry (
  100. IN PMEMDBHASH pHashTable,
  101. OUT PHASHENUM EnumPtr
  102. )
  103. {
  104. ZeroMemory (EnumPtr, sizeof (HASHENUM));
  105. return EnumNextHashEntry (pHashTable, EnumPtr);
  106. }
  107. BOOL
  108. EnumNextHashEntry (
  109. IN PMEMDBHASH pHashTable,
  110. IN OUT PHASHENUM EnumPtr
  111. )
  112. {
  113. for (;;) {
  114. if (EnumPtr->Bucket == HASH_BUCKETS) {
  115. //
  116. // The completion case
  117. //
  118. return FALSE;
  119. }
  120. if (!EnumPtr->BucketPtr) {
  121. //
  122. // This case occurs when we are begining to enumerate a bucket
  123. //
  124. EnumPtr->BucketPtr = (PBUCKETSTRUCT) pHashTable->Buf + EnumPtr->Bucket;
  125. if (EnumPtr->BucketPtr->Offset == INVALID_OFFSET) {
  126. EnumPtr->BucketPtr = NULL;
  127. EnumPtr->Bucket += 1;
  128. continue;
  129. }
  130. //
  131. // Return this first item in the bucket
  132. //
  133. EnumPtr->LastOffset = EnumPtr->BucketPtr->Offset;
  134. return TRUE;
  135. }
  136. //
  137. // This case occurs when we are continuing enumeration of a bucket
  138. //
  139. if (EnumPtr->BucketPtr->Offset == INVALID_OFFSET) {
  140. //
  141. // Current bucket item (and also the last bucket item) may have
  142. // been deleted -- check that now
  143. //
  144. if (!EnumPtr->PrevBucketPtr) {
  145. //
  146. // Last item has been deleted; continue to next bucket
  147. //
  148. EnumPtr->BucketPtr = NULL;
  149. EnumPtr->Bucket += 1;
  150. continue;
  151. }
  152. //
  153. // Previous bucket item is valid; use it.
  154. //
  155. EnumPtr->BucketPtr = EnumPtr->PrevBucketPtr;
  156. } else {
  157. //
  158. // Current bucket item may have been deleted, but another item was
  159. // moved to its place -- check that now
  160. //
  161. if (EnumPtr->BucketPtr->Offset != EnumPtr->LastOffset) {
  162. EnumPtr->LastOffset = EnumPtr->BucketPtr->Offset;
  163. return TRUE;
  164. }
  165. }
  166. //
  167. // We now know that the current bucket item was not changed, so it
  168. // becomes our previous item and we move on to the next item (if
  169. // one exists)
  170. //
  171. if (EnumPtr->BucketPtr->NextItem == INVALID_OFFSET) {
  172. //
  173. // End of bucket reached
  174. //
  175. EnumPtr->BucketPtr = NULL;
  176. EnumPtr->Bucket += 1;
  177. continue;
  178. }
  179. EnumPtr->PrevBucketPtr = EnumPtr->BucketPtr;
  180. EnumPtr->BucketPtr = HASHBUFPTR (EnumPtr->BucketPtr->NextItem);
  181. EnumPtr->LastOffset = EnumPtr->BucketPtr->Offset;
  182. MYASSERT(EnumPtr->LastOffset != INVALID_OFFSET);
  183. break;
  184. }
  185. return TRUE;
  186. }
  187. BOOL
  188. WriteHashBlock (
  189. IN PMEMDBHASH pHashTable,
  190. IN OUT PBYTE *Buf
  191. )
  192. {
  193. *(((PUINT)*Buf)++) = pHashTable->End;
  194. *(((PUINT)*Buf)++) = pHashTable->FreeHead;
  195. CopyMemory(*Buf, pHashTable->Buf, pHashTable->End);
  196. *Buf += pHashTable->End;
  197. return TRUE;
  198. }
  199. BOOL
  200. ReadHashBlock (
  201. IN PMEMDBHASH pHashTable,
  202. IN OUT PBYTE *Buf
  203. )
  204. {
  205. pHashTable->End = *(((PUINT)*Buf)++);
  206. pHashTable->FreeHead = *(((PUINT)*Buf)++);
  207. if (pHashTable->End > pHashTable->Size) {
  208. //
  209. // if the hash table in the file will not fit in the buffer
  210. // already allocated, free current buffer and allocate new one.
  211. //
  212. MemFree (g_hHeap, 0, pHashTable->Buf);
  213. pHashTable->Size = pHashTable->End;
  214. pHashTable->Buf = (PBYTE) MemAlloc (g_hHeap, 0, pHashTable->Size);
  215. }
  216. CopyMemory(pHashTable->Buf, *Buf, pHashTable->End);
  217. *Buf += pHashTable->End;
  218. return TRUE;
  219. }
  220. UINT GetHashTableBlockSize (
  221. IN PMEMDBHASH pHashTable
  222. )
  223. {
  224. return 2*sizeof(UINT) + pHashTable->End;
  225. }
  226. UINT
  227. pCalculateHashVal (
  228. IN PCWSTR String
  229. )
  230. {
  231. UINT Hash = 0;
  232. while (*String) {
  233. Hash = (Hash << 5) | (Hash >> 29);
  234. Hash += towlower (*String);
  235. String++;
  236. }
  237. Hash %= HASH_BUCKETS;
  238. return Hash;
  239. }
  240. UINT
  241. pAllocBucket (
  242. IN PMEMDBHASH pHashTable
  243. )
  244. {
  245. UINT rBucketOffset;
  246. PBYTE TempBuf;
  247. PBUCKETSTRUCT BucketPtr;
  248. if (pHashTable->FreeHead != INVALID_OFFSET) {
  249. rBucketOffset = pHashTable->FreeHead;
  250. BucketPtr = HASHBUFPTR (rBucketOffset);
  251. pHashTable->FreeHead = BucketPtr->NextItem;
  252. MYASSERT (rBucketOffset < pHashTable->End);
  253. } else {
  254. if (pHashTable->End + sizeof (BUCKETSTRUCT) > pHashTable->Size) {
  255. pHashTable->Size += HASH_BLOCK_SIZE;
  256. TempBuf = MemReAlloc (g_hHeap, 0, pHashTable->Buf, pHashTable->Size);
  257. DEBUGMSG ((DBG_NAUSEA, "Realloc'd memdb hash table"));
  258. if (!TempBuf) {
  259. DEBUGMSG ((DBG_ERROR, "Out of memory!"));
  260. pHashTable->Size -= HASH_BLOCK_SIZE;
  261. return INVALID_OFFSET;
  262. }
  263. pHashTable->Buf = TempBuf;
  264. }
  265. rBucketOffset = pHashTable->End;
  266. pHashTable->End += sizeof (BUCKETSTRUCT);
  267. BucketPtr = HASHBUFPTR (rBucketOffset);
  268. }
  269. BucketPtr->Offset = INVALID_OFFSET;
  270. BucketPtr->NextItem = INVALID_OFFSET;
  271. return rBucketOffset;
  272. }
  273. BOOL
  274. AddHashTableEntry (
  275. IN PMEMDBHASH pHashTable,
  276. IN PCWSTR FullString,
  277. IN UINT Offset
  278. )
  279. {
  280. UINT Bucket;
  281. PBUCKETSTRUCT BucketPtr, PrevBucketPtr;
  282. UINT BucketOffset;
  283. UINT NewOffset;
  284. UINT PrevBucketOffset;
  285. Bucket = pCalculateHashVal (FullString);
  286. BucketPtr = (PBUCKETSTRUCT) pHashTable->Buf + Bucket;
  287. //
  288. // See if root bucket item has been used or not
  289. //
  290. if (BucketPtr->Offset != INVALID_OFFSET) {
  291. //
  292. // Yes - add to end of the chain
  293. //
  294. BucketOffset = Bucket * sizeof (BUCKETSTRUCT);
  295. do {
  296. BucketPtr = HASHBUFPTR (BucketOffset);
  297. PrevBucketOffset = BucketOffset;
  298. BucketOffset = BucketPtr->NextItem;
  299. } while (BucketOffset != INVALID_OFFSET);
  300. //
  301. // Add to the chain
  302. //
  303. NewOffset = pAllocBucket(pHashTable);
  304. PrevBucketPtr = HASHBUFPTR (PrevBucketOffset);
  305. PrevBucketPtr->NextItem = NewOffset;
  306. if (NewOffset == INVALID_OFFSET) {
  307. return FALSE;
  308. }
  309. BucketPtr = HASHBUFPTR (NewOffset);
  310. MYASSERT (BucketPtr->NextItem == INVALID_OFFSET);
  311. }
  312. BucketPtr->Offset = Offset;
  313. #ifdef DEBUG
  314. {
  315. UINT HashOffset;
  316. HashOffset = FindStringInHashTable (pHashTable, FullString);
  317. MYASSERT (HashOffset != INVALID_OFFSET);
  318. DEBUGMSG_IF ((HashOffset != Offset, DBG_MEMDB, "Duplicate in hash table: %s", FullString));
  319. }
  320. #endif
  321. return TRUE;
  322. }
  323. PBUCKETSTRUCT
  324. pFindBucketItemInHashTable (
  325. IN PMEMDBHASH pHashTable,
  326. IN PCWSTR FullString,
  327. OUT PBUCKETSTRUCT *PrevBucketPtr, OPTIONAL
  328. OUT PUINT HashOffsetPtr OPTIONAL
  329. )
  330. {
  331. UINT Bucket;
  332. UINT BucketOffset;
  333. PBUCKETSTRUCT BucketPtr = NULL;
  334. WCHAR TempStr[MEMDB_MAX];
  335. Bucket = pCalculateHashVal (FullString);
  336. BucketOffset = Bucket * sizeof (BUCKETSTRUCT);
  337. #ifdef DEBUG
  338. {
  339. //
  340. // Circular link check
  341. //
  342. UINT Prev, Next;
  343. UINT Turtle, Rabbit;
  344. BOOL Even = FALSE;
  345. Rabbit = BucketOffset;
  346. Turtle = Rabbit;
  347. while (Rabbit != INVALID_OFFSET) {
  348. // Make rabbit point to next item in chain
  349. Prev = Rabbit;
  350. BucketPtr = HASHBUFPTR (Rabbit);
  351. Rabbit = BucketPtr->NextItem;
  352. // We should always be ahead of the turtle
  353. if (Rabbit == Turtle) {
  354. BucketPtr = HASHBUFPTR (Rabbit);
  355. Next = BucketPtr->NextItem;
  356. DEBUGMSG ((
  357. DBG_WHOOPS,
  358. "Circular link detected in memdb hash table! Turtle=%u, Rabbit=%u, Next=%u, Prev=%u",
  359. Turtle,
  360. Rabbit,
  361. Next,
  362. Prev
  363. ));
  364. return NULL;
  365. }
  366. // Make turtle point to next item in chain (1 of every 2 passes)
  367. if (Even) {
  368. BucketPtr = HASHBUFPTR (Turtle);
  369. Turtle = BucketPtr->NextItem;
  370. }
  371. Even = !Even;
  372. }
  373. }
  374. #endif
  375. BucketPtr = HASHBUFPTR (BucketOffset);
  376. if (PrevBucketPtr) {
  377. *PrevBucketPtr = BucketPtr;
  378. }
  379. //
  380. // If root bucket is not empty, scan bucket for FullString
  381. //
  382. if (BucketPtr->Offset != INVALID_OFFSET) {
  383. do {
  384. BucketPtr = HASHBUFPTR (BucketOffset);
  385. //
  386. // Build string using offset
  387. //
  388. PrivateBuildKeyFromIndex (
  389. 0,
  390. BucketPtr->Offset,
  391. TempStr,
  392. NULL,
  393. NULL,
  394. NULL
  395. );
  396. //
  397. // Do compare and return if match is found
  398. //
  399. if (StringIMatchW (FullString, TempStr)) {
  400. if (HashOffsetPtr) {
  401. *HashOffsetPtr = BucketOffset;
  402. }
  403. return BucketPtr;
  404. }
  405. if (PrevBucketPtr) {
  406. *PrevBucketPtr = BucketPtr;
  407. }
  408. BucketOffset = BucketPtr->NextItem;
  409. } while (BucketOffset != INVALID_OFFSET);
  410. }
  411. return NULL;
  412. }
  413. UINT
  414. FindStringInHashTable (
  415. IN PMEMDBHASH pHashTable,
  416. IN PCWSTR FullString
  417. )
  418. {
  419. PBUCKETSTRUCT BucketPtr;
  420. BucketPtr = pFindBucketItemInHashTable (pHashTable, FullString, NULL, NULL);
  421. if (BucketPtr) {
  422. return BucketPtr->Offset;
  423. }
  424. return INVALID_OFFSET;
  425. }
  426. BOOL
  427. RemoveHashTableEntry (
  428. IN PMEMDBHASH pHashTable,
  429. IN PCWSTR FullString
  430. )
  431. {
  432. PBUCKETSTRUCT BucketPtr;
  433. PBUCKETSTRUCT PrevBucketPtr;
  434. UINT NextOffset;
  435. PBUCKETSTRUCT NextBucketPtr;
  436. UINT BucketOffset;
  437. BucketPtr = pFindBucketItemInHashTable (pHashTable, FullString, &PrevBucketPtr, &BucketOffset);
  438. if (!BucketPtr) {
  439. return FALSE;
  440. }
  441. if (PrevBucketPtr != BucketPtr) {
  442. //
  443. // If not at the first level (prev != current), give the block
  444. // to free space.
  445. //
  446. PrevBucketPtr->NextItem = BucketPtr->NextItem;
  447. BucketPtr->NextItem = pHashTable->FreeHead;
  448. BucketPtr->Offset = INVALID_OFFSET;
  449. pHashTable->FreeHead = BucketOffset;
  450. } else {
  451. //
  452. // Invalidate next item pointer if at the first level
  453. //
  454. if (BucketPtr->NextItem != INVALID_OFFSET) {
  455. //
  456. // Copy next item to root array
  457. //
  458. NextOffset = BucketPtr->NextItem;
  459. NextBucketPtr = HASHBUFPTR (NextOffset);
  460. CopyMemory (BucketPtr, NextBucketPtr, sizeof (BUCKETSTRUCT));
  461. //
  462. // Donate next item to free space
  463. //
  464. NextBucketPtr->NextItem = pHashTable->FreeHead;
  465. NextBucketPtr->Offset = INVALID_OFFSET;
  466. pHashTable->FreeHead = NextOffset;
  467. } else {
  468. //
  469. // Delete of last item in bucket -- invalidate the root array item
  470. //
  471. BucketPtr->NextItem = INVALID_OFFSET;
  472. BucketPtr->Offset = INVALID_OFFSET;
  473. }
  474. }
  475. return TRUE;
  476. }