862 lines
16 KiB
C++
862 lines
16 KiB
C++
/*++
|
|
|
|
FHashEx.inl
|
|
|
|
This file contains the template implementation of the class TFHashEx.
|
|
|
|
--*/
|
|
|
|
|
|
//---------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF /* This is the type used to point or reference items in the cache*/
|
|
>
|
|
TFHashEx< Data, Key, KEYREF >::TFHashEx( ) :
|
|
m_cBuckets( 0 ),
|
|
m_cActiveBuckets( 0 ),
|
|
m_cNumAlloced( 0 ),
|
|
m_cIncrement( 0 ),
|
|
m_ppBucket( 0 ),
|
|
m_pfnHash( 0 ),
|
|
m_pGetKey( 0 ),
|
|
m_pMatchKey( 0 ),
|
|
m_load( 0 ) {
|
|
//
|
|
// Very basic constructor
|
|
//
|
|
|
|
}
|
|
|
|
//---------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
BOOL
|
|
TFHashEx< Data, Key, KEYREF >::Init(
|
|
NEXTPTR pNext,
|
|
int cInitial,
|
|
int cIncrement,
|
|
DWORD (*pfnHash)(KEYREF),
|
|
int load,
|
|
GETKEY pGetKey,
|
|
MATCHKEY pMatchKey
|
|
) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Initialize the hash table
|
|
|
|
Arguments :
|
|
|
|
pNext - A pointer to Member with class Data where we can hold
|
|
our bucket pointers !
|
|
cInitial - Initial size of the hash table
|
|
cIncrement - Amount to grow the hash table by !
|
|
pfnHash - Hash Function -
|
|
load - Average bucket length before growing the table !
|
|
|
|
Return Value :
|
|
|
|
TRUE if successfull FALSE otherwise
|
|
|
|
--*/
|
|
|
|
m_pGetKey = pGetKey ;
|
|
m_pMatchKey = pMatchKey ;
|
|
|
|
//
|
|
// Compute nearest power of 2
|
|
//
|
|
|
|
m_pNext = pNext ;
|
|
|
|
int power = cInitial ;
|
|
while( power & (power-1) )
|
|
power = power & (power-1) ;
|
|
power<<= 1 ;
|
|
|
|
cInitial = power;
|
|
m_load = load ;
|
|
m_pfnHash = pfnHash ;
|
|
|
|
//
|
|
// Number of ActiveBuckets is initially half that of the number of buckets.
|
|
//
|
|
|
|
m_cActiveBuckets = power/2 ;
|
|
m_cBuckets = power ;
|
|
m_cInserts = m_cActiveBuckets * m_load ;
|
|
m_cIncrement = m_cActiveBuckets / 4;
|
|
m_cNumAlloced = cInitial + 5 * m_cIncrement ;
|
|
|
|
//
|
|
// Allocate bucket pointers and zero initialize
|
|
//
|
|
|
|
m_ppBucket = new Data*[m_cNumAlloced] ;
|
|
|
|
if( m_ppBucket ) {
|
|
ZeroMemory( m_ppBucket, m_cNumAlloced * sizeof( Data*) ) ;
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
return TRUE ;
|
|
}
|
|
return FALSE ;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
BOOL
|
|
TFHashEx< Data, Key, KEYREF >::IsValid( BOOL fCheckHash ) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Check that the hash table is valid
|
|
|
|
Arguments :
|
|
|
|
fCheckHash - verify that all the buckets contain the correct hash values !
|
|
|
|
Return Value :
|
|
|
|
TRUE if successfull FALSE otherwise
|
|
|
|
--*/
|
|
|
|
//
|
|
// This function checks that all member variables are consistent and correct.
|
|
// Do not call this function until AFTER calling the Init() function.
|
|
//
|
|
|
|
if( m_cBuckets <= 0 ||
|
|
m_cActiveBuckets <= 0 ||
|
|
m_cNumAlloced <= 0 ||
|
|
m_cIncrement <= 0 ||
|
|
m_load <= 0 )
|
|
return FALSE ;
|
|
|
|
if( m_cActiveBuckets < (m_cBuckets / 2) || m_cActiveBuckets > m_cBuckets )
|
|
return FALSE ;
|
|
|
|
if( m_cActiveBuckets > m_cNumAlloced )
|
|
return FALSE ;
|
|
|
|
if( m_cInserts > (m_load * m_cActiveBuckets) )
|
|
return FALSE ;
|
|
|
|
if( m_ppBucket == 0 )
|
|
return FALSE ;
|
|
|
|
if( fCheckHash ) {
|
|
//
|
|
// Examine every bucket chain to ensure that elements are in correct slots.
|
|
//
|
|
for( int i=0; i<m_cNumAlloced; i++ ) {
|
|
|
|
if( i>=m_cActiveBuckets ) {
|
|
if( m_ppBucket[i] != 0 ) {
|
|
return FALSE ;
|
|
}
|
|
} else {
|
|
for( Data *p = m_ppBucket[i]; p != 0; p = p->*m_pNext ) {
|
|
KEYREF keyref = (p->*m_pGetKey)();
|
|
if( ComputeIndex( m_pfnHash( keyref ) ) != unsigned(i) ) {
|
|
return FALSE ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
TFHashEx< Data, Key, KEYREF >::~TFHashEx() {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Destroy the hash table !
|
|
|
|
Arguments :
|
|
|
|
None
|
|
|
|
Return Value :
|
|
|
|
None
|
|
|
|
--*/
|
|
//
|
|
// The destructor discards any memory we have allocated.
|
|
//
|
|
Clear();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
void
|
|
TFHashEx< Data, Key, KEYREF >::Clear() {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Delete all entries in the table, and reset all member variables !
|
|
User must call Init() again before the table is usable !
|
|
|
|
Arguments :
|
|
|
|
None.
|
|
|
|
Return Value :
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
//
|
|
// Discards any memory we have allocated - after this, you must
|
|
// call Init() again!
|
|
//
|
|
if( m_ppBucket ) {
|
|
_ASSERT( IsValid( TRUE ) ) ;
|
|
|
|
for( int i=0; i<m_cNumAlloced; i++ ) {
|
|
Data *p, *pNext ;
|
|
for( p = m_ppBucket[i], pNext = p ? p->*m_pNext : 0;
|
|
p!=0; p=pNext, pNext= p ? p->*m_pNext : 0 ) {
|
|
delete p ;
|
|
}
|
|
}
|
|
delete[] m_ppBucket;
|
|
}
|
|
|
|
m_cBuckets = 0;
|
|
m_cActiveBuckets = 0;
|
|
m_cNumAlloced = 0;
|
|
m_cIncrement = 0;
|
|
m_ppBucket = 0;
|
|
m_pfnHash = 0;
|
|
m_load = 0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
void
|
|
TFHashEx< Data, Key, KEYREF >::Empty() {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Remove all entries in the table, and reset all member variables !
|
|
User must call Init() again before the table is usable !
|
|
This is just like Clear() but it does do a "delete".
|
|
|
|
Arguments :
|
|
|
|
None.
|
|
|
|
Return Value :
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
//
|
|
// Discards any memory we have allocated - after this, you must
|
|
// call Init() again!
|
|
//
|
|
if( m_ppBucket ) {
|
|
_ASSERT( IsValid( TRUE ) ) ;
|
|
|
|
delete[] m_ppBucket;
|
|
}
|
|
|
|
m_cBuckets = 0;
|
|
m_cActiveBuckets = 0;
|
|
m_cNumAlloced = 0;
|
|
m_cIncrement = 0;
|
|
m_ppBucket = 0;
|
|
m_pfnHash = 0;
|
|
m_load = 0;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
DWORD
|
|
TFHashEx< Data, Key, KEYREF >::ComputeIndex( DWORD dw ) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Compute which bucket an element should be in
|
|
|
|
This function tells us where we should store elements. To do this we mod with
|
|
m_cBuckets. Since we only have m_cActiveBuckets in reality, we check the result
|
|
of the mod and subtract m_cBuckets over 2 if necessary.
|
|
|
|
Arguments :
|
|
|
|
dw - the hash value of the entry we are adding to the table
|
|
|
|
Return Value :
|
|
|
|
Index to the bucket to use !
|
|
|
|
--*/
|
|
|
|
DWORD dwTemp = dw % m_cBuckets ;
|
|
return (dwTemp >= (unsigned)m_cActiveBuckets) ? dwTemp - (m_cBuckets/2) : dwTemp ;
|
|
}
|
|
|
|
|
|
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
BOOL
|
|
TFHashEx< Data, Key, KEYREF >::Insert( Data& d ) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Insert a Data element into the hash table
|
|
|
|
Arguments :
|
|
|
|
d - reference to the item to be inserted into the table
|
|
|
|
Return Value :
|
|
|
|
TRUE if successfull - FALSE otherwise !
|
|
|
|
--*/
|
|
|
|
_ASSERT( d.*m_pNext == 0 ) ;
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
|
|
if( InsertData( d ) )
|
|
return TRUE ;
|
|
return FALSE ;
|
|
}
|
|
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
BOOL
|
|
TFHashEx< Data, Key, KEYREF >::Insert( Data* pd ) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Insert a Data element into the hash table
|
|
|
|
Arguments :
|
|
|
|
pd - pointer to the item to be inserted into the table
|
|
|
|
Return Value :
|
|
|
|
TRUE if successfull - FALSE otherwise !
|
|
|
|
--*/
|
|
|
|
_ASSERT( pd->*m_pNext == 0 ) ;
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
|
|
KEYREF keyref = (pd->*m_pGetKey)() ;
|
|
if( InsertDataHash( m_pfnHash( keyref ), pd ) )
|
|
return TRUE ;
|
|
return FALSE ;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
Data*
|
|
TFHashEx< Data, Key, KEYREF >::InsertDataHash(
|
|
DWORD dwHash,
|
|
Data& d
|
|
) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Insert an element into the hash table.
|
|
We will use member's of Data to hold the bucket chain.
|
|
|
|
Arguments :
|
|
|
|
dw - the hash value of the entry we are adding to the table
|
|
d - The item we are adding to the table !
|
|
|
|
Return Value :
|
|
|
|
Pointer to the Data Item in its final resting place !
|
|
|
|
--*/
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
_ASSERT( d.*m_pNext == 0 ) ;
|
|
|
|
//
|
|
// First check whether it is time to grow the hash table.
|
|
//
|
|
if( --m_cInserts == 0 ) {
|
|
|
|
//
|
|
// Check whether we need to reallocate the array of Bucket pointers.
|
|
//
|
|
if( m_cIncrement + m_cActiveBuckets > m_cNumAlloced ) {
|
|
|
|
|
|
Data** pTemp = new Data*[m_cNumAlloced + 10 * m_cIncrement ] ;
|
|
|
|
if( pTemp == 0 ) {
|
|
//
|
|
// bugbug ... need to handles this error better !?
|
|
//
|
|
return 0 ;
|
|
} else {
|
|
ZeroMemory( pTemp, (m_cNumAlloced + 10 *m_cIncrement)* sizeof( Data*) ) ;
|
|
CopyMemory( pTemp, m_ppBucket, m_cNumAlloced * sizeof( Data* ) ) ;
|
|
delete[] m_ppBucket;
|
|
m_cNumAlloced += 10 * m_cIncrement ;
|
|
m_ppBucket = pTemp ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Okay grow the array by m_cIncrement.
|
|
//
|
|
m_cActiveBuckets += m_cIncrement ;
|
|
if( m_cActiveBuckets > m_cBuckets )
|
|
m_cBuckets *= 2 ;
|
|
m_cInserts = m_cIncrement * m_load ;
|
|
|
|
//
|
|
// Now do some rehashing of elements.
|
|
//
|
|
|
|
for( int i = -m_cIncrement; i < 0; i++ ) {
|
|
int iCurrent = (m_cActiveBuckets + i) - (m_cBuckets / 2) ;
|
|
Data** ppNext = &m_ppBucket[ iCurrent ] ;
|
|
Data* p = *ppNext ;
|
|
while( p ) {
|
|
|
|
KEYREF keyref = (p->*m_pGetKey)();
|
|
int index = ComputeIndex( m_pfnHash( keyref ) ) ;
|
|
Data* pNext = p->*m_pNext ;
|
|
if( index != iCurrent) {
|
|
*ppNext = pNext ;
|
|
p->*m_pNext = m_ppBucket[index] ;
|
|
m_ppBucket[index] = p ;
|
|
} else {
|
|
ppNext = &(p->*m_pNext) ;
|
|
}
|
|
p = pNext ;
|
|
}
|
|
}
|
|
_ASSERT( IsValid( TRUE ) ) ;
|
|
}
|
|
|
|
//
|
|
// Finally, insert into the Hash Table.
|
|
//
|
|
//DWORD index = ComputeIndex( m_pfnHash( d.GetKey() ) ) ;
|
|
|
|
KEYREF keyref = (d.*m_pGetKey)();
|
|
_ASSERT( dwHash == m_pfnHash( keyref ) ) ;
|
|
DWORD index = ComputeIndex( dwHash ) ;
|
|
|
|
_ASSERT( index < unsigned(m_cActiveBuckets) ) ;
|
|
|
|
d.*m_pNext = m_ppBucket[index] ;
|
|
m_ppBucket[index] = &d ;
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
|
|
return &d ;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
Data*
|
|
TFHashEx< Data, Key, KEYREF >::InsertDataHash(
|
|
DWORD dwHash,
|
|
Data* pd
|
|
) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Insert an element into the hash table.
|
|
We will use member's of Data to hold the bucket chain.
|
|
|
|
Arguments :
|
|
|
|
dw - the hash value of the entry we are adding to the table
|
|
pd - Pointer to the item we are adding to the table !
|
|
|
|
Return Value :
|
|
|
|
Pointer to the Data Item in its final resting place !
|
|
|
|
--*/
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
_ASSERT( pd->*m_pNext == 0 ) ;
|
|
|
|
//
|
|
// First check whether it is time to grow the hash table.
|
|
//
|
|
if( --m_cInserts == 0 ) {
|
|
|
|
//
|
|
// Check whether we need to reallocate the array of Bucket pointers.
|
|
//
|
|
if( m_cIncrement + m_cActiveBuckets > m_cNumAlloced ) {
|
|
|
|
|
|
Data** pTemp = new Data*[m_cNumAlloced + 10 * m_cIncrement ] ;
|
|
|
|
if( pTemp == 0 ) {
|
|
//
|
|
// bugbug ... need to handles this error better !?
|
|
//
|
|
return 0 ;
|
|
} else {
|
|
ZeroMemory( pTemp, (m_cNumAlloced + 10 *m_cIncrement)* sizeof( Data*) ) ;
|
|
CopyMemory( pTemp, m_ppBucket, m_cNumAlloced * sizeof( Data* ) ) ;
|
|
delete[] m_ppBucket;
|
|
m_cNumAlloced += 10 * m_cIncrement ;
|
|
m_ppBucket = pTemp ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Okay grow the array by m_cIncrement.
|
|
//
|
|
m_cActiveBuckets += m_cIncrement ;
|
|
if( m_cActiveBuckets > m_cBuckets )
|
|
m_cBuckets *= 2 ;
|
|
m_cInserts = m_cIncrement * m_load ;
|
|
|
|
//
|
|
// Now do some rehashing of elements.
|
|
//
|
|
|
|
for( int i = -m_cIncrement; i < 0; i++ ) {
|
|
int iCurrent = (m_cActiveBuckets + i) - (m_cBuckets / 2) ;
|
|
Data** ppNext = &m_ppBucket[ iCurrent ] ;
|
|
Data* p = *ppNext ;
|
|
while( p ) {
|
|
|
|
KEYREF keyref = (p->*m_pGetKey)();
|
|
int index = ComputeIndex( m_pfnHash( keyref ) ) ;
|
|
Data* pNext = p->*m_pNext ;
|
|
if( index != iCurrent) {
|
|
*ppNext = pNext ;
|
|
p->*m_pNext = m_ppBucket[index] ;
|
|
m_ppBucket[index] = p ;
|
|
} else {
|
|
ppNext = &(p->*m_pNext) ;
|
|
}
|
|
p = pNext ;
|
|
}
|
|
}
|
|
_ASSERT( IsValid( TRUE ) ) ;
|
|
}
|
|
|
|
//
|
|
// Finally, insert into the Hash Table.
|
|
//
|
|
//DWORD index = ComputeIndex( m_pfnHash( d.GetKey() ) ) ;
|
|
|
|
KEYREF keyref = (pd->*m_pGetKey)();
|
|
_ASSERT( dwHash == m_pfnHash( keyref ) ) ;
|
|
DWORD index = ComputeIndex( dwHash ) ;
|
|
|
|
_ASSERT( index < unsigned(m_cActiveBuckets) ) ;
|
|
|
|
pd->*m_pNext = m_ppBucket[index] ;
|
|
m_ppBucket[index] = pd ;
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
|
|
return pd ;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
inline Data*
|
|
TFHashEx< Data, Key, KEYREF >::InsertData( Data& d ) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Insert an element into the hash table.
|
|
We will use member's of Data to hold the bucket chain,
|
|
and we will also compute the hash of the key !
|
|
|
|
Arguments :
|
|
|
|
d - The item we are adding to the table !
|
|
|
|
Return Value :
|
|
|
|
Pointer to the Data Item in its final resting place !
|
|
|
|
--*/
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
|
|
KEYREF keyref = (d.*m_pGetKey)() ;
|
|
return InsertDataHash( m_pfnHash( keyref ), d ) ;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
BOOL
|
|
TFHashEx< Data, Key, KEYREF >::Search( KEYREF k,
|
|
Data &dOut
|
|
) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Search for an element in the hashtable.
|
|
|
|
Arguments :
|
|
|
|
|
|
k - key of the item to find
|
|
dOut - A reference that we will set to the
|
|
located data item
|
|
|
|
Return Value :
|
|
|
|
TRUE if found, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
const Data* pData = SearchKey( k ) ;
|
|
if( pData ) {
|
|
dOut = *pData ;
|
|
return TRUE ;
|
|
}
|
|
return FALSE ;
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
Data*
|
|
TFHashEx< Data, Key, KEYREF >::SearchKeyHash(
|
|
DWORD dwHash,
|
|
KEYREF k
|
|
) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Search for an element in the Hash Table,
|
|
|
|
Arguments :
|
|
|
|
dwHash - the hash value of the entry we are adding to the table
|
|
k - reference to the key we are to compare against
|
|
|
|
Return Value :
|
|
|
|
Pointer to the Data Item in its final resting place !
|
|
|
|
--*/
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
_ASSERT( dwHash == (m_pfnHash)(k) ) ;
|
|
|
|
DWORD index = ComputeIndex( dwHash ) ;
|
|
Data* p = m_ppBucket[index] ;
|
|
while( p ) {
|
|
if( (p->*m_pMatchKey)( k ) )
|
|
break ;
|
|
p = p->*m_pNext ;
|
|
}
|
|
return p ;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
inline Data*
|
|
TFHashEx< Data, Key, KEYREF >::SearchKey( KEYREF k ) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Search for an element in the Hash Table,
|
|
We will compute the hash of the key.
|
|
|
|
Arguments :
|
|
|
|
k - reference to the key we are to compare against
|
|
|
|
Return Value :
|
|
|
|
Pointer to the Data Item in its final resting place !
|
|
|
|
--*/
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
return SearchKeyHash( m_pfnHash( k ), k ) ;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
BOOL
|
|
TFHashEx< Data, Key, KEYREF >::Delete( KEYREF k ) {
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Find an element in the hash table, remove it from
|
|
the table and then destroy it !
|
|
|
|
Arguments :
|
|
|
|
k - reference to the key we are to compare against
|
|
|
|
Return Value :
|
|
|
|
TRUE if an item is found and destroyed, FALSE otherwise !
|
|
|
|
--*/
|
|
|
|
|
|
Data* p = DeleteData( k, 0 ) ;
|
|
if( p ) {
|
|
delete p ;
|
|
return TRUE ;
|
|
}
|
|
return FALSE ;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
Data*
|
|
TFHashEx< Data, Key, KEYREF >::DeleteData( KEYREF k,
|
|
Data* pd
|
|
) {
|
|
//
|
|
// Remove an element from the Hash Table. We only need the
|
|
// Key to find the element we wish to remove.
|
|
//
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
|
|
DWORD dwHash = (m_pfnHash)( k ) ;
|
|
|
|
DWORD index = ComputeIndex( dwHash ) ;
|
|
Data** ppNext = &m_ppBucket[index] ;
|
|
Data* p = *ppNext ;
|
|
|
|
while( p ) {
|
|
if( (p->*m_pMatchKey)( k ) )
|
|
break ;
|
|
ppNext = &(p->*m_pNext) ;
|
|
p = *ppNext ;
|
|
}
|
|
if( p ) {
|
|
//
|
|
// If we were given a pointer to a data block, than the client
|
|
// wants us to check to make sure that we are deleting the correct
|
|
// instance !!
|
|
//
|
|
if( !pd || pd == p ) {
|
|
*ppNext = p->*m_pNext ;
|
|
p->*m_pNext = 0 ;
|
|
|
|
|
|
//
|
|
// Finally - since we removed something from the hash table
|
|
// increment the number of inserts so that we don't keep splitting
|
|
// the table unnecessarily !
|
|
//
|
|
m_cInserts++ ;
|
|
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
} else {
|
|
p = 0 ;
|
|
}
|
|
}
|
|
_ASSERT( IsValid( FALSE ) ) ;
|
|
return p ;
|
|
}
|
|
|
|
template< class Data,
|
|
class Key,
|
|
class KEYREF
|
|
>
|
|
DWORD
|
|
TFHashEx< Data, Key, KEYREF >::ComputeHash( KEYREF k ) {
|
|
|
|
return m_pfnHash( k ) ;
|
|
|
|
}
|
|
|
|
|