windows-nt/Source/XPSP1/NT/ds/netapi/svcimgs/ntrepl/util/genhash.c
2020-09-26 16:20:57 +08:00

1765 lines
48 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
genhash.c
Abstract:
Generic Hash Table routines. Each hash table is an array of FRS_LIST entries
that provide interlocked access to each row of the hash table. Each table
is managed by a GENERIC_HASH_TABLE struct which holds function entry points for
freeing an entry, comparing keys, performing a hash calculation, printing
an entry and dumping the table.
Note: All hash entries must be prefixed with GENERIC_HASH_ROW_ENTRY at the
beginning of the structure.
Author:
David Orbits [davidor] 22-Apr-1997
Environment:
User Mode Service
Revision History:
--*/
#include <ntreppch.h>
#pragma hdrstop
#include <frs.h>
#include <genhash.h>
#include <tablefcn.h>
#pragma warning( disable:4102) // unreferenced label
PGENERIC_HASH_TABLE
GhtCreateTable(
PCHAR ArgName,
ULONG NumberRows,
ULONG KeyOffset,
ULONG KeyLength,
PGENERIC_HASH_FREE_ROUTINE GhtFree,
PGENERIC_HASH_COMPARE_ROUTINE GhtCompare,
PGENERIC_HASH_CALC_ROUTINE GhtHashCalc,
PGENERIC_HASH_PRINT_ROUTINE GhtPrint
)
/*++
Routine Description:
Allocate and initialize a hash table.
Arguments:
ArgName -- The table name. [16 byte max]
NumberRows -- The number of rows in the table.
KeyOffset -- The byte offset to the key value in each table entry.
KeyLength -- The byte length of the key value in each table entry.
GhtFree -- function to call to free an entry.
GhtCompare -- function to comoare two keys.
GhtHashCalc -- function to calculate the ULONG hash value on a key.
GhtPrint -- Function to print out a table entry.
GhtDump -- Function to call to dump all table entries.
Return Value:
ptr to a GENERIC_HASH_TABLE struct or NULL if failure.
use GetLastError for the error status.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtCreateTable:"
PGENERIC_HASH_TABLE HashTable;
PGENERIC_HASH_ROW_ENTRY RowBase, HashRowEntry;
ULONG NameLen;
ULONG WStatus;
ULONG i;
HashTable = (PGENERIC_HASH_TABLE) FrsAllocType(GENERIC_HASH_TABLE_TYPE);
RowBase = (PGENERIC_HASH_ROW_ENTRY) FrsAlloc(
NumberRows * sizeof(GENERIC_HASH_ROW_ENTRY));
NameLen = min(strlen(ArgName), 15);
CopyMemory(HashTable->Name, ArgName, NameLen);
HashTable->Name[NameLen] = '\0';
HashTable->NumberRows = NumberRows;
HashTable->GhtFree = GhtFree;
HashTable->GhtCompare = GhtCompare;
HashTable->GhtHashCalc = GhtHashCalc;
HashTable->GhtPrint = GhtPrint;
HashTable->KeyOffset = KeyOffset;
HashTable->KeyLength = KeyLength;
HashTable->RowLockEnabled = TRUE;
HashTable->RefCountEnabled = TRUE;
HashTable->HeapHandle = NULL;
HashTable->UseOffsets = FALSE;
HashTable->OffsetBase = 0;
HashTable->HashRowBase = RowBase;
HashTable->LockTimeout = 10000; // milliseconds
//
// Initialize all the hash table rows. Each has a critical section and
// an event to wait on.
//
HashRowEntry = RowBase;
for (i=0; i<NumberRows; i++) {
//
// Create the event first so if we fail GhtDestroyTable sees a null handle.
//
//HashRowEntry->Event = CreateEvent(NULL, TRUE, FALSE, NULL);
WStatus = FrsRtlInitializeList(&HashRowEntry->HashRow);
if (WStatus != ERROR_SUCCESS) {
goto CLEANUP;
}
//if (HashRowEntry->Event == NULL) {
// WStatus = GetLastError();
// goto CLEANUP;
//}
HashRowEntry += 1;
}
return HashTable;
CLEANUP:
HashTable->NumberRows = i+1;
GhtDestroyTable(HashTable);
SetLastError(WStatus);
return NULL;
}
VOID
GhtDestroyTable(
PGENERIC_HASH_TABLE HashTable
)
/*++
Routine Description:
Free all the memory for a hash table.
This includes any data elements left in the table.
No locks are acquired.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtDestroyTable:"
PGENERIC_HASH_ROW_ENTRY RowEntry;
ULONG i;
if (HashTable == NULL) {
return;
}
RowEntry = HashTable->HashRowBase;
DPRINT1(5, "GhtDestroyTable for %s\n", HashTable->Name);
//
// Loop through all the Hash table rows and delete any entries still on
// each row.
//
for (i=0; i<HashTable->NumberRows; i++, RowEntry++) {
if (RowEntry->HashRow.Count != 0) {
//DPRINT2(5, "HashRow: %d, RowCount %d\n",i, RowEntry->HashRow.Count);
}
ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &pE->ListEntry);
//DPRINT4(5, " Deleteing entry: %08x, Hval %08x, Index %d, refcnt %d\n",
// pE, pE->HashValue, i, pE->ReferenceCount);
(HashTable->GhtFree)(HashTable, pE);
);
FrsRtlDeleteList(&RowEntry->HashRow);
//if (RowEntry->Event != NULL) {
// FRS_CLOSE(RowEntry->Event);
//}
}
FrsFree(HashTable->HashRowBase);
FrsFreeType(HashTable);
return;
}
ULONG
GhtCleanTableByFilter(
PGENERIC_HASH_TABLE HashTable,
IN PGENERIC_HASH_ENUM_ROUTINE Function,
PVOID Context
)
/*++
Routine Description:
Free the elements in the hash table for which the predicate function
returns TRUE.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
Function - The function to call for each record in the table. It is of
of type PGENERIC_HASH_FILTER_ROUTINE.
Return TRUE to delete the entry in the table.
Context -- Arg to pass thru to the filter function.
Return Value:
The number of entries deleted.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtCleanTableByFilter:"
PGENERIC_HASH_ROW_ENTRY RowEntry;
ULONG i;
ULONG Count = 0;
if (HashTable == NULL) {
return Count;
}
RowEntry = HashTable->HashRowBase;
//
// Loop through all the Hash table rows and delete any entries still on
// each row.
//
for (i=0; i<HashTable->NumberRows; i++, RowEntry++) {
if (RowEntry->HashRow.Count != 0) {
//DPRINT2(4, "HashRow: %d, RowCount %d\n", i, RowEntry->HashRow.Count);
}
ForEachListEntry(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
//
// The iterator pE is of type GENERIC_HASH_ENTRY_HEADER.
// Call predicate to see if we should do the delete.
//
if ((Function)(HashTable, pE, Context)) {
FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &pE->ListEntry);
//DPRINT4(4, "Deleting entry: %08x, Hval %08x, Index %d, refcnt %d\n",
// pE, pE->HashValue, i, pE->ReferenceCount);
(HashTable->GhtFree)(HashTable, pE);
Count += 1;
}
);
}
return Count;
}
#if DBG
VOID
GhtDumpTable(
ULONG Sev,
PGENERIC_HASH_TABLE HashTable
)
/*++
Routine Description:
Call the print routine for each element in the table.
Arguments:
Sev -- DPRINT severity level.
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtDumpTable:"
PGENERIC_HASH_ROW_ENTRY HashRowEntry;
ULONG i;
HashRowEntry = HashTable->HashRowBase;
DPRINT(Sev,"----------------------------------------------\n");
DPRINT(Sev,"----------------------------------------------\n");
DPRINT1(Sev, "GhtDumpTable for %s\n", HashTable->Name);
DPRINT(Sev,"----------------------------------------------\n");
DPRINT(Sev,"----------------------------------------------\n");
//
// Loop through all the Hash table rows and call the print function for
// each element.
//
for (i=0; i<HashTable->NumberRows; i++) {
if (HashRowEntry->HashRow.Count != 0) {
DPRINT(Sev, " \n");
DPRINT(Sev,"----------------------------------------------\n");
DPRINT2(Sev, "HashRow: %d, RowCount %d\n",
i, HashRowEntry->HashRow.Count);
DPRINT5(Sev, "Inserts: %d, Removes: %d, Compares: %d, Lookups: %d, Lookupfails: %d \n",
HashRowEntry->RowInserts,
HashRowEntry->RowRemoves,
HashRowEntry->RowCompares,
HashRowEntry->RowLookups,
HashRowEntry->RowLookupFails);
}
ForEachListEntry(&HashRowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
(HashTable->GhtPrint)(HashTable, pE);
);
HashRowEntry += 1;
}
}
#endif DBG
ULONG_PTR
GhtEnumerateTable(
IN PGENERIC_HASH_TABLE HashTable,
IN PGENERIC_HASH_ENUM_ROUTINE Function,
IN PVOID Context
)
/*++
Routine Description:
This routine walks through the entries in a generic hash table and
calls the function provided with the entry address and the context.
No locks are taken by this function so the called function can make
calls to other GHT routines to lookup or insert new entries.
THis routine increments the ref count on each entry before it makes the
call to ensure the entry does not vanish. It keeps a pointer to the
entry that tells it where to continue the scan. If the argument function
inserts an entry that is earlier in the table the enumeration will not
pick it up.
Arguments:
HashTable - The context of the Hash Table to enumerate.
Function - The function to call for each record in the table. It is of
of type PGENERIC_HASH_ENUM_ROUTINE. Return FALSE to abort the
enumeration else true.
Context - A context ptr to pass through to the RecordFunction.
Return Value:
The status code from the argument function.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtEnumerateTable:"
PGENERIC_HASH_ROW_ENTRY RowEntry;
ULONG i;
ULONG_PTR WStatus;
RowEntry = HashTable->HashRowBase;
DPRINT(5,"----------------------------------------------\n");
DPRINT(5,"----------------------------------------------\n");
DPRINT1(5, "GhtEnumerateTable for %s\n", HashTable->Name);
DPRINT(5,"----------------------------------------------\n");
DPRINT(5,"----------------------------------------------\n");
//
// Loop through all the Hash table rows and call the print function for
// each element.
//
for (i=0; i<HashTable->NumberRows; i++) {
if (RowEntry->HashRow.Count != 0) {
DPRINT(5, " \n");
DPRINT(5,"----------------------------------------------\n");
DPRINT2(5, "HashRow: %d, RowCount %d\n",
i, RowEntry->HashRow.Count);
}
ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
InterlockedIncrement(&pE->ReferenceCount);
DPRINT2(5, "inc ref: %08x, %d\n", pE, pE->ReferenceCount);
WStatus = (Function)(HashTable, pE, Context);
InterlockedDecrement(&pE->ReferenceCount);
DPRINT2(5, "dec ref: %08x, %d\n", pE, pE->ReferenceCount);
// Note: If caller needs this we should add code to check for
// zero ref count and call the delete function.
if( WStatus != 0 ) {
return WStatus;
}
);
RowEntry += 1;
}
return ERROR_SUCCESS;
}
ULONG_PTR
GhtEnumerateTableNoRef(
IN PGENERIC_HASH_TABLE HashTable,
IN PGENERIC_HASH_ENUM_ROUTINE Function,
IN PVOID Context
)
/*++
Routine Description:
This routine walks through the entries in a generic hash table and
calls the function provided with the entry address and the context.
No locks are taken by this function so the called function can make
calls to other GHT routines to lookup or insert new entries.
THis routine does not take a ref count out on the entry.
It keeps a pointer to the next entry that tells it where to continue
the scan if the argument function deletes the entry. If the argument function
inserts an entry that is earlier in the table the enumeration will not
pick it up.
Arguments:
HashTable - The context of the Hash Table to enumerate.
Function - The function to call for each record in the table. It is of
of type PGENERIC_HASH_ENUM_ROUTINE. Return FALSE to abort the
enumeration else true.
Context - A context ptr to pass through to the RecordFunction.
Return Value:
The status code from the argument function.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtEnumerateTableNoRef:"
PGENERIC_HASH_ROW_ENTRY RowEntry;
ULONG i;
ULONG_PTR WStatus;
RowEntry = HashTable->HashRowBase;
DPRINT1(5, "GhtEnumerateTableNoRef for %s\n", HashTable->Name);
//
// Loop through all the Hash table rows and call the print function for
// each element.
//
for (i=0; i<HashTable->NumberRows; i++) {
if (RowEntry->HashRow.Count != 0) {
DPRINT(5, " \n");
DPRINT(5,"----------------------------------------------\n");
DPRINT2(5, "HashRow: %d, RowCount %d\n",
i, RowEntry->HashRow.Count);
}
ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
WStatus = (Function)(HashTable, pE, Context);
if (WStatus != 0) {
return WStatus;
}
);
RowEntry += 1;
}
return (ULONG_PTR)0;
}
PGENERIC_HASH_ENTRY_HEADER
GhtGetNextEntry(
IN PGENERIC_HASH_TABLE HashTable,
PGENERIC_HASH_ENTRY_HEADER HashEntry
)
/*++
Routine Description:
This routine returns the next entry in the table that follows the HashEntry
argument. If the HashEntry is NULL it returns the first entry.
It gets the row lock containing the current entry, decrements the
ref count on the entry. It scans forward to the next entry in the table
getting its row lock if needed, increments its ref count and returns the
pointer. If the end of table is reached NULL is returned.
If an entry is inserted earlier in the table the enumeration will not
pick it up.
Arguments:
HashTable - The context of the Hash Table to enumerate.
HashEntry - The current entry we are looking at. Used to get the next entry
If null start scan at beginning of table.
Return Value:
The status code from the argument function.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtGetNextEntry:"
PGENERIC_HASH_ROW_ENTRY LastRow;
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
PLIST_ENTRY Entry;
RowEntry = HashTable->HashRowBase;
//
// Get the hash value for the element and compute the index and RowEntry
// address. Then get the row lock.
//
if (HashEntry != NULL) {
Hval = HashEntry->HashValue;
HvalIndex = Hval % HashTable->NumberRows;
RowEntry += HvalIndex;
//
// Get the row lock and decrement the ref count.
// (could delete if it hits zero)
//
FrsRtlAcquireListLock(&RowEntry->HashRow);
InterlockedDecrement(&HashEntry->ReferenceCount);
//
// look for next entry in same row.
// if found, bump ref count, drop lock, return entry.
//
Entry = GetListNext(&HashEntry->ListEntry);
if (Entry != &RowEntry->HashRow.ListHead) {
goto FOUND;
}
//
// if not found drop row lock and execute scan code below
// starting from next row entry.
//
FrsRtlReleaseListLock(&RowEntry->HashRow);
RowEntry += 1;
}
//
// Scan the rest of the table for a non-empty row.
//
LastRow = HashTable->HashRowBase + HashTable->NumberRows;
while (RowEntry < LastRow) {
if (RowEntry->HashRow.Count != 0) {
//
// Found one. Get the row lock and recheck the count incase
// someone beat us too it.
//
FrsRtlAcquireListLock(&RowEntry->HashRow);
if (RowEntry->HashRow.Count == 0) {
//
// Too bad. Continue scan.
//
FrsRtlReleaseListLock(&RowEntry->HashRow);
RowEntry += 1;
continue;
}
//
// We got one. Get the entry address, bump the ref count, drop lock.
//
FRS_ASSERT(!IsListEmpty(&RowEntry->HashRow.ListHead));
Entry = GetListHead(&RowEntry->HashRow.ListHead);
goto FOUND;
}
RowEntry += 1;
}
return NULL;
FOUND:
HashEntry = CONTAINING_RECORD(Entry, GENERIC_HASH_ENTRY_HEADER, ListEntry);
InterlockedIncrement(&HashEntry->ReferenceCount);
FrsRtlReleaseListLock(&RowEntry->HashRow);
return HashEntry;
}
ULONG
GhtCountEntries(
IN PGENERIC_HASH_TABLE HashTable
)
/*++
Routine Description:
This routine walks through the rows in a generic hash table and
adds up the entry count. It takes no locks so the count is approx.
Caller must know the table can't go away.
Arguments:
HashTable - The context of the Hash Table to count.
Return Value:
The count.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtCountEntries:"
ULONG Total = 0;
PGENERIC_HASH_ROW_ENTRY LastRow, RowEntry = HashTable->HashRowBase;
//
// Loop through all the Hash table rows and add counts.
//
LastRow = RowEntry + HashTable->NumberRows;
while (RowEntry < LastRow) {
Total += RowEntry->HashRow.Count;
RowEntry += 1;
}
return Total;
}
PGENERIC_HASH_ENTRY_HEADER
GhtGetEntryNumber(
IN PGENERIC_HASH_TABLE HashTable,
IN LONG EntryNumber
)
/*++
Routine Description:
This routine walks through the rows in a generic hash table
counting entries as it goes. It returns the requested entry (by number)
from the table. Note this will not be the same entry from call to call
because of intervening inserts and deletes. It takes no locks until it
gets to the row of the table that contains the entry.
The ref count on the entry is incremented.
Arguments:
HashTable - The context of the Hash Table to enumerate.
EntryNumber - The ordinal number of the entry in the table.
zero is the first entry.
Return Value:
The address of the entry.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtGetEntryNumber:"
PGENERIC_HASH_ROW_ENTRY LastRow, RowEntry = HashTable->HashRowBase;
ULONG Rcount;
PLIST_ENTRY Entry;
PGENERIC_HASH_ENTRY_HEADER HashEntry;
FRS_ASSERT(EntryNumber >= 0);
//
// Loop through Hash table rows looking for the row with the entry.
//
LastRow = RowEntry + HashTable->NumberRows;
while (RowEntry < LastRow) {
Rcount = RowEntry->HashRow.Count;
if (Rcount > 0) {
EntryNumber -= Rcount;
if (EntryNumber < 0) {
//
// Should be in this row. Get the row lock and recheck
// the count incase someone beat us too it.
//
FrsRtlAcquireListLock(&RowEntry->HashRow);
if (RowEntry->HashRow.Count < Rcount) {
//
// Too bad. It got shorter, Retry test.
//
FrsRtlReleaseListLock(&RowEntry->HashRow);
EntryNumber += Rcount;
continue;
}
//
// We got one. Get the entry address, bump the ref count, drop lock.
//
EntryNumber += Rcount;
Entry = GetListHead(&RowEntry->HashRow.ListHead);
while (EntryNumber-- > 0) {
FRS_ASSERT(Entry != &RowEntry->HashRow.ListHead);
Entry = GetListNext(Entry);
}
HashEntry = CONTAINING_RECORD(Entry, GENERIC_HASH_ENTRY_HEADER, ListEntry);
InterlockedIncrement(&HashEntry->ReferenceCount);
FrsRtlReleaseListLock(&RowEntry->HashRow);
return HashEntry;
}
}
RowEntry += 1;
}
return NULL;
}
PGENERIC_HASH_ENTRY_HEADER
GhtQuickCheck(
PGENERIC_HASH_TABLE HashTable,
PGENERIC_HASH_ROW_ENTRY RowEntry,
PGENERIC_HASH_ENTRY_HEADER HashEntry,
ULONG Hval
)
/*++
Routine Description:
Internal function to do a quick scan of a row to find an entry.
Used in debug code to check that an entry is actually in the table.
Assumes caller has the row lock.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
RowEntry -- ptr to the ROW_ENTRY struct.
HashEntry -- ptr to the hash entry we are looking for.
Hval -- hash value we are looking for.
Return Value:
ptr to entry if we find it.
NULL if we don't.
--*/
{
PCHAR pKeyValue;
pKeyValue = ((PCHAR)HashEntry) + HashTable->KeyOffset;
ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
//
// The iterator pE is of type PGENERIC_HASH_ENTRY_HEADER.
//
if (pE->HashValue == Hval) {
if ((HashTable->GhtCompare)(pKeyValue,
((PCHAR)pE) + HashTable->KeyOffset,
HashTable->KeyLength)) {
//
// Found it.
//
return pE;
}
}
);
return NULL;
}
GHT_STATUS
GhtLookup2(
PGENERIC_HASH_TABLE HashTable,
PVOID pKeyValue,
BOOL WaitIfLocked,
PVOID *RetHashEntry,
ULONG DupIndex
)
/*++
Routine Description:
Takes the KeyValue and calls the hash function which returns a ULONG. Then
calculate the index of HashValue Mod TableLenth. With the index find the hash
row header and acquire the row lock. It then walks the list looking for a hash
value match on the KeyValue. The entires are kept in ascending order so the
lookup stops as soon as new entry value is < the list entry value. Then call
the compare routine to see if the key data in the entry (entry+key offset)
matches the keyValue passed in. If it matches, the ref count in the entry is
bumped and the address is returned.
If there are duplicate entries then the ptr to the nth oldest duplicate is
returned where n is supplied by DupIndex. A value of 0 for DupIndex means
return the last duplicate in the list. This is the most recent duplicate
inserted since insert puts new entries at the end of the duplicate group. A
value of one returns the oldest duplicate as determined by time of insert.
TBI -
If the row is locked and WaitIfLocked is TRUE then we wait on the row event.
If the row is locked and WaitIfLocked is FALSE then return status
GHT_STATUS_LOCKCONFLICT. In this case you can't tell if the entry is in
the table.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
pKeyValue -- ptr to the keyValue we are looking for.
WaitIfLocked -- True means wait if the row is locked.
RetHashEntry -- Returned ptr if found or NULL.
DupIndex -- return the nth duplicate, if 0 return last duplicate in list.
Return Value:
GHT_STATUS_NOT_FOUND -- if not found.
GHT_STATUS_SUCCESS -- if found.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtLookup2:"
ULONG GStatus;
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
PGENERIC_HASH_ENTRY_HEADER LastFoundpE = NULL;
// Note: Get lock earlier if table resize support is added.
if (pKeyValue == NULL) {
*RetHashEntry = NULL;
return GHT_STATUS_NOT_FOUND;
}
//
// Compute the hash index and calculate the row pointer.
//
Hval = (HashTable->GhtHashCalc)(pKeyValue, HashTable->KeyLength);
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
if (FrsRtlCountList(&RowEntry->HashRow) == 0) {
*RetHashEntry = NULL;
RowEntry->RowLookupFails += 1;
return GHT_STATUS_NOT_FOUND;
}
if (DupIndex == 0) {
DupIndex = 0xFFFFFFFF;
}
FrsRtlAcquireListLock(&RowEntry->HashRow);
//
// Walk the list looking for a match on the
// the hash value then try and match the KeyValue.
//
ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
//
// The iterator pE is of type PGENERIC_HASH_ENTRY_HEADER.
//
RowEntry->RowCompares += 1;
if (Hval < pE->HashValue) {
//
// Not on the list.
//
break;
}
if (pE->HashValue == Hval) {
if ((HashTable->GhtCompare)(pKeyValue,
((PCHAR)pE) + HashTable->KeyOffset,
HashTable->KeyLength)) {
//
// Found it. Check DupIndex count.
//
RowEntry->RowLookups += 1;
LastFoundpE = pE;
if (--DupIndex == 0) {
break;
}
}
}
);
if (LastFoundpE != NULL) {
//
// Found one. Bump ref count, release the lock, return success.
//
InterlockedIncrement(&LastFoundpE->ReferenceCount);
DPRINT2(5, ":: inc ref: %08x, %d\n", LastFoundpE, LastFoundpE->ReferenceCount);
GStatus = GHT_STATUS_SUCCESS;
} else {
RowEntry->RowLookupFails += 1;
GStatus = GHT_STATUS_NOT_FOUND;
}
ReleaseListLock(&RowEntry->HashRow);
*RetHashEntry = LastFoundpE;
return GStatus;
}
GHT_STATUS
GhtInsert(
PGENERIC_HASH_TABLE HashTable,
PVOID HashEntryArg,
BOOL WaitIfLocked,
BOOL DuplicatesOk
)
/*++
Routine Description:
Inserts a HashEntry into the HashTable. It calls the hash function with a ptr
to the key data (HashEntry+key offset) which returns a ULONG that is stored in
HashEntry.HashValue. Insert then calculates the index of HashValue Mod
TableLenth. With the index it finds the hash row header and acquires the row
lock. It then walks the list looking for a hash value match. The entires are
kept in ascending order so the lookup stops as soon as new entry value is < the
list entry value. It then inserts the entry in the table, updates the counts in
the row header, releases the lock and returns. If it finds a match it calls the
user compare function with HashEntry+offset and ListEntry+offset to validate the
match. The validate returns true if it matches and false if it fails (i.e.
continue walking the list). Duplicates are allowed when DuplicatesOk is True.
Insert returns GHT_STATUS_SUCCESS if the entry was inserted and
GHT_STATUS_FAILURE if this is a duplicate node and DuplicatesOk is False (the
compare function returned TRUE). The refcount is incremented if the node was
inserted.
Note: All hash entries must be prefixed with GENERIC_HASH_ROW_ENTRY at the
beginning of the structure.
TBI -
If the row is locked and WaitIfLocked is FALSE then return status
GHT_STATUS_LOCKCONFLICT else wait on the row.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
HashEntryArg -- ptr to new entry to insert.
WaitIfLocked -- True means wait if the row is locked.
DuplicatesOk -- True means duplicate entries are ok. They are placed at
the end of the list of duplicates.
Return Value:
GHT_STATUS_FAILURE -- Conflicting entry is in table already.
GHT_STATUS_SUCCESS -- Insert was successful.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtInsert:"
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
PVOID pKeyValue;
PLIST_ENTRY BeforeEntry;
PGENERIC_HASH_ENTRY_HEADER HashEntry =
(PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
//
// Compute the hash value on the key in the entry.
//
pKeyValue = ((PCHAR)HashEntry) + HashTable->KeyOffset;
Hval = (HashTable->GhtHashCalc)(pKeyValue, HashTable->KeyLength);
HashEntry->HashValue = Hval;
//
// Compute the index and calculate the row pointer.
//
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
FrsRtlAcquireListLock(&RowEntry->HashRow);
BeforeEntry = &RowEntry->HashRow.ListHead; // incase of empty list.
//
// Walk the list with the lock looking for a match on the
// the hash value then try and match the KeyValue.
//
ForEachListEntryLock(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
RowEntry->RowCompares += 1;
if (Hval < pE->HashValue) {
//
// Not on the list. Put new entry before this one.
//
BeforeEntry = &pE->ListEntry;
break;
}
if (pE->HashValue == Hval) {
if ((HashTable->GhtCompare)(pKeyValue,
((PCHAR)pE) + HashTable->KeyOffset,
HashTable->KeyLength)) {
//
// Found it. Release the lock and return failure if no
// duplicates are allowed.
//
if (!DuplicatesOk) {
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
}
}
);
//
// Put new entry on the list in front of 'BeforeEntry'.
//
InterlockedIncrement(&HashEntry->ReferenceCount);
DPRINT2(5, ":: inc ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
RowEntry->RowInserts += 1;
FrsRtlInsertBeforeEntryListLock( &RowEntry->HashRow,
BeforeEntry,
&HashEntry->ListEntry);
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_SUCCESS;
}
GHT_STATUS
GhtDeleteEntryByAddress(
PGENERIC_HASH_TABLE HashTable,
PVOID HashEntryArg,
BOOL WaitIfLocked
)
/*++
Routine Description:
Takes HashEntry address and fetches the hash value to acquire the row lock.
Decrement the reference count and if it is one (the count for being in the
table) remove the entry from the row and call the memory free function to
release the entries memory. Drop the row lock. Return GHT_STATUS_SUCCESS if we
deleted the entry.
TBI -
Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
WaitIfLocked was FALSE.
Note: This function is only safe if you have a reference on the entry otherwise
another thread could have already deleted the entry and your entry address is
pointing at freed memory.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
HashEntryArg -- ptr to entry to delete.
WaitIfLocked -- True means wait if the row is locked.
Return Value:
GHT_STATUS_FAILURE -- Entry was not deleted.
GHT_STATUS_SUCCESS -- Entry was deleted.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtDeleteEntryByAddress:"
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
BOOL Found;
ULONG GhtStatus;
LONG NewCount;
PGENERIC_HASH_ENTRY_HEADER HashEntry =
(PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
GhtStatus = GHT_STATUS_FAILURE;
//
// Get the hash value for the element and compute the index and RowEntry
// address. Then get the row lock.
//
Hval = HashEntry->HashValue;
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
FrsRtlAcquireListLock(&RowEntry->HashRow);
#if DBG
//
// check if the entry is actually on the List.
//
// Walk the list with the lock looking for a match on the
// the hash value then try and match the KeyValue.
//
Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
if (!Found) {
DPRINT4(0, "GhtDeleteEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
HashEntry, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"entry not on list");
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
#endif
//
// Decrement the ref count.
//
NewCount = InterlockedDecrement(&HashEntry->ReferenceCount);
DPRINT2(5, ":: dec ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
if (NewCount <= 0) {
DPRINT4(0, "GhtDeleteEntryByAddress - ref count equal zero: %08x, %08x, %d, %s\n",
HashEntry, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"ref count <= zero");
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
if (NewCount == 1) {
//
// Ref count zero. Remove and free the entry.
//
FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &HashEntry->ListEntry);
(HashTable->GhtFree)(HashTable, HashEntry);
GhtStatus = GHT_STATUS_SUCCESS;
}
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GhtStatus;
}
GHT_STATUS
GhtRemoveEntryByAddress(
PGENERIC_HASH_TABLE HashTable,
PVOID HashEntryArg,
BOOL WaitIfLocked
)
/*++
Routine Description:
Takes HashEntry address and fetches the hash value to acquire the row lock.
Remove the entry from the table. The reference count is decremented.
Return GHT_STATUS_SUCCESS.
TBI -
Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
WaitIfLocked was FALSE.
Note: This function is only safe if you have a reference on the entry otherwise
another thread could have already deleted the entry and your entry address is
pointing at freed memory.
Also Note: The caller must have a lock that prevents other threads from
changing the entry. In addition removing an entry from one hash table and
inserting it on another will confuse another thread that may be accessing the
entry so the caller better be sure that no other thread assumes the hash
table can't change while it has a reference to the entry.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
HashEntryArg -- ptr to entry to delete.
WaitIfLocked -- True means wait if the row is locked.
Return Value:
GHT_STATUS_SUCCESS -- if the entry was removed successfully.
GHT_STATUS_FAILURE -- if the entry was not on the list.
--*/
{
#undef DEBSUB
#define DEBSUB "GhtRemoveEntryByAddress:"
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
BOOL Found;
LONG NewCount;
PGENERIC_HASH_ENTRY_HEADER HashEntry =
(PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
//
// Get the hash value for the element and compute the index and RowEntry
// address. Then get the row lock.
//
Hval = HashEntry->HashValue;
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
FrsRtlAcquireListLock(&RowEntry->HashRow);
#if DBG
//
// check if the entry is actually on the List.
//
// Walk the list with the lock looking for a match on the
// the hash value then try and match the KeyValue.
//
Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
if (!Found) {
DPRINT4(0, "GhtRemoveEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
HashEntry, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"entry not on list-2");
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
#endif
//
// Decrement the ref count.
//
NewCount = InterlockedDecrement(&HashEntry->ReferenceCount);
DPRINT2(5, ":: dec ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
if (NewCount < 0) {
DPRINT4(0, ":: ERROR- GhtRemoveEntryByAddress - ref count less than zero: %08x, %08x, %d, %s\n",
HashEntry, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"ref count less than zero-2");
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
if (NewCount > 1) {
//
// Other Refs than the caller's exist. Print a warning.
//
DPRINT5(1, ":: WARNING- GhtRemoveEntryByAddress - ref count(%d) > 1: %08x, %08x, %d, %s\n",
NewCount, HashEntry, Hval, HvalIndex, HashTable->Name);
}
FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &HashEntry->ListEntry);
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_SUCCESS;
}
GHT_STATUS
GhtReferenceEntryByAddress(
PGENERIC_HASH_TABLE HashTable,
PVOID HashEntryArg,
BOOL WaitIfLocked
)
/*++
Routine Description:
Takes HashEntry address and fetches the hash value to acquire the row lock.
Increment the reference count. Drop the row lock.
TBI -
Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
WaitIfLocked was FALSE.
Note: This function is only safe if you have a reference on the entry otherwise
another thread could have already deleted the entry and your entry address is
pointing at freed memory. A Lookup which gave you the address bumps the
reference count. An insert in which you kept the address does NOT bump
the reference count.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
HashEntryArg -- ptr to entry to reference.
WaitIfLocked -- True means wait if the row is locked.
Return Value:
GHT_STATUS_SUCCESS
--*/
{
#undef DEBSUB
#define DEBSUB "GhtReferenceEntryByAddress:"
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
BOOL Found;
PGENERIC_HASH_ENTRY_HEADER HashEntry =
(PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
//
// Get the hash value for the element and compute the index and RowEntry
// address. Then get the row lock.
//
Hval = HashEntry->HashValue;
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
FrsRtlAcquireListLock(&RowEntry->HashRow);
#if DBG
//
// check if the entry is actually on the List.
//
// Walk the list with the lock looking for a match on the
// the hash value then try and match the KeyValue.
//
Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
if (!Found) {
DPRINT4(0, "GhtReferenceEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
HashEntry, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"entry not on list-3");
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
#endif
//
// Increment the ref count.
//
InterlockedIncrement(&HashEntry->ReferenceCount);
DPRINT2(5, ":: inc ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_SUCCESS;
}
GHT_STATUS
GhtDereferenceEntryByAddress(
PGENERIC_HASH_TABLE HashTable,
PVOID HashEntryArg,
BOOL WaitIfLocked
)
/*++
Routine Description:
Takes HashEntry address and fetches the hash value to acquire the row lock.
Decrement the reference count. Drop the row lock.
TBI -
Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
WaitIfLocked was FALSE.
Note: This function is only safe if you have a reference on the entry otherwise
another thread could have already deleted the entry and your entry address is
pointing at freed memory. A Lookup which gave you the address bumps the
reference count. An insert in which you kept the address does NOT bump
the reference count.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
HashEntryArg -- ptr to entry to reference.
WaitIfLocked -- True means wait if the row is locked.
Return Value:
GHT_STATUS_SUCCESS
--*/
{
#undef DEBSUB
#define DEBSUB "GhtDereferenceEntryByAddress:"
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
BOOL Found;
LONG NewCount;
PGENERIC_HASH_ENTRY_HEADER HashEntry =
(PGENERIC_HASH_ENTRY_HEADER)HashEntryArg;
//
// Get the hash value for the element and compute the index and RowEntry
// address. Then get the row lock.
//
Hval = HashEntry->HashValue;
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
FrsRtlAcquireListLock(&RowEntry->HashRow);
#if DBG
//
// check if the entry is actually on the List.
//
// Walk the list with the lock looking for a match on the
// the hash value then try and match the KeyValue.
//
Found = GhtQuickCheck(HashTable, RowEntry, HashEntry, Hval) != NULL;
if (!Found) {
DPRINT4(0, "GhtDereferenceEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
HashEntry, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"entry not on list-4");
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
#endif
//
// Decrement the ref count.
//
NewCount = InterlockedDecrement(&HashEntry->ReferenceCount);
DPRINT2(5, ":: dec ref: %08x, %d\n", HashEntry, HashEntry->ReferenceCount);
if (NewCount <= 0) {
DPRINT4(0, "GhtDereferenceEntryByAddress - ref count now zero: %08x, %08x, %d, %s\n",
HashEntry, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"ref count now zero-4");
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_SUCCESS;
}
GHT_STATUS
GhtAdjustRefCountByKey(
PGENERIC_HASH_TABLE HashTable,
PVOID pKeyValue,
LONG Delta,
ULONG ActionIfZero,
BOOL WaitIfLocked,
PVOID *RetHashEntry
)
/*++
Routine Description:
Takes keyvalue, finds the HashEntry address and adds Delta to
the reference count. Drop the row lock.
** WARNING **
If you allow duplicate entries in the hash table this routine will not work
because you can't guarantee that you will adjust a given entry.
TBI -
Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
WaitIfLocked was FALSE.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
pKeyValue -- ptr to a datavalue for the key.
Delta -- The amount of the ref count adjustment.
ActionIfZero -- If RC is zero Choice of nothing, remove, remove and delete.
WaitIfLocked -- True means wait if the row is locked.
RetHashEntry -- If GHT_ACTION_REMOVE requested, the hash entry
address is returned if element removed else NULL returned.
Return Value:
GHT_STATUS_SUCCESS
GHT_STATUS_NOT_FOUND
--*/
{
#undef DEBSUB
#define DEBSUB "GhtDecrementRefCountByKey:"
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
LONG NewCount;
if (ActionIfZero == GHT_ACTION_REMOVE) {
*RetHashEntry = NULL;
}
// Note: Get lock earlier if table resize support is added.
//
// Compute the hash index and calculate the row pointer.
//
Hval = (HashTable->GhtHashCalc)(pKeyValue, HashTable->KeyLength);
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
if (FrsRtlCountList(&RowEntry->HashRow) == 0) {
RowEntry->RowLookupFails += 1;
return GHT_STATUS_NOT_FOUND;
}
//
// Walk the list with the lock looking for a match on the
// the hash value then try and match the KeyValue.
//
ForEachListEntry(&RowEntry->HashRow, GENERIC_HASH_ENTRY_HEADER, ListEntry,
//
// pE is iterator of type GENERIC_HASH_ENTRY_HEADER.
//
RowEntry->RowCompares += 1;
if (pE->HashValue == Hval) {
if ((HashTable->GhtCompare)(pKeyValue,
((PCHAR)pE) + HashTable->KeyOffset,
HashTable->KeyLength)) {
//
// Found it. Adjust ref count,
//
NewCount = InterlockedExchangeAdd(&pE->ReferenceCount, Delta);
DPRINT2(5, ":: adj ref: %08x, %d\n", pE, pE->ReferenceCount);
RowEntry->RowLookups += 1;
if (NewCount <= 0) {
if (NewCount < 0) {
DPRINT4(0, "GhtDecrementRefCountByKey - ref count neg: %08x, %08x, %d, %s\n",
pE, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"ref count neg-5");
ReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
//
// Ref count zero. Optionally remove and free the entry.
//
if (ActionIfZero == GHT_ACTION_REMOVE) {
*RetHashEntry = pE;
FrsRtlRemoveEntryListLock(&RowEntry->HashRow, &pE->ListEntry);
} else
if (ActionIfZero == GHT_ACTION_DELETE) {
(HashTable->GhtFree)(HashTable, pE);
} else {
//
// Not good. Action was noop so refcount expected to
// be > 0.
//
DPRINT4(0, "GhtDecrementRefCountByKey - ref count zero with Noop Action: %08x, %08x, %d, %s\n",
pE, Hval, HvalIndex, HashTable->Name);
FRS_ASSERT(!"ref count zero-6");
ReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_FAILURE;
}
}
ReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_SUCCESS;
}
}
);
RowEntry->RowLookupFails += 1;
return GHT_STATUS_NOT_FOUND;
}
GHT_STATUS
GhtSwapEntryByAddress(
PGENERIC_HASH_TABLE HashTable,
PVOID OldHashEntryArg,
PVOID NewHashEntryArg,
BOOL WaitIfLocked
)
/*++
Routine Description:
This routine replaces an existing old hash entry with a new entry.
It verifies tha the old hash entry is still in the table.
It assumes that the key value of the new entry is the same as the old entry.
NO CHECK IS MADE.
The expected use is when the caller needs to reallocate an entry with
more storage.
NOTE ALSO: The reference count is copied from the old entry to the new one.
Using this routine means that the caller is using GhtDecrementRefCountByKey()
and GhtIncrementRefCountByKey() to access the ref counts on any element in the
table since the entry could get swapped making the pointer invalid.
TBI -
Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
WaitIfLocked was FALSE.
Note: This function is only safe if you have a reference on the entry otherwise
another thread could have already deleted the entry and your entry address is
pointing at freed memory. A Lookup which gave you the address bumps the
reference count. An insert in which you kept the address does NOT bump
the reference count.
Arguments:
HashTable -- ptr to a GENERIC_HASH_TABLE struct.
OldHashEntry -- ptr to entry to swap out of table.
NewHashEntry -- ptr to entry to swap in to table.
WaitIfLocked -- True means wait if the row is locked.
Return Value:
GHT_STATUS_SUCCESS if swap ok.
GHT_STATUS_NOT_FOUND if old entry not in table.
--*/
// Note: TBD if necc, implement GhtIncrementRefCountByKey.
{
#undef DEBSUB
#define DEBSUB "GhtSwapEntryByAddress:"
ULONG Hval, HvalIndex;
PGENERIC_HASH_ROW_ENTRY RowEntry;
PLIST_ENTRY BeforeEntry;
BOOL Found;
PGENERIC_HASH_ENTRY_HEADER Entry;
PGENERIC_HASH_ENTRY_HEADER NewHashEntry =
(PGENERIC_HASH_ENTRY_HEADER)NewHashEntryArg;
PGENERIC_HASH_ENTRY_HEADER OldHashEntry =
(PGENERIC_HASH_ENTRY_HEADER)OldHashEntryArg;
//
// Get the hash value for the element and compute the index and RowEntry
// address. Then get the row lock.
//
Hval = OldHashEntry->HashValue;
HvalIndex = Hval % HashTable->NumberRows;
RowEntry = HashTable->HashRowBase + HvalIndex;
FrsRtlAcquireListLock(&RowEntry->HashRow);
//
// check if the entry is actually on the List.
//
// Walk the list with the lock looking for a match on the
// the hash value then try and match the KeyValue.
//
Entry = GhtQuickCheck(HashTable, RowEntry, OldHashEntry, Hval);
if (Entry != OldHashEntry) {
DPRINT4(0, "GhtSwapEntryByAddress - entry not on list %08x, %08x, %d, %s\n",
OldHashEntry, Hval, HvalIndex, HashTable->Name);
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_NOT_FOUND;
}
//
// Copy the ref count and hash value from the old entry to the new one.
//
NewHashEntry->ReferenceCount = OldHashEntry->ReferenceCount;
NewHashEntry->HashValue = OldHashEntry->HashValue;
//
// Pull the old entry out and replace with the new entry.
// List counts do not change so do list juggling here.
//
BeforeEntry = OldHashEntry->ListEntry.Flink;
FrsRemoveEntryList(&OldHashEntry->ListEntry);
InsertTailList(BeforeEntry, &NewHashEntry->ListEntry);
FrsRtlReleaseListLock(&RowEntry->HashRow);
return GHT_STATUS_SUCCESS;
}