|
|
/*==========================================================================
* * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved. * * File: HashTable.cpp * Content: Hash Table Object * * History: * Date By Reason * ==== == ====== * 08/13/2001 masonb Created * * ***************************************************************************/
#include "dncmni.h"
//**********************************************************************
// Constant definitions
//**********************************************************************
//**********************************************************************
// Macro definitions
//**********************************************************************
//**********************************************************************
// Structure definitions
//**********************************************************************
//**********************************************************************
// Variable definitions
//**********************************************************************
//**********************************************************************
// Function prototypes
//**********************************************************************
//**********************************************************************
// Function definitions
//**********************************************************************
//
// pool management functions
//
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::HashEntry_Alloc"
BOOL CHashTable::HashEntry_Alloc( void *pItem, void* pvContext ) { DNASSERT( pItem != NULL );
_HASHTABLE_ENTRY* pEntry = (_HASHTABLE_ENTRY*)pItem;
pEntry->blLinkage.Initialize();
return TRUE; }
#ifdef DBG
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::HashEntry_Init"
void CHashTable::HashEntry_Init( void *pItem, void* pvContext ) { DNASSERT( pItem != NULL );
const _HASHTABLE_ENTRY* pEntry = (_HASHTABLE_ENTRY*)pItem;
DNASSERT( pEntry->blLinkage.IsEmpty() ); }
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::HashEntry_Release"
void CHashTable::HashEntry_Release( void *pItem ) { DNASSERT( pItem != NULL );
const _HASHTABLE_ENTRY* pEntry = (_HASHTABLE_ENTRY*)pItem;
DNASSERT( pEntry->blLinkage.IsEmpty() ); }
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::HashEntry_Dealloc"
void CHashTable::HashEntry_Dealloc( void *pItem ) { DNASSERT( pItem != NULL );
const _HASHTABLE_ENTRY* pEntry = (_HASHTABLE_ENTRY*)pItem;
DNASSERT( pEntry->blLinkage.IsEmpty() ); }
#endif // DBG
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::CHashTable"
CHashTable::CHashTable() { DEBUG_ONLY(m_fInitialized = FALSE); };
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::~CHashTable"
CHashTable::~CHashTable() { #ifdef DBG
DNASSERT(!m_fInitialized); #endif // DBG
};
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::Initialize"
BOOL CHashTable::Initialize( BYTE bBitDepth, #ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
BYTE bGrowBits, #endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
PFNHASHTABLECOMPARE pfnCompare, PFNHASHTABLEHASH pfnHash ) { DNASSERT(bBitDepth != 0);
memset(this, 0, sizeof(CHashTable));
m_dwAllocatedEntries = 1 << bBitDepth; m_pfnCompareFunction = pfnCompare; m_pfnHashFunction = pfnHash;
m_pblEntries = (CBilink*)DNMalloc(sizeof(CBilink) * m_dwAllocatedEntries); if (m_pblEntries == NULL) { DPFERR("Failed to allocate hash entries array"); return FALSE; }
#ifdef DBG
if (!m_EntryPool.Initialize(sizeof(_HASHTABLE_ENTRY), HashEntry_Alloc, HashEntry_Init, HashEntry_Release, HashEntry_Dealloc)) #else
if (!m_EntryPool.Initialize(sizeof(_HASHTABLE_ENTRY), HashEntry_Alloc, NULL, NULL, NULL)) #endif // DBG
{ DPFERR("Failed to initialize hash entries pool"); DNFree(m_pblEntries); m_pblEntries = NULL; return FALSE; }
for (DWORD dwEntry = 0; dwEntry < m_dwAllocatedEntries; dwEntry++) { m_pblEntries[dwEntry].Initialize(); }
m_bBitDepth = bBitDepth; #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
//
// Pre-allocate all hash entries now.
//
if (m_EntryPool.Preallocate(m_dwAllocatedEntries, NULL) != m_dwAllocatedEntries) { DPFERR("Failed to preallocate hash entries"); m_EntryPool.DeInitialize(); DNFree(m_pblEntries); m_pblEntries = NULL; return FALSE; } #else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
m_bGrowBits = bGrowBits; #endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
DEBUG_ONLY(m_fInitialized = TRUE);
DPFX(DPFPREP, 5,"[%p] Hash table initialized", this);
return TRUE; };
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::Deinitialize"
void CHashTable::Deinitialize( void ) { #ifdef DBG
DNASSERT(m_fInitialized); m_fInitialized = FALSE; #endif // DBG
DNASSERT( m_dwEntriesInUse == 0 );
if (m_pblEntries) { DNFree(m_pblEntries); m_pblEntries = NULL; }
m_EntryPool.DeInitialize();
DPFX(DPFPREP, 5,"[%p] Hash table deinitialized", this); };
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::GrowTable"
void CHashTable::GrowTable( void ) { #ifdef DBG
DNASSERT( m_fInitialized != FALSE); DNASSERT( m_bGrowBits != 0 ); #endif // DBG
CBilink* pblOldEntries; BYTE bNewHashBitDepth; DWORD dwNewHashSize; DWORD dwOldHashSize; #ifdef DBG
DWORD dwOldEntryCount; #endif // DBG
// We're more than 50% full, find a new has table size that will accomodate
// all of the current entries, and keep a pointer to the old data in case
// the memory allocation fails.
pblOldEntries = m_pblEntries; bNewHashBitDepth = m_bBitDepth;
do { bNewHashBitDepth += m_bGrowBits; } while (m_dwEntriesInUse >= (DWORD)((1 << bNewHashBitDepth) / 2));
//
// assert that we don't pull up half of the machine's address space!
//
DNASSERT( bNewHashBitDepth <= ( sizeof( UINT_PTR ) * 8 / 2 ) );
dwNewHashSize = 1 << bNewHashBitDepth;
m_pblEntries = (CBilink*)DNMalloc(sizeof(CBilink) * dwNewHashSize); if ( m_pblEntries == NULL ) { // Allocation failed, restore the old data pointer and insert the item
// into the hash table. This will probably result in adding to a bucket.
m_pblEntries = pblOldEntries; DPFX(DPFPREP, 0, "Warning: Failed to grow hash table when 50% full!" ); return; }
// we have more memory, reorient the hash table and re-add all of
// the old items
DEBUG_ONLY( dwOldEntryCount = m_dwEntriesInUse );
dwOldHashSize = 1 << m_bBitDepth; m_bBitDepth = bNewHashBitDepth;
m_dwAllocatedEntries = dwNewHashSize; m_dwEntriesInUse = 0;
for (DWORD dwEntry = 0; dwEntry < dwNewHashSize; dwEntry++) { m_pblEntries[dwEntry].Initialize(); }
DNASSERT( dwOldHashSize > 0 ); while ( dwOldHashSize > 0 ) { dwOldHashSize--; while (pblOldEntries[dwOldHashSize].GetNext() != &pblOldEntries[dwOldHashSize]) { BOOL fTempReturn; PVOID pvKey; PVOID pvData; _HASHTABLE_ENTRY* pTempEntry;
pTempEntry = CONTAINING_OBJECT(pblOldEntries[dwOldHashSize ].GetNext(), _HASHTABLE_ENTRY, blLinkage);
pTempEntry->blLinkage.RemoveFromList();
pvKey = pTempEntry->pvKey; pvData = pTempEntry->pvData; m_EntryPool.Release( pTempEntry );
// Since we're returning the current hash table entry to the pool
// it will be immediately reused in the new table. We should never
// have a problem adding to the new list.
fTempReturn = Insert( pvKey, pvData ); DNASSERT( fTempReturn != FALSE ); DEBUG_ONLY( dwOldEntryCount-- ); } } #ifdef DBG
DNASSERT(dwOldEntryCount == 0); #endif // DBG
DNFree(pblOldEntries); pblOldEntries = NULL; };
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::Insert"
BOOL CHashTable::Insert( PVOID pvKey, PVOID pvData ) { BOOL fFound; CBilink* pLink; _HASHTABLE_ENTRY* pNewEntry;
DEBUG_ONLY( DNASSERT( m_fInitialized != FALSE ) );
pNewEntry = NULL;
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
// grow the map if applicable
if ( ( m_dwEntriesInUse >= ( m_dwAllocatedEntries / 2 ) ) && ( m_bGrowBits != 0 ) ) { GrowTable(); } #endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
// get a new table entry before trying the lookup
pNewEntry = (_HASHTABLE_ENTRY*)m_EntryPool.Get(); if ( pNewEntry == NULL ) { DPFX(DPFPREP, 0, "Problem allocating new hash table entry" ); return FALSE; }
// scan for this item in the list, since we're only supposed to have
// unique items in the list, ASSERT if a duplicate is found
fFound = LocalFind( pvKey, &pLink ); DNASSERT( pLink != NULL ); DNASSERT( fFound == FALSE ); DNASSERT( pLink != NULL );
// officially add entry to the hash table
m_dwEntriesInUse++; pNewEntry->pvKey = pvKey; pNewEntry->pvData = pvData; pNewEntry->blLinkage.InsertAfter(pLink);
return TRUE; }
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::Remove"
BOOL CHashTable::Remove( PVOID pvKey ) { #ifdef DBG
DNASSERT( m_fInitialized != FALSE ); #endif // DBG
CBilink* pLink; _HASHTABLE_ENTRY* pEntry;
if ( !LocalFind( pvKey, &pLink ) ) { return FALSE; } DNASSERT( pLink != NULL );
pEntry = CONTAINING_OBJECT(pLink, _HASHTABLE_ENTRY, blLinkage);
pEntry->blLinkage.RemoveFromList();
m_EntryPool.Release( pEntry );
DNASSERT( m_dwEntriesInUse != 0 ); m_dwEntriesInUse--;
return TRUE; }
#ifndef DPNBUILD_NOREGISTRY
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::RemoveAll"
void CHashTable::RemoveAll( void ) { DWORD dwHashSize; DWORD dwTemp; CBilink* pLink; _HASHTABLE_ENTRY* pEntry; #ifdef DBG
DNASSERT( m_fInitialized != FALSE ); #endif // DBG
dwHashSize = 1 << m_bBitDepth; for(dwTemp = 0; dwTemp < dwHashSize; dwTemp++) { pLink = m_pblEntries[dwTemp].GetNext(); while (pLink != &m_pblEntries[dwTemp]) { pEntry = CONTAINING_OBJECT(pLink, _HASHTABLE_ENTRY, blLinkage); pLink = pLink->GetNext(); pEntry->blLinkage.RemoveFromList(); m_EntryPool.Release( pEntry ); DNASSERT( m_dwEntriesInUse != 0 ); m_dwEntriesInUse--; } }
DNASSERT( m_dwEntriesInUse == 0 ); }
#endif // ! DPNBUILD_NOREGISTRY
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::Find"
BOOL CHashTable::Find( PVOID pvKey, PVOID* ppvData ) { #ifdef DBG
DNASSERT( m_fInitialized != FALSE ); #endif // DBG
CBilink* pLink; _HASHTABLE_ENTRY* pEntry;
if (!LocalFind(pvKey, &pLink)) { return FALSE; } DNASSERT(pLink != NULL);
pEntry = CONTAINING_OBJECT(pLink, _HASHTABLE_ENTRY, blLinkage);
*ppvData = pEntry->pvData;
return TRUE; }
#undef DPF_MODNAME
#define DPF_MODNAME "CHashTable::LocalFind"
BOOL CHashTable::LocalFind( PVOID pvKey, CBilink** ppLinkage ) { #ifdef DBG
DNASSERT( m_fInitialized != FALSE); #endif // DBG
DWORD dwHashResult; CBilink* pLink; _HASHTABLE_ENTRY* pEntry;
dwHashResult = (*m_pfnHashFunction)(pvKey, m_bBitDepth ); DNASSERT(dwHashResult < (DWORD)(1 << m_bBitDepth));
pLink = m_pblEntries[dwHashResult].GetNext(); while (pLink != &m_pblEntries[dwHashResult]) { pEntry = CONTAINING_OBJECT(pLink, _HASHTABLE_ENTRY, blLinkage);
if ( (*m_pfnCompareFunction)( pvKey, pEntry->pvKey ) ) { *ppLinkage = pLink; return TRUE; } pLink = pLink->GetNext(); }
// entry was not found, return pointer to linkage to insert after if a new
// entry is being added to the table
*ppLinkage = pLink;
return FALSE; }
|