windows-nt/Source/XPSP1/NT/sdktools/fsdump/inc/vs_hash.h
2020-09-26 16:20:57 +08:00

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