|
|
/*++
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
|