508 lines
14 KiB
C++
508 lines
14 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
bshash.h
|
|
|
|
Abstract:
|
|
|
|
Template for a hash table class.
|
|
|
|
Author:
|
|
|
|
Stefan R. Steiner [SSteiner] 1-Mar-1998
|
|
|
|
Revision History:
|
|
|
|
3/9/2000 SSteiner Converted it for use with fsdump
|
|
10/27/1999 aoltean Took it from bscommon and remove the critical section.
|
|
|
|
--*/
|
|
|
|
|
|
#ifndef _H_BS_HASH_
|
|
#define _H_BS_HASH_
|
|
|
|
#define BSHASHMAP_NO_ERROR 0
|
|
#define BSHASHMAP_ALREADY_EXISTS 1
|
|
#define BSHASHMAP_OUT_OF_MEMORY 2
|
|
//
|
|
// Forward defines
|
|
//
|
|
|
|
template< class KeyType, class ValueType > class TBsHashMapBucket;
|
|
template< class KeyType, class ValueType > class TBsHashMapBucketElem;
|
|
|
|
//
|
|
// The equality test
|
|
//
|
|
inline BOOL AreKeysEqual( const PSID& lhK, const PSID& rhK )
|
|
{
|
|
return ( ::EqualSid( lhK, rhK ) );
|
|
}
|
|
|
|
inline BOOL AreKeysEqual( const LPCWSTR& lhK, const LPCWSTR& rhK )
|
|
{
|
|
return (::wcscmp(lhK, rhK) == 0);
|
|
}
|
|
|
|
template < class KeyType >
|
|
inline BOOL AreKeysEqual( const KeyType& lhK, const KeyType& rhK )
|
|
{
|
|
return ( ::memcmp( &lhK, &rhK, sizeof KeyType ) == 0 );
|
|
// return lhK == rhK;
|
|
}
|
|
|
|
//
|
|
// Some possible hash table sizes
|
|
//
|
|
#define BSHASHMAP_HUGE 65521
|
|
#define BSHASHMAP_LARGE 4091
|
|
#define BSHASHMAP_MEDIUM 211
|
|
#define BSHASHMAP_SMALL 23
|
|
|
|
//
|
|
// template< class KeyType, class ValueType > class bshashmap
|
|
//
|
|
// TBsHashMap template. Uses a hash table to maintain a mapping of KeyType
|
|
// keys to ValueType values.
|
|
//
|
|
//template < class KeyType, class ValueType > typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
|
|
|
|
/*
|
|
Hash table class. methods hash the key value to the correct bucket, the bucket
|
|
class methods then operate on the element list associated with the bucket.
|
|
*/
|
|
template < class KeyType, class ValueType >
|
|
class TBsHashMap
|
|
{
|
|
public:
|
|
typedef LONG ( *PFN_HASH_FUNC )( const KeyType& Key, LONG NumBuckets );
|
|
typedef TBsHashMapBucket< KeyType, ValueType > BucketType;
|
|
typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
|
|
|
|
TBsHashMap( LONG NumBuckets = BSHASHMAP_SMALL, PFN_HASH_FUNC pfHashFunc = DefaultHashFunc )
|
|
: m_pfHashFunc( pfHashFunc ),
|
|
m_cNumBuckets( NumBuckets ),
|
|
m_cNumElems( 0 )
|
|
{
|
|
m_pHashTab = new BucketType [ m_cNumBuckets ];
|
|
if ( m_pHashTab == NULL ) {
|
|
m_cNumBuckets = 0;
|
|
throw E_OUTOFMEMORY; // fix future prefix bug
|
|
}
|
|
m_pElemEnum = NULL;
|
|
m_bInEnum = FALSE;
|
|
}
|
|
|
|
virtual ~TBsHashMap()
|
|
{
|
|
|
|
|
|
Unlock(); // unlock the CS from either StartEnum() or TryEnterCriticalSection()
|
|
|
|
//
|
|
// First go through the double-linked list and delete all of the elements
|
|
//
|
|
|
|
for ( ElemType *pElem = m_ElemChainHead.m_pForward, *pNextElem = pElem->m_pForward;
|
|
pElem != &m_ElemChainHead;
|
|
pElem = pNextElem, pNextElem = pNextElem->m_pForward )
|
|
delete pElem;
|
|
delete [] m_pHashTab;
|
|
}
|
|
|
|
// Clear all entries
|
|
void Clear()
|
|
{
|
|
if ( m_cNumElems == 0 )
|
|
return; // no work to do
|
|
Lock();
|
|
for ( ElemType *pElem = m_ElemChainHead.m_pForward, *pNextElem = pElem->m_pForward;
|
|
pElem != &m_ElemChainHead;
|
|
pElem = pNextElem, pNextElem = pNextElem->m_pForward )
|
|
delete pElem;
|
|
delete [] m_pHashTab;
|
|
m_pHashTab = new BucketType [ m_cNumBuckets ];
|
|
if ( m_pHashTab == NULL ) {
|
|
m_cNumBuckets = 0;
|
|
throw E_OUTOFMEMORY; // fix future prefix bug
|
|
}
|
|
m_pElemEnum = NULL;
|
|
m_cNumElems = 0;
|
|
Unlock();
|
|
}
|
|
|
|
// returns:
|
|
// BSHASHMAP_NO_ERROR - successful completion
|
|
// BSHASHMAP_OUT_OF_MEMORY - out of memory
|
|
// BSHASHMAP_ALREADY_EXISTS - Key already exists in map. Old value is
|
|
// replaced by passed in Value.
|
|
|
|
LONG Insert(
|
|
IN const KeyType& Key,
|
|
IN const ValueType& Value,
|
|
OUT void **ppCookie = NULL
|
|
)
|
|
{
|
|
Lock();
|
|
LONG status;
|
|
LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
|
|
|
|
assert( hashVal % m_cNumBuckets == hashVal );
|
|
|
|
status = m_pHashTab[ hashVal ].Insert( Key, Value, &m_ElemChainHead );
|
|
|
|
if ( status == BSHASHMAP_NO_ERROR ) {
|
|
++m_cNumElems;
|
|
if ( ppCookie != NULL )
|
|
*ppCookie = ( void * )m_ElemChainHead.m_pBackward;
|
|
}
|
|
Unlock();
|
|
return status;
|
|
}
|
|
|
|
// Erase an entry. Returns TRUE if it succeeds.
|
|
BOOL Erase( const KeyType& Key )
|
|
{
|
|
Lock();
|
|
BOOL erased = FALSE;
|
|
LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
|
|
|
|
assert( hashVal % m_cNumBuckets == hashVal );
|
|
|
|
erased = m_pHashTab[ hashVal ].Erase( Key, &m_ElemChainHead );
|
|
if ( erased ) {
|
|
--m_cNumElems;
|
|
}
|
|
Unlock();
|
|
return erased;
|
|
}
|
|
|
|
// Erase by cookie
|
|
BOOL EraseByCookie( void *pCookie )
|
|
{
|
|
Lock();
|
|
|
|
BucketType::EraseElement( ( ElemType *)pCookie );
|
|
--m_cNumElems;
|
|
|
|
Unlock();
|
|
return TRUE;
|
|
}
|
|
|
|
// Find an entry. Returns TRUE if it succeeds. pValue may be NULL, in
|
|
// which case this method is just a test of existence
|
|
BOOL Find( const KeyType& Key, ValueType *pValue = NULL )
|
|
{
|
|
Lock();
|
|
ElemType *pElem;
|
|
LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
|
|
BOOL found = FALSE;
|
|
|
|
assert( hashVal % m_cNumBuckets == hashVal );
|
|
|
|
found = m_pHashTab[ hashVal ].Find( Key, &pElem );
|
|
if ( found && pValue != NULL ) {
|
|
*pValue = pElem->m_Value;
|
|
}
|
|
Unlock();
|
|
return found;
|
|
}
|
|
|
|
// Find an entry and return a pointer to the value to allow inplace update. The
|
|
// caller must call Unlock() when finished with the Value item. If the item is
|
|
// not found, this method returns FALSE and hash table is not locked.
|
|
BOOL FindForUpdate( const KeyType& Key, ValueType **ppValue )
|
|
{
|
|
Lock();
|
|
ElemType *pElem;
|
|
LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
|
|
BOOL found = FALSE;
|
|
|
|
assert( hashVal % m_cNumBuckets == hashVal );
|
|
|
|
found = m_pHashTab[ hashVal ].Find( Key, &pElem );
|
|
if ( found ) {
|
|
*ppValue = &(pElem->m_Value);
|
|
} else
|
|
Unlock(); // Item not found so unlock the table
|
|
return found;
|
|
}
|
|
|
|
// Default hash function
|
|
static LONG DefaultHashFunc( const KeyType &Key, LONG NumBuckets )
|
|
{
|
|
const BYTE *pByteKey = (BYTE *)&Key;
|
|
LONG dwHashVal = 0;
|
|
|
|
for ( LONG i = 0; i < sizeof KeyType; ++i ) {
|
|
dwHashVal += pByteKey[i];
|
|
}
|
|
// wprintf( L"Key: dwSerialNum: %u, hashed to: %u\n", Key.m_dwVolSerialNumber, dwHashVal % NumBuckets );
|
|
// cout << "Key: " << Key << " hashed to: " << dwHashVal % NumBuckets << endl;
|
|
return dwHashVal % NumBuckets;
|
|
}
|
|
|
|
// Start enumerating all entries in the hash table. Always returns TRUE.
|
|
// Sets the index to the first element in the list. Lock() calls
|
|
// EnteringCriticalSection.
|
|
BOOL StartEnum()
|
|
{
|
|
assert( m_bInEnum == FALSE );
|
|
Lock(); // Enumerating the table locks out all other threads
|
|
m_pElemEnum = m_ElemChainHead.m_pForward; // Start at the head of the double-linked list
|
|
m_bInEnum = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
// Returns the value of the current entry, and then moves the index
|
|
// to the next item in the list. Must call StartEnum() first.
|
|
BOOL GetNextEnum( KeyType *pKey, ValueType *pValue )
|
|
{
|
|
assert( m_bInEnum == TRUE );
|
|
if ( m_pElemEnum == &m_ElemChainHead )
|
|
return FALSE; // Finished enumerating
|
|
*pKey = m_pElemEnum->m_Key;
|
|
*pValue = m_pElemEnum->m_Value;
|
|
m_pElemEnum = m_pElemEnum->m_pForward;
|
|
return TRUE;
|
|
}
|
|
|
|
// End enumerating the table. This function must be called when finished,
|
|
// otherwise other threads will not be able to get past the critical section,
|
|
// because the Unlock() call below, calls LeavingCriticalSection().
|
|
BOOL EndEnum()
|
|
{
|
|
assert( m_bInEnum == TRUE );
|
|
m_pElemEnum = NULL;
|
|
m_bInEnum = FALSE;
|
|
Unlock();
|
|
return TRUE;
|
|
}
|
|
|
|
LONG Size()
|
|
{
|
|
return m_cNumElems;
|
|
}
|
|
|
|
LONG NumBuckets()
|
|
{
|
|
return m_cNumBuckets;
|
|
}
|
|
|
|
inline void Lock()
|
|
{
|
|
}
|
|
|
|
inline void Unlock()
|
|
{
|
|
}
|
|
|
|
private:
|
|
BucketType *m_pHashTab;
|
|
LONG m_cNumBuckets;
|
|
LONG m_cNumElems;
|
|
ElemType m_ElemChainHead; // head of double-linked list of all elements
|
|
ElemType *m_pElemEnum; // Current position of the enumeration
|
|
BOOL m_bInEnum; // true StartEnum() was called and EndEnum() hasn't
|
|
PFN_HASH_FUNC m_pfHashFunc;
|
|
};
|
|
|
|
/*
|
|
Hash bucket class. Methods operate on the element list associated with the hash bucket
|
|
*/
|
|
template < class KeyType, class ValueType >
|
|
class TBsHashMapBucket {
|
|
friend class TBsHashMap< KeyType, ValueType >;
|
|
|
|
private:
|
|
typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
|
|
TBsHashMapBucket( )
|
|
{
|
|
m_pHead = NULL; // done here to allow for easier debugging
|
|
}
|
|
|
|
virtual ~TBsHashMapBucket( )
|
|
{
|
|
;
|
|
} // -- not really needed; however, if commented out, memory exception occurs during destruction
|
|
|
|
|
|
/*
|
|
Adds an element to the hash table. If the Key for the new element already
|
|
exists, in the table, set the key's vvalue to this new value, in the table.
|
|
*/
|
|
LONG Insert( const KeyType &Key, const ValueType &Val, ElemType *pElemChainHead )
|
|
{
|
|
ElemType *pElem;
|
|
|
|
//
|
|
// if the element exists in this hash bucket's element list, set the new value
|
|
//
|
|
|
|
if ( Find( Key, &pElem ) == TRUE ) {
|
|
pElem->m_Value = Val;
|
|
return BSHASHMAP_ALREADY_EXISTS;
|
|
}
|
|
|
|
//
|
|
// if the element doesn't exist, create a new element
|
|
//
|
|
|
|
ElemType *pVal = new ElemType( Key, Val );
|
|
if ( pVal == NULL ) {
|
|
return BSHASHMAP_OUT_OF_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Add the element into the hash bucket list
|
|
//
|
|
|
|
if ( m_pHead != NULL )
|
|
m_pHead->m_ppPrevious = &(pVal->m_pNext);
|
|
pVal->m_pNext = m_pHead;
|
|
m_pHead = pVal;
|
|
pVal->m_ppPrevious = &m_pHead;
|
|
|
|
//
|
|
// Set the back pointer - double-linked list of elements
|
|
//
|
|
|
|
pVal->m_pBackward = pElemChainHead->m_pBackward;
|
|
pVal->m_pForward = pElemChainHead;
|
|
pVal->m_pBackward->m_pForward = pVal;
|
|
pElemChainHead->m_pBackward = pVal;
|
|
return BSHASHMAP_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
Deletes an element from this hash bucket's list, within the hash table.
|
|
*/
|
|
BOOL Erase( const KeyType &Key, ElemType *pElemChainHead )
|
|
{
|
|
//
|
|
// Walk the list of elements for this hash bucket
|
|
//
|
|
|
|
for ( ElemType *pElem = m_pHead; pElem != NULL; pElem = pElem->m_pNext ) {
|
|
|
|
//
|
|
// if the key is found, delete it from the hash bucket's list.
|
|
//
|
|
|
|
if ( AreKeysEqual( pElem->m_Key, Key ) ) {
|
|
EraseElement( pElem );
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
Erases one element from the two chains
|
|
*/
|
|
inline static void EraseElement( ElemType *pElem )
|
|
{
|
|
assert( pElem->IsValid() );
|
|
|
|
// remove it from the hash chain
|
|
if ( pElem->m_pNext != NULL )
|
|
pElem->m_pNext->m_ppPrevious = pElem->m_ppPrevious;
|
|
*( pElem->m_ppPrevious ) = pElem->m_pNext;
|
|
|
|
// remove it from the double-linked list of elements
|
|
pElem->m_pBackward->m_pForward = pElem->m_pForward;
|
|
pElem->m_pForward->m_pBackward = pElem->m_pBackward;
|
|
delete pElem;
|
|
}
|
|
|
|
/*
|
|
Looks for an element in the list associated with this hash bucket.
|
|
*/
|
|
BOOL Find( const KeyType &Key, ElemType **ppElemFound )
|
|
{
|
|
//
|
|
// Walk the list for this bucket, looking for the key.
|
|
//
|
|
|
|
for ( ElemType *pElem = m_pHead; pElem != NULL; pElem = pElem->m_pNext ) {
|
|
if ( AreKeysEqual( pElem->m_Key, Key ) ) {
|
|
*ppElemFound = pElem;
|
|
return TRUE;
|
|
}
|
|
}
|
|
*ppElemFound = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
private:
|
|
ElemType *m_pHead;
|
|
};
|
|
|
|
//
|
|
// template< class KeyType, class ValueType > class TBsHashMapBucketElem
|
|
//
|
|
// Template for individual elements in a bucket of a CMap
|
|
//
|
|
#define BS_HASH_ELEM_SIGNATURE "ELEMTYPE"
|
|
#define BS_HASH_ELEM_SIGNATURE_LEN 8
|
|
|
|
template< class KeyType, class ValueType >
|
|
class TBsHashMapBucketElem {
|
|
friend class TBsHashMapBucket< KeyType, ValueType >;
|
|
friend class TBsHashMap< KeyType, ValueType >;
|
|
|
|
private:
|
|
typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
|
|
|
|
TBsHashMapBucketElem() : m_ppPrevious( NULL ),
|
|
m_pNext( NULL )
|
|
{
|
|
m_pForward = this;
|
|
m_pBackward = this;
|
|
}
|
|
|
|
TBsHashMapBucketElem( const KeyType K, const ValueType V ) : m_Key( K ), m_Value( V )
|
|
{
|
|
#ifdef _DEBUG
|
|
memcpy( m_sSignature, BS_HASH_ELEM_SIGNATURE, sizeof( m_sSignature ) / sizeof( char ) );
|
|
#endif
|
|
}
|
|
|
|
BOOL
|
|
IsValid()
|
|
{
|
|
assert( this != NULL );
|
|
#ifdef _DEBUG
|
|
return( memcmp( m_sSignature, BS_HASH_ELEM_SIGNATURE, sizeof( m_sSignature ) / sizeof( char ) ) == 0 );
|
|
#else
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
|
|
virtual ~TBsHashMapBucketElem()
|
|
{
|
|
#ifdef _DEBUG // make sure reuse of list will cause errors
|
|
m_pNext = NULL;
|
|
m_pForward = NULL;
|
|
m_pBackward = NULL;
|
|
memset( m_sSignature, 0xAA, sizeof( m_sSignature ) / sizeof( char ) );
|
|
#endif
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
char m_sSignature[BS_HASH_ELEM_SIGNATURE_LEN];
|
|
#endif
|
|
ElemType **m_ppPrevious; // pointer to previous reference
|
|
ElemType *m_pNext; // pointer to next element in bucket
|
|
ElemType *m_pForward; // forward pointer to next element in double-link list of all elements
|
|
ElemType *m_pBackward; // backward pointer to next element in double-link list of all elements
|
|
KeyType m_Key;
|
|
ValueType m_Value;
|
|
};
|
|
|
|
#endif
|