You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
471 lines
11 KiB
471 lines
11 KiB
/*==========================================================================
|
|
*
|
|
* 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;
|
|
}
|