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.

1764 lines
48 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. genhash.c
  5. Abstract:
  6. Generic Hash Table routines. Each hash table is an array of FRS_LIST entries
  7. that provide interlocked access to each row of the hash table. Each table
  8. is managed by a GENERIC_HASH_TABLE struct which holds function entry points for
  9. freeing an entry, comparing keys, performing a hash calculation, printing
  10. an entry and dumping the table.
  11. Note: All hash entries must be prefixed with GENERIC_HASH_ROW_ENTRY at the
  12. beginning of the structure.
  13. Author:
  14. David Orbits [davidor] 22-Apr-1997
  15. Environment:
  16. User Mode Service
  17. Revision History:
  18. --*/
  19. #include <ntreppch.h>
  20. #pragma hdrstop
  21. #include <frs.h>
  22. #include <genhash.h>
  23. #include <tablefcn.h>
  24. #pragma warning( disable:4102) // unreferenced label
  25. PGENERIC_HASH_TABLE
  26. GhtCreateTable(
  27. PCHAR ArgName,
  28. ULONG NumberRows,
  29. ULONG KeyOffset,
  30. ULONG KeyLength,
  31. PGENERIC_HASH_FREE_ROUTINE GhtFree,
  32. PGENERIC_HASH_COMPARE_ROUTINE GhtCompare,
  33. PGENERIC_HASH_CALC_ROUTINE GhtHashCalc,
  34. PGENERIC_HASH_PRINT_ROUTINE GhtPrint
  35. )
  36. /*++
  37. Routine Description:
  38. Allocate and initialize a hash table.
  39. Arguments:
  40. ArgName -- The table name. [16 byte max]
  41. NumberRows -- The number of rows in the table.
  42. KeyOffset -- The byte offset to the key value in each table entry.
  43. KeyLength -- The byte length of the key value in each table entry.
  44. GhtFree -- function to call to free an entry.
  45. GhtCompare -- function to comoare two keys.
  46. GhtHashCalc -- function to calculate the ULONG hash value on a key.
  47. GhtPrint -- Function to print out a table entry.
  48. GhtDump -- Function to call to dump all table entries.
  49. Return Value:
  50. ptr to a GENERIC_HASH_TABLE struct or NULL if failure.
  51. use GetLastError for the error status.
  52. --*/
  53. {
  54. #undef DEBSUB
  55. #define DEBSUB "GhtCreateTable:"
  56. PGENERIC_HASH_TABLE HashTable;
  57. PGENERIC_HASH_ROW_ENTRY RowBase, HashRowEntry;
  58. ULONG NameLen;
  59. ULONG WStatus;
  60. ULONG i;
  61. HashTable = (PGENERIC_HASH_TABLE) FrsAllocType(GENERIC_HASH_TABLE_TYPE);
  62. RowBase = (PGENERIC_HASH_ROW_ENTRY) FrsAlloc(
  63. NumberRows * sizeof(GENERIC_HASH_ROW_ENTRY));
  64. NameLen = min(strlen(ArgName), 15);
  65. CopyMemory(HashTable->Name, ArgName, NameLen);
  66. HashTable->Name[NameLen] = '\0';
  67. HashTable->NumberRows = NumberRows;
  68. HashTable->GhtFree = GhtFree;
  69. HashTable->GhtCompare = GhtCompare;
  70. HashTable->GhtHashCalc = GhtHashCalc;
  71. HashTable->GhtPrint = GhtPrint;
  72. HashTable->KeyOffset = KeyOffset;
  73. HashTable->KeyLength = KeyLength;
  74. HashTable->RowLockEnabled = TRUE;
  75. HashTable->RefCountEnabled = TRUE;
  76. HashTable->HeapHandle = NULL;
  77. HashTable->UseOffsets = FALSE;
  78. HashTable->OffsetBase = 0;
  79. HashTable->HashRowBase = RowBase;
  80. HashTable->LockTimeout = 10000; // milliseconds
  81. //
  82. // Initialize all the hash table rows. Each has a critical section and
  83. // an event to wait on.
  84. //
  85. HashRowEntry = RowBase;
  86. for (i=0; i<NumberRows; i++) {
  87. //
  88. // Create the event first so if we fail GhtDestroyTable sees a null handle.
  89. //
  90. //HashRowEntry->Event = CreateEvent(NULL, TRUE, FALSE, NULL);
  91. WStatus = FrsRtlInitializeList(&HashRowEntry->HashRow);
  92. if (WStatus != ERROR_SUCCESS) {
  93. goto CLEANUP;
  94. }
  95. //if (HashRowEntry->Event == NULL) {
  96. // WStatus = GetLastError();
  97. // goto CLEANUP;
  98. //}
  99. HashRowEntry += 1;
  100. }
  101. return HashTable;
  102. CLEANUP:
  103. HashTable->NumberRows = i+1;
  104. GhtDestroyTable(HashTable);
  105. SetLastError(WStatus);
  106. return NULL;
  107. }
  108. VOID
  109. GhtDestroyTable(
  110. PGENERIC_HASH_TABLE HashTable
  111. )
  112. /*++
  113. Routine Description:
  114. Free all the memory for a hash table.
  115. This includes any data elements left in the table.
  116. No locks are acquired.
  117. Arguments:
  118. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  119. Return Value:
  120. None.
  121. --*/
  122. {
  123. #undef DEBSUB
  124. #define DEBSUB "GhtDestroyTable:"
  125. PGENERIC_HASH_ROW_ENTRY RowEntry;
  126. ULONG i;
  127. if (HashTable == NULL) {
  128. return;
  129. }
  130. RowEntry = HashTable->HashRowBase;
  131. DPRINT1(5, "GhtDestroyTable for %s\n", HashTable->Name);
  132. //
  133. // Loop through all the Hash table rows and delete any entries still on
  134. // each row.
  135. //
  136. for (i=0; i<HashTable->NumberRows; i++, RowEntry++) {
  137. if (RowEntry->HashRow.Count != 0) {
  138. //DPRINT2(5, "HashRow: %d, RowCount %d\n",i, RowEntry->HashRow.Count);
  139. }
  140. ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  141. FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &pE->ListEntry);
  142. //DPRINT4(5, " Deleteing entry: %08x, Hval %08x, Index %d, refcnt %d\n",
  143. // pE, pE->HashValue, i, pE->ReferenceCount);
  144. (HashTable->GhtFree)(HashTable, pE);
  145. );
  146. FrsRtlDeleteList(&RowEntry->HashRow);
  147. //if (RowEntry->Event != NULL) {
  148. // FRS_CLOSE(RowEntry->Event);
  149. //}
  150. }
  151. FrsFree(HashTable->HashRowBase);
  152. FrsFreeType(HashTable);
  153. return;
  154. }
  155. ULONG
  156. GhtCleanTableByFilter(
  157. PGENERIC_HASH_TABLE HashTable,
  158. IN PGENERIC_HASH_ENUM_ROUTINE Function,
  159. PVOID Context
  160. )
  161. /*++
  162. Routine Description:
  163. Free the elements in the hash table for which the predicate function
  164. returns TRUE.
  165. Arguments:
  166. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  167. Function - The function to call for each record in the table. It is of
  168. of type PGENERIC_HASH_FILTER_ROUTINE.
  169. Return TRUE to delete the entry in the table.
  170. Context -- Arg to pass thru to the filter function.
  171. Return Value:
  172. The number of entries deleted.
  173. --*/
  174. {
  175. #undef DEBSUB
  176. #define DEBSUB "GhtCleanTableByFilter:"
  177. PGENERIC_HASH_ROW_ENTRY RowEntry;
  178. ULONG i;
  179. ULONG Count = 0;
  180. if (HashTable == NULL) {
  181. return Count;
  182. }
  183. RowEntry = HashTable->HashRowBase;
  184. //
  185. // Loop through all the Hash table rows and delete any entries still on
  186. // each row.
  187. //
  188. for (i=0; i<HashTable->NumberRows; i++, RowEntry++) {
  189. if (RowEntry->HashRow.Count != 0) {
  190. //DPRINT2(4, "HashRow: %d, RowCount %d\n", i, RowEntry->HashRow.Count);
  191. }
  192. ForEachListEntry(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  193. //
  194. // The iterator pE is of type GENERIC_HASH_ENTRY_HEADER.
  195. // Call predicate to see if we should do the delete.
  196. //
  197. if ((Function)(HashTable, pE, Context)) {
  198. FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &pE->ListEntry);
  199. //DPRINT4(4, "Deleting entry: %08x, Hval %08x, Index %d, refcnt %d\n",
  200. // pE, pE->HashValue, i, pE->ReferenceCount);
  201. (HashTable->GhtFree)(HashTable, pE);
  202. Count += 1;
  203. }
  204. );
  205. }
  206. return Count;
  207. }
  208. #if DBG
  209. VOID
  210. GhtDumpTable(
  211. ULONG Sev,
  212. PGENERIC_HASH_TABLE HashTable
  213. )
  214. /*++
  215. Routine Description:
  216. Call the print routine for each element in the table.
  217. Arguments:
  218. Sev -- DPRINT severity level.
  219. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  220. Return Value:
  221. None.
  222. --*/
  223. {
  224. #undef DEBSUB
  225. #define DEBSUB "GhtDumpTable:"
  226. PGENERIC_HASH_ROW_ENTRY HashRowEntry;
  227. ULONG i;
  228. HashRowEntry = HashTable->HashRowBase;
  229. DPRINT(Sev,"----------------------------------------------\n");
  230. DPRINT(Sev,"----------------------------------------------\n");
  231. DPRINT1(Sev, "GhtDumpTable for %s\n", HashTable->Name);
  232. DPRINT(Sev,"----------------------------------------------\n");
  233. DPRINT(Sev,"----------------------------------------------\n");
  234. //
  235. // Loop through all the Hash table rows and call the print function for
  236. // each element.
  237. //
  238. for (i=0; i<HashTable->NumberRows; i++) {
  239. if (HashRowEntry->HashRow.Count != 0) {
  240. DPRINT(Sev, " \n");
  241. DPRINT(Sev,"----------------------------------------------\n");
  242. DPRINT2(Sev, "HashRow: %d, RowCount %d\n",
  243. i, HashRowEntry->HashRow.Count);
  244. DPRINT5(Sev, "Inserts: %d, Removes: %d, Compares: %d, Lookups: %d, Lookupfails: %d \n",
  245. HashRowEntry->RowInserts,
  246. HashRowEntry->RowRemoves,
  247. HashRowEntry->RowCompares,
  248. HashRowEntry->RowLookups,
  249. HashRowEntry->RowLookupFails);
  250. }
  251. ForEachListEntry(&HashRowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  252. (HashTable->GhtPrint)(HashTable, pE);
  253. );
  254. HashRowEntry += 1;
  255. }
  256. }
  257. #endif DBG
  258. ULONG_PTR
  259. GhtEnumerateTable(
  260. IN PGENERIC_HASH_TABLE HashTable,
  261. IN PGENERIC_HASH_ENUM_ROUTINE Function,
  262. IN PVOID Context
  263. )
  264. /*++
  265. Routine Description:
  266. This routine walks through the entries in a generic hash table and
  267. calls the function provided with the entry address and the context.
  268. No locks are taken by this function so the called function can make
  269. calls to other GHT routines to lookup or insert new entries.
  270. THis routine increments the ref count on each entry before it makes the
  271. call to ensure the entry does not vanish. It keeps a pointer to the
  272. entry that tells it where to continue the scan. If the argument function
  273. inserts an entry that is earlier in the table the enumeration will not
  274. pick it up.
  275. Arguments:
  276. HashTable - The context of the Hash Table to enumerate.
  277. Function - The function to call for each record in the table. It is of
  278. of type PGENERIC_HASH_ENUM_ROUTINE. Return FALSE to abort the
  279. enumeration else true.
  280. Context - A context ptr to pass through to the RecordFunction.
  281. Return Value:
  282. The status code from the argument function.
  283. --*/
  284. {
  285. #undef DEBSUB
  286. #define DEBSUB "GhtEnumerateTable:"
  287. PGENERIC_HASH_ROW_ENTRY RowEntry;
  288. ULONG i;
  289. ULONG_PTR WStatus;
  290. RowEntry = HashTable->HashRowBase;
  291. DPRINT(5,"----------------------------------------------\n");
  292. DPRINT(5,"----------------------------------------------\n");
  293. DPRINT1(5, "GhtEnumerateTable for %s\n", HashTable->Name);
  294. DPRINT(5,"----------------------------------------------\n");
  295. DPRINT(5,"----------------------------------------------\n");
  296. //
  297. // Loop through all the Hash table rows and call the print function for
  298. // each element.
  299. //
  300. for (i=0; i<HashTable->NumberRows; i++) {
  301. if (RowEntry->HashRow.Count != 0) {
  302. DPRINT(5, " \n");
  303. DPRINT(5,"----------------------------------------------\n");
  304. DPRINT2(5, "HashRow: %d, RowCount %d\n",
  305. i, RowEntry->HashRow.Count);
  306. }
  307. ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  308. InterlockedIncrement(&pE->ReferenceCount);
  309. DPRINT2(5, "inc ref: %08x, %d\n", pE, pE->ReferenceCount);
  310. WStatus = (Function)(HashTable, pE, Context);
  311. InterlockedDecrement(&pE->ReferenceCount);
  312. DPRINT2(5, "dec ref: %08x, %d\n", pE, pE->ReferenceCount);
  313. // Note: If caller needs this we should add code to check for
  314. // zero ref count and call the delete function.
  315. if( WStatus != 0 ) {
  316. return WStatus;
  317. }
  318. );
  319. RowEntry += 1;
  320. }
  321. return ERROR_SUCCESS;
  322. }
  323. ULONG_PTR
  324. GhtEnumerateTableNoRef(
  325. IN PGENERIC_HASH_TABLE HashTable,
  326. IN PGENERIC_HASH_ENUM_ROUTINE Function,
  327. IN PVOID Context
  328. )
  329. /*++
  330. Routine Description:
  331. This routine walks through the entries in a generic hash table and
  332. calls the function provided with the entry address and the context.
  333. No locks are taken by this function so the called function can make
  334. calls to other GHT routines to lookup or insert new entries.
  335. THis routine does not take a ref count out on the entry.
  336. It keeps a pointer to the next entry that tells it where to continue
  337. the scan if the argument function deletes the entry. If the argument function
  338. inserts an entry that is earlier in the table the enumeration will not
  339. pick it up.
  340. Arguments:
  341. HashTable - The context of the Hash Table to enumerate.
  342. Function - The function to call for each record in the table. It is of
  343. of type PGENERIC_HASH_ENUM_ROUTINE. Return FALSE to abort the
  344. enumeration else true.
  345. Context - A context ptr to pass through to the RecordFunction.
  346. Return Value:
  347. The status code from the argument function.
  348. --*/
  349. {
  350. #undef DEBSUB
  351. #define DEBSUB "GhtEnumerateTableNoRef:"
  352. PGENERIC_HASH_ROW_ENTRY RowEntry;
  353. ULONG i;
  354. ULONG_PTR WStatus;
  355. RowEntry = HashTable->HashRowBase;
  356. DPRINT1(5, "GhtEnumerateTableNoRef for %s\n", HashTable->Name);
  357. //
  358. // Loop through all the Hash table rows and call the print function for
  359. // each element.
  360. //
  361. for (i=0; i<HashTable->NumberRows; i++) {
  362. if (RowEntry->HashRow.Count != 0) {
  363. DPRINT(5, " \n");
  364. DPRINT(5,"----------------------------------------------\n");
  365. DPRINT2(5, "HashRow: %d, RowCount %d\n",
  366. i, RowEntry->HashRow.Count);
  367. }
  368. ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  369. WStatus = (Function)(HashTable, pE, Context);
  370. if (WStatus != 0) {
  371. return WStatus;
  372. }
  373. );
  374. RowEntry += 1;
  375. }
  376. return (ULONG_PTR)0;
  377. }
  378. PGENERIC_HASH_ENTRY_HEADER
  379. GhtGetNextEntry(
  380. IN PGENERIC_HASH_TABLE HashTable,
  381. PGENERIC_HASH_ENTRY_HEADER HashEntry
  382. )
  383. /*++
  384. Routine Description:
  385. This routine returns the next entry in the table that follows the HashEntry
  386. argument. If the HashEntry is NULL it returns the first entry.
  387. It gets the row lock containing the current entry, decrements the
  388. ref count on the entry. It scans forward to the next entry in the table
  389. getting its row lock if needed, increments its ref count and returns the
  390. pointer. If the end of table is reached NULL is returned.
  391. If an entry is inserted earlier in the table the enumeration will not
  392. pick it up.
  393. Arguments:
  394. HashTable - The context of the Hash Table to enumerate.
  395. HashEntry - The current entry we are looking at. Used to get the next entry
  396. If null start scan at beginning of table.
  397. Return Value:
  398. The status code from the argument function.
  399. --*/
  400. {
  401. #undef DEBSUB
  402. #define DEBSUB "GhtGetNextEntry:"
  403. PGENERIC_HASH_ROW_ENTRY LastRow;
  404. ULONG Hval, HvalIndex;
  405. PGENERIC_HASH_ROW_ENTRY RowEntry;
  406. PLIST_ENTRY Entry;
  407. RowEntry = HashTable->HashRowBase;
  408. //
  409. // Get the hash value for the element and compute the index and RowEntry
  410. // address. Then get the row lock.
  411. //
  412. if (HashEntry != NULL) {
  413. Hval = HashEntry->HashValue;
  414. HvalIndex = Hval % HashTable->NumberRows;
  415. RowEntry += HvalIndex;
  416. //
  417. // Get the row lock and decrement the ref count.
  418. // (could delete if it hits zero)
  419. //
  420. FrsRtlAcquireListLock(&RowEntry->HashRow);
  421. InterlockedDecrement(&HashEntry->ReferenceCount);
  422. //
  423. // look for next entry in same row.
  424. // if found, bump ref count, drop lock, return entry.
  425. //
  426. Entry = GetListNext(&HashEntry->ListEntry);
  427. if (Entry != &RowEntry->HashRow.ListHead) {
  428. goto FOUND;
  429. }
  430. //
  431. // if not found drop row lock and execute scan code below
  432. // starting from next row entry.
  433. //
  434. FrsRtlReleaseListLock(&RowEntry->HashRow);
  435. RowEntry += 1;
  436. }
  437. //
  438. // Scan the rest of the table for a non-empty row.
  439. //
  440. LastRow = HashTable->HashRowBase + HashTable->NumberRows;
  441. while (RowEntry < LastRow) {
  442. if (RowEntry->HashRow.Count != 0) {
  443. //
  444. // Found one. Get the row lock and recheck the count incase
  445. // someone beat us too it.
  446. //
  447. FrsRtlAcquireListLock(&RowEntry->HashRow);
  448. if (RowEntry->HashRow.Count == 0) {
  449. //
  450. // Too bad. Continue scan.
  451. //
  452. FrsRtlReleaseListLock(&RowEntry->HashRow);
  453. RowEntry += 1;
  454. continue;
  455. }
  456. //
  457. // We got one. Get the entry address, bump the ref count, drop lock.
  458. //
  459. FRS_ASSERT(!IsListEmpty(&RowEntry->HashRow.ListHead));
  460. Entry = GetListHead(&RowEntry->HashRow.ListHead);
  461. goto FOUND;
  462. }
  463. RowEntry += 1;
  464. }
  465. return NULL;
  466. FOUND:
  467. HashEntry = CONTAINING_RECORD(Entry, GENERIC_HASH_ENTRY_HEADER, ListEntry);
  468. InterlockedIncrement(&HashEntry->ReferenceCount);
  469. FrsRtlReleaseListLock(&RowEntry->HashRow);
  470. return HashEntry;
  471. }
  472. ULONG
  473. GhtCountEntries(
  474. IN PGENERIC_HASH_TABLE HashTable
  475. )
  476. /*++
  477. Routine Description:
  478. This routine walks through the rows in a generic hash table and
  479. adds up the entry count. It takes no locks so the count is approx.
  480. Caller must know the table can't go away.
  481. Arguments:
  482. HashTable - The context of the Hash Table to count.
  483. Return Value:
  484. The count.
  485. --*/
  486. {
  487. #undef DEBSUB
  488. #define DEBSUB "GhtCountEntries:"
  489. ULONG Total = 0;
  490. PGENERIC_HASH_ROW_ENTRY LastRow, RowEntry = HashTable->HashRowBase;
  491. //
  492. // Loop through all the Hash table rows and add counts.
  493. //
  494. LastRow = RowEntry + HashTable->NumberRows;
  495. while (RowEntry < LastRow) {
  496. Total += RowEntry->HashRow.Count;
  497. RowEntry += 1;
  498. }
  499. return Total;
  500. }
  501. PGENERIC_HASH_ENTRY_HEADER
  502. GhtGetEntryNumber(
  503. IN PGENERIC_HASH_TABLE HashTable,
  504. IN LONG EntryNumber
  505. )
  506. /*++
  507. Routine Description:
  508. This routine walks through the rows in a generic hash table
  509. counting entries as it goes. It returns the requested entry (by number)
  510. from the table. Note this will not be the same entry from call to call
  511. because of intervening inserts and deletes. It takes no locks until it
  512. gets to the row of the table that contains the entry.
  513. The ref count on the entry is incremented.
  514. Arguments:
  515. HashTable - The context of the Hash Table to enumerate.
  516. EntryNumber - The ordinal number of the entry in the table.
  517. zero is the first entry.
  518. Return Value:
  519. The address of the entry.
  520. --*/
  521. {
  522. #undef DEBSUB
  523. #define DEBSUB "GhtGetEntryNumber:"
  524. PGENERIC_HASH_ROW_ENTRY LastRow, RowEntry = HashTable->HashRowBase;
  525. ULONG Rcount;
  526. PLIST_ENTRY Entry;
  527. PGENERIC_HASH_ENTRY_HEADER HashEntry;
  528. FRS_ASSERT(EntryNumber >= 0);
  529. //
  530. // Loop through Hash table rows looking for the row with the entry.
  531. //
  532. LastRow = RowEntry + HashTable->NumberRows;
  533. while (RowEntry < LastRow) {
  534. Rcount = RowEntry->HashRow.Count;
  535. if (Rcount > 0) {
  536. EntryNumber -= Rcount;
  537. if (EntryNumber < 0) {
  538. //
  539. // Should be in this row. Get the row lock and recheck
  540. // the count incase someone beat us too it.
  541. //
  542. FrsRtlAcquireListLock(&RowEntry->HashRow);
  543. if (RowEntry->HashRow.Count < Rcount) {
  544. //
  545. // Too bad. It got shorter, Retry test.
  546. //
  547. FrsRtlReleaseListLock(&RowEntry->HashRow);
  548. EntryNumber += Rcount;
  549. continue;
  550. }
  551. //
  552. // We got one. Get the entry address, bump the ref count, drop lock.
  553. //
  554. EntryNumber += Rcount;
  555. Entry = GetListHead(&RowEntry->HashRow.ListHead);
  556. while (EntryNumber-- > 0) {
  557. FRS_ASSERT(Entry != &RowEntry->HashRow.ListHead);
  558. Entry = GetListNext(Entry);
  559. }
  560. HashEntry = CONTAINING_RECORD(Entry, GENERIC_HASH_ENTRY_HEADER, ListEntry);
  561. InterlockedIncrement(&HashEntry->ReferenceCount);
  562. FrsRtlReleaseListLock(&RowEntry->HashRow);
  563. return HashEntry;
  564. }
  565. }
  566. RowEntry += 1;
  567. }
  568. return NULL;
  569. }
  570. PGENERIC_HASH_ENTRY_HEADER
  571. GhtQuickCheck(
  572. PGENERIC_HASH_TABLE HashTable,
  573. PGENERIC_HASH_ROW_ENTRY RowEntry,
  574. PGENERIC_HASH_ENTRY_HEADER HashEntry,
  575. ULONG Hval
  576. )
  577. /*++
  578. Routine Description:
  579. Internal function to do a quick scan of a row to find an entry.
  580. Used in debug code to check that an entry is actually in the table.
  581. Assumes caller has the row lock.
  582. Arguments:
  583. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  584. RowEntry -- ptr to the ROW_ENTRY struct.
  585. HashEntry -- ptr to the hash entry we are looking for.
  586. Hval -- hash value we are looking for.
  587. Return Value:
  588. ptr to entry if we find it.
  589. NULL if we don't.
  590. --*/
  591. {
  592. PCHAR pKeyValue;
  593. pKeyValue = ((PCHAR)HashEntry) + HashTable->KeyOffset;
  594. ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  595. //
  596. // The iterator pE is of type PGENERIC_HASH_ENTRY_HEADER.
  597. //
  598. if (pE->HashValue == Hval) {
  599. if ((HashTable->GhtCompare)(pKeyValue,
  600. ((PCHAR)pE) + HashTable->KeyOffset,
  601. HashTable->KeyLength)) {
  602. //
  603. // Found it.
  604. //
  605. return pE;
  606. }
  607. }
  608. );
  609. return NULL;
  610. }
  611. GHT_STATUS
  612. GhtLookup2(
  613. PGENERIC_HASH_TABLE HashTable,
  614. PVOID pKeyValue,
  615. BOOL WaitIfLocked,
  616. PVOID *RetHashEntry,
  617. ULONG DupIndex
  618. )
  619. /*++
  620. Routine Description:
  621. Takes the KeyValue and calls the hash function which returns a ULONG. Then
  622. calculate the index of HashValue Mod TableLenth. With the index find the hash
  623. row header and acquire the row lock. It then walks the list looking for a hash
  624. value match on the KeyValue. The entires are kept in ascending order so the
  625. lookup stops as soon as new entry value is < the list entry value. Then call
  626. the compare routine to see if the key data in the entry (entry+key offset)
  627. matches the keyValue passed in. If it matches, the ref count in the entry is
  628. bumped and the address is returned.
  629. If there are duplicate entries then the ptr to the nth oldest duplicate is
  630. returned where n is supplied by DupIndex. A value of 0 for DupIndex means
  631. return the last duplicate in the list. This is the most recent duplicate
  632. inserted since insert puts new entries at the end of the duplicate group. A
  633. value of one returns the oldest duplicate as determined by time of insert.
  634. TBI -
  635. If the row is locked and WaitIfLocked is TRUE then we wait on the row event.
  636. If the row is locked and WaitIfLocked is FALSE then return status
  637. GHT_STATUS_LOCKCONFLICT. In this case you can't tell if the entry is in
  638. the table.
  639. Arguments:
  640. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  641. pKeyValue -- ptr to the keyValue we are looking for.
  642. WaitIfLocked -- True means wait if the row is locked.
  643. RetHashEntry -- Returned ptr if found or NULL.
  644. DupIndex -- return the nth duplicate, if 0 return last duplicate in list.
  645. Return Value:
  646. GHT_STATUS_NOT_FOUND -- if not found.
  647. GHT_STATUS_SUCCESS -- if found.
  648. --*/
  649. {
  650. #undef DEBSUB
  651. #define DEBSUB "GhtLookup2:"
  652. ULONG GStatus;
  653. ULONG Hval, HvalIndex;
  654. PGENERIC_HASH_ROW_ENTRY RowEntry;
  655. PGENERIC_HASH_ENTRY_HEADER LastFoundpE = NULL;
  656. // Note: Get lock earlier if table resize support is added.
  657. if (pKeyValue == NULL) {
  658. *RetHashEntry = NULL;
  659. return GHT_STATUS_NOT_FOUND;
  660. }
  661. //
  662. // Compute the hash index and calculate the row pointer.
  663. //
  664. Hval = (HashTable->GhtHashCalc)(pKeyValue, HashTable->KeyLength);
  665. HvalIndex = Hval % HashTable->NumberRows;
  666. RowEntry = HashTable->HashRowBase + HvalIndex;
  667. if (FrsRtlCountList(&RowEntry->HashRow) == 0) {
  668. *RetHashEntry = NULL;
  669. RowEntry->RowLookupFails += 1;
  670. return GHT_STATUS_NOT_FOUND;
  671. }
  672. if (DupIndex == 0) {
  673. DupIndex = 0xFFFFFFFF;
  674. }
  675. FrsRtlAcquireListLock(&RowEntry->HashRow);
  676. //
  677. // Walk the list looking for a match on the
  678. // the hash value then try and match the KeyValue.
  679. //
  680. ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  681. //
  682. // The iterator pE is of type PGENERIC_HASH_ENTRY_HEADER.
  683. //
  684. RowEntry->RowCompares += 1;
  685. if (Hval < pE->HashValue) {
  686. //
  687. // Not on the list.
  688. //
  689. break;
  690. }
  691. if (pE->HashValue == Hval) {
  692. if ((HashTable->GhtCompare)(pKeyValue,
  693. ((PCHAR)pE) + HashTable->KeyOffset,
  694. HashTable->KeyLength)) {
  695. //
  696. // Found it. Check DupIndex count.
  697. //
  698. RowEntry->RowLookups += 1;
  699. LastFoundpE = pE;
  700. if (--DupIndex == 0) {
  701. break;
  702. }
  703. }
  704. }
  705. );
  706. if (LastFoundpE != NULL) {
  707. //
  708. // Found one. Bump ref count, release the lock, return success.
  709. //
  710. InterlockedIncrement(&LastFoundpE->ReferenceCount);
  711. DPRINT2(5, ":: inc ref: %08x, %d\n", LastFoundpE, LastFoundpE->ReferenceCount);
  712. GStatus = GHT_STATUS_SUCCESS;
  713. } else {
  714. RowEntry->RowLookupFails += 1;
  715. GStatus = GHT_STATUS_NOT_FOUND;
  716. }
  717. ReleaseListLock(&RowEntry->HashRow);
  718. *RetHashEntry = LastFoundpE;
  719. return GStatus;
  720. }
  721. GHT_STATUS
  722. GhtInsert(
  723. PGENERIC_HASH_TABLE HashTable,
  724. PVOID HashEntryArg,
  725. BOOL WaitIfLocked,
  726. BOOL DuplicatesOk
  727. )
  728. /*++
  729. Routine Description:
  730. Inserts a HashEntry into the HashTable. It calls the hash function with a ptr
  731. to the key data (HashEntry+key offset) which returns a ULONG that is stored in
  732. HashEntry.HashValue. Insert then calculates the index of HashValue Mod
  733. TableLenth. With the index it finds the hash row header and acquires the row
  734. lock. It then walks the list looking for a hash value match. The entires are
  735. kept in ascending order so the lookup stops as soon as new entry value is < the
  736. list entry value. It then inserts the entry in the table, updates the counts in
  737. the row header, releases the lock and returns. If it finds a match it calls the
  738. user compare function with HashEntry+offset and ListEntry+offset to validate the
  739. match. The validate returns true if it matches and false if it fails (i.e.
  740. continue walking the list). Duplicates are allowed when DuplicatesOk is True.
  741. Insert returns GHT_STATUS_SUCCESS if the entry was inserted and
  742. GHT_STATUS_FAILURE if this is a duplicate node and DuplicatesOk is False (the
  743. compare function returned TRUE). The refcount is incremented if the node was
  744. inserted.
  745. Note: All hash entries must be prefixed with GENERIC_HASH_ROW_ENTRY at the
  746. beginning of the structure.
  747. TBI -
  748. If the row is locked and WaitIfLocked is FALSE then return status
  749. GHT_STATUS_LOCKCONFLICT else wait on the row.
  750. Arguments:
  751. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  752. HashEntryArg -- ptr to new entry to insert.
  753. WaitIfLocked -- True means wait if the row is locked.
  754. DuplicatesOk -- True means duplicate entries are ok. They are placed at
  755. the end of the list of duplicates.
  756. Return Value:
  757. GHT_STATUS_FAILURE -- Conflicting entry is in table already.
  758. GHT_STATUS_SUCCESS -- Insert was successful.
  759. --*/
  760. {
  761. #undef DEBSUB
  762. #define DEBSUB "GhtInsert:"
  763. ULONG Hval, HvalIndex;
  764. PGENERIC_HASH_ROW_ENTRY RowEntry;
  765. PVOID pKeyValue;
  766. PLIST_ENTRY BeforeEntry;
  767. PGENERIC_HASH_ENTRY_HEADER HashEntry =
  768. (PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
  769. //
  770. // Compute the hash value on the key in the entry.
  771. //
  772. pKeyValue = ((PCHAR)HashEntry) + HashTable->KeyOffset;
  773. Hval = (HashTable->GhtHashCalc)(pKeyValue, HashTable->KeyLength);
  774. HashEntry->HashValue = Hval;
  775. //
  776. // Compute the index and calculate the row pointer.
  777. //
  778. HvalIndex = Hval % HashTable->NumberRows;
  779. RowEntry = HashTable->HashRowBase + HvalIndex;
  780. FrsRtlAcquireListLock(&RowEntry->HashRow);
  781. BeforeEntry = &RowEntry->HashRow.ListHead; // incase of empty list.
  782. //
  783. // Walk the list with the lock looking for a match on the
  784. // the hash value then try and match the KeyValue.
  785. //
  786. ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  787. RowEntry->RowCompares += 1;
  788. if (Hval < pE->HashValue) {
  789. //
  790. // Not on the list. Put new entry before this one.
  791. //
  792. BeforeEntry = &pE->ListEntry;
  793. break;
  794. }
  795. if (pE->HashValue == Hval) {
  796. if ((HashTable->GhtCompare)(pKeyValue,
  797. ((PCHAR)pE) + HashTable->KeyOffset,
  798. HashTable->KeyLength)) {
  799. //
  800. // Found it. Release the lock and return failure if no
  801. // duplicates are allowed.
  802. //
  803. if (!DuplicatesOk) {
  804. FrsRtlReleaseListLock(&RowEntry->HashRow);
  805. return GHT_STATUS_FAILURE;
  806. }
  807. }
  808. }
  809. );
  810. //
  811. // Put new entry on the list in front of 'BeforeEntry'.
  812. //
  813. InterlockedIncrement(&HashEntry->ReferenceCount);
  814. DPRINT2(5, ":: inc ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
  815. RowEntry->RowInserts += 1;
  816. FrsRtlInsertBeforeEntryListLock( &RowEntry->HashRow,
  817. BeforeEntry,
  818. &HashEntry->ListEntry);
  819. FrsRtlReleaseListLock(&RowEntry->HashRow);
  820. return GHT_STATUS_SUCCESS;
  821. }
  822. GHT_STATUS
  823. GhtDeleteEntryByAddress(
  824. PGENERIC_HASH_TABLE HashTable,
  825. PVOID HashEntryArg,
  826. BOOL WaitIfLocked
  827. )
  828. /*++
  829. Routine Description:
  830. Takes HashEntry address and fetches the hash value to acquire the row lock.
  831. Decrement the reference count and if it is one (the count for being in the
  832. table) remove the entry from the row and call the memory free function to
  833. release the entries memory. Drop the row lock. Return GHT_STATUS_SUCCESS if we
  834. deleted the entry.
  835. TBI -
  836. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  837. WaitIfLocked was FALSE.
  838. Note: This function is only safe if you have a reference on the entry otherwise
  839. another thread could have already deleted the entry and your entry address is
  840. pointing at freed memory.
  841. Arguments:
  842. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  843. HashEntryArg -- ptr to entry to delete.
  844. WaitIfLocked -- True means wait if the row is locked.
  845. Return Value:
  846. GHT_STATUS_FAILURE -- Entry was not deleted.
  847. GHT_STATUS_SUCCESS -- Entry was deleted.
  848. --*/
  849. {
  850. #undef DEBSUB
  851. #define DEBSUB "GhtDeleteEntryByAddress:"
  852. ULONG Hval, HvalIndex;
  853. PGENERIC_HASH_ROW_ENTRY RowEntry;
  854. BOOL Found;
  855. ULONG GhtStatus;
  856. LONG NewCount;
  857. PGENERIC_HASH_ENTRY_HEADER HashEntry =
  858. (PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
  859. GhtStatus = GHT_STATUS_FAILURE;
  860. //
  861. // Get the hash value for the element and compute the index and RowEntry
  862. // address. Then get the row lock.
  863. //
  864. Hval = HashEntry->HashValue;
  865. HvalIndex = Hval % HashTable->NumberRows;
  866. RowEntry = HashTable->HashRowBase + HvalIndex;
  867. FrsRtlAcquireListLock(&RowEntry->HashRow);
  868. #if DBG
  869. //
  870. // check if the entry is actually on the List.
  871. //
  872. // Walk the list with the lock looking for a match on the
  873. // the hash value then try and match the KeyValue.
  874. //
  875. Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
  876. if (!Found) {
  877. DPRINT4(0, "GhtDeleteEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
  878. HashEntry, Hval, HvalIndex, HashTable->Name);
  879. FRS_ASSERT(!"entry not on list");
  880. FrsRtlReleaseListLock(&RowEntry->HashRow);
  881. return GHT_STATUS_FAILURE;
  882. }
  883. #endif
  884. //
  885. // Decrement the ref count.
  886. //
  887. NewCount = InterlockedDecrement(&HashEntry->ReferenceCount);
  888. DPRINT2(5, ":: dec ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
  889. if (NewCount <= 0) {
  890. DPRINT4(0, "GhtDeleteEntryByAddress - ref count equal zero: %08x, %08x, %d, %s\n",
  891. HashEntry, Hval, HvalIndex, HashTable->Name);
  892. FRS_ASSERT(!"ref count <= zero");
  893. FrsRtlReleaseListLock(&RowEntry->HashRow);
  894. return GHT_STATUS_FAILURE;
  895. }
  896. if (NewCount == 1) {
  897. //
  898. // Ref count zero. Remove and free the entry.
  899. //
  900. FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &HashEntry->ListEntry);
  901. (HashTable->GhtFree)(HashTable, HashEntry);
  902. GhtStatus = GHT_STATUS_SUCCESS;
  903. }
  904. FrsRtlReleaseListLock(&RowEntry->HashRow);
  905. return GhtStatus;
  906. }
  907. GHT_STATUS
  908. GhtRemoveEntryByAddress(
  909. PGENERIC_HASH_TABLE HashTable,
  910. PVOID HashEntryArg,
  911. BOOL WaitIfLocked
  912. )
  913. /*++
  914. Routine Description:
  915. Takes HashEntry address and fetches the hash value to acquire the row lock.
  916. Remove the entry from the table. The reference count is decremented.
  917. Return GHT_STATUS_SUCCESS.
  918. TBI -
  919. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  920. WaitIfLocked was FALSE.
  921. Note: This function is only safe if you have a reference on the entry otherwise
  922. another thread could have already deleted the entry and your entry address is
  923. pointing at freed memory.
  924. Also Note: The caller must have a lock that prevents other threads from
  925. changing the entry. In addition removing an entry from one hash table and
  926. inserting it on another will confuse another thread that may be accessing the
  927. entry so the caller better be sure that no other thread assumes the hash
  928. table can't change while it has a reference to the entry.
  929. Arguments:
  930. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  931. HashEntryArg -- ptr to entry to delete.
  932. WaitIfLocked -- True means wait if the row is locked.
  933. Return Value:
  934. GHT_STATUS_SUCCESS -- if the entry was removed successfully.
  935. GHT_STATUS_FAILURE -- if the entry was not on the list.
  936. --*/
  937. {
  938. #undef DEBSUB
  939. #define DEBSUB "GhtRemoveEntryByAddress:"
  940. ULONG Hval, HvalIndex;
  941. PGENERIC_HASH_ROW_ENTRY RowEntry;
  942. BOOL Found;
  943. LONG NewCount;
  944. PGENERIC_HASH_ENTRY_HEADER HashEntry =
  945. (PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
  946. //
  947. // Get the hash value for the element and compute the index and RowEntry
  948. // address. Then get the row lock.
  949. //
  950. Hval = HashEntry->HashValue;
  951. HvalIndex = Hval % HashTable->NumberRows;
  952. RowEntry = HashTable->HashRowBase + HvalIndex;
  953. FrsRtlAcquireListLock(&RowEntry->HashRow);
  954. #if DBG
  955. //
  956. // check if the entry is actually on the List.
  957. //
  958. // Walk the list with the lock looking for a match on the
  959. // the hash value then try and match the KeyValue.
  960. //
  961. Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
  962. if (!Found) {
  963. DPRINT4(0, "GhtRemoveEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
  964. HashEntry, Hval, HvalIndex, HashTable->Name);
  965. FRS_ASSERT(!"entry not on list-2");
  966. FrsRtlReleaseListLock(&RowEntry->HashRow);
  967. return GHT_STATUS_FAILURE;
  968. }
  969. #endif
  970. //
  971. // Decrement the ref count.
  972. //
  973. NewCount = InterlockedDecrement(&HashEntry->ReferenceCount);
  974. DPRINT2(5, ":: dec ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
  975. if (NewCount < 0) {
  976. DPRINT4(0, ":: ERROR- GhtRemoveEntryByAddress - ref count less than zero: %08x, %08x, %d, %s\n",
  977. HashEntry, Hval, HvalIndex, HashTable->Name);
  978. FRS_ASSERT(!"ref count less than zero-2");
  979. FrsRtlReleaseListLock(&RowEntry->HashRow);
  980. return GHT_STATUS_FAILURE;
  981. }
  982. if (NewCount > 1) {
  983. //
  984. // Other Refs than the caller's exist. Print a warning.
  985. //
  986. DPRINT5(1, ":: WARNING- GhtRemoveEntryByAddress - ref count(%d) > 1: %08x, %08x, %d, %s\n",
  987. NewCount, HashEntry, Hval, HvalIndex, HashTable->Name);
  988. }
  989. FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &HashEntry->ListEntry);
  990. FrsRtlReleaseListLock(&RowEntry->HashRow);
  991. return GHT_STATUS_SUCCESS;
  992. }
  993. GHT_STATUS
  994. GhtReferenceEntryByAddress(
  995. PGENERIC_HASH_TABLE HashTable,
  996. PVOID HashEntryArg,
  997. BOOL WaitIfLocked
  998. )
  999. /*++
  1000. Routine Description:
  1001. Takes HashEntry address and fetches the hash value to acquire the row lock.
  1002. Increment the reference count. Drop the row lock.
  1003. TBI -
  1004. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  1005. WaitIfLocked was FALSE.
  1006. Note: This function is only safe if you have a reference on the entry otherwise
  1007. another thread could have already deleted the entry and your entry address is
  1008. pointing at freed memory. A Lookup which gave you the address bumps the
  1009. reference count. An insert in which you kept the address does NOT bump
  1010. the reference count.
  1011. Arguments:
  1012. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  1013. HashEntryArg -- ptr to entry to reference.
  1014. WaitIfLocked -- True means wait if the row is locked.
  1015. Return Value:
  1016. GHT_STATUS_SUCCESS
  1017. --*/
  1018. {
  1019. #undef DEBSUB
  1020. #define DEBSUB "GhtReferenceEntryByAddress:"
  1021. ULONG Hval, HvalIndex;
  1022. PGENERIC_HASH_ROW_ENTRY RowEntry;
  1023. BOOL Found;
  1024. PGENERIC_HASH_ENTRY_HEADER HashEntry =
  1025. (PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
  1026. //
  1027. // Get the hash value for the element and compute the index and RowEntry
  1028. // address. Then get the row lock.
  1029. //
  1030. Hval = HashEntry->HashValue;
  1031. HvalIndex = Hval % HashTable->NumberRows;
  1032. RowEntry = HashTable->HashRowBase + HvalIndex;
  1033. FrsRtlAcquireListLock(&RowEntry->HashRow);
  1034. #if DBG
  1035. //
  1036. // check if the entry is actually on the List.
  1037. //
  1038. // Walk the list with the lock looking for a match on the
  1039. // the hash value then try and match the KeyValue.
  1040. //
  1041. Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
  1042. if (!Found) {
  1043. DPRINT4(0, "GhtReferenceEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
  1044. HashEntry, Hval, HvalIndex, HashTable->Name);
  1045. FRS_ASSERT(!"entry not on list-3");
  1046. FrsRtlReleaseListLock(&RowEntry->HashRow);
  1047. return GHT_STATUS_FAILURE;
  1048. }
  1049. #endif
  1050. //
  1051. // Increment the ref count.
  1052. //
  1053. InterlockedIncrement(&HashEntry->ReferenceCount);
  1054. DPRINT2(5, ":: inc ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
  1055. FrsRtlReleaseListLock(&RowEntry->HashRow);
  1056. return GHT_STATUS_SUCCESS;
  1057. }
  1058. GHT_STATUS
  1059. GhtDereferenceEntryByAddress(
  1060. PGENERIC_HASH_TABLE HashTable,
  1061. PVOID HashEntryArg,
  1062. BOOL WaitIfLocked
  1063. )
  1064. /*++
  1065. Routine Description:
  1066. Takes HashEntry address and fetches the hash value to acquire the row lock.
  1067. Decrement the reference count. Drop the row lock.
  1068. TBI -
  1069. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  1070. WaitIfLocked was FALSE.
  1071. Note: This function is only safe if you have a reference on the entry otherwise
  1072. another thread could have already deleted the entry and your entry address is
  1073. pointing at freed memory. A Lookup which gave you the address bumps the
  1074. reference count. An insert in which you kept the address does NOT bump
  1075. the reference count.
  1076. Arguments:
  1077. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  1078. HashEntryArg -- ptr to entry to reference.
  1079. WaitIfLocked -- True means wait if the row is locked.
  1080. Return Value:
  1081. GHT_STATUS_SUCCESS
  1082. --*/
  1083. {
  1084. #undef DEBSUB
  1085. #define DEBSUB "GhtDereferenceEntryByAddress:"
  1086. ULONG Hval, HvalIndex;
  1087. PGENERIC_HASH_ROW_ENTRY RowEntry;
  1088. BOOL Found;
  1089. LONG NewCount;
  1090. PGENERIC_HASH_ENTRY_HEADER HashEntry =
  1091. (PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
  1092. //
  1093. // Get the hash value for the element and compute the index and RowEntry
  1094. // address. Then get the row lock.
  1095. //
  1096. Hval = HashEntry->HashValue;
  1097. HvalIndex = Hval % HashTable->NumberRows;
  1098. RowEntry = HashTable->HashRowBase + HvalIndex;
  1099. FrsRtlAcquireListLock(&RowEntry->HashRow);
  1100. #if DBG
  1101. //
  1102. // check if the entry is actually on the List.
  1103. //
  1104. // Walk the list with the lock looking for a match on the
  1105. // the hash value then try and match the KeyValue.
  1106. //
  1107. Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
  1108. if (!Found) {
  1109. DPRINT4(0, "GhtDereferenceEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
  1110. HashEntry, Hval, HvalIndex, HashTable->Name);
  1111. FRS_ASSERT(!"entry not on list-4");
  1112. FrsRtlReleaseListLock(&RowEntry->HashRow);
  1113. return GHT_STATUS_FAILURE;
  1114. }
  1115. #endif
  1116. //
  1117. // Decrement the ref count.
  1118. //
  1119. NewCount = InterlockedDecrement(&HashEntry->ReferenceCount);
  1120. DPRINT2(5, ":: dec ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
  1121. if (NewCount <= 0) {
  1122. DPRINT4(0, "GhtDereferenceEntryByAddress - ref count now zero: %08x, %08x, %d, %s\n",
  1123. HashEntry, Hval, HvalIndex, HashTable->Name);
  1124. FRS_ASSERT(!"ref count now zero-4");
  1125. FrsRtlReleaseListLock(&RowEntry->HashRow);
  1126. return GHT_STATUS_FAILURE;
  1127. }
  1128. FrsRtlReleaseListLock(&RowEntry->HashRow);
  1129. return GHT_STATUS_SUCCESS;
  1130. }
  1131. GHT_STATUS
  1132. GhtAdjustRefCountByKey(
  1133. PGENERIC_HASH_TABLE HashTable,
  1134. PVOID pKeyValue,
  1135. LONG Delta,
  1136. ULONG ActionIfZero,
  1137. BOOL WaitIfLocked,
  1138. PVOID *RetHashEntry
  1139. )
  1140. /*++
  1141. Routine Description:
  1142. Takes keyvalue, finds the HashEntry address and adds Delta to
  1143. the reference count. Drop the row lock.
  1144. ** WARNING **
  1145. If you allow duplicate entries in the hash table this routine will not work
  1146. because you can't guarantee that you will adjust a given entry.
  1147. TBI -
  1148. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  1149. WaitIfLocked was FALSE.
  1150. Arguments:
  1151. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  1152. pKeyValue -- ptr to a datavalue for the key.
  1153. Delta -- The amount of the ref count adjustment.
  1154. ActionIfZero -- If RC is zero Choice of nothing, remove, remove and delete.
  1155. WaitIfLocked -- True means wait if the row is locked.
  1156. RetHashEntry -- If GHT_ACTION_REMOVE requested, the hash entry
  1157. address is returned if element removed else NULL returned.
  1158. Return Value:
  1159. GHT_STATUS_SUCCESS
  1160. GHT_STATUS_NOT_FOUND
  1161. --*/
  1162. {
  1163. #undef DEBSUB
  1164. #define DEBSUB "GhtDecrementRefCountByKey:"
  1165. ULONG Hval, HvalIndex;
  1166. PGENERIC_HASH_ROW_ENTRY RowEntry;
  1167. LONG NewCount;
  1168. if (ActionIfZero == GHT_ACTION_REMOVE) {
  1169. *RetHashEntry = NULL;
  1170. }
  1171. // Note: Get lock earlier if table resize support is added.
  1172. //
  1173. // Compute the hash index and calculate the row pointer.
  1174. //
  1175. Hval = (HashTable->GhtHashCalc)(pKeyValue, HashTable->KeyLength);
  1176. HvalIndex = Hval % HashTable->NumberRows;
  1177. RowEntry = HashTable->HashRowBase + HvalIndex;
  1178. if (FrsRtlCountList(&RowEntry->HashRow) == 0) {
  1179. RowEntry->RowLookupFails += 1;
  1180. return GHT_STATUS_NOT_FOUND;
  1181. }
  1182. //
  1183. // Walk the list with the lock looking for a match on the
  1184. // the hash value then try and match the KeyValue.
  1185. //
  1186. ForEachListEntry(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
  1187. //
  1188. // pE is iterator of type GENERIC_HASH_ENTRY_HEADER.
  1189. //
  1190. RowEntry->RowCompares += 1;
  1191. if (pE->HashValue == Hval) {
  1192. if ((HashTable->GhtCompare)(pKeyValue,
  1193. ((PCHAR)pE) + HashTable->KeyOffset,
  1194. HashTable->KeyLength)) {
  1195. //
  1196. // Found it. Adjust ref count,
  1197. //
  1198. NewCount = InterlockedExchangeAdd(&pE->ReferenceCount, Delta);
  1199. DPRINT2(5, ":: adj ref: %08x, %d\n", pE, pE->ReferenceCount);
  1200. RowEntry->RowLookups += 1;
  1201. if (NewCount <= 0) {
  1202. if (NewCount < 0) {
  1203. DPRINT4(0, "GhtDecrementRefCountByKey - ref count neg: %08x, %08x, %d, %s\n",
  1204. pE, Hval, HvalIndex, HashTable->Name);
  1205. FRS_ASSERT(!"ref count neg-5");
  1206. ReleaseListLock(&RowEntry->HashRow);
  1207. return GHT_STATUS_FAILURE;
  1208. }
  1209. //
  1210. // Ref count zero. Optionally remove and free the entry.
  1211. //
  1212. if (ActionIfZero == GHT_ACTION_REMOVE) {
  1213. *RetHashEntry = pE;
  1214. FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &pE->ListEntry);
  1215. } else
  1216. if (ActionIfZero == GHT_ACTION_DELETE) {
  1217. (HashTable->GhtFree)(HashTable, pE);
  1218. } else {
  1219. //
  1220. // Not good. Action was noop so refcount expected to
  1221. // be > 0.
  1222. //
  1223. DPRINT4(0, "GhtDecrementRefCountByKey - ref count zero with Noop Action: %08x, %08x, %d, %s\n",
  1224. pE, Hval, HvalIndex, HashTable->Name);
  1225. FRS_ASSERT(!"ref count zero-6");
  1226. ReleaseListLock(&RowEntry->HashRow);
  1227. return GHT_STATUS_FAILURE;
  1228. }
  1229. }
  1230. ReleaseListLock(&RowEntry->HashRow);
  1231. return GHT_STATUS_SUCCESS;
  1232. }
  1233. }
  1234. );
  1235. RowEntry->RowLookupFails += 1;
  1236. return GHT_STATUS_NOT_FOUND;
  1237. }
  1238. GHT_STATUS
  1239. GhtSwapEntryByAddress(
  1240. PGENERIC_HASH_TABLE HashTable,
  1241. PVOID OldHashEntryArg,
  1242. PVOID NewHashEntryArg,
  1243. BOOL WaitIfLocked
  1244. )
  1245. /*++
  1246. Routine Description:
  1247. This routine replaces an existing old hash entry with a new entry.
  1248. It verifies tha the old hash entry is still in the table.
  1249. It assumes that the key value of the new entry is the same as the old entry.
  1250. NO CHECK IS MADE.
  1251. The expected use is when the caller needs to reallocate an entry with
  1252. more storage.
  1253. NOTE ALSO: The reference count is copied from the old entry to the new one.
  1254. Using this routine means that the caller is using GhtDecrementRefCountByKey()
  1255. and GhtIncrementRefCountByKey() to access the ref counts on any element in the
  1256. table since the entry could get swapped making the pointer invalid.
  1257. TBI -
  1258. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  1259. WaitIfLocked was FALSE.
  1260. Note: This function is only safe if you have a reference on the entry otherwise
  1261. another thread could have already deleted the entry and your entry address is
  1262. pointing at freed memory. A Lookup which gave you the address bumps the
  1263. reference count. An insert in which you kept the address does NOT bump
  1264. the reference count.
  1265. Arguments:
  1266. HashTable -- ptr to a GENERIC_HASH_TABLE struct.
  1267. OldHashEntry -- ptr to entry to swap out of table.
  1268. NewHashEntry -- ptr to entry to swap in to table.
  1269. WaitIfLocked -- True means wait if the row is locked.
  1270. Return Value:
  1271. GHT_STATUS_SUCCESS if swap ok.
  1272. GHT_STATUS_NOT_FOUND if old entry not in table.
  1273. --*/
  1274. // Note: TBD if necc, implement GhtIncrementRefCountByKey.
  1275. {
  1276. #undef DEBSUB
  1277. #define DEBSUB "GhtSwapEntryByAddress:"
  1278. ULONG Hval, HvalIndex;
  1279. PGENERIC_HASH_ROW_ENTRY RowEntry;
  1280. PLIST_ENTRY BeforeEntry;
  1281. BOOL Found;
  1282. PGENERIC_HASH_ENTRY_HEADER Entry;
  1283. PGENERIC_HASH_ENTRY_HEADER NewHashEntry =
  1284. (PGENERIC_HASH_ENTRY_HEADER)NewHashEntryArg;
  1285. PGENERIC_HASH_ENTRY_HEADER OldHashEntry =
  1286. (PGENERIC_HASH_ENTRY_HEADER)OldHashEntryArg;
  1287. //
  1288. // Get the hash value for the element and compute the index and RowEntry
  1289. // address. Then get the row lock.
  1290. //
  1291. Hval = OldHashEntry->HashValue;
  1292. HvalIndex = Hval % HashTable->NumberRows;
  1293. RowEntry = HashTable->HashRowBase + HvalIndex;
  1294. FrsRtlAcquireListLock(&RowEntry->HashRow);
  1295. //
  1296. // check if the entry is actually on the List.
  1297. //
  1298. // Walk the list with the lock looking for a match on the
  1299. // the hash value then try and match the KeyValue.
  1300. //
  1301. Entry = GhtQuickCheck(HashTable, RowEntry, OldHashEntry, Hval);
  1302. if (Entry != OldHashEntry) {
  1303. DPRINT4(0, "GhtSwapEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
  1304. OldHashEntry, Hval, HvalIndex, HashTable->Name);
  1305. FrsRtlReleaseListLock(&RowEntry->HashRow);
  1306. return GHT_STATUS_NOT_FOUND;
  1307. }
  1308. //
  1309. // Copy the ref count and hash value from the old entry to the new one.
  1310. //
  1311. NewHashEntry->ReferenceCount = OldHashEntry->ReferenceCount;
  1312. NewHashEntry->HashValue = OldHashEntry->HashValue;
  1313. //
  1314. // Pull the old entry out and replace with the new entry.
  1315. // List counts do not change so do list juggling here.
  1316. //
  1317. BeforeEntry = OldHashEntry->ListEntry.Flink;
  1318. FrsRemoveEntryList(&OldHashEntry->ListEntry);
  1319. InsertTailList(BeforeEntry, &NewHashEntry->ListEntry);
  1320. FrsRtlReleaseListLock(&RowEntry->HashRow);
  1321. return GHT_STATUS_SUCCESS;
  1322. }