windows-nt/Source/XPSP1/NT/termsrv/remdsk/rds/t120/common/hash.cpp
2020-09-26 16:20:57 +08:00

644 lines
15 KiB
C++

/* hash.cpp
*
* Copyright (c) 1996 by Microsoft Corporation
*
* Written by: Christos Tsollis
*
* Revisions:
*
* Abstract:
*
* This is the implementation of a dictionary data structure.
*
*/
#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_UTILITY);
#define MyMalloc(size) new BYTE[size]
#define MyFree(ptr) (delete [] (BYTE *) (ptr))
#define Max(a,b) (((a) > (b)) ? (a) : (b))
/* Function: Constructor
*
* Parameters:
* num_of_buckets: Number of buckets in the dictionary
* dtype: Dictionary type
*
* Return value:
* None
*/
DictionaryClass::DictionaryClass (ULong num_of_buckets, DictionaryType dtype) :
Type (dtype), NumOfExternItems (0)
{
DWORD i;
PDICTIONARY_ITEM p; // Goes through the initially allocated dictionary items to initialize the stack
NumOfBuckets = Max (num_of_buckets, DEFAULT_NUMBER_OF_BUCKETS);
/* Allocate the space needed for the dictionary */
dwNormalSize = NumOfBuckets * (4 * sizeof (PDICTIONARY_ITEM) + 3 * sizeof (DICTIONARY_ITEM));
Buckets = (PDICTIONARY_ITEM *) MyMalloc (dwNormalSize);
if (Buckets == NULL)
return;
/* Initialize the Buckets */
for (i = 0; i < NumOfBuckets; i++)
Buckets[i] = NULL;
// Initialize the class iterator
pCurrent = NULL;
/* Initialize the Dictionary items array.
* This is a stack of pointers to the real dictionary items. The stack is initialized with
* the addresses of the dictionary items
*/
ItemArray = (PDICTIONARY_ITEM *) ((PBYTE) Buckets + NumOfBuckets * sizeof (PDICTIONARY_ITEM));
ItemCount = 3 * NumOfBuckets;
p = (PDICTIONARY_ITEM) (ItemArray + ItemCount);
for (i = 0; i < ItemCount; i++)
ItemArray[i] = p++;
}
/* Function: Copy Constructor
*
* Parameters:
* original: The original dictionary to make a copy of
*
* Return value:
* None
*
* Note:
* This copy constructor will work ONLY for DWORD_DICTIONARY dictionaries.
* It will NOT work for the string dictionary types.
*/
DictionaryClass::DictionaryClass (const DictionaryClass& original)
{
DWORD i;
PDICTIONARY_ITEM p, q, r;
NumOfBuckets = original.NumOfBuckets;
Type = original.Type;
NumOfExternItems = original.NumOfExternItems;
// Allocate the space needed for the dictionary
dwNormalSize = original.dwNormalSize;
Buckets = (PDICTIONARY_ITEM *) MyMalloc (dwNormalSize);
if (Buckets == NULL)
return;
// Initialize the class iterator
pCurrent = NULL;
/* Initialize the Dictionary items array */
ItemArray = (PDICTIONARY_ITEM *) ((PBYTE) Buckets + NumOfBuckets * sizeof (PDICTIONARY_ITEM));
ItemCount = 3 * NumOfBuckets;
// Traverse the whole original hash sturcture to create the copy
// p: goes through the original items
// q: goes through current instance's items and initializes them
// r: remembers the previous item in the new instance so that its "next" field could be set
q = (PDICTIONARY_ITEM) (ItemArray + ItemCount);
for (q--, i = 0; i < NumOfBuckets; i++) {
for (r = NULL, p = original.Buckets[i]; p != NULL; p = p->next) {
// Check if there are unused items in the current dictionary
if (ItemCount <= 0) {
q = (PDICTIONARY_ITEM) MyMalloc (sizeof (DICTIONARY_ITEM));
if (q == NULL)
break;
}
else {
ItemCount--;
q++;
}
q->value = p->value;
q->key = p->key;
if (p == original.Buckets[i])
Buckets[i] = q;
else
r->next = q;
r = q;
}
// Set the "next" field for the last item in the bucket
if (r == NULL)
Buckets[i] = NULL;
else
r->next = NULL;
}
// Initialize the rest of the ItemArray array
for (i = 0; i < ItemCount; i++)
ItemArray[i] = q++;
}
/* Function: Destructor
*
* Parameters:
* None.
*
* Return value:
* None
*
*/
DictionaryClass::~DictionaryClass ()
{
DWORD i;
DWORD dwOffset; // Offset of the dictionary item. If the offset does not indicate
// that the item is from the initially allocated array, it has to
// be freed.
PDICTIONARY_ITEM p, q;
if (Buckets != NULL) {
// Go through the buckets to free the non-native items
for (i = 0; i < NumOfBuckets; i++)
for (p = Buckets[i]; p != NULL; ) {
if (Type >= STRING_DICTIONARY)
MyFree (p->key);
dwOffset = (PBYTE) p - (PBYTE) Buckets;
if (dwOffset >= 0 && dwOffset < dwNormalSize)
p = p->next;
else {
// if the item was not allocated in the initialization, we should free it.
q = p;
p = p->next;
MyFree (q);
}
}
MyFree (Buckets);
Buckets = NULL;
}
}
/* Function: hashFunction
*
* Parameters:
* key: The key value
*
* Return value:
* An integer in the range [0..NumOfBuckets-1] that indicates the bucket used for the "key".
*
*/
DWORD DictionaryClass::hashFunction (DWORD key)
{
if (Type < STRING_DICTIONARY)
return (key % NumOfBuckets);
else
return (*((unsigned char *) key) % NumOfBuckets);
}
/* Function: LengthStrcmp
*
* Parameters:
* DictionaryKey: Pointer to dictionary storage that keeps a length-sensitive string (which
* is a length followed by a string of that length.
* ChallengeKey: Pointer to the length-sensitive key that is compared with the "DictionaryKey"
* length: The length of the "ChallengeKey" string
*
* Return value:
* 0 if the "DictionaryKey" and "ChallengeKey" strings are the same. 1, otherwise.
*
* Note:
* This function is only used for dictionaries of type LENGTH_STRING_DICTIONARY.
*/
int DictionaryClass::LengthStrcmp (DWORD DictionaryKey, DWORD ChallengeKey, ULong length)
{
ULong i;
char *pDictionaryChar; // Goes through the dictionary key string
char *pChallengeChar; // Goes through the challenge string
// Compare the lengths first
if (length != * (ULong *) DictionaryKey)
return 1;
// Now, compare the strings themselves
pDictionaryChar = (char *) (DictionaryKey + sizeof (ULong));
pChallengeChar = (char *) ChallengeKey;
for (i = 0; i < length; i++)
if (*pDictionaryChar++ != *pChallengeChar++)
return 1;
return 0;
}
/* Function: insert
* Inserts (key, value) pair in the dictionary
*
* Parameters:
* new_key: The new key that has to be inserted in the dictionary
* new_value: The value associated with the "new_key"
* length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of the new key
*
* Return value:
* TRUE, if the operation succeeds, FALSE, otherwise.
*
* Note:
* If the "new_key" is already in the dictionary, the (new_key, new_value) pair is NOT
* inserted (the dictionary remains the same), and the return value is TRUE.
*/
BOOL DictionaryClass::insert (DWORD new_key, DWORD new_value, ULong length)
{
PDICTIONARY_ITEM pNewItem; // Points to the allocated new dictionary item
PDICTIONARY_ITEM p; // Goes through the bucket for the "new_key", searching for "new_key"
DWORD hash_val; // The bucket ID for "new_key"
BOOL bIsNative = TRUE; // TRUE, if the new allocated dictionary item is from the cache, FALSE otherwise
if (Buckets == NULL)
return FALSE;
// Find if the item is already in the bucket, and if it's not, where it will get added.
p = Buckets[hash_val = hashFunction (new_key)];
ASSERT (hash_val >= 0 && hash_val < NumOfBuckets);
if (p != NULL) {
switch (Type) {
#if 0
case STRING_DICTIONARY:
ASSERT (length == 0);
for (; lstrcmp ((LPCTSTR) p->key, (LPCTSTR) new_key) && p->next != NULL; p = p->next);
if (0 == lstrcmp ((LPCTSTR) p->key, (LPCTSTR) new_key)) {
// the key already exists; no need to add it
return TRUE;
}
break;
#endif
case LENGTH_STRING_DICTIONARY:
ASSERT (length > 0);
for (; LengthStrcmp (p->key, new_key, length) && p->next != NULL; p = p->next);
if (0 == LengthStrcmp (p->key, new_key, length)) {
// the key already exists; no need to add it
return TRUE;
}
break;
default:
ASSERT (length == 0);
for (; p->key != new_key && p->next != NULL; p = p->next);
if (p->key == new_key) {
// the key already exists; no need to add it
return TRUE;
}
break;
}
}
// Allocate the new item
if (ItemCount > 0)
pNewItem = ItemArray[--ItemCount]; // from the cache
else { // the cache is empty, we have to malloc the new item
pNewItem = (PDICTIONARY_ITEM) MyMalloc (sizeof (DICTIONARY_ITEM));
if (pNewItem != NULL) {
bIsNative = FALSE;
NumOfExternItems++;
}
else {
return FALSE;
}
}
ASSERT (pNewItem != NULL);
// Fill in the "key" field of the new item
switch (Type) {
#if 0
case STRING_DICTIONARY:
ASSERT (length == 0);
pNewItem->key = (DWORD) MyMalloc ((lstrlen ((LPCTSTR) new_key) + 1) * sizeof(TCHAR));
if (pNewItem->key == (DWORD) NULL) {
if (bIsNative == FALSE) {
// We have to free the allocated hash item
MyFree (pNewItem);
NumOfExternItems--;
}
else
ItemCount++;
return FALSE;
}
lstrcpy ((LPTSTR) pNewItem->key, (LPCTSTR) new_key);
break;
#endif
case LENGTH_STRING_DICTIONARY:
ASSERT (length > 0);
pNewItem->key = (DWORD) MyMalloc (sizeof (ULong) + length * sizeof (TCHAR));
if (pNewItem->key != (DWORD) NULL) {
* ((ULong *) (pNewItem->key)) = length;
memcpy ((void *) (pNewItem->key + sizeof (ULong)), (void *) new_key, length * sizeof (TCHAR));
}
else {
if (bIsNative == FALSE) {
// We have to free the allocated hash item
MyFree (pNewItem);
NumOfExternItems--;
}
else
ItemCount++;
return FALSE;
}
break;
default:
ASSERT (length == 0);
pNewItem->key = new_key;
break;
}
// Fill in the rest of the fields of the new item
pNewItem->value = new_value;
pNewItem->next = NULL;
// Insert the item in its bucket
if (p == NULL)
Buckets[hash_val] = pNewItem;
else
p->next = pNewItem;
return TRUE;
}
/* Function: remove
* Removes (key, value) pair from the dictionary
*
* Parameters:
* Key: The key that has to be removed from the dictionary
* length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of the "Key"
*
* Return value:
* None.
*
*/
BOOL DictionaryClass::remove (DWORD Key, ULong length)
{
PDICTIONARY_ITEM p, q; // They go through the dictionary items in "Key"'s bucket.
// p: points to the current dictionary item in the bucket
// q: points to the previous item
DWORD hash_val; // The bucket ID for "Key"
if (Buckets != NULL) {
// Find the item in the dictionary
p = Buckets [hash_val = hashFunction (Key)];
ASSERT (hash_val >= 0 && hash_val < NumOfBuckets);
switch (Type) {
#if 0
case STRING_DICTIONARY:
ASSERT (length == 0);
for (q = NULL; p != NULL && lstrcmp ((LPCTSTR) p->key, (LPCTSTR) Key); p = (q = p)->next) ;
break;
#endif
case LENGTH_STRING_DICTIONARY:
ASSERT (length > 0);
for (q = NULL; p != NULL && LengthStrcmp (p->key, Key, length); p = (q = p)->next) ;
break;
default:
ASSERT (length == 0);
for (q = NULL; p != NULL && p->key != Key; p = (q = p)->next);
break;
}
// Remove the item
if (p != NULL) {
if (q == NULL)
Buckets[hash_val] = p->next;
else
q->next = p->next;
// Free the item found
ASSERT (p != NULL);
if (Type >= STRING_DICTIONARY)
MyFree (p->key);
hash_val = (PBYTE) p - (PBYTE) Buckets;
if (hash_val >= 0 && hash_val < dwNormalSize)
ItemArray[ItemCount++] = p;
else {
MyFree (p);
NumOfExternItems--;
}
return TRUE;
}
}
return FALSE;
}
/* Function: find
* Looks up the key in the dictionary
*
* Parameters
* Key: The key to lookup
* pValue: Pointer to receive the value associated with "Key"
* It can be NULL, if we just try to find whether "Key" is in the dictionary
* length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of "Key"
*
* Return value:
* FALSE, if "Key" is not found in the dictionary
* TRUE, otherwise.
*/
BOOL DictionaryClass::find (DWORD Key, LPDWORD pValue, ULong length)
{
PDICTIONARY_ITEM p; // Goes through the dictionary items in "Key"'s bucket.
if (Buckets != NULL) {
// Find the item in the dictionary
p = Buckets [hashFunction (Key)];
switch (Type) {
#if 0
case STRING_DICTIONARY:
ASSERT (length == 0);
for (; p != NULL && lstrcmp ((LPCTSTR) p->key, (LPCTSTR) Key); p = p->next) ;
break;
#endif
case LENGTH_STRING_DICTIONARY:
ASSERT (length > 0);
for (; p != NULL && LengthStrcmp (p->key, Key, length); p = p->next) ;
break;
default:
ASSERT (length == 0);
for (; p != NULL && p->key != Key; p = p->next);
break;
}
if (p != NULL) {
// "Key" was found
if (pValue != NULL)
*pValue = p->value;
return TRUE;
}
}
if (pValue != NULL)
*pValue = 0;
return FALSE;
}
LPVOID DictionaryClass::Find(DWORD Key, UINT length)
{
LPVOID Val;
return find(Key, (LPDWORD) &Val, (ULONG) length) ? Val : NULL;
}
/* Function: iterate
* Iterates through the items of a dictionary. It remembers where it has
* stopped during the last call and starts from there.
*
* Parameters
* pValue: Pointer to DWORD that will hold the next value from the dictionary.
* It can be NULL.
* pKey: Pointer to DWORD or unsigned short value to receive the key associated with the value.
* It can be NULL.
*
* Return value:
* FALSE, when we reach the end of the dictionary
* TRUE, otherwise. Then, *pKey and *pValue are valid
*
*/
BOOL DictionaryClass::iterate (LPDWORD pValue, LPDWORD pKey)
{
if (Buckets != NULL) {
if (pCurrent) {
pCurrent = pCurrent->next;
}
else {
// start from the 1st item in the dictionary
pCurrent = Buckets[ulCurrentBucket = 0];
}
// Advance "pCurrent" until it's not NULL, or we reach the end of the dictionary.
for (; ulCurrentBucket < NumOfBuckets; pCurrent = Buckets[++ulCurrentBucket]) {
if (pCurrent != NULL) {
// we found the next item
if (pKey)
*pKey = pCurrent->key;
if (pValue)
*pValue = pCurrent->value;
return TRUE;
}
}
}
pCurrent = NULL;
return FALSE;
}
LPVOID DictionaryClass::Iterate(LPDWORD pKey)
{
LPVOID Val;
return iterate((LPDWORD) &Val, pKey) ? Val : NULL;
}
/* Function: isEmpty
*
* Parameters
* None.
*
* Return value:
* TRUE, if the dictionary is empty. FALSE, otherwise.
*
*/
BOOL DictionaryClass::isEmpty (void)
{
DWORD i;
if (Buckets != NULL) {
for (i = 0; i < NumOfBuckets; i++)
if (Buckets[i] != NULL)
return FALSE;
}
return TRUE;
}
/* Function: clear
* Clears up the dictionary. No (key, value) pairs are left in the dictionary.
*
* Parameters:
* None.
*
* Return value:
* None.
*
*/
void DictionaryClass::clear (void)
{
DWORD i; // Goes through the "Buckets" array
DWORD dwOffset; // The offset of a dictionary item is used to determine
// whether it's a native item (that needs to be returned to the cache),
// or not (and needs to be freed).
PDICTIONARY_ITEM p, q; // Go through the items in a bucket
if (Buckets != NULL) {
// Go through the buckets to free the non-native items
for (i = 0; i < NumOfBuckets; i++) {
for (p = Buckets[i]; p != NULL; ) {
if (Type >= STRING_DICTIONARY)
MyFree (p->key);
// Compute the offset of the current dictionary item
dwOffset = (PBYTE) p - (PBYTE) Buckets;
if (dwOffset >= 0 && dwOffset < dwNormalSize)
p = p->next;
else {
// if the item was not allocated in the initialization, we should free it.
q = p;
p = p->next;
MyFree (q);
}
}
Buckets[i] = NULL;
}
// Initialize the class iterator
pCurrent = NULL;
/* Initialize the Dictionary items array */
ItemCount = 3 * NumOfBuckets;
p = (PDICTIONARY_ITEM) (ItemArray + ItemCount);
for (i = 0; i < ItemCount; i++)
ItemArray[i] = p++;
NumOfExternItems = 0;
}
}