windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/iisrtl/hashtab.cxx

1086 lines
27 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995-1996 Microsoft Corporation
Module Name :
hashtab.cxx
Abstract:
Implements the member functions for Hash table
Author:
Murali R. Krishnan ( MuraliK ) 02-Oct-1996
Environment:
Win32 - User Mode
Project:
Internet Server DLL
Functions Exported:
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
#include "precomp.hxx"
# if !defined(dllexp)
# define dllexp __declspec( dllexport)
# endif
# include <hashtab.hxx>
/*++
Organization of Hash Table
The hash table consists of a set of hash buckets controlled
by the number of buckets specified during creation.
Each bucket consists of a set of bucket chunks. Each bucket
owns a separate critical section to protect the entries in
the bucket itself.
Each bucket chunk consists of an array of MAX_ELEMENTS_PER_BUCKET
HashTableBucketElement Entries (HTBE_ENTRY).
Each HTBE_ENTRY maintains a hash value and pointer to the Hash Element.
--*/
/************************************************************
* HASH_TABLE_BUCKET
************************************************************/
struct HTBE_ENTRY {
DWORD m_hashValue;
HT_ELEMENT * m_phte;
inline
BOOL IsMatch( DWORD hashValue, LPCSTR pszKey, DWORD cchKey) const
{ return ((hashValue == m_hashValue) &&
(NULL != m_phte) &&
m_phte->IsMatch( pszKey, cchKey)
);
}
inline
BOOL IsMatch( IN HT_ELEMENT * phte) const
{ return ( phte == m_phte); }
inline BOOL
IsEmpty( VOID) const { return ( NULL == m_phte); }
VOID Print( VOID) const
{ m_phte->Print(); }
};
typedef HTBE_ENTRY * PHTBE_ENTRY;
//
// Chunk size should be carefully (empirically) chosen.
// Small Chunk size => large number of chunks
// Large Chunk size => high cost of search on failures.
// For now we choose the chunk size to be 20 entries.
# define MAX_ELEMENTS_PER_BUCKET ( 20 )
struct dllexp HTB_ELEMENT {
HTBE_ENTRY m_rgElements[MAX_ELEMENTS_PER_BUCKET];
DWORD m_nElements;
LIST_ENTRY m_ListEntry;
HTB_ELEMENT(VOID)
: m_nElements ( 0)
{
InitializeListHead( &m_ListEntry);
ZeroMemory( m_rgElements, sizeof( m_rgElements));
}
~HTB_ELEMENT(VOID)
{ Cleanup(); }
VOID Cleanup( VOID);
inline
HT_ELEMENT * Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey);
inline
BOOL Insert( IN DWORD hashVal, IN HT_ELEMENT * phte);
inline
BOOL Delete( IN HT_ELEMENT * phte);
VOID Print( IN DWORD level) const;
HTBE_ENTRY * FirstElement(VOID) { return ( m_rgElements); }
HTBE_ENTRY * LastElement(VOID)
{ return ( m_rgElements + MAX_ELEMENTS_PER_BUCKET); }
VOID NextElement( HTBE_ENTRY * & phtbe)
{ phtbe++; }
VOID IncrementElements(VOID) { m_nElements++; }
VOID DecrementElements(VOID) { m_nElements--; }
DWORD NumElements( VOID) const { return ( m_nElements); }
BOOL IsSpaceAvailable(VOID) const
{ return ( NumElements() < MAX_ELEMENTS_PER_BUCKET); }
DWORD FindNextElement( IN OUT LPDWORD pdwPos,
OUT HT_ELEMENT ** pphte);
};
typedef HTB_ELEMENT * PHTB_ELEMENT;
class dllexp HASH_TABLE_BUCKET {
public:
HASH_TABLE_BUCKET(VOID);
~HASH_TABLE_BUCKET( VOID);
HT_ELEMENT * Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey);
BOOL Insert( IN DWORD hashVal,
IN HT_ELEMENT * phte,
IN BOOL fCheckForDuplicate);
BOOL Delete( IN HT_ELEMENT * phte);
VOID Print( IN DWORD level);
DWORD NumEntries( VOID);
DWORD InitializeIterator( IN HT_ITERATOR * phti);
DWORD FindNextElement( IN HT_ITERATOR * phti,
OUT HT_ELEMENT ** pphte);
DWORD CloseIterator( IN HT_ITERATOR * phti);
private:
CRITICAL_SECTION m_csLock;
LIST_ENTRY m_lHead;
DWORD m_nEntries;
HTB_ELEMENT m_htbeFirst; // the first bucket chunk
VOID Lock(VOID) { EnterCriticalSection( &m_csLock); }
VOID Unlock( VOID) { LeaveCriticalSection( &m_csLock); }
};
/************************************************************
* Member Functions of HTB_ELEMENT
************************************************************/
VOID
HTB_ELEMENT::Cleanup( VOID)
{
if ( m_nElements > 0) {
PHTBE_ENTRY phtbeEntry;
// free up all the entries in this bucket.
for (phtbeEntry = FirstElement();
phtbeEntry < (LastElement());
NextElement( phtbeEntry)) {
if ( !phtbeEntry->IsEmpty() ) {
// release the object now.
DecrementElements();
// Assert that ref == 1
DerefAndKillElement( phtbeEntry->m_phte);
phtbeEntry->m_phte = NULL;
phtbeEntry->m_hashValue = 0;
}
} // for
}
DBG_ASSERT( 0 == m_nElements);
return;
} // HTB_ELEMENT::Cleanup()
inline
HT_ELEMENT *
HTB_ELEMENT::Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey)
{
HT_ELEMENT * phte = NULL;
if ( m_nElements > 0) {
PHTBE_ENTRY phtbeEntry;
// find the entry by scanning all entries in this bucket chunk
// if found, increment ref count and return a pointer to the object
for (phtbeEntry = FirstElement();
phtbeEntry < (LastElement());
NextElement( phtbeEntry)) {
//
// If the hash values match and the strings match up, return
// the corresponding hash table entry object
//
if ( phtbeEntry->IsMatch( hashValue, pszKey, cchKey)) {
// we found the entry. return it.
phte = phtbeEntry->m_phte;
DBG_REQUIRE( phte->Reference() > 0);
break;
}
} // for
}
return ( phte);
} // HTB_ELEMENT::Lookup()
inline BOOL
HTB_ELEMENT::Insert( IN DWORD hashVal,
IN HT_ELEMENT * phte
)
{
if ( m_nElements < MAX_ELEMENTS_PER_BUCKET) {
// there is some empty space.
// Find one such a slot and add this new entry
PHTBE_ENTRY phtbeEntry;
for (phtbeEntry = FirstElement();
phtbeEntry < LastElement();
NextElement( phtbeEntry)) {
if ( phtbeEntry->IsEmpty() ) {
DBG_ASSERT( NULL != phte);
// Assume that the object phte already has non-zero ref count
// we found a free entry. insert the new element here.
phtbeEntry->m_hashValue = hashVal;
phtbeEntry->m_phte = phte;
IncrementElements();
return ( TRUE);
}
} // for
// we should not come here. If we do then there is trouble :(
DBG_ASSERT( FALSE);
}
SetLastError( ERROR_INSUFFICIENT_BUFFER);
return ( FALSE);
} // HTB_ELEMENT::Insert()
DWORD
HTB_ELEMENT::FindNextElement( IN OUT LPDWORD pdwPos, OUT HT_ELEMENT ** pphte)
{
DWORD dwErr = ERROR_NO_MORE_ITEMS;
DBG_ASSERT( NULL != pdwPos );
DBG_ASSERT( NULL != pphte );
// Find the first valid element to return back.
//
// Given that deletion might happen any time, we cannot rely on the
// comparison *pdwPos < m_nElements
//
// Do scans with *pdwPos < MAX_ELEMENTS_PER_BUCKET
//
if ( *pdwPos < MAX_ELEMENTS_PER_BUCKET ) {
PHTBE_ENTRY phtbeEntry;
// find the entry by scanning all entries in this bucket chunk
// if found, increment ref count and return a pointer to the object
for (phtbeEntry = m_rgElements + *pdwPos;
phtbeEntry < (LastElement());
NextElement( phtbeEntry)) {
if ( phtbeEntry->m_phte != NULL ) {
//
// Store the element pointer and the offset
// and return after referencing the element
//
*pphte = phtbeEntry->m_phte;
(*pphte)->Reference();
*pdwPos = ( 1 + DIFF(phtbeEntry - FirstElement()));
dwErr = NO_ERROR;
break;
}
} // for
}
return ( dwErr);
} // HTB_ELEMENT::FindNextElement()
inline BOOL
HTB_ELEMENT::Delete( IN HT_ELEMENT * phte)
{
DBG_ASSERT( NULL != phte);
if ( m_nElements > 0) {
PHTBE_ENTRY phtbeEntry;
// find the entry by scanning all entries in this bucket chunk
// if found, increment ref count and return a pointer to the object
for (phtbeEntry = FirstElement();
phtbeEntry < (LastElement());
NextElement( phtbeEntry)) {
//
// If the hash values match and the strings match up,
// decrement ref count and kill the element.
//
if ( phtbeEntry->IsMatch( phte)) {
// We found the entry. Remove it from the table
phtbeEntry->m_phte = NULL;
DecrementElements();
DerefAndKillElement( phte);
return ( TRUE);
}
} // for
}
return ( FALSE);
} // HTB_ELEMENT::Delete()
VOID
HTB_ELEMENT::Print(IN DWORD level) const
{
const HTBE_ENTRY * phtbeEntry;
CHAR rgchBuffer[MAX_ELEMENTS_PER_BUCKET * 22 + 200];
DWORD cch;
DWORD i;
cch = wsprintf( rgchBuffer,
"HTB_ELEMENT(%08x) # Elements %4d; "
"Flink: %08x Blink: %08x\n"
,
this, m_nElements,
m_ListEntry.Flink, m_ListEntry.Blink);
if ( level > 0) {
// NYI: I need to walk down the entire array.
// Not just the first few entries
for( i = 0; i < m_nElements; i++) {
phtbeEntry = &m_rgElements[i];
cch += wsprintf( rgchBuffer + cch,
" %08x %08x",
phtbeEntry->m_hashValue,
phtbeEntry->m_phte
);
if ( i % 4 == 0) {
rgchBuffer[cch++] = '\n';
rgchBuffer[cch] = '\0';
}
} // for
}
DBGDUMP(( DBG_CONTEXT, rgchBuffer));
return;
} // HTB_ELEMENT::Print()
/************************************************************
* Member Functions of HASH_TABLE_BUCKET
************************************************************/
HASH_TABLE_BUCKET::HASH_TABLE_BUCKET(VOID)
: m_nEntries ( 0),
m_htbeFirst()
{
InitializeListHead( &m_lHead);
INITIALIZE_CRITICAL_SECTION( & m_csLock);
} // HASH_TABLE_BUCKET::HASH_TABLE_BUCKET()
HASH_TABLE_BUCKET::~HASH_TABLE_BUCKET( VOID)
{
PLIST_ENTRY pl;
PHTB_ELEMENT phtbe;
// Free up the elements in the list
Lock();
while ( !IsListEmpty( &m_lHead)) {
pl = RemoveHeadList( &m_lHead);
phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT, m_ListEntry);
delete phtbe;
} // while
m_htbeFirst.Cleanup();
Unlock();
DeleteCriticalSection( &m_csLock);
} // HASH_TABLE_BUCKET::~HASH_TABLE_BUCKET()
HT_ELEMENT *
HASH_TABLE_BUCKET::Lookup( IN DWORD hashValue, IN LPCSTR pszKey, DWORD cchKey)
{
HT_ELEMENT * phte;
Lock();
// 1. search in the first bucket
phte = m_htbeFirst.Lookup( hashValue, pszKey, cchKey);
if ( NULL == phte ) {
// 2. search in the auxiliary buckets
PLIST_ENTRY pl;
for ( pl = m_lHead.Flink; (phte == NULL) && (pl != &m_lHead);
pl = pl->Flink) {
HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl,
HTB_ELEMENT,
m_ListEntry);
phte = phtbe->Lookup( hashValue, pszKey, cchKey);
} // for
}
Unlock();
return (phte);
} // HASH_TABLE_BUCKET::Lookup()
BOOL
HASH_TABLE_BUCKET::Insert( IN DWORD hashValue,
IN HT_ELEMENT * phte,
IN BOOL fCheckForDuplicate)
{
BOOL fReturn = FALSE;
if ( fCheckForDuplicate) {
Lock();
// do a lookup and find out if this data exists.
HT_ELEMENT * phteLookedup = Lookup( hashValue,
phte->QueryKey(),
phte->QueryKeyLen()
);
if ( NULL != phteLookedup) {
// the element is already present - return failure
DerefAndKillElement( phteLookedup);
}
Unlock();
if ( NULL != phteLookedup) {
SetLastError( ERROR_DUP_NAME);
return ( FALSE);
}
}
Lock();
// 1. try inserting in the first bucket chunk, if possible
if ( m_htbeFirst.IsSpaceAvailable()) {
fReturn = m_htbeFirst.Insert( hashValue, phte);
} else {
// 2. Find the first chunk that has space and insert it there.
PLIST_ENTRY pl;
HTB_ELEMENT * phtbe;
for ( pl = m_lHead.Flink; (pl != &m_lHead);
pl = pl->Flink) {
phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT, m_ListEntry);
if ( phtbe->IsSpaceAvailable()) {
fReturn = phtbe->Insert( hashValue, phte);
break;
}
} // for
if ( !fReturn ) {
//
// We ran out of space.
// Allocate a new bucket and insert the new element.
//
phtbe = new HTB_ELEMENT();
if ( NULL != phtbe) {
// add the bucket to the list of buckets and
// then add the element to the bucket
InsertTailList( &m_lHead, &phtbe->m_ListEntry);
fReturn = phtbe->Insert(hashValue, phte);
} else {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT,
" HTB(%08x)::Insert: Unable to add a chunk\n",
this));
}
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
}
}
}
Unlock();
return ( fReturn);
} // HASH_TABLE_BUCKET::Insert()
BOOL
HASH_TABLE_BUCKET::Delete( IN HT_ELEMENT * phte)
{
BOOL fReturn = FALSE;
// We do not know which bucket this element belongs to.
// So we should try all chunks to delete this element.
Lock();
// 1. try deleting the element from first bucket chunk, if possible
fReturn = m_htbeFirst.Delete( phte);
if (!fReturn) {
// it was not on the first bucket chunk.
// 2. Find the first chunk that might contain this element
// and delete it.
PLIST_ENTRY pl;
for ( pl = m_lHead.Flink;
!fReturn && (pl != &m_lHead);
pl = pl->Flink) {
HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl,
HTB_ELEMENT,
m_ListEntry);
fReturn = phtbe->Delete( phte);
} // for
// the element should have been in the hash table,
// otherwise the app is calling with wrong entry
DBG_ASSERT( fReturn);
}
Unlock();
return ( fReturn);
} // HASH_TABLE_BUCKET::Delete()
DWORD
HASH_TABLE_BUCKET::NumEntries( VOID)
{
DWORD nEntries;
Lock();
nEntries = m_htbeFirst.NumElements();
PLIST_ENTRY pl;
for ( pl = m_lHead.Flink;
(pl != &m_lHead);
pl = pl->Flink) {
HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
m_ListEntry);
nEntries += phtbe->NumElements();
} // for
Unlock();
return (nEntries);
} // HASH_TABLE_BUCKET::NumEntries()
DWORD
HASH_TABLE_BUCKET::InitializeIterator( IN HT_ITERATOR * phti)
{
DWORD dwErr = ERROR_NO_MORE_ITEMS;
//
// find the first chunk that has a valid element.
// if we find one, leave the lock on for subsequent accesses.
// CloseIterator will shut down the lock
// If we do not find one, we should unlock and return
//
phti->nChunkId = NULL;
phti->nPos = 0;
Lock();
if ( m_htbeFirst.NumElements() > 0) {
phti->nChunkId = (PVOID ) &m_htbeFirst;
dwErr = NO_ERROR;
} else {
// find the first chunk that has an element
PLIST_ENTRY pl;
for ( pl = m_lHead.Flink; (pl != &m_lHead); pl = pl->Flink) {
HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
m_ListEntry);
if ( phtbe->NumElements() > 0) {
phti->nChunkId = (PVOID ) phtbe;
dwErr = NO_ERROR;
break;
}
} // for
}
// if we did not find any elements, then unlock and return
// Otherwise leave the unlocking to the CloseIterator()
if ( dwErr == ERROR_NO_MORE_ITEMS) {
// get out of this bucket completely.
Unlock();
}
return ( dwErr);
} // HASH_TABLE_BUCKET::InitializeIterator()
DWORD
HASH_TABLE_BUCKET::FindNextElement( IN HT_ITERATOR * phti,
OUT HT_ELEMENT ** pphte)
{
// this function should be called only when the bucket is locked.
DWORD dwErr;
HTB_ELEMENT * phtbe = (HTB_ELEMENT * )phti->nChunkId;
//
// phti contains the <chunk, pos> from which we should start scan for
// next element.
//
DBG_ASSERT( NULL != phtbe);
dwErr = phtbe->FindNextElement( &phti->nPos, pphte);
if ( ERROR_NO_MORE_ITEMS == dwErr ) {
// scan the rest of the chunks for next element
PLIST_ENTRY pl = ((phtbe == &m_htbeFirst) ? m_lHead.Flink :
phtbe->m_ListEntry.Flink);
for ( ; (pl != &m_lHead); pl = pl->Flink) {
phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
m_ListEntry);
if ( phtbe->NumElements() > 0) {
phti->nPos = 0;
dwErr = phtbe->FindNextElement( &phti->nPos, pphte);
DBG_ASSERT( NO_ERROR == dwErr);
phti->nChunkId = (PVOID ) phtbe;
break;
}
} // for
}
if ( dwErr == ERROR_NO_MORE_ITEMS) {
phti->nChunkId = NULL;
}
return ( dwErr);
} // HASH_TABLE_BUCKET::FindNextElement()
DWORD
HASH_TABLE_BUCKET::CloseIterator( IN HT_ITERATOR * phti)
{
// just unlock the current bucket.
Unlock();
return ( NO_ERROR);
} // HASH_TABLE_BUCKET::CloseIterator()
VOID
HASH_TABLE_BUCKET::Print( IN DWORD level)
{
Lock();
DBGPRINTF(( DBG_CONTEXT,
"\n\nHASH_TABLE_BUCKET (%08x): Head.Flink=%08x; Head.Blink=%08x\n"
" Bucket Chunk # 0:\n"
,
this, m_lHead.Flink, m_lHead.Blink
));
m_htbeFirst.Print( level);
if ( level > 0) {
PLIST_ENTRY pl;
DWORD i;
for ( pl = m_lHead.Flink, i = 1;
(pl != &m_lHead);
pl = pl->Flink, i++) {
HTB_ELEMENT * phtbe = CONTAINING_RECORD( pl, HTB_ELEMENT,
m_ListEntry);
DBGPRINTF(( DBG_CONTEXT, "\n Bucket Chunk # %d\n", i));
phtbe->Print( level);
} // for
}
Unlock();
return;
} // HASH_TABLE_BUCKET::Print()
/************************************************************
* Member Functions of HASH_TABLE
************************************************************/
HASH_TABLE::HASH_TABLE( IN DWORD nBuckets,
IN LPCSTR pszIdentifier,
IN DWORD dwHashTableFlags
)
: m_nBuckets ( nBuckets),
m_dwFlags ( dwHashTableFlags),
m_nEntries ( 0),
m_nLookups ( 0),
m_nHits ( 0),
m_nInserts ( 0),
m_nFlushes ( 0)
{
if ( NULL != pszIdentifier) {
lstrcpynA( m_rgchId, pszIdentifier, sizeof( m_rgchId));
}
m_prgBuckets = new HASH_TABLE_BUCKET[nBuckets];
} // HASH_TABLE::HASH_TABLE()
DWORD
HASH_TABLE::CalculateHash( IN LPCSTR pszKey, DWORD cchKey) const
{
DWORD hash = 0;
DBG_ASSERT( pszKey != NULL );
if ( cchKey > 8) {
//
// hash the last 8 characters
//
pszKey = (pszKey + cchKey - 8);
}
while ( *pszKey != '\0') {
//
// This is an extremely slimey way of getting upper case.
// Kids, don't try this at home
// -johnson
//
DWORD ch = ((*pszKey++) & ~0x20);
// NYI: this is a totally pipe-line unfriendly code. Improve this.
hash <<= 2;
hash ^= ch;
hash += ch;
} // while
//
// Multiply by length (to introduce some randomness. Murali said so.
//
return( hash * cchKey);
} // CalculateHash()
VOID
HASH_TABLE::Cleanup(VOID)
{
if ( NULL != m_prgBuckets ) {
delete [] m_prgBuckets;
m_prgBuckets = NULL;
}
} // HASH_TABLE::Cleanup()
# define INCREMENT_LOOKUPS() \
{ InterlockedIncrement( (LPLONG ) &m_nLookups); }
# define INCREMENT_HITS( phte) \
if ( NULL != phte) { InterlockedIncrement( (LPLONG ) &m_nHits); }
# define INCREMENT_INSERTS() \
{ InterlockedIncrement( (LPLONG ) &m_nInserts); }
# define INCREMENT_FLUSHES() \
{ InterlockedIncrement( (LPLONG ) &m_nFlushes); }
# define INCREMENT_ENTRIES( fRet) \
if ( fRet) { InterlockedIncrement( (LPLONG ) &m_nEntries); }
# define DECREMENT_ENTRIES( fRet) \
if ( fRet) { InterlockedDecrement( (LPLONG ) &m_nEntries); }
HT_ELEMENT *
HASH_TABLE::Lookup( IN LPCSTR pszKey, DWORD cchKey)
{
// 1. Calculate the hash value for pszKey
// 2. Find the bucket for the hash value
// 3. Search for given item in the bucket
// 4. return the result, after updating statistics
DWORD hashVal = CalculateHash( pszKey, cchKey);
HT_ELEMENT * phte;
INCREMENT_LOOKUPS();
DBG_ASSERT( NULL != m_prgBuckets);
phte = m_prgBuckets[hashVal % m_nBuckets].Lookup( hashVal, pszKey, cchKey);
INCREMENT_HITS( phte);
return ( phte);
} // HASH_TABLE::Lookup()
BOOL
HASH_TABLE::Insert( HT_ELEMENT * phte, IN BOOL fCheckBeforeInsert)
{
// 1. Calculate the hash value for key of the HT_ELEMENT object
// 2. Find the bucket for the hash value
// 3. Check if this item is not already present and insert
// it into the hash table.
// (the check can be bypassed if fCheck is set to FALSE)
// 4. return the result, after updating statistics
DWORD hashVal = CalculateHash( phte->QueryKey(),
phte->QueryKeyLen() );
BOOL fRet;
INCREMENT_INSERTS();
DBG_ASSERT( NULL != m_prgBuckets);
fRet = m_prgBuckets[hashVal % m_nBuckets].Insert( hashVal,
phte,
fCheckBeforeInsert);
IF_DEBUG( ERROR) {
if ( !fRet) {
DBGPRINTF(( DBG_CONTEXT,
" Unable to insert %08x into bucket %d."
" Bucket has %d elements. Error = %d\n",
phte, hashVal % m_nBuckets,
m_prgBuckets[hashVal % m_nBuckets].NumEntries(),
GetLastError()
));
}
}
INCREMENT_ENTRIES( fRet);
return ( fRet);
} // HASH_TABLE::Insert()
BOOL
HASH_TABLE::Delete( HT_ELEMENT * phte)
{
BOOL fRet;
DWORD hashVal = CalculateHash( phte->QueryKey(), phte->QueryKeyLen());
DBG_ASSERT( NULL != m_prgBuckets);
fRet = m_prgBuckets[hashVal % m_nBuckets].Delete( phte);
DECREMENT_ENTRIES( fRet);
return ( fRet);
} // HASH_TABLE::Delete()
VOID
HASH_TABLE::Print( IN DWORD level)
{
DWORD i;
DBGPRINTF(( DBG_CONTEXT,
"HASH_TABLE(%08x) "
"%s: nBuckets = %d; dwFlags = %d;"
" nEntries = %d; nLookups = %d; nHits = %d;"
" nInserts = %d; nFlushes = %d;"
" m_prgBuckets = %d\n",
this, m_rgchId, m_nBuckets, m_dwFlags,
m_nEntries, m_nLookups, m_nHits, m_nInserts,
m_nFlushes, m_prgBuckets));
if ( level == 0 ) {
CHAR rgchBuff[2000];
DWORD cch;
cch = wsprintfA( rgchBuff, "\tBucket NumEntries\n");
DBG_ASSERT( NULL != m_prgBuckets);
for (i = 0; i < m_nBuckets; i++) {
cch += wsprintf( rgchBuff + cch, "\t[%4d] %4d,\n",
i, m_prgBuckets[i].NumEntries());
} // for
DBGDUMP(( DBG_CONTEXT, rgchBuff));
} else {
DBG_ASSERT( NULL != m_prgBuckets);
for (i = 0; i < m_nBuckets; i++) {
m_prgBuckets[i].Print( level);
} // for
}
return;
} // HASH_TABLE::Print()
DWORD
HASH_TABLE::InitializeIterator( IN HT_ITERATOR * phti)
{
DWORD dwErr = ERROR_NO_MORE_ITEMS;
DBG_ASSERT( IsValid());
DBG_ASSERT( NULL != phti);
// initialize the iterator
phti->nBucketNumber = INFINITE;
phti->nChunkId = NULL;
phti->nPos = 0;
if ( m_nEntries > 0) {
// set the iterator to point to the first bucket with some elements.
for ( DWORD i = 0; (i < m_nBuckets); i++) {
dwErr = m_prgBuckets[i].InitializeIterator( phti);
if ( dwErr == NO_ERROR) {
phti->nBucketNumber = i;
break;
}
}
}
return ( dwErr);
} // HASH_TABLE::InitializeIterator()
DWORD
HASH_TABLE::FindNextElement( IN HT_ITERATOR * phti,
OUT HT_ELEMENT ** pphte)
{
DWORD dwErr = ERROR_NO_MORE_ITEMS;
DBG_ASSERT( IsValid());
DBG_ASSERT( NULL != phti);
DBG_ASSERT( NULL != pphte);
if ( INFINITE != phti->nBucketNumber) {
// iterator has some valid state use it.
DBG_ASSERT( phti->nBucketNumber < m_nBuckets);
dwErr =
m_prgBuckets[ phti->nBucketNumber].FindNextElement( phti, pphte);
if ( ERROR_NO_MORE_ITEMS == dwErr) {
DBG_REQUIRE( m_prgBuckets[ phti->nBucketNumber].
CloseIterator( phti)
== NO_ERROR
);
// hunt for the next bucket with an element.
for ( DWORD i = (phti->nBucketNumber + 1); (i < m_nBuckets); i++) {
dwErr = m_prgBuckets[i].InitializeIterator( phti);
if ( dwErr == NO_ERROR) {
phti->nBucketNumber = i;
dwErr = m_prgBuckets[ i].FindNextElement( phti, pphte);
DBG_ASSERT( dwErr == NO_ERROR);
break;
}
} // for
if ( ERROR_NO_MORE_ITEMS == dwErr) {
// reset the bucket number
phti->nBucketNumber = INFINITE;
}
}
}
return ( dwErr);
} // HASH_TABLE::FindNextElement()
DWORD
HASH_TABLE::CloseIterator( IN HT_ITERATOR * phti)
{
DBG_ASSERT( IsValid());
DBG_ASSERT( NULL != phti);
if ( INFINITE != phti->nBucketNumber) {
DBG_ASSERT( phti->nBucketNumber < m_nBuckets);
DBG_REQUIRE( m_prgBuckets[ phti->nBucketNumber].
CloseIterator( phti)
== NO_ERROR
);
phti->nBucketNumber = INFINITE;
}
return ( NO_ERROR);
} // HASH_TABLE::CloseIterator()
/************************ End of File ***********************/