/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: qhash.c Abstract: Quick Hash Table routines. Unlike the Genhash table functions these routines use a fixed size node (QHASH_ENTRY) and the data fields are passed as parameters and copied into the node. The generic hash functions include a link entry in the users struct to link the node onto a hash chain. The genhash functions also include reference counts for the nodes. The generic hash functions have a lock per hash row where a Qhash table has only a single lock for the table. The PQHASH_TABLE struct is a typed struc allocated with FrsAllocTypeSize(). QHASH tables can be used in two ways, for fixed size QuadWord keys and for more complex non-Quadword keys. For QHASH tables with QuadWord keys: The macro SET_QHASH_TABLE_HASH_CALC() is used to specify the hash function to use for the table. The key is supplied as a quadword and each entry has longword flags and a quadword data field for the callers info. For QHASH tables with Large keys: When the QHASH table is created you specify it as a large key table (i.e. not a simple Quadword Key) by doing: SET_QHASH_TABLE_FLAG(HashTable, QHASH_FLAG_LARGE_KEY); For large key tables the QHASH_ENTRY Flags ULONG_PTR and the Flags argument to QHashInsert() are expected to point at a caller defined data node with the large key value for the node at offset zero. On lookups the HashCalc2 function set by SET_QHASH_TABLE_HASH_CALC2() is used to calculate both the quadword key for the hashtable entry and the hash value used for indexing the main array. In addition the caller specifies an exact key match function via SET_QHASH_TABLE_KEY_MATCH() to be used after the initial quadword key matches. This key match function is passed both the lookup argument key and the node address that was saved in the QHASH_ENTRY Flags ULONG_PTR so it can perform the complete key match. The macros QHashAcquireLock(_Table_) and QHashReleaseLock(_Table_) can be used to lock the table over multiple operations. The number of entries in the hash table array is specified by the allocation size when the table is allocated. When a collision occurs additional entries are allocated and placed on a free list for use in the collision lists. The storage for the base hash array and the collision entries are released when the table is freed by calling FrsFreeType(Table). An example of allocating a Qhash table with 100 entries in the base hash array: //PQHASH_TABLE FrsWriteFilter; //#define FRS_WRITE_FILTER_SIZE sizeof(QHASH_ENTRY)*100 // FrsWriteFilter = FrsAllocTypeSize(QHASH_TABLE_TYPE, FRS_WRITE_FILTER_SIZE); // SET_QHASH_TABLE_HASH_CALC(FrsWriteFilter, JrnlHashCalcUsn); Author: David Orbits [davidor] 22-Apr-1997 Environment: User Mode Service Revision History: --*/ #include #pragma hdrstop #include ULONG QHashDump ( PQHASH_TABLE Table, PQHASH_ENTRY BeforeNode, PQHASH_ENTRY TargetNode, PVOID Context ) /*++ Routine Description: This function is called thru QHashEnumerateTable() to dump an entry. Arguments: Table - the hash table being enumerated BeforeNode -- ptr to the QhashEntry before the node of interest. TargetNode -- ptr to the QhashEntry of interest. Context - Unused. Return Value: FrsErrorStatus --*/ { #undef DEBSUB #define DEBSUB "QHashDump:" DPRINT4(5, "Link: %08x, Flags: %08x, Tag: %08x %08x, Data: %08x %08x\n", TargetNode->NextEntry, TargetNode->Flags, PRINTQUAD(TargetNode->QKey), PRINTQUAD(TargetNode->QData)); return FrsErrorSuccess; } VOID QHashExtendTable( IN PQHASH_TABLE HashTable ) /*++ Routine Description: Extend the number of entries in the hash table by allocating an extension block of up to QHASH_EXTENSION_MAX entries. The caller has the table lock. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "QHashExtendTable:" ULONG i, NumberExtEntries; PQHASH_ENTRY Ext; // // Allocate a block of memory. // Ext = FrsAlloc(HashTable->ExtensionAllocSize); InsertTailList(&HashTable->ExtensionListHead, (PLIST_ENTRY)Ext); NumberExtEntries = (HashTable->ExtensionAllocSize - sizeof(LIST_ENTRY)) / sizeof(QHASH_ENTRY); // // Put the entries on the free list. // (PCHAR) Ext = (PCHAR)Ext + sizeof(LIST_ENTRY); HashTable->FreeList.Next = &Ext->NextEntry; for (i=0; iNextEntry.Next = &((Ext+1)->NextEntry); Ext++; } Ext -= 1; Ext->NextEntry.Next = NULL; } ULONG QHashEnumerateTable( IN PQHASH_TABLE HashTable, IN PQHASH_ENUM_ROUTINE Function, IN PVOID Context ) /*++ Routine Description: This routine walks through the entries in the QHash table and calls the function provided with the entry address and the context. The table lock is acquired and released here. 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 PQHASH_ENUM_ROUTINE. Return FALSE to abort the enumeration else true. Context - A context ptr to pass through to the RecordFunction. Return Value: The FrsErrorStatus code from the argument function. --*/ { #undef DEBSUB #define DEBSUB "QHashEnumerateTable:" PQHASH_ENTRY HashRowEntry; PQHASH_ENTRY BeforeEntry; ULONG i, FStatus; if (HashTable == NULL) { return FrsErrorSuccess; } HashRowEntry = HashTable->HashRowBase; // // Loop through all the Hash table rows and call the function for // each element. // QHashAcquireLock(HashTable); for (i=0; iNumberEntries; i++, HashRowEntry++) { if (HashRowEntry->QKey != QUADZERO) { FStatus = (Function)(HashTable, NULL, HashRowEntry, Context); if (FStatus == FrsErrorDeleteRequested) { HashRowEntry->QKey = QUADZERO; } else if (FStatus != FrsErrorSuccess) { QHashReleaseLock(HashTable); return FStatus; } } // // Enumerate collision list if present. // if (HashRowEntry->NextEntry.Next == NULL) { continue; } BeforeEntry = HashRowEntry; ForEachSingleListEntry(&HashRowEntry->NextEntry, QHASH_ENTRY, NextEntry, // Enumerator pE is of type PQHASH_ENTRY FStatus = (Function)(HashTable, BeforeEntry, pE, Context); if (FStatus == FrsErrorDeleteRequested) { RemoveSingleListEntry(UNUSED); PushEntryList(&HashTable->FreeList, &pE->NextEntry); pE = PreviousSingleListEntry(QHASH_ENTRY, NextEntry); } else if (FStatus != FrsErrorSuccess) { QHashReleaseLock(HashTable); return FStatus; } BeforeEntry = pE; ); } QHashReleaseLock(HashTable); return FStatus; } GHT_STATUS QHashLookup( IN PQHASH_TABLE HashTable, IN PVOID ArgQKey, OUT PULONGLONG QData, OUT PULONG_PTR Flags ) /*++ Routine Description: Lookup the Quadword Key in the hash table and if found, return the Qdata and the flags DWORD. The table lock is acquired and released here. Note: A zero value for QKey is an error because a zero is used to denote an empty hash table slot. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. ArgQKey -- ptr to the Key we are looking for. QData -- If found this is the returned quadword data. (NULL if unused) Flags -- If found this is the returned flags word. Return Value: GHT_STATUS_NOT_FOUND -- if not found. GHT_STATUS_SUCCESS -- if found. --*/ { #undef DEBSUB #define DEBSUB "QHashLookup:" ULONGLONG QKey; ULONG GStatus; ULONG Hval, HvalIndex; PQHASH_ENTRY RowEntry; PQHASH_ENTRY LastFoundpE = NULL; if (IS_QHASH_LARGE_KEY(HashTable)) { Hval = (HashTable->HashCalc2)(ArgQKey, &QKey); DPRINT3(5, "QHashLookup (%08x): Hval: %08x QKey: %08lx %08lx\n", HashTable, Hval, PRINTQUAD(QKey)); } else { CopyMemory(&QKey, ArgQKey, 8); Hval = (HashTable->HashCalc)(&QKey, 8); } FRS_ASSERT(QKey != QUADZERO); // // Compute the hash index and calculate the row pointer. // HvalIndex = Hval % HashTable->NumberEntries; RowEntry = HashTable->HashRowBase + HvalIndex; QHashAcquireLock(HashTable); if (RowEntry->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) { // // Match. Return quadword data and flags. // if (QData != NULL) { *QData = RowEntry->QData; } *Flags = RowEntry->Flags; DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags); QHashReleaseLock(HashTable); return GHT_STATUS_SUCCESS; } } if (RowEntry->NextEntry.Next == NULL) { QHashReleaseLock(HashTable); return GHT_STATUS_NOT_FOUND; } // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // if (QKey < pE->QKey) { // // Not on the list. // break; } if (pE->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) { // // Found it. // if (QData != NULL) { *QData = pE->QData; } *Flags = pE->Flags; DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags); QHashReleaseLock(HashTable); return GHT_STATUS_SUCCESS; } } ); QHashReleaseLock(HashTable); return GHT_STATUS_NOT_FOUND; } PQHASH_ENTRY QHashLookupLock( IN PQHASH_TABLE HashTable, IN PVOID ArgQKey ) /*++ Routine Description: Lookup the Quadword Key in the hash table and if found, return the pointer to the entry. The table lock is acquired and released by the caller. Restriction: Once the caller drops the table lock no further ref to the QHASH_ENTRY is allowed since another thread could delete/update it. Note: A zero value for the key is an error because a zero is used to denote an empty hash table slot. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. ArgQKey -- ptr to the Key we are looking for. Return Value: Pointer to QHashEntry, Null if not found. --*/ { #undef DEBSUB #define DEBSUB "QHashLookupLock:" ULONGLONG QKey; ULONG Hval, HvalIndex; PQHASH_ENTRY RowEntry; if (IS_QHASH_LARGE_KEY(HashTable)) { Hval = (HashTable->HashCalc2)(ArgQKey, &QKey); DPRINT3(5, "QHash lookuplock (%08x): Hval: %08x QKey: %08lx %08lx\n", HashTable, Hval, PRINTQUAD(QKey)); } else { CopyMemory(&QKey, ArgQKey, 8); Hval = (HashTable->HashCalc)(&QKey, 8); } FRS_ASSERT(QKey != QUADZERO); // // Compute the hash index and calculate the row pointer. // HvalIndex = Hval % HashTable->NumberEntries; RowEntry = HashTable->HashRowBase + HvalIndex; if (RowEntry->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) { DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags); return RowEntry; } } if (RowEntry->NextEntry.Next == NULL) { return NULL; } // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // Check for early terminate and then for a match. // if (QKey < pE->QKey) { return NULL; } if (pE->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) { DPRINT5(5, "QHash Lookup (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags); return pE; } } ); return NULL; } GHT_STATUS QHashInsert( IN PQHASH_TABLE HashTable, IN PVOID ArgQKey, IN PULONGLONG QData, IN ULONG_PTR Flags, IN BOOL HaveLock ) /*++ Routine Description: Insert the Quadword Key in the hash table and if found, return the data and the flags DWORD. The keys are in numerically increasing order on the collision chains. The table lock is acquired and released here. Note: A zero value for the key is an error because a zero is used to denote an empty hash table slot. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. ArgQKey -- ptr to the Key we are inserting. QData -- This is ptr to the quadword data. (NULL if unused). Flags -- This is the flags word data. For large Key QHASH tables this is the ptr to the data node. Note that we assume the large Key is at a zero offset in the node when doing lookups. HaveLock -- True means the caller has taken the lock else we take it. Return Value: GHT_STATUS_FAILURE -- Conflicting entry is in table already. GHT_STATUS_SUCCESS -- Insert was successful. --*/ { #undef DEBSUB #define DEBSUB "QHashInsert:" ULONGLONG QKey; ULONG Hval, HvalIndex; PQHASH_ENTRY RowEntry, AfterEntry; PQHASH_ENTRY pNew; PSINGLE_LIST_ENTRY NewEntry; if (IS_QHASH_LARGE_KEY(HashTable)) { Hval = (HashTable->HashCalc2)(ArgQKey, &QKey); DPRINT3(5, "QHashInsert (%08x): Hval: %08x QKey: %08lx %08lx\n", HashTable, Hval, PRINTQUAD(QKey)); } else { CopyMemory(&QKey, ArgQKey, 8); Hval = (HashTable->HashCalc)(&QKey, 8); } FRS_ASSERT(QKey != QUADZERO); // // Compute the hash index and calculate the row pointer. // HvalIndex = Hval % HashTable->NumberEntries; RowEntry = HashTable->HashRowBase + HvalIndex; if (!HaveLock) {QHashAcquireLock(HashTable);} if (RowEntry->QKey == QUADZERO) { pNew = RowEntry; goto INSERT_ENTRY; } if (RowEntry->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) { if (!HaveLock) {QHashReleaseLock(HashTable);} return GHT_STATUS_FAILURE; } } AfterEntry = RowEntry; // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // if (QKey < pE->QKey) { // // Not on the list. // break; } if (pE->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) { // // Found it, collision. // if (!HaveLock) {QHashReleaseLock(HashTable);} return GHT_STATUS_FAILURE; } } AfterEntry = pE; ); // // Not found. Allocate a new entry and put it in the list. // NewEntry = PopEntryList(&HashTable->FreeList); if (NewEntry == NULL) { // // Allocate a table extension block. // QHashExtendTable(HashTable); NewEntry = PopEntryList(&HashTable->FreeList); } // // Insert entry in the list. // pNew = CONTAINING_RECORD(NewEntry, QHASH_ENTRY, NextEntry); PushEntryList( &AfterEntry->NextEntry, &pNew->NextEntry); // // Insert the data and drop the lock. // INSERT_ENTRY: pNew->QKey = QKey; pNew->Flags = Flags; if (QData != NULL) { pNew->QData = *QData; } else { pNew->QData = QUADZERO; } DPRINT5(5, "QHash Insert (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pNew, PRINTQUAD(pNew->QKey), PRINTQUAD(pNew->QData), pNew->Flags); if (!HaveLock) {QHashReleaseLock(HashTable);} return GHT_STATUS_SUCCESS; } PQHASH_ENTRY QHashInsertLock( IN PQHASH_TABLE HashTable, IN PVOID ArgQKey, IN PULONGLONG QData, IN ULONG_PTR Flags ) /*++ Routine Description: Insert the quadword key in the hash table. Return the pointer to the entry. The caller has acquired the table lock and will release it. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. ArgQKey -- ptr to the Key we are inserting. QData -- This is ptr to the quadword data. (NULL if unused) Flags -- This is the flags word data. For large Key QHASH tables this is the ptr to the data node. Note that we assume the large Key is at a zero offset in the node when doing lookups. Return Value: The ptr to the inserted entry or the existing entry if already in the table. --*/ { #undef DEBSUB #define DEBSUB "QHashInsertLock:" ULONGLONG QKey; ULONG Hval, HvalIndex; PQHASH_ENTRY RowEntry, AfterEntry; PQHASH_ENTRY pNew; PSINGLE_LIST_ENTRY NewEntry; if (IS_QHASH_LARGE_KEY(HashTable)) { Hval = (HashTable->HashCalc2)(ArgQKey, &QKey); DPRINT3(5, "QHashInsertLock (%08x): Hval: %08x QKey: %08lx %08lx\n", HashTable, Hval, PRINTQUAD(QKey)); } else { CopyMemory(&QKey, ArgQKey, 8); Hval = (HashTable->HashCalc)(&QKey, 8); } FRS_ASSERT(QKey != QUADZERO); // // Compute the hash index and calculate the row pointer. // HvalIndex = Hval % HashTable->NumberEntries; RowEntry = HashTable->HashRowBase + HvalIndex; if (RowEntry->QKey == QUADZERO) { pNew = RowEntry; goto INSERT_ENTRY; } if (RowEntry->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) { return RowEntry; } } AfterEntry = RowEntry; // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. Check for early termination. // if (QKey < pE->QKey) { break; } if (pE->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) { // // Found it, return the pointer. // return pE; } } AfterEntry = pE; ); // // Not found. Allocate a new entry and put it in the list. // NewEntry = PopEntryList(&HashTable->FreeList); if (NewEntry == NULL) { // // Allocate a table extension block. // QHashExtendTable(HashTable); NewEntry = PopEntryList(&HashTable->FreeList); } // // Insert entry in the list. // pNew = CONTAINING_RECORD(NewEntry, QHASH_ENTRY, NextEntry); PushEntryList( &AfterEntry->NextEntry, &pNew->NextEntry); // // Insert the data. // INSERT_ENTRY: pNew->QKey = QKey; pNew->Flags = Flags; if (QData != NULL) { pNew->QData = *QData; } else { pNew->QData = QUADZERO; } DPRINT5(5, "QHash Insert (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pNew, PRINTQUAD(pNew->QKey), PRINTQUAD(pNew->QData), pNew->Flags); return pNew; } GHT_STATUS QHashUpdate( IN PQHASH_TABLE HashTable, IN PVOID ArgQKey, IN PULONGLONG QData, IN ULONG_PTR Flags ) /*++ Routine Description: Lookup the Quadword key in the hash table and if found, update the entry. The table lock is acquired and released here. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. ArgQKey -- ptr to the Key we are updating. QData -- This is ptr to the quadword data. (NULL if unused) Flags -- This is the flags word data. For large Key QHASH tables this is the ptr to the data node. Note that we assume the large Key is at a zero offset in the node when doing lookups. Return Value: GHT_STATUS_FAILURE -- Entry not in table already. GHT_STATUS_SUCCESS -- Update was successful. --*/ { #undef DEBSUB #define DEBSUB "QHashUpdate:" ULONGLONG QKey; ULONG GStatus; ULONG Hval, HvalIndex; PQHASH_ENTRY RowEntry; PQHASH_ENTRY LastFoundpE = NULL; if (IS_QHASH_LARGE_KEY(HashTable)) { Hval = (HashTable->HashCalc2)(ArgQKey, &QKey); DPRINT3(5, "QHashupdate (%08x): Hval: %08x QKey: %08lx %08lx\n", HashTable, Hval, PRINTQUAD(QKey)); } else { CopyMemory(&QKey, ArgQKey, 8); Hval = (HashTable->HashCalc)(&QKey, 8); } FRS_ASSERT(QKey != QUADZERO); // // Compute the hash index and calculate the row pointer. // HvalIndex = Hval % HashTable->NumberEntries; RowEntry = HashTable->HashRowBase + HvalIndex; QHashAcquireLock(HashTable); if (RowEntry->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) { if (QData != NULL) { RowEntry->QData = *QData; } RowEntry->Flags = Flags; DPRINT5(5, "QHash Update (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags); QHashReleaseLock(HashTable); return GHT_STATUS_SUCCESS; } } if (RowEntry->NextEntry.Next == NULL) { QHashReleaseLock(HashTable); return GHT_STATUS_NOT_FOUND; } // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // if (QKey < pE->QKey) { // // Not on the list. // QHashReleaseLock(HashTable); return GHT_STATUS_NOT_FOUND; } if (pE->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) { // // Found it. // if (QData != NULL) { pE->QData = *QData; } pE->Flags = Flags; DPRINT5(5, "QHash Update (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags); QHashReleaseLock(HashTable); return GHT_STATUS_SUCCESS; } } ); QHashReleaseLock(HashTable); return GHT_STATUS_NOT_FOUND; } GHT_STATUS QHashDelete( IN PQHASH_TABLE HashTable, IN PVOID ArgQKey ) /*++ Routine Description: Lookup the Key in the hash table and if found remove it and put it on the free list. The table lock is acquired and released here. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. ArgQKey -- ptr to the Key we are looking for. Return Value: GHT_STATUS_NOT_FOUND -- if not found. GHT_STATUS_SUCCESS -- if found and deleted. --*/ { #undef DEBSUB #define DEBSUB "QHashDelete:" ULONGLONG QKey; ULONG GStatus; ULONG Hval, HvalIndex; PQHASH_ENTRY RowEntry; PQHASH_ENTRY LastFoundpE = NULL; if (IS_QHASH_LARGE_KEY(HashTable)) { Hval = (HashTable->HashCalc2)(ArgQKey, &QKey); } else { CopyMemory(&QKey, ArgQKey, 8); Hval = (HashTable->HashCalc)(&QKey, 8); } FRS_ASSERT(QKey != QUADZERO); // // Compute the hash index and calculate the row pointer. // HvalIndex = Hval % HashTable->NumberEntries; RowEntry = HashTable->HashRowBase + HvalIndex; QHashAcquireLock(HashTable); if (RowEntry->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) { DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags); if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) RowEntry->Flags != NULL)) { (HashTable->HashFree)((PVOID) (RowEntry->Flags)); } RowEntry->QKey = QUADZERO; RowEntry->Flags = 0; QHashReleaseLock(HashTable); return GHT_STATUS_SUCCESS; } } if (RowEntry->NextEntry.Next == NULL) { QHashReleaseLock(HashTable); return GHT_STATUS_NOT_FOUND; } // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // if (QKey < pE->QKey) { // // Not on the list. // break; } if (pE->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) { // // Found it. Remove from list and put on free list. // DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags); if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) pE->Flags != NULL)) { (HashTable->HashFree)((PVOID) (pE->Flags)); } pE->Flags = 0; RemoveSingleListEntry(NOT_USED); PushEntryList(&HashTable->FreeList, &pE->NextEntry); QHashReleaseLock(HashTable); return GHT_STATUS_SUCCESS; } } ); QHashReleaseLock(HashTable); return GHT_STATUS_NOT_FOUND; } VOID QHashDeleteLock( IN PQHASH_TABLE HashTable, IN PVOID ArgQKey ) /*++ Routine Description: Delete the entry in the hash table. Assumes the caller has the lock on the table and has not dropped the lock since doing the QHashLookupLock() call. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. ArgQKey -- ptr to the Key we are looking for. Return Value: --*/ { #undef DEBSUB #define DEBSUB "QHashDeleteLock:" ULONGLONG QKey; ULONG Hval, HvalIndex; PQHASH_ENTRY RowEntry; if (IS_QHASH_LARGE_KEY(HashTable)) { Hval = (HashTable->HashCalc2)(ArgQKey, &QKey); } else { CopyMemory(&QKey, ArgQKey, 8); Hval = (HashTable->HashCalc)(&QKey, 8); } FRS_ASSERT(QKey != QUADZERO); // // Compute the hash index and calculate the row pointer. // HvalIndex = Hval % HashTable->NumberEntries; RowEntry = HashTable->HashRowBase + HvalIndex; if (RowEntry->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, RowEntry->Flags)) { DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags); if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) RowEntry->Flags != NULL)) { (HashTable->HashFree)((PVOID) (RowEntry->Flags)); } RowEntry->QKey = QUADZERO; RowEntry->Flags = 0; return; } } if (RowEntry->NextEntry.Next == NULL) { return; } // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // Check for early termination. // if (QKey < pE->QKey) { break; } if (pE->QKey == QKey) { if (DOES_QHASH_LARGE_KEY_MATCH(HashTable, ArgQKey, pE->Flags)) { // // Found it. Remove from list and put on free list. // DPRINT5(5, "QHash Delete (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags); if (IS_QHASH_LARGE_KEY(HashTable) && ((PVOID) pE->Flags != NULL)) { (HashTable->HashFree)((PVOID) (pE->Flags)); } pE->Flags = 0; RemoveSingleListEntry(NOT_USED); PushEntryList(&HashTable->FreeList, &pE->NextEntry); return; } } ); return; } #if 0 ////// Currently Unused ////// VOID QHashDeleteNodeLock( IN PQHASH_TABLE HashTable, IN PQHASH_ENTRY BeforeNode, IN PQHASH_ENTRY TargetNode ) /*++ Routine Description: Delete the TargetNode entry in the hash table. This is a singly linked list so Before Node has to be adjusted. If BeforeNode is NULL then the TargetNode is head of the collision chain and is not deleted, instead the key is set to 0. Assumes the caller has the lock on the table and has not dropped the lock since getting the Node addresses. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. BeforeNode -- ptr to the QhashEntry before the node to be deleted. TargetNode -- ptr to the QhashEntry to delete. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "QHashDeleteNodeLock:" FRS_ASSERT(TargetNode != NULL); // // Special case for node that is part of the main hash vector. // if (BeforeNode == NULL) { TargetNode->QKey = QUADZERO; TargetNode->Flags = 0; return; } // // Unlink the target entry and put on the free list. // BeforeNode->NextEntry.Next = TargetNode->NextEntry.Next; TargetNode->NextEntry.Next = NULL; TargetNode->Flags = 0; PushEntryList(&HashTable->FreeList, &TargetNode->NextEntry); return; } #endif // 0 VOID QHashDeleteByFlags( IN PQHASH_TABLE HashTable, IN ULONG_PTR Flags ) /*++ Routine Description: Delete all entries in the hash table that match the given Flags argument. The table lock is acquired and released here. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. Flags -- Match key to select elements to delete. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "QHashDeleteByFlags:" PQHASH_ENTRY RowEntry; ULONG i; FRS_ASSERT(!IS_QHASH_LARGE_KEY(HashTable)); RowEntry = HashTable->HashRowBase; // // Loop through all the Hash table rows and delete each matching element. // QHashAcquireLock(HashTable); for (i=0; iNumberEntries; i++, RowEntry++) { if (RowEntry->Flags == Flags) { DPRINT5(5, "QHash DeleteByFlags (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, RowEntry, PRINTQUAD(RowEntry->QKey), PRINTQUAD(RowEntry->QData), RowEntry->Flags); RowEntry->QKey = QUADZERO; RowEntry->Flags = 0; } if (RowEntry->NextEntry.Next == NULL) { continue; } // // Scan the collision list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // Check for match. Remove from list and put on free list. // if (pE->Flags == Flags) { DPRINT5(5, "QHash DeleteByFlags (%08x): Entry: %08x Tag: %08lx %08lx, Data: %08lx %08lx, Flags: %08x\n", HashTable, pE, PRINTQUAD(pE->QKey), PRINTQUAD(pE->QData), pE->Flags); pE->Flags = 0; RemoveSingleListEntry(NOT_USED); PushEntryList(&HashTable->FreeList, &pE->NextEntry); } ); } QHashReleaseLock(HashTable); return; } VOID QHashEmptyLargeKeyTable( IN PQHASH_TABLE HashTable ) /*++ Routine Description: Delete all the large key nodes in the QHash table. Put all collision entries on the free list. The table lock is acquired and released here. Arguments: HashTable -- ptr to a PQHASH_TABLE struct. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "QHashEmptyLargeKeyTable:" PQHASH_ENTRY RowEntry; ULONG i; // // No work if not a large key table. // if (!IS_QHASH_LARGE_KEY(HashTable)) { return; } RowEntry = HashTable->HashRowBase; // // Loop through all the Hash table rows and delete each matching element. // QHashAcquireLock(HashTable); for (i=0; iNumberEntries; i++, RowEntry++) { if ((PVOID)RowEntry->Flags != NULL) { (HashTable->HashFree)((PVOID) (RowEntry->Flags)); } RowEntry->QKey = QUADZERO; RowEntry->Flags = 0; if (RowEntry->NextEntry.Next == NULL) { continue; } // // Scan the collision list. // Free the large key node and put qhash collision entries on free list. // ForEachSingleListEntry(&RowEntry->NextEntry, QHASH_ENTRY, NextEntry, // // The iterator pE is of type PQHASH_ENTRY. // if ((PVOID)RowEntry->Flags != NULL) { (HashTable->HashFree)((PVOID) (pE->Flags)); } pE->Flags = 0; RemoveSingleListEntry(NOT_USED); PushEntryList(&HashTable->FreeList, &pE->NextEntry); ); } QHashReleaseLock(HashTable); return; }