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.
320 lines
7.3 KiB
320 lines
7.3 KiB
/*++
|
|
|
|
Copyright (c) 1989-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
hash.c
|
|
|
|
Abstract:
|
|
|
|
Abstract Data Type: Hash
|
|
|
|
Author:
|
|
|
|
Jiandong Ruan
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "hash.h"
|
|
|
|
typedef struct SMB_HASH_TABLE {
|
|
KSPIN_LOCK Lock;
|
|
DWORD NumberOfBuckets;
|
|
PSMB_HASH HashFunc; // Hash function
|
|
PSMB_HASH_CMP CmpFunc; // Compare function
|
|
PSMB_HASH_DEL DelFunc; // Delete function: called before an entry is removed from the hash table
|
|
PSMB_HASH_ADD AddFunc; // Add function: called before an entry is added into the hash table
|
|
PSMB_HASH_REFERENCE RefFunc;
|
|
PSMB_HASH_DEREFERENCE DerefFunc;
|
|
PSMB_HASH_GET_KEY GetKeyFunc;
|
|
LIST_ENTRY Buckets[0];
|
|
} SMB_HASH_TABLE, *PSMB_HASH_TABLE;
|
|
|
|
VOID __inline
|
|
SmbLockHashTable(
|
|
PSMB_HASH_TABLE HashTbl,
|
|
KIRQL *Irql
|
|
)
|
|
{
|
|
KeAcquireSpinLock(&HashTbl->Lock, Irql);
|
|
}
|
|
|
|
|
|
VOID __inline
|
|
SmbUnlockHashTable(
|
|
PSMB_HASH_TABLE HashTbl,
|
|
KIRQL Irql
|
|
)
|
|
{
|
|
KeReleaseSpinLock(&HashTbl->Lock, Irql);
|
|
}
|
|
|
|
|
|
PSMB_HASH_TABLE
|
|
SmbCreateHashTable(
|
|
DWORD NumberOfBuckets,
|
|
PSMB_HASH HashFunc,
|
|
PSMB_HASH_GET_KEY GetKeyFunc,
|
|
PSMB_HASH_CMP CmpFunc,
|
|
PSMB_HASH_ADD AddFunc, // optional
|
|
PSMB_HASH_DEL DelFunc, // optional
|
|
PSMB_HASH_REFERENCE RefFunc, // optional
|
|
PSMB_HASH_DEREFERENCE DerefFunc // optional
|
|
)
|
|
{
|
|
PSMB_HASH_TABLE HashTbl = NULL;
|
|
DWORD i, Size = 0;
|
|
|
|
//
|
|
// To avoid overflow below, set a upperbound on the number of buckets
|
|
// 65536 is large enough!!!
|
|
//
|
|
if (0 == NumberOfBuckets || NumberOfBuckets >= 0x10000) {
|
|
goto error;
|
|
}
|
|
|
|
if (NULL == HashFunc || NULL == CmpFunc || NULL == GetKeyFunc) {
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Allocate memory
|
|
//
|
|
Size = sizeof(SMB_HASH_TABLE) + sizeof(LIST_ENTRY)*NumberOfBuckets;
|
|
HashTbl = ExAllocatePoolWithTag(NonPagedPool, Size, 'HBMS');
|
|
if (NULL == HashTbl) {
|
|
goto error;
|
|
}
|
|
RtlZeroMemory(HashTbl, Size);
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
KeInitializeSpinLock(&HashTbl->Lock);
|
|
HashTbl->NumberOfBuckets = NumberOfBuckets;
|
|
HashTbl->HashFunc = HashFunc;
|
|
HashTbl->CmpFunc = CmpFunc;
|
|
HashTbl->GetKeyFunc = GetKeyFunc;
|
|
HashTbl->DelFunc = DelFunc;
|
|
HashTbl->AddFunc = AddFunc;
|
|
HashTbl->RefFunc = RefFunc;
|
|
HashTbl->DerefFunc = DerefFunc;
|
|
for (i = 0; i < NumberOfBuckets; i++) {
|
|
InitializeListHead(&HashTbl->Buckets[i]);
|
|
}
|
|
|
|
error:
|
|
return HashTbl;
|
|
}
|
|
|
|
VOID
|
|
SmbDestroyHashTable(
|
|
PSMB_HASH_TABLE HashTbl
|
|
)
|
|
{
|
|
KIRQL Irql = 0;
|
|
DWORD i = 0;
|
|
PLIST_ENTRY entry = NULL;
|
|
|
|
if (NULL == HashTbl) {
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < HashTbl->NumberOfBuckets; i++) {
|
|
SmbLockHashTable(HashTbl, &Irql);
|
|
while(!IsListEmpty(&HashTbl->Buckets[i])) {
|
|
entry = RemoveHeadList(&HashTbl->Buckets[i]);
|
|
InitializeListHead(entry);
|
|
SmbUnlockHashTable(HashTbl, Irql);
|
|
if (HashTbl->DelFunc) {
|
|
HashTbl->DelFunc(entry);
|
|
}
|
|
SmbLockHashTable(HashTbl, &Irql);
|
|
}
|
|
SmbUnlockHashTable(HashTbl, Irql);
|
|
}
|
|
ExFreePool(HashTbl);
|
|
|
|
error:
|
|
return;
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
SmbLookupLockedHashTable(
|
|
PSMB_HASH_TABLE HashTbl,
|
|
PVOID Key,
|
|
DWORD Hash
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Look up hash table
|
|
Note: spinlock should held
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
NULL Not found
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pHead = NULL, FoundEntry = NULL;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
ASSERT (Hash < HashTbl->NumberOfBuckets);
|
|
pHead = HashTbl->Buckets[Hash].Flink;
|
|
while(pHead != &HashTbl->Buckets[Hash]) {
|
|
if (HashTbl->CmpFunc(pHead, Key) == 0) {
|
|
FoundEntry = pHead;
|
|
break;
|
|
}
|
|
pHead = pHead->Flink;
|
|
}
|
|
|
|
return FoundEntry;
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
SmbLookupHashTable(
|
|
PSMB_HASH_TABLE HashTbl,
|
|
PVOID Key
|
|
)
|
|
{
|
|
DWORD dwHash = 0;
|
|
KIRQL Irql = 0;
|
|
PLIST_ENTRY FoundEntry = NULL;
|
|
|
|
dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
|
|
|
|
SmbLockHashTable(HashTbl, &Irql);
|
|
FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
|
|
SmbUnlockHashTable(HashTbl, Irql);
|
|
|
|
return FoundEntry;
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
SmbLookupHashTableAndReference(
|
|
PSMB_HASH_TABLE HashTbl,
|
|
PVOID Key
|
|
)
|
|
{
|
|
DWORD dwHash = 0;
|
|
KIRQL Irql = 0;
|
|
PLIST_ENTRY FoundEntry = NULL;
|
|
|
|
if (NULL == HashTbl->RefFunc) {
|
|
return NULL;
|
|
}
|
|
dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
|
|
|
|
SmbLockHashTable(HashTbl, &Irql);
|
|
FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
|
|
if (FoundEntry) {
|
|
LONG RefCount = 0;
|
|
|
|
RefCount = HashTbl->RefFunc(FoundEntry);
|
|
ASSERT(RefCount > 1);
|
|
}
|
|
SmbUnlockHashTable(HashTbl, Irql);
|
|
|
|
return FoundEntry;
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
SmbAddToHashTable(
|
|
PSMB_HASH_TABLE HashTbl,
|
|
PLIST_ENTRY Entry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add the entry into hash table if it isn't in the hash table
|
|
Otherwise, increase the RefCount of the existing entry if RefFunc is set
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DWORD dwHash = 0;
|
|
KIRQL Irql = 0;
|
|
PVOID Key = NULL;
|
|
PLIST_ENTRY FoundEntry = NULL;
|
|
|
|
Key = HashTbl->GetKeyFunc(Entry);
|
|
dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
|
|
|
|
SmbLockHashTable(HashTbl, &Irql);
|
|
FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
|
|
if (NULL == FoundEntry) {
|
|
if (HashTbl->AddFunc) {
|
|
HashTbl->AddFunc(Entry);
|
|
}
|
|
InsertTailList(&HashTbl->Buckets[dwHash], Entry);
|
|
FoundEntry = Entry;
|
|
} else {
|
|
if (HashTbl->RefFunc) {
|
|
HashTbl->RefFunc(FoundEntry);
|
|
}
|
|
}
|
|
SmbUnlockHashTable(HashTbl, Irql);
|
|
return FoundEntry;
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
SmbRemoveFromHashTable(
|
|
PSMB_HASH_TABLE HashTbl,
|
|
PLIST_ENTRY Key
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrement the refcount of the entry. If the refoucne is zero, delete it.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DWORD dwHash = 0;
|
|
KIRQL Irql = 0;
|
|
PLIST_ENTRY FoundEntry = NULL;
|
|
|
|
dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
|
|
|
|
SmbLockHashTable(HashTbl, &Irql);
|
|
FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
|
|
if (FoundEntry) {
|
|
if (HashTbl->DerefFunc) {
|
|
LONG RefCount;
|
|
|
|
RefCount = HashTbl->DerefFunc(FoundEntry);
|
|
ASSERT(RefCount >= 0);
|
|
if (RefCount) {
|
|
FoundEntry = NULL;
|
|
}
|
|
}
|
|
|
|
if (FoundEntry) {
|
|
RemoveEntryList(FoundEntry);
|
|
InitializeListHead(FoundEntry);
|
|
if (HashTbl->DelFunc) {
|
|
HashTbl->DelFunc(FoundEntry);
|
|
}
|
|
}
|
|
}
|
|
SmbUnlockHashTable(HashTbl, Irql);
|
|
|
|
return FoundEntry;
|
|
}
|