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.

1327 lines
34 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. qhash.c
  5. Abstract:
  6. Quick Hash Table routines. Unlike the Genhash table functions these routines
  7. use a fixed size node (QHASH_ENTRY) and the data fields are passed as
  8. parameters and copied into the node. The generic hash functions include a
  9. link entry in the users struct to link the node onto a hash chain. The genhash
  10. functions also include reference counts for the nodes.
  11. The generic hash functions have a lock per hash row where a Qhash table
  12. has only a single lock for the table.
  13. The PQHASH_TABLE struct is a typed struc allocated with FrsAllocTypeSize().
  14. QHASH tables can be used in two ways, for fixed size QuadWord keys and for
  15. more complex non-Quadword keys.
  16. For QHASH tables with QuadWord keys:
  17. The macro SET_QHASH_TABLE_HASH_CALC() is used to specify the hash function
  18. to use for the table. The key is supplied as a quadword and each entry has
  19. longword flags and a quadword data field for the callers info.
  20. For QHASH tables with Large keys:
  21. When the QHASH table is created you specify it as a large key table (i.e.
  22. not a simple Quadword Key) by doing:
  23. SET_QHASH_TABLE_FLAG(HashTable, QHASH_FLAG_LARGE_KEY);
  24. For large key tables the QHASH_ENTRY Flags ULONG_PTR and the Flags argument
  25. to QHashInsert() are expected to point at a caller defined data node with
  26. the large key value for the node at offset zero. On lookups the HashCalc2
  27. function set by SET_QHASH_TABLE_HASH_CALC2() is used to calculate both the
  28. quadword key for the hashtable entry and the hash value used for indexing
  29. the main array. In addition the caller specifies an exact key match
  30. function via SET_QHASH_TABLE_KEY_MATCH() to be used after the initial
  31. quadword key matches. This key match function is passed both the lookup
  32. argument key and the node address that was saved in the QHASH_ENTRY Flags
  33. ULONG_PTR so it can perform the complete key match.
  34. The macros QHashAcquireLock(_Table_) and QHashReleaseLock(_Table_) can
  35. be used to lock the table over multiple operations.
  36. The number of entries in the hash table array is specified by the allocation size
  37. when the table is allocated. When a collision occurs additional entries
  38. are allocated and placed on a free list for use in the collision lists.
  39. The storage for the base hash array and the collision entries are released
  40. when the table is freed by calling FrsFreeType(Table).
  41. An example of allocating a Qhash table with 100 entries in the base hash array:
  42. //PQHASH_TABLE FrsWriteFilter;
  43. //#define FRS_WRITE_FILTER_SIZE sizeof(QHASH_ENTRY)*100
  44. // FrsWriteFilter = FrsAllocTypeSize(QHASH_TABLE_TYPE, FRS_WRITE_FILTER_SIZE);
  45. // SET_QHASH_TABLE_HASH_CALC(FrsWriteFilter, JrnlHashCalcUsn);
  46. Author:
  47. David Orbits [davidor] 22-Apr-1997
  48. Environment:
  49. User Mode Service
  50. Revision History:
  51. --*/
  52. #include <ntreppch.h>
  53. #pragma hdrstop
  54. #include <frs.h>
  55. ULONG
  56. QHashDump (
  57. PQHASH_TABLE Table,
  58. PQHASH_ENTRY BeforeNode,
  59. PQHASH_ENTRY TargetNode,
  60. PVOID Context
  61. )
  62. /*++
  63. Routine Description:
  64. This function is called thru QHashEnumerateTable() to dump an entry.
  65. Arguments:
  66. Table - the hash table being enumerated
  67. BeforeNode -- ptr to the QhashEntry before the node of interest.
  68. TargetNode -- ptr to the QhashEntry of interest.
  69. Context - Unused.
  70. Return Value:
  71. FrsErrorStatus
  72. --*/
  73. {
  74. #undef DEBSUB
  75. #define DEBSUB "QHashDump:"
  76. DPRINT4(5, "Link: %08x, Flags: %08x, Tag: %08x %08x, Data: %08x %08x\n",
  77. TargetNode->NextEntry,
  78. TargetNode->Flags,
  79. PRINTQUAD(TargetNode->QKey),
  80. PRINTQUAD(TargetNode->QData));
  81. return FrsErrorSuccess;
  82. }
  83. VOID
  84. QHashExtendTable(
  85. IN PQHASH_TABLE HashTable
  86. )
  87. /*++
  88. Routine Description:
  89. Extend the number of entries in the hash table by allocating an
  90. extension block of up to QHASH_EXTENSION_MAX entries.
  91. The caller has the table lock.
  92. Arguments:
  93. HashTable -- ptr to a PQHASH_TABLE struct.
  94. Return Value:
  95. None.
  96. --*/
  97. {
  98. #undef DEBSUB
  99. #define DEBSUB "QHashExtendTable:"
  100. ULONG i, NumberExtEntries;
  101. PQHASH_ENTRY Ext;
  102. //
  103. // Allocate a block of memory.
  104. //
  105. Ext = FrsAlloc(HashTable->ExtensionAllocSize);
  106. InsertTailList(&HashTable->ExtensionListHead, (PLIST_ENTRY)Ext);
  107. NumberExtEntries = (HashTable->ExtensionAllocSize - sizeof(LIST_ENTRY)) /
  108. sizeof(QHASH_ENTRY);
  109. //
  110. // Put the entries on the free list.
  111. //
  112. (PCHAR) Ext = (PCHAR)Ext + sizeof(LIST_ENTRY);
  113. HashTable->FreeList.Next = &Ext->NextEntry;
  114. for (i=0; i<NumberExtEntries; i++) {
  115. Ext->NextEntry.Next = &((Ext+1)->NextEntry);
  116. Ext++;
  117. }
  118. Ext -= 1;
  119. Ext->NextEntry.Next = NULL;
  120. }
  121. ULONG
  122. QHashEnumerateTable(
  123. IN PQHASH_TABLE HashTable,
  124. IN PQHASH_ENUM_ROUTINE Function,
  125. IN PVOID Context
  126. )
  127. /*++
  128. Routine Description:
  129. This routine walks through the entries in the QHash table
  130. and calls the function provided with the entry address and the context.
  131. The table lock is acquired and released here.
  132. Arguments:
  133. HashTable - The context of the Hash Table to enumerate.
  134. Function - The function to call for each record in the table. It is of
  135. of type PQHASH_ENUM_ROUTINE. Return FALSE to abort the
  136. enumeration else true.
  137. Context - A context ptr to pass through to the RecordFunction.
  138. Return Value:
  139. The FrsErrorStatus code from the argument function.
  140. --*/
  141. {
  142. #undef DEBSUB
  143. #define DEBSUB "QHashEnumerateTable:"
  144. PQHASH_ENTRY HashRowEntry;
  145. PQHASH_ENTRY BeforeEntry;
  146. ULONG i, FStatus;
  147. if (HashTable == NULL) {
  148. return FrsErrorSuccess;
  149. }
  150. HashRowEntry = HashTable->HashRowBase;
  151. //
  152. // Loop through all the Hash table rows and call the function for
  153. // each element.
  154. //
  155. QHashAcquireLock(HashTable);
  156. for (i=0; i<HashTable->NumberEntries; i++, HashRowEntry++) {
  157. if (HashRowEntry->QKey != QUADZERO) {
  158. FStatus = (Function)(HashTable, NULL, HashRowEntry, Context);
  159. if (FStatus == FrsErrorDeleteRequested) {
  160. HashRowEntry->QKey = QUADZERO;
  161. }
  162. else
  163. if (FStatus != FrsErrorSuccess) {
  164. QHashReleaseLock(HashTable);
  165. return FStatus;
  166. }
  167. }
  168. //
  169. // Enumerate collision list if present.
  170. //
  171. if (HashRowEntry->NextEntry.Next == NULL) {
  172. continue;
  173. }
  174. BeforeEntry = HashRowEntry;
  175. ForEachSingleListEntry(&HashRowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  176. // Enumerator pE is of type PQHASH_ENTRY
  177. FStatus = (Function)(HashTable, BeforeEntry, pE, Context);
  178. if (FStatus == FrsErrorDeleteRequested) {
  179. RemoveSingleListEntry(UNUSED);
  180. PushEntryList(&HashTable->FreeList, &pE->NextEntry);
  181. pE = PreviousSingleListEntry(QHASH_ENTRY, NextEntry);
  182. }
  183. else
  184. if (FStatus != FrsErrorSuccess) {
  185. QHashReleaseLock(HashTable);
  186. return FStatus;
  187. }
  188. BeforeEntry = pE;
  189. );
  190. }
  191. QHashReleaseLock(HashTable);
  192. return FStatus;
  193. }
  194. GHT_STATUS
  195. QHashLookup(
  196. IN PQHASH_TABLE HashTable,
  197. IN PVOID ArgQKey,
  198. OUT PULONGLONG QData,
  199. OUT PULONG_PTR Flags
  200. )
  201. /*++
  202. Routine Description:
  203. Lookup the Quadword Key in the hash table and if found, return the Qdata
  204. and the flags DWORD.
  205. The table lock is acquired and released here.
  206. Note: A zero value for QKey is an error because a zero is used
  207. to denote an empty hash table slot.
  208. Arguments:
  209. HashTable -- ptr to a PQHASH_TABLE struct.
  210. ArgQKey -- ptr to the Key we are looking for.
  211. QData -- If found this is the returned quadword data. (NULL if unused)
  212. Flags -- If found this is the returned flags word.
  213. Return Value:
  214. GHT_STATUS_NOT_FOUND -- if not found.
  215. GHT_STATUS_SUCCESS -- if found.
  216. --*/
  217. {
  218. #undef DEBSUB
  219. #define DEBSUB "QHashLookup:"
  220. ULONGLONG QKey;
  221. ULONG GStatus;
  222. ULONG Hval, HvalIndex;
  223. PQHASH_ENTRY RowEntry;
  224. PQHASH_ENTRY LastFoundpE = NULL;
  225. if (IS_QHASH_LARGE_KEY(HashTable)) {
  226. Hval = (HashTable->HashCalc2)(ArgQKey, &QKey);
  227. DPRINT3(5, "QHashLookup (%08x): Hval: %08x QKey: %08lx %08lx\n",
  228. HashTable, Hval, PRINTQUAD(QKey));
  229. } else {
  230. CopyMemory(&QKey, ArgQKey, 8);
  231. Hval = (HashTable->HashCalc)(&QKey, 8);
  232. }
  233. FRS_ASSERT(QKey != QUADZERO);
  234. //
  235. // Compute the hash index and calculate the row pointer.
  236. //
  237. HvalIndex = Hval % HashTable->NumberEntries;
  238. RowEntry = HashTable->HashRowBase + HvalIndex;
  239. QHashAcquireLock(HashTable);
  240. if (RowEntry->QKey == QKey) {
  241. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) {
  242. //
  243. // Match. Return quadword data and flags.
  244. //
  245. if (QData != NULL) {
  246. *QData = RowEntry->QData;
  247. }
  248. *Flags = RowEntry->Flags;
  249. DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  250. HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags);
  251. QHashReleaseLock(HashTable);
  252. return GHT_STATUS_SUCCESS;
  253. }
  254. }
  255. if (RowEntry->NextEntry.Next == NULL) {
  256. QHashReleaseLock(HashTable);
  257. return GHT_STATUS_NOT_FOUND;
  258. }
  259. //
  260. // Scan the collision list.
  261. //
  262. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  263. //
  264. // The iterator pE is of type PQHASH_ENTRY.
  265. //
  266. if (QKey < pE->QKey) {
  267. //
  268. // Not on the list.
  269. //
  270. break;
  271. }
  272. if (pE->QKey == QKey) {
  273. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) {
  274. //
  275. // Found it.
  276. //
  277. if (QData != NULL) {
  278. *QData = pE->QData;
  279. }
  280. *Flags = pE->Flags;
  281. DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  282. HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags);
  283. QHashReleaseLock(HashTable);
  284. return GHT_STATUS_SUCCESS;
  285. }
  286. }
  287. );
  288. QHashReleaseLock(HashTable);
  289. return GHT_STATUS_NOT_FOUND;
  290. }
  291. PQHASH_ENTRY
  292. QHashLookupLock(
  293. IN PQHASH_TABLE HashTable,
  294. IN PVOID ArgQKey
  295. )
  296. /*++
  297. Routine Description:
  298. Lookup the Quadword Key in the hash table and if found, return the pointer to
  299. the entry. The table lock is acquired and released by the caller.
  300. Restriction:
  301. Once the caller drops the table lock no further ref to the QHASH_ENTRY
  302. is allowed since another thread could delete/update it.
  303. Note: A zero value for the key is an error because a zero is used
  304. to denote an empty hash table slot.
  305. Arguments:
  306. HashTable -- ptr to a PQHASH_TABLE struct.
  307. ArgQKey -- ptr to the Key we are looking for.
  308. Return Value:
  309. Pointer to QHashEntry, Null if not found.
  310. --*/
  311. {
  312. #undef DEBSUB
  313. #define DEBSUB "QHashLookupLock:"
  314. ULONGLONG QKey;
  315. ULONG Hval, HvalIndex;
  316. PQHASH_ENTRY RowEntry;
  317. if (IS_QHASH_LARGE_KEY(HashTable)) {
  318. Hval = (HashTable->HashCalc2)(ArgQKey, &QKey);
  319. DPRINT3(5, "QHash lookuplock (%08x): Hval: %08x QKey: %08lx %08lx\n",
  320. HashTable, Hval, PRINTQUAD(QKey));
  321. } else {
  322. CopyMemory(&QKey, ArgQKey, 8);
  323. Hval = (HashTable->HashCalc)(&QKey, 8);
  324. }
  325. FRS_ASSERT(QKey != QUADZERO);
  326. //
  327. // Compute the hash index and calculate the row pointer.
  328. //
  329. HvalIndex = Hval % HashTable->NumberEntries;
  330. RowEntry = HashTable->HashRowBase + HvalIndex;
  331. if (RowEntry->QKey == QKey) {
  332. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) {
  333. DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  334. HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags);
  335. return RowEntry;
  336. }
  337. }
  338. if (RowEntry->NextEntry.Next == NULL) {
  339. return NULL;
  340. }
  341. //
  342. // Scan the collision list.
  343. //
  344. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  345. //
  346. // The iterator pE is of type PQHASH_ENTRY.
  347. // Check for early terminate and then for a match.
  348. //
  349. if (QKey < pE->QKey) {
  350. return NULL;
  351. }
  352. if (pE->QKey == QKey) {
  353. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) {
  354. DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  355. HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags);
  356. return pE;
  357. }
  358. }
  359. );
  360. return NULL;
  361. }
  362. GHT_STATUS
  363. QHashInsert(
  364. IN PQHASH_TABLE HashTable,
  365. IN PVOID ArgQKey,
  366. IN PULONGLONG QData,
  367. IN ULONG_PTR Flags,
  368. IN BOOL HaveLock
  369. )
  370. /*++
  371. Routine Description:
  372. Insert the Quadword Key in the hash table and if found, return the data
  373. and the flags DWORD. The keys are in numerically increasing order on the
  374. collision chains.
  375. The table lock is acquired and released here.
  376. Note: A zero value for the key is an error because a zero is used
  377. to denote an empty hash table slot.
  378. Arguments:
  379. HashTable -- ptr to a PQHASH_TABLE struct.
  380. ArgQKey -- ptr to the Key we are inserting.
  381. QData -- This is ptr to the quadword data. (NULL if unused).
  382. Flags -- This is the flags word data. For large Key QHASH tables this
  383. is the ptr to the data node. Note that we assume the large
  384. Key is at a zero offset in the node when doing lookups.
  385. HaveLock -- True means the caller has taken the lock else we take it.
  386. Return Value:
  387. GHT_STATUS_FAILURE -- Conflicting entry is in table already.
  388. GHT_STATUS_SUCCESS -- Insert was successful.
  389. --*/
  390. {
  391. #undef DEBSUB
  392. #define DEBSUB "QHashInsert:"
  393. ULONGLONG QKey;
  394. ULONG Hval, HvalIndex;
  395. PQHASH_ENTRY RowEntry, AfterEntry;
  396. PQHASH_ENTRY pNew;
  397. PSINGLE_LIST_ENTRY NewEntry;
  398. if (IS_QHASH_LARGE_KEY(HashTable)) {
  399. Hval = (HashTable->HashCalc2)(ArgQKey, &QKey);
  400. DPRINT3(5, "QHashInsert (%08x): Hval: %08x QKey: %08lx %08lx\n",
  401. HashTable, Hval, PRINTQUAD(QKey));
  402. } else {
  403. CopyMemory(&QKey, ArgQKey, 8);
  404. Hval = (HashTable->HashCalc)(&QKey, 8);
  405. }
  406. FRS_ASSERT(QKey != QUADZERO);
  407. //
  408. // Compute the hash index and calculate the row pointer.
  409. //
  410. HvalIndex = Hval % HashTable->NumberEntries;
  411. RowEntry = HashTable->HashRowBase + HvalIndex;
  412. if (!HaveLock) {QHashAcquireLock(HashTable);}
  413. if (RowEntry->QKey == QUADZERO) {
  414. pNew = RowEntry;
  415. goto INSERT_ENTRY;
  416. }
  417. if (RowEntry->QKey == QKey) {
  418. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) {
  419. if (!HaveLock) {QHashReleaseLock(HashTable);}
  420. return GHT_STATUS_FAILURE;
  421. }
  422. }
  423. AfterEntry = RowEntry;
  424. //
  425. // Scan the collision list.
  426. //
  427. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  428. //
  429. // The iterator pE is of type PQHASH_ENTRY.
  430. //
  431. if (QKey < pE->QKey) {
  432. //
  433. // Not on the list.
  434. //
  435. break;
  436. }
  437. if (pE->QKey == QKey) {
  438. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) {
  439. //
  440. // Found it, collision.
  441. //
  442. if (!HaveLock) {QHashReleaseLock(HashTable);}
  443. return GHT_STATUS_FAILURE;
  444. }
  445. }
  446. AfterEntry = pE;
  447. );
  448. //
  449. // Not found. Allocate a new entry and put it in the list.
  450. //
  451. NewEntry = PopEntryList(&HashTable->FreeList);
  452. if (NewEntry == NULL) {
  453. //
  454. // Allocate a table extension block.
  455. //
  456. QHashExtendTable(HashTable);
  457. NewEntry = PopEntryList(&HashTable->FreeList);
  458. }
  459. //
  460. // Insert entry in the list.
  461. //
  462. pNew = CONTAINING_RECORD(NewEntry, QHASH_ENTRY, NextEntry);
  463. PushEntryList( &AfterEntry->NextEntry, &pNew->NextEntry);
  464. //
  465. // Insert the data and drop the lock.
  466. //
  467. INSERT_ENTRY:
  468. pNew->QKey = QKey;
  469. pNew->Flags = Flags;
  470. if (QData != NULL) {
  471. pNew->QData = *QData;
  472. } else {
  473. pNew->QData = QUADZERO;
  474. }
  475. DPRINT5(5, "QHash Insert (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  476. HashTable, pNew, PRINTQUAD(pNew->QKey), PRINTQUAD(pNew->QData), pNew->Flags);
  477. if (!HaveLock) {QHashReleaseLock(HashTable);}
  478. return GHT_STATUS_SUCCESS;
  479. }
  480. PQHASH_ENTRY
  481. QHashInsertLock(
  482. IN PQHASH_TABLE HashTable,
  483. IN PVOID ArgQKey,
  484. IN PULONGLONG QData,
  485. IN ULONG_PTR Flags
  486. )
  487. /*++
  488. Routine Description:
  489. Insert the quadword key in the hash table. Return the pointer to the entry.
  490. The caller has acquired the table lock and will release it.
  491. Arguments:
  492. HashTable -- ptr to a PQHASH_TABLE struct.
  493. ArgQKey -- ptr to the Key we are inserting.
  494. QData -- This is ptr to the quadword data. (NULL if unused)
  495. Flags -- This is the flags word data. For large Key QHASH tables this
  496. is the ptr to the data node. Note that we assume the large
  497. Key is at a zero offset in the node when doing lookups.
  498. Return Value:
  499. The ptr to the inserted entry or the existing entry if already in the table.
  500. --*/
  501. {
  502. #undef DEBSUB
  503. #define DEBSUB "QHashInsertLock:"
  504. ULONGLONG QKey;
  505. ULONG Hval, HvalIndex;
  506. PQHASH_ENTRY RowEntry, AfterEntry;
  507. PQHASH_ENTRY pNew;
  508. PSINGLE_LIST_ENTRY NewEntry;
  509. if (IS_QHASH_LARGE_KEY(HashTable)) {
  510. Hval = (HashTable->HashCalc2)(ArgQKey, &QKey);
  511. DPRINT3(5, "QHashInsertLock (%08x): Hval: %08x QKey: %08lx %08lx\n",
  512. HashTable, Hval, PRINTQUAD(QKey));
  513. } else {
  514. CopyMemory(&QKey, ArgQKey, 8);
  515. Hval = (HashTable->HashCalc)(&QKey, 8);
  516. }
  517. FRS_ASSERT(QKey != QUADZERO);
  518. //
  519. // Compute the hash index and calculate the row pointer.
  520. //
  521. HvalIndex = Hval % HashTable->NumberEntries;
  522. RowEntry = HashTable->HashRowBase + HvalIndex;
  523. if (RowEntry->QKey == QUADZERO) {
  524. pNew = RowEntry;
  525. goto INSERT_ENTRY;
  526. }
  527. if (RowEntry->QKey == QKey) {
  528. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) {
  529. return RowEntry;
  530. }
  531. }
  532. AfterEntry = RowEntry;
  533. //
  534. // Scan the collision list.
  535. //
  536. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  537. //
  538. // The iterator pE is of type PQHASH_ENTRY. Check for early termination.
  539. //
  540. if (QKey < pE->QKey) {
  541. break;
  542. }
  543. if (pE->QKey == QKey) {
  544. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) {
  545. //
  546. // Found it, return the pointer.
  547. //
  548. return pE;
  549. }
  550. }
  551. AfterEntry = pE;
  552. );
  553. //
  554. // Not found. Allocate a new entry and put it in the list.
  555. //
  556. NewEntry = PopEntryList(&HashTable->FreeList);
  557. if (NewEntry == NULL) {
  558. //
  559. // Allocate a table extension block.
  560. //
  561. QHashExtendTable(HashTable);
  562. NewEntry = PopEntryList(&HashTable->FreeList);
  563. }
  564. //
  565. // Insert entry in the list.
  566. //
  567. pNew = CONTAINING_RECORD(NewEntry, QHASH_ENTRY, NextEntry);
  568. PushEntryList( &AfterEntry->NextEntry, &pNew->NextEntry);
  569. //
  570. // Insert the data.
  571. //
  572. INSERT_ENTRY:
  573. pNew->QKey = QKey;
  574. pNew->Flags = Flags;
  575. if (QData != NULL) {
  576. pNew->QData = *QData;
  577. } else {
  578. pNew->QData = QUADZERO;
  579. }
  580. DPRINT5(5, "QHash Insert (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  581. HashTable, pNew, PRINTQUAD(pNew->QKey), PRINTQUAD(pNew->QData), pNew->Flags);
  582. return pNew;
  583. }
  584. GHT_STATUS
  585. QHashUpdate(
  586. IN PQHASH_TABLE HashTable,
  587. IN PVOID ArgQKey,
  588. IN PULONGLONG QData,
  589. IN ULONG_PTR Flags
  590. )
  591. /*++
  592. Routine Description:
  593. Lookup the Quadword key in the hash table and if found, update the entry.
  594. The table lock is acquired and released here.
  595. Arguments:
  596. HashTable -- ptr to a PQHASH_TABLE struct.
  597. ArgQKey -- ptr to the Key we are updating.
  598. QData -- This is ptr to the quadword data. (NULL if unused)
  599. Flags -- This is the flags word data. For large Key QHASH tables this
  600. is the ptr to the data node. Note that we assume the large
  601. Key is at a zero offset in the node when doing lookups.
  602. Return Value:
  603. GHT_STATUS_FAILURE -- Entry not in table already.
  604. GHT_STATUS_SUCCESS -- Update was successful.
  605. --*/
  606. {
  607. #undef DEBSUB
  608. #define DEBSUB "QHashUpdate:"
  609. ULONGLONG QKey;
  610. ULONG GStatus;
  611. ULONG Hval, HvalIndex;
  612. PQHASH_ENTRY RowEntry;
  613. PQHASH_ENTRY LastFoundpE = NULL;
  614. if (IS_QHASH_LARGE_KEY(HashTable)) {
  615. Hval = (HashTable->HashCalc2)(ArgQKey, &QKey);
  616. DPRINT3(5, "QHashupdate (%08x): Hval: %08x QKey: %08lx %08lx\n",
  617. HashTable, Hval, PRINTQUAD(QKey));
  618. } else {
  619. CopyMemory(&QKey, ArgQKey, 8);
  620. Hval = (HashTable->HashCalc)(&QKey, 8);
  621. }
  622. FRS_ASSERT(QKey != QUADZERO);
  623. //
  624. // Compute the hash index and calculate the row pointer.
  625. //
  626. HvalIndex = Hval % HashTable->NumberEntries;
  627. RowEntry = HashTable->HashRowBase + HvalIndex;
  628. QHashAcquireLock(HashTable);
  629. if (RowEntry->QKey == QKey) {
  630. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) {
  631. if (QData != NULL) {
  632. RowEntry->QData = *QData;
  633. }
  634. RowEntry->Flags = Flags;
  635. DPRINT5(5, "QHash Update (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  636. HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags);
  637. QHashReleaseLock(HashTable);
  638. return GHT_STATUS_SUCCESS;
  639. }
  640. }
  641. if (RowEntry->NextEntry.Next == NULL) {
  642. QHashReleaseLock(HashTable);
  643. return GHT_STATUS_NOT_FOUND;
  644. }
  645. //
  646. // Scan the collision list.
  647. //
  648. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  649. //
  650. // The iterator pE is of type PQHASH_ENTRY.
  651. //
  652. if (QKey < pE->QKey) {
  653. //
  654. // Not on the list.
  655. //
  656. QHashReleaseLock(HashTable);
  657. return GHT_STATUS_NOT_FOUND;
  658. }
  659. if (pE->QKey == QKey) {
  660. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) {
  661. //
  662. // Found it.
  663. //
  664. if (QData != NULL) {
  665. pE->QData = *QData;
  666. }
  667. pE->Flags = Flags;
  668. DPRINT5(5, "QHash Update (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  669. HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags);
  670. QHashReleaseLock(HashTable);
  671. return GHT_STATUS_SUCCESS;
  672. }
  673. }
  674. );
  675. QHashReleaseLock(HashTable);
  676. return GHT_STATUS_NOT_FOUND;
  677. }
  678. GHT_STATUS
  679. QHashDelete(
  680. IN PQHASH_TABLE HashTable,
  681. IN PVOID ArgQKey
  682. )
  683. /*++
  684. Routine Description:
  685. Lookup the Key in the hash table and if found remove it and put it on the
  686. free list.
  687. The table lock is acquired and released here.
  688. Arguments:
  689. HashTable -- ptr to a PQHASH_TABLE struct.
  690. ArgQKey -- ptr to the Key we are looking for.
  691. Return Value:
  692. GHT_STATUS_NOT_FOUND -- if not found.
  693. GHT_STATUS_SUCCESS -- if found and deleted.
  694. --*/
  695. {
  696. #undef DEBSUB
  697. #define DEBSUB "QHashDelete:"
  698. ULONGLONG QKey;
  699. ULONG GStatus;
  700. ULONG Hval, HvalIndex;
  701. PQHASH_ENTRY RowEntry;
  702. PQHASH_ENTRY LastFoundpE = NULL;
  703. if (IS_QHASH_LARGE_KEY(HashTable)) {
  704. Hval = (HashTable->HashCalc2)(ArgQKey, &QKey);
  705. } else {
  706. CopyMemory(&QKey, ArgQKey, 8);
  707. Hval = (HashTable->HashCalc)(&QKey, 8);
  708. }
  709. FRS_ASSERT(QKey != QUADZERO);
  710. //
  711. // Compute the hash index and calculate the row pointer.
  712. //
  713. HvalIndex = Hval % HashTable->NumberEntries;
  714. RowEntry = HashTable->HashRowBase + HvalIndex;
  715. QHashAcquireLock(HashTable);
  716. if (RowEntry->QKey == QKey) {
  717. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) {
  718. DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  719. HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags);
  720. if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) RowEntry->Flags != NULL)) {
  721. (HashTable->HashFree)((PVOID) (RowEntry->Flags));
  722. }
  723. RowEntry->QKey = QUADZERO;
  724. RowEntry->Flags = 0;
  725. QHashReleaseLock(HashTable);
  726. return GHT_STATUS_SUCCESS;
  727. }
  728. }
  729. if (RowEntry->NextEntry.Next == NULL) {
  730. QHashReleaseLock(HashTable);
  731. return GHT_STATUS_NOT_FOUND;
  732. }
  733. //
  734. // Scan the collision list.
  735. //
  736. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  737. //
  738. // The iterator pE is of type PQHASH_ENTRY.
  739. //
  740. if (QKey < pE->QKey) {
  741. //
  742. // Not on the list.
  743. //
  744. break;
  745. }
  746. if (pE->QKey == QKey) {
  747. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) {
  748. //
  749. // Found it. Remove from list and put on free list.
  750. //
  751. DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  752. HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags);
  753. if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) pE->Flags != NULL)) {
  754. (HashTable->HashFree)((PVOID) (pE->Flags));
  755. }
  756. pE->Flags = 0;
  757. RemoveSingleListEntry(NOT_USED);
  758. PushEntryList(&HashTable->FreeList, &pE->NextEntry);
  759. QHashReleaseLock(HashTable);
  760. return GHT_STATUS_SUCCESS;
  761. }
  762. }
  763. );
  764. QHashReleaseLock(HashTable);
  765. return GHT_STATUS_NOT_FOUND;
  766. }
  767. VOID
  768. QHashDeleteLock(
  769. IN PQHASH_TABLE HashTable,
  770. IN PVOID ArgQKey
  771. )
  772. /*++
  773. Routine Description:
  774. Delete the entry in the hash table. Assumes the caller has the lock on the
  775. table and has not dropped the lock since doing the QHashLookupLock() call.
  776. Arguments:
  777. HashTable -- ptr to a PQHASH_TABLE struct.
  778. ArgQKey -- ptr to the Key we are looking for.
  779. Return Value:
  780. --*/
  781. {
  782. #undef DEBSUB
  783. #define DEBSUB "QHashDeleteLock:"
  784. ULONGLONG QKey;
  785. ULONG Hval, HvalIndex;
  786. PQHASH_ENTRY RowEntry;
  787. if (IS_QHASH_LARGE_KEY(HashTable)) {
  788. Hval = (HashTable->HashCalc2)(ArgQKey, &QKey);
  789. } else {
  790. CopyMemory(&QKey, ArgQKey, 8);
  791. Hval = (HashTable->HashCalc)(&QKey, 8);
  792. }
  793. FRS_ASSERT(QKey != QUADZERO);
  794. //
  795. // Compute the hash index and calculate the row pointer.
  796. //
  797. HvalIndex = Hval % HashTable->NumberEntries;
  798. RowEntry = HashTable->HashRowBase + HvalIndex;
  799. if (RowEntry->QKey == QKey) {
  800. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) {
  801. DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  802. HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags);
  803. if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) RowEntry->Flags != NULL)) {
  804. (HashTable->HashFree)((PVOID) (RowEntry->Flags));
  805. }
  806. RowEntry->QKey = QUADZERO;
  807. RowEntry->Flags = 0;
  808. return;
  809. }
  810. }
  811. if (RowEntry->NextEntry.Next == NULL) {
  812. return;
  813. }
  814. //
  815. // Scan the collision list.
  816. //
  817. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  818. //
  819. // The iterator pE is of type PQHASH_ENTRY.
  820. // Check for early termination.
  821. //
  822. if (QKey < pE->QKey) {
  823. break;
  824. }
  825. if (pE->QKey == QKey) {
  826. if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) {
  827. //
  828. // Found it. Remove from list and put on free list.
  829. //
  830. DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  831. HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags);
  832. if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) pE->Flags != NULL)) {
  833. (HashTable->HashFree)((PVOID) (pE->Flags));
  834. }
  835. pE->Flags = 0;
  836. RemoveSingleListEntry(NOT_USED);
  837. PushEntryList(&HashTable->FreeList, &pE->NextEntry);
  838. return;
  839. }
  840. }
  841. );
  842. return;
  843. }
  844. #if 0
  845. ////// Currently Unused //////
  846. VOID
  847. QHashDeleteNodeLock(
  848. IN PQHASH_TABLE HashTable,
  849. IN PQHASH_ENTRY BeforeNode,
  850. IN PQHASH_ENTRY TargetNode
  851. )
  852. /*++
  853. Routine Description:
  854. Delete the TargetNode entry in the hash table. This is a singly linked list
  855. so Before Node has to be adjusted. If BeforeNode is NULL then the TargetNode
  856. is head of the collision chain and is not deleted, instead the key is set to 0.
  857. Assumes the caller has the lock on the table and has not dropped the lock
  858. since getting the Node addresses.
  859. Arguments:
  860. HashTable -- ptr to a PQHASH_TABLE struct.
  861. BeforeNode -- ptr to the QhashEntry before the node to be deleted.
  862. TargetNode -- ptr to the QhashEntry to delete.
  863. Return Value:
  864. None.
  865. --*/
  866. {
  867. #undef DEBSUB
  868. #define DEBSUB "QHashDeleteNodeLock:"
  869. FRS_ASSERT(TargetNode != NULL);
  870. //
  871. // Special case for node that is part of the main hash vector.
  872. //
  873. if (BeforeNode == NULL) {
  874. TargetNode->QKey = QUADZERO;
  875. TargetNode->Flags = 0;
  876. return;
  877. }
  878. //
  879. // Unlink the target entry and put on the free list.
  880. //
  881. BeforeNode->NextEntry.Next = TargetNode->NextEntry.Next;
  882. TargetNode->NextEntry.Next = NULL;
  883. TargetNode->Flags = 0;
  884. PushEntryList(&HashTable->FreeList, &TargetNode->NextEntry);
  885. return;
  886. }
  887. #endif // 0
  888. VOID
  889. QHashDeleteByFlags(
  890. IN PQHASH_TABLE HashTable,
  891. IN ULONG_PTR Flags
  892. )
  893. /*++
  894. Routine Description:
  895. Delete all entries in the hash table that match the given Flags argument.
  896. The table lock is acquired and released here.
  897. Arguments:
  898. HashTable -- ptr to a PQHASH_TABLE struct.
  899. Flags -- Match key to select elements to delete.
  900. Return Value:
  901. None.
  902. --*/
  903. {
  904. #undef DEBSUB
  905. #define DEBSUB "QHashDeleteByFlags:"
  906. PQHASH_ENTRY RowEntry;
  907. ULONG i;
  908. FRS_ASSERT(!IS_QHASH_LARGE_KEY(HashTable));
  909. RowEntry = HashTable->HashRowBase;
  910. //
  911. // Loop through all the Hash table rows and delete each matching element.
  912. //
  913. QHashAcquireLock(HashTable);
  914. for (i=0; i<HashTable->NumberEntries; i++, RowEntry++) {
  915. if (RowEntry->Flags == Flags) {
  916. DPRINT5(5, "QHash DeleteByFlags (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  917. HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags);
  918. RowEntry->QKey = QUADZERO;
  919. RowEntry->Flags = 0;
  920. }
  921. if (RowEntry->NextEntry.Next == NULL) {
  922. continue;
  923. }
  924. //
  925. // Scan the collision list.
  926. //
  927. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  928. //
  929. // The iterator pE is of type PQHASH_ENTRY.
  930. // Check for match. Remove from list and put on free list.
  931. //
  932. if (pE->Flags == Flags) {
  933. DPRINT5(5, "QHash DeleteByFlags (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n",
  934. HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags);
  935. pE->Flags = 0;
  936. RemoveSingleListEntry(NOT_USED);
  937. PushEntryList(&HashTable->FreeList, &pE->NextEntry);
  938. }
  939. );
  940. }
  941. QHashReleaseLock(HashTable);
  942. return;
  943. }
  944. VOID
  945. QHashEmptyLargeKeyTable(
  946. IN PQHASH_TABLE HashTable
  947. )
  948. /*++
  949. Routine Description:
  950. Delete all the large key nodes in the QHash table.
  951. Put all collision entries on the free list.
  952. The table lock is acquired and released here.
  953. Arguments:
  954. HashTable -- ptr to a PQHASH_TABLE struct.
  955. Return Value:
  956. None.
  957. --*/
  958. {
  959. #undef DEBSUB
  960. #define DEBSUB "QHashEmptyLargeKeyTable:"
  961. PQHASH_ENTRY RowEntry;
  962. ULONG i;
  963. //
  964. // No work if not a large key table.
  965. //
  966. if (!IS_QHASH_LARGE_KEY(HashTable)) {
  967. return;
  968. }
  969. RowEntry = HashTable->HashRowBase;
  970. //
  971. // Loop through all the Hash table rows and delete each matching element.
  972. //
  973. QHashAcquireLock(HashTable);
  974. for (i=0; i<HashTable->NumberEntries; i++, RowEntry++) {
  975. if ((PVOID)RowEntry->Flags != NULL) {
  976. (HashTable->HashFree)((PVOID) (RowEntry->Flags));
  977. }
  978. RowEntry->QKey = QUADZERO;
  979. RowEntry->Flags = 0;
  980. if (RowEntry->NextEntry.Next == NULL) {
  981. continue;
  982. }
  983. //
  984. // Scan the collision list.
  985. // Free the large key node and put qhash collision entries on free list.
  986. //
  987. ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry,
  988. //
  989. // The iterator pE is of type PQHASH_ENTRY.
  990. //
  991. if ((PVOID)RowEntry->Flags != NULL) {
  992. (HashTable->HashFree)((PVOID) (pE->Flags));
  993. }
  994. pE->Flags = 0;
  995. RemoveSingleListEntry(NOT_USED);
  996. PushEntryList(&HashTable->FreeList, &pE->NextEntry);
  997. );
  998. }
  999. QHashReleaseLock(HashTable);
  1000. return;
  1001. }