windows-nt/Source/XPSP1/NT/windows/winstate/cobra/utils/memdb/hash.c
2020-09-26 16:20:57 +08:00

628 lines
14 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
hash.c
Abstract:
Hashing routines used to speed lookup of memdb keys.
Author:
Jim Schmidt (jimschm) 8-Aug-1996
Revision History:
Jim Schmidt (jimschm) 21-Oct-1997 Split from memdb.c
--*/
#include "pch.h"
#include "memdbp.h"
#define DBG_MEMDB "MemDb"
//
// #defines
//
#define HASH_BUCKETS 7001//4099
#define HASH_BLOCK_SIZE (HASH_BUCKETS * sizeof (BUCKETSTRUCT))
#define HASHBUFPTR(offset) ((PBUCKETSTRUCT) (pHashTable->Buf + offset))
typedef struct _tagHASHSTRUCT {
UINT Offset;
UINT NextItem;
} BUCKETSTRUCT, *PBUCKETSTRUCT;
typedef struct {
PBUCKETSTRUCT BucketPtr;
PBUCKETSTRUCT PrevBucketPtr;
UINT Bucket;
UINT LastOffset;
} HASHENUM, *PHASHENUM;
BOOL
EnumFirstHashEntry (
IN PMEMDBHASH pHashTable,
OUT PHASHENUM HashEnum
);
BOOL
EnumNextHashEntry (
IN PMEMDBHASH pHashTable,
IN OUT PHASHENUM HashEnum
);
//
// Local privates
//
VOID
pResetHashBlock (
IN PMEMDBHASH pHashTable
);
//
// Implementation
//
PMEMDBHASH
CreateHashBlock (
VOID
)
{
PMEMDBHASH pHashTable;
pHashTable = (PMEMDBHASH) MemAlloc (g_hHeap, 0, sizeof(MEMDBHASH));
if (!pHashTable) {
DEBUGMSG ((DBG_ERROR, "Could not allocate hash table!\n"));
return NULL;
}
pHashTable->Size = HASH_BLOCK_SIZE * 2;
pHashTable->Buf = (PBYTE) MemAlloc (g_hHeap, 0, pHashTable->Size);
if (!pHashTable->Buf) {
DEBUGMSG ((DBG_ERROR, "Could not allocate hash buffer!\n"));
MemFree (g_hHeap, 0, pHashTable);
return NULL;
}
pResetHashBlock(pHashTable);
return pHashTable;
}
VOID
pResetHashBlock (
IN PMEMDBHASH pHashTable
)
{
PBUCKETSTRUCT BucketPtr;
INT i;
pHashTable->End = HASH_BLOCK_SIZE;
pHashTable->FreeHead = INVALID_OFFSET;
BucketPtr = (PBUCKETSTRUCT) pHashTable->Buf;
for (i = 0 ; i < HASH_BUCKETS ; i++) {
BucketPtr->Offset = INVALID_OFFSET;
BucketPtr->NextItem = INVALID_OFFSET;
BucketPtr++;
}
}
VOID
FreeHashBlock (
IN PMEMDBHASH pHashTable
)
{
if (pHashTable->Buf) {
MemFree (g_hHeap, 0, pHashTable->Buf);
}
MemFree (g_hHeap, 0, pHashTable);
}
BOOL
EnumFirstHashEntry (
IN PMEMDBHASH pHashTable,
OUT PHASHENUM EnumPtr
)
{
ZeroMemory (EnumPtr, sizeof (HASHENUM));
return EnumNextHashEntry (pHashTable, EnumPtr);
}
BOOL
EnumNextHashEntry (
IN PMEMDBHASH pHashTable,
IN OUT PHASHENUM EnumPtr
)
{
for (;;) {
if (EnumPtr->Bucket == HASH_BUCKETS) {
//
// The completion case
//
return FALSE;
}
if (!EnumPtr->BucketPtr) {
//
// This case occurs when we are begining to enumerate a bucket
//
EnumPtr->BucketPtr = (PBUCKETSTRUCT) pHashTable->Buf + EnumPtr->Bucket;
if (EnumPtr->BucketPtr->Offset == INVALID_OFFSET) {
EnumPtr->BucketPtr = NULL;
EnumPtr->Bucket += 1;
continue;
}
//
// Return this first item in the bucket
//
EnumPtr->LastOffset = EnumPtr->BucketPtr->Offset;
return TRUE;
}
//
// This case occurs when we are continuing enumeration of a bucket
//
if (EnumPtr->BucketPtr->Offset == INVALID_OFFSET) {
//
// Current bucket item (and also the last bucket item) may have
// been deleted -- check that now
//
if (!EnumPtr->PrevBucketPtr) {
//
// Last item has been deleted; continue to next bucket
//
EnumPtr->BucketPtr = NULL;
EnumPtr->Bucket += 1;
continue;
}
//
// Previous bucket item is valid; use it.
//
EnumPtr->BucketPtr = EnumPtr->PrevBucketPtr;
} else {
//
// Current bucket item may have been deleted, but another item was
// moved to its place -- check that now
//
if (EnumPtr->BucketPtr->Offset != EnumPtr->LastOffset) {
EnumPtr->LastOffset = EnumPtr->BucketPtr->Offset;
return TRUE;
}
}
//
// We now know that the current bucket item was not changed, so it
// becomes our previous item and we move on to the next item (if
// one exists)
//
if (EnumPtr->BucketPtr->NextItem == INVALID_OFFSET) {
//
// End of bucket reached
//
EnumPtr->BucketPtr = NULL;
EnumPtr->Bucket += 1;
continue;
}
EnumPtr->PrevBucketPtr = EnumPtr->BucketPtr;
EnumPtr->BucketPtr = HASHBUFPTR (EnumPtr->BucketPtr->NextItem);
EnumPtr->LastOffset = EnumPtr->BucketPtr->Offset;
MYASSERT(EnumPtr->LastOffset != INVALID_OFFSET);
break;
}
return TRUE;
}
BOOL
WriteHashBlock (
IN PMEMDBHASH pHashTable,
IN OUT PBYTE *Buf
)
{
*(((PUINT)*Buf)++) = pHashTable->End;
*(((PUINT)*Buf)++) = pHashTable->FreeHead;
CopyMemory(*Buf, pHashTable->Buf, pHashTable->End);
*Buf += pHashTable->End;
return TRUE;
}
BOOL
ReadHashBlock (
IN PMEMDBHASH pHashTable,
IN OUT PBYTE *Buf
)
{
pHashTable->End = *(((PUINT)*Buf)++);
pHashTable->FreeHead = *(((PUINT)*Buf)++);
if (pHashTable->End > pHashTable->Size) {
//
// if the hash table in the file will not fit in the buffer
// already allocated, free current buffer and allocate new one.
//
MemFree (g_hHeap, 0, pHashTable->Buf);
pHashTable->Size = pHashTable->End;
pHashTable->Buf = (PBYTE) MemAlloc (g_hHeap, 0, pHashTable->Size);
}
CopyMemory(pHashTable->Buf, *Buf, pHashTable->End);
*Buf += pHashTable->End;
return TRUE;
}
UINT GetHashTableBlockSize (
IN PMEMDBHASH pHashTable
)
{
return 2*sizeof(UINT) + pHashTable->End;
}
UINT
pCalculateHashVal (
IN PCWSTR String
)
{
UINT Hash = 0;
while (*String) {
Hash = (Hash << 5) | (Hash >> 29);
Hash += towlower (*String);
String++;
}
Hash %= HASH_BUCKETS;
return Hash;
}
UINT
pAllocBucket (
IN PMEMDBHASH pHashTable
)
{
UINT rBucketOffset;
PBYTE TempBuf;
PBUCKETSTRUCT BucketPtr;
if (pHashTable->FreeHead != INVALID_OFFSET) {
rBucketOffset = pHashTable->FreeHead;
BucketPtr = HASHBUFPTR (rBucketOffset);
pHashTable->FreeHead = BucketPtr->NextItem;
MYASSERT (rBucketOffset < pHashTable->End);
} else {
if (pHashTable->End + sizeof (BUCKETSTRUCT) > pHashTable->Size) {
pHashTable->Size += HASH_BLOCK_SIZE;
TempBuf = MemReAlloc (g_hHeap, 0, pHashTable->Buf, pHashTable->Size);
DEBUGMSG ((DBG_NAUSEA, "Realloc'd memdb hash table"));
if (!TempBuf) {
DEBUGMSG ((DBG_ERROR, "Out of memory!"));
pHashTable->Size -= HASH_BLOCK_SIZE;
return INVALID_OFFSET;
}
pHashTable->Buf = TempBuf;
}
rBucketOffset = pHashTable->End;
pHashTable->End += sizeof (BUCKETSTRUCT);
BucketPtr = HASHBUFPTR (rBucketOffset);
}
BucketPtr->Offset = INVALID_OFFSET;
BucketPtr->NextItem = INVALID_OFFSET;
return rBucketOffset;
}
BOOL
AddHashTableEntry (
IN PMEMDBHASH pHashTable,
IN PCWSTR FullString,
IN UINT Offset
)
{
UINT Bucket;
PBUCKETSTRUCT BucketPtr, PrevBucketPtr;
UINT BucketOffset;
UINT NewOffset;
UINT PrevBucketOffset;
Bucket = pCalculateHashVal (FullString);
BucketPtr = (PBUCKETSTRUCT) pHashTable->Buf + Bucket;
//
// See if root bucket item has been used or not
//
if (BucketPtr->Offset != INVALID_OFFSET) {
//
// Yes - add to end of the chain
//
BucketOffset = Bucket * sizeof (BUCKETSTRUCT);
do {
BucketPtr = HASHBUFPTR (BucketOffset);
PrevBucketOffset = BucketOffset;
BucketOffset = BucketPtr->NextItem;
} while (BucketOffset != INVALID_OFFSET);
//
// Add to the chain
//
NewOffset = pAllocBucket(pHashTable);
PrevBucketPtr = HASHBUFPTR (PrevBucketOffset);
PrevBucketPtr->NextItem = NewOffset;
if (NewOffset == INVALID_OFFSET) {
return FALSE;
}
BucketPtr = HASHBUFPTR (NewOffset);
MYASSERT (BucketPtr->NextItem == INVALID_OFFSET);
}
BucketPtr->Offset = Offset;
#ifdef DEBUG
{
UINT HashOffset;
HashOffset = FindStringInHashTable (pHashTable, FullString);
MYASSERT (HashOffset != INVALID_OFFSET);
DEBUGMSG_IF ((HashOffset != Offset, DBG_MEMDB, "Duplicate in hash table: %s", FullString));
}
#endif
return TRUE;
}
PBUCKETSTRUCT
pFindBucketItemInHashTable (
IN PMEMDBHASH pHashTable,
IN PCWSTR FullString,
OUT PBUCKETSTRUCT *PrevBucketPtr, OPTIONAL
OUT PUINT HashOffsetPtr OPTIONAL
)
{
UINT Bucket;
UINT BucketOffset;
PBUCKETSTRUCT BucketPtr = NULL;
WCHAR TempStr[MEMDB_MAX];
Bucket = pCalculateHashVal (FullString);
BucketOffset = Bucket * sizeof (BUCKETSTRUCT);
#ifdef DEBUG
{
//
// Circular link check
//
UINT Prev, Next;
UINT Turtle, Rabbit;
BOOL Even = FALSE;
Rabbit = BucketOffset;
Turtle = Rabbit;
while (Rabbit != INVALID_OFFSET) {
// Make rabbit point to next item in chain
Prev = Rabbit;
BucketPtr = HASHBUFPTR (Rabbit);
Rabbit = BucketPtr->NextItem;
// We should always be ahead of the turtle
if (Rabbit == Turtle) {
BucketPtr = HASHBUFPTR (Rabbit);
Next = BucketPtr->NextItem;
DEBUGMSG ((
DBG_WHOOPS,
"Circular link detected in memdb hash table! Turtle=%u, Rabbit=%u, Next=%u, Prev=%u",
Turtle,
Rabbit,
Next,
Prev
));
return NULL;
}
// Make turtle point to next item in chain (1 of every 2 passes)
if (Even) {
BucketPtr = HASHBUFPTR (Turtle);
Turtle = BucketPtr->NextItem;
}
Even = !Even;
}
}
#endif
BucketPtr = HASHBUFPTR (BucketOffset);
if (PrevBucketPtr) {
*PrevBucketPtr = BucketPtr;
}
//
// If root bucket is not empty, scan bucket for FullString
//
if (BucketPtr->Offset != INVALID_OFFSET) {
do {
BucketPtr = HASHBUFPTR (BucketOffset);
//
// Build string using offset
//
PrivateBuildKeyFromIndex (
0,
BucketPtr->Offset,
TempStr,
NULL,
NULL,
NULL
);
//
// Do compare and return if match is found
//
if (StringIMatchW (FullString, TempStr)) {
if (HashOffsetPtr) {
*HashOffsetPtr = BucketOffset;
}
return BucketPtr;
}
if (PrevBucketPtr) {
*PrevBucketPtr = BucketPtr;
}
BucketOffset = BucketPtr->NextItem;
} while (BucketOffset != INVALID_OFFSET);
}
return NULL;
}
UINT
FindStringInHashTable (
IN PMEMDBHASH pHashTable,
IN PCWSTR FullString
)
{
PBUCKETSTRUCT BucketPtr;
BucketPtr = pFindBucketItemInHashTable (pHashTable, FullString, NULL, NULL);
if (BucketPtr) {
return BucketPtr->Offset;
}
return INVALID_OFFSET;
}
BOOL
RemoveHashTableEntry (
IN PMEMDBHASH pHashTable,
IN PCWSTR FullString
)
{
PBUCKETSTRUCT BucketPtr;
PBUCKETSTRUCT PrevBucketPtr;
UINT NextOffset;
PBUCKETSTRUCT NextBucketPtr;
UINT BucketOffset;
BucketPtr = pFindBucketItemInHashTable (pHashTable, FullString, &PrevBucketPtr, &BucketOffset);
if (!BucketPtr) {
return FALSE;
}
if (PrevBucketPtr != BucketPtr) {
//
// If not at the first level (prev != current), give the block
// to free space.
//
PrevBucketPtr->NextItem = BucketPtr->NextItem;
BucketPtr->NextItem = pHashTable->FreeHead;
BucketPtr->Offset = INVALID_OFFSET;
pHashTable->FreeHead = BucketOffset;
} else {
//
// Invalidate next item pointer if at the first level
//
if (BucketPtr->NextItem != INVALID_OFFSET) {
//
// Copy next item to root array
//
NextOffset = BucketPtr->NextItem;
NextBucketPtr = HASHBUFPTR (NextOffset);
CopyMemory (BucketPtr, NextBucketPtr, sizeof (BUCKETSTRUCT));
//
// Donate next item to free space
//
NextBucketPtr->NextItem = pHashTable->FreeHead;
NextBucketPtr->Offset = INVALID_OFFSET;
pHashTable->FreeHead = NextOffset;
} else {
//
// Delete of last item in bucket -- invalidate the root array item
//
BucketPtr->NextItem = INVALID_OFFSET;
BucketPtr->Offset = INVALID_OFFSET;
}
}
return TRUE;
}