Windows2003-3790/inetsrv/query/h/twidhash.hxx
2020-09-30 16:53:55 +02:00

869 lines
21 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000.
//
// File: twidhash.hxx
//
// Contents: WORKID Hash Table (Closed) implementation for mapping
// WORKIDs to a 32bit value (row offset in the table).
// Classes:
//
// Functions:
//
// History: 11-21-94 srikants Created
// 2-16-95 srikants Modified to use templates
//
// Notes : The "WorkId" itself will be used as a hash value as it is
// supposed to be unique for this implementation.
//
//----------------------------------------------------------------------------
#pragma once
//
// Bit 32 of an WORKID is not set for any valid wid. It is used by OFS
// to indicate a deleted WORKID. We will use that fact to mark deleted
// entries in the hash table.
//
const widDeleted = 0x80000000; // Used for marking "deleted" entries
#include <pshpack4.h>
class CWidHashEntry
{
public:
CWidHashEntry( WORKID wid = widInvalid ) : _wid(wid) { }
void SetWorkId( WORKID wid ) { _wid = wid; }
WORKID WorkId() const
{
return _wid;
}
ULONG HashValue() const { return _wid; }
BOOL IsDeleted() const
{
return widDeleted == _wid;
}
BOOL IsFree() const
{
return (widInvalid == _wid) || (widDeleted == _wid);
}
BOOL IsValid() const
{
return widInvalid != _wid;
}
protected:
WORKID _wid; // WorkId being hashed (hash Key)
};
class CWidValueHashEntry : public CWidHashEntry
{
public:
CWidValueHashEntry( WORKID wid=widInvalid, ULONG value = 0 )
: CWidHashEntry( wid ), _value(value)
{
}
void SetValue( ULONG value )
{
_value = value;
}
void Get( WORKID & wid, ULONG & value ) const
{
wid = _wid;
value = _value;
}
ULONG Value() const
{
return _value;
}
private:
ULONG _value; // The value for the BookMark (hash Value)
};
class CDirWidHashEntry: public CWidHashEntry
{
public:
CDirWidHashEntry ( WORKID wid=widInvalid, BOOL fDeep=FALSE, BOOL fInScope=FALSE )
: CWidHashEntry( wid ), _fDeep(fDeep), _fInScope(fInScope)
{
}
BOOL fInScope()const
{
return _fInScope;
}
BOOL fDeep ()const
{
return _fDeep;
}
private:
BOOL _fInScope;
BOOL _fDeep;
};
#include <poppack.h>
const TWID_INIT_HASH_SIZE = 31;
const MEASURE_OF_SLACK = 3;
template <class T> class TWidHashTable
{
public:
TWidHashTable( ULONG cHashInit = TWID_INIT_HASH_SIZE,
BOOL fReHash = TRUE );
~TWidHashTable();
BOOL IsWorkIdPresent( WORKID wid )
{
ULONG iTable;
return _LookUp( wid, iTable );
}
ULONG AddEntry( const T & entry );
T & FindOrAdd( WORKID wid );
void ReplaceOrAddEntry( const T & entry );
BOOL DeleteWorkId( WORKID wid );
void DeleteItem( T & item );
BOOL LookUpWorkId( T & entry );
void DeleteAllEntries();
const T & GetEntry( ULONG i )
{
Win4Assert( i < _size );
return _pTable[i];
}
ULONG Size() const { return _size; }
ULONG Count() const { return _count; }
private:
BOOL _IsFull() const
{
return (_count + _count/MEASURE_OF_SLACK) >= _size;
}
BOOL _LookUp( WORKID wid, ULONG & riTable );
ULONG _GrowSize(ULONG count) const;
void _GrowAndReHash();
BOOL _IsSizeValid( ULONG size, ULONG count ) const;
T * _AllocAndInit(ULONG size ) const;
static void _InitTable( T * pTable, ULONG size );
ULONG _size;
ULONG _count;
T * _pTable;
//
// Number of deleted entries. These can be re-used for adding new
// entries.
//
ULONG _cDeleted;
BOOL _fReHash;
#if DBG==1
//
// For keeping track of usage statistics.
//
unsigned _cMaxChainLen;
unsigned _cTotalSearches;
LONGLONG _cTotalLength;
#endif // DBG==1
};
//+---------------------------------------------------------------------------
//
// Function: CWorkIdHashTable :: ~ctor
//
// Synopsis: Constructs the hash table. It allocates a table of size
// cHashInit and initializes it to be "empty".
//
// Arguments: [cHashInit] - Number of buckets to allocate. A minimum of
// TWID_INIT_HASH_SIZE will be allocated.
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> TWidHashTable<T>::TWidHashTable(
ULONG cHashInit,
BOOL fReHash ) : _fReHash( fReHash )
{
Win4Assert( 0 != cHashInit );
#if DBG==1
_cMaxChainLen = 0;
_cTotalSearches = 0;
_cTotalLength = 0;
#endif // DBG==1
_count = 0;
_size = max( cHashInit, TWID_INIT_HASH_SIZE ) ;
_pTable = _AllocAndInit(_size);
_cDeleted = 0;
Win4Assert( !_IsFull() );
}
template<class T> TWidHashTable<T>::~TWidHashTable()
{
if ( 0 != _size )
{
Win4Assert( 0 != _pTable );
delete [] ((BYTE *)_pTable);
}
}
template<class T> T & TWidHashTable<T>::FindOrAdd( WORKID wid )
{
ULONG iItem;
if ( _LookUp( wid, iItem ) )
return _pTable[ iItem ];
T entry;
entry.SetWorkId( wid );
return _pTable[ AddEntry( entry ) ];
}
//+---------------------------------------------------------------------------
//
// Function: _LookUp
//
// Synopsis: Looks up for the specified wid in the hash table.
//
// Arguments: [wid] - WorkId to look up.
// [riTable] - (output) Will contain the index in the hash
// table of the entry if found.
//
// Returns: TRUE if found; FALSE o/w
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> BOOL TWidHashTable<T>::_LookUp( WORKID wid, ULONG &riTable )
{
Win4Assert( 0 != _size );
Win4Assert( widInvalid != wid );
Win4Assert( !_IsFull() );
ULONG cur = wid % _size;
ULONG start = cur;
ULONG delta = cur;
#if DBG==1
ULONG cSearchLen = 1;
#endif // DBG==1
BOOL fFound = FALSE;
while ( !fFound && (widInvalid != _pTable[cur].WorkId()) )
{
if ( wid == _pTable[cur].WorkId() )
{
riTable = cur;
fFound = TRUE;
}
else
{
cur = (cur + delta) % _size;
if ( cur == start ) // wrapped around
{
if ( 1 != delta )
{
delta = 1;
cur = (cur + 1) % _size;
}
else
{
break;
}
}
#if DBG==1
cSearchLen++;
#endif // DBG==1
}
}
#if DBG==1
_cTotalSearches++;
_cTotalLength += cSearchLen;
if (cSearchLen > _cMaxChainLen)
_cMaxChainLen = cSearchLen;
#endif // DBG==1
return fFound;
}
//+---------------------------------------------------------------------------
//
// Function: LookUpWorkId
//
// Synopsis: Looks up the specified work id in the hash table.
//
// Arguments: [wid] - WorkId to look up
// [value] - (output) Value associated with the wid (if lookup
// is successful).
//
// Returns: TRUE if found; FALSE o/w
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> BOOL TWidHashTable<T>::LookUpWorkId( T & entry )
{
const WORKID wid = entry.WorkId();
Win4Assert( widInvalid != wid );
ULONG iTable;
BOOL fFound = _LookUp( wid, iTable );
if ( fFound )
{
Win4Assert( iTable < _size );
entry = _pTable[iTable];
}
return fFound;
}
//+---------------------------------------------------------------------------
//
// Function: DeleteWorkId
//
// Synopsis: Deletes the entry associated with the specified workid from
// the hash table.
//
// Arguments: [wid] - WorkId to delete.
//
// Returns: TRUE if the given workid was present in the hash table.
// FALSE o/w
//
// History: 1-09-95 srikants Created
//
// Notes: The entry deleted will be identified as "deleted" and will
// be later available for "reuse".
//
//----------------------------------------------------------------------------
template<class T> BOOL TWidHashTable<T>::DeleteWorkId( WORKID wid )
{
Win4Assert( widInvalid != wid );
ULONG iTable;
BOOL fFound = _LookUp( wid, iTable );
if ( fFound )
{
Win4Assert( iTable < _size );
Win4Assert( wid == _pTable[iTable].WorkId() );
Win4Assert( _count > 0 );
_pTable[iTable].SetWorkId( widDeleted );
_count--;
_cDeleted++;
}
return fFound;
}
//+---------------------------------------------------------------------------
//
// Function: DeleteItem
//
// Synopsis: Deletes an entry in the hash table.
//
// Arguments: [item] - reference to the item to be deleted.
//
// History: 28-Feb-96 dlee Created
//
// Notes: The entry deleted will be identified as "deleted" and will
// be later available for "reuse" if there are other items
// in the hash table. Otherwise it is marked as widInvalid.
//
//----------------------------------------------------------------------------
template<class T> void TWidHashTable<T>::DeleteItem( T & item )
{
Win4Assert( widInvalid != item.WorkId() );
Win4Assert( widDeleted != item.WorkId() );
Win4Assert( _count > 0 );
_count--;
// If there aren't any items in the hash table, don't worry about
// breaking up a hash chain, especially since finding a wid in an
// array of widDeleted items is a linear search...
if ( 0 == _count )
item.SetWorkId( widInvalid );
else
{
item.SetWorkId( widDeleted );
_cDeleted++;
}
}
//+---------------------------------------------------------------------------
//
// Function: AddWorkId
//
// Synopsis: Adds the specified workid to the hash table. If one is
// already present, its value will be modified.
//
// Arguments: [wid] - WorkId to be added.
// [value] - Value associated with the workid.
//
// History: 1-09-95 srikants Created
//
// Notes: If the table gets "full" as a result of adding the
// workid, the table will be re-sized and entries re-hashed.
//
//----------------------------------------------------------------------------
template<class T> ULONG TWidHashTable<T>::AddEntry( const T & entry )
{
const WORKID wid = entry.WorkId();
Win4Assert( 0 != _size );
Win4Assert( widInvalid != wid );
Win4Assert( widDeleted != wid );
Win4Assert( !_IsFull() );
ULONG cur = wid % _size;
ULONG start = cur;
//
// Initialize use a delta which is not 1 to prevent data from getting
// clumped in one place.
//
ULONG delta = cur;
#if DBG==1
ULONG cSearchLen = 1;
#endif // DBG==1
while ( !_pTable[cur].IsFree() )
{
if ( wid == _pTable[cur].WorkId() )
{
// just replacing the value of the workid.
_pTable[cur] = entry;
return cur;
}
cur = (cur + delta) % _size;
if ( cur == start ) // wrapped around
{
if ( 1 == delta )
{
if ( _fReHash )
{
Win4Assert( delta != 1 );
}
else
{
// If no rehashing is turned on, give out any old record
// and pump out a warning. Clients expect this behavior!
ciDebugOut(( DEB_IWARN,
"TWidHashTable::AddEntry handing out duplicate" ));
return cur;
}
}
delta = 1;
cur = (cur + 1) % _size;
}
#if DBG==1
cSearchLen++;
#endif // DBG==1
}
#if DBG==1
_cTotalSearches++;
_cTotalLength += cSearchLen;
if (cSearchLen > _cMaxChainLen)
_cMaxChainLen = cSearchLen;
#endif DBG==1
#if DBG==1
ULONG iTable;
Win4Assert( !_LookUp( wid, iTable ) );
#endif // DBG
if ( widDeleted == _pTable[cur].WorkId() )
{
Win4Assert( _cDeleted > 0 );
_cDeleted--;
}
_pTable[cur] = entry;
_count++;
if ( _fReHash && _IsFull() )
{
_GrowAndReHash();
}
return cur;
}
//+---------------------------------------------------------------------------
//
// Function: ReplaceOrAddWorkId
//
// Synopsis: Replaces the value associated with the given workid if one
// exists; creates a new one if the given wid is not in the
// hash table.
//
// Arguments: [wid] - Workid to be replaced/added
// [value] - Value associated with the workid.
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> void TWidHashTable<T>::ReplaceOrAddEntry( const T & entry )
{
ULONG iTable = _size;
BOOL fFound = FALSE;
const WORKID wid = entry.WorkId();
if ( _cDeleted )
{
//
// If there are "deleted" entries present, we must first do a
// look up to see if an entry is already present. AddWorkId
// will use the first free entry available and may not be able
// to detect this workid.
//
fFound = _LookUp( wid, iTable );
}
if ( fFound )
{
Win4Assert( iTable < _size );
Win4Assert( _pTable[iTable].WorkId() == wid );
_pTable[iTable] = entry;
}
else
{
AddEntry( entry );
}
}
//+---------------------------------------------------------------------------
//
// Function: DeleteAllEntries
//
// Synopsis: Deletes all the entries in the hash table (efficiently).
//
// History: 11-22-94 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> void TWidHashTable<T>::DeleteAllEntries()
{
#if DBG==1
_cMaxChainLen = 0;
_cTotalSearches = 0;
_cTotalLength = 0;
#endif // DBG==1
_InitTable( _pTable, _size );
_count = 0;
_cDeleted = 0;
}
//+---------------------------------------------------------------------------
//
// Function: _IsSizeValid
//
// Synopsis: Determines if the specified size and count are in agreement
// or not.
//
// Arguments: [size] - The total size of the hash table.
// [count] - Number of entries used in the hash table.
//
// Returns: TRUE if okay; FALSE o/w
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> BOOL TWidHashTable<T>::_IsSizeValid ( ULONG size, ULONG count ) const
{
if (size == 0 && count == 0)
return(TRUE);
// is it a power of 2 plus 1?
for (ULONG i = 1; i < size; i *= 2 )
continue;
if (i/2 + 1 != size)
return(FALSE);
// is there enough slack?
if (count + count/MEASURE_OF_SLACK > size)
return(FALSE);
return(TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: _GrowSize
//
// Synopsis: For a given valid hash table entries, this routine figures
// out the next valid size (close approximation to a prime).
//
// Arguments: [count] - Number of valid entries in the hash table.
//
// Returns: The size of the hash table for the given number of valid
// entries.
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> ULONG TWidHashTable<T>::_GrowSize ( ULONG count ) const
{
ULONG size = (count + count/MEASURE_OF_SLACK + 1);
// make it power of two + 1
// a good approximation of a prime
for ( unsigned sizeInit = 1; sizeInit < size; sizeInit *= 2 )
continue;
return(sizeInit + 1);
}
//+---------------------------------------------------------------------------
//
// Function: _AllocAndInit
//
// Synopsis: Helper function to allocate a buffer for the specified number
// of entries and initialize the buffer with "unused" entries.
//
// Arguments: [size] - Number of entries for the hash table.
//
// Returns: Pointer to the buffer.
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> T * TWidHashTable<T>::_AllocAndInit( ULONG size ) const
{
ULONG cb = sizeof(T)*size;
//
// Note that we are not using the new CWorkIdHashEntry [size] because
// that will call the constructor on all the entries and we have an
// easier way of initializing the array
//
T * pTable = (T *) new BYTE [cb];
_InitTable( pTable, size );
return pTable;
}
//+---------------------------------------------------------------------------
//
// Function: _InitTable
//
// Synopsis: Helper function to initialize the hash table array.
//
// Arguments: [pTable] - Pointer to the table.
// [size] - Size (in entries) of the table.
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> void TWidHashTable<T>::_InitTable(
T * pTable,
ULONG size )
{
ULONG cb = sizeof(T)*size;
RtlFillMemory( pTable, cb, 0xFF );
}
//+---------------------------------------------------------------------------
//
// Function: _GrowAndReHash
//
// Synopsis: Grows the current hash table and re-hashes all the entries
// in the new table.
//
// History: 1-09-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template<class T> void TWidHashTable<T>::_GrowAndReHash()
{
ULONG sizeNew = _GrowSize( _count );
Win4Assert( sizeNew != _size );
T * pNewTable = _AllocAndInit( sizeNew );
ULONG sizeOld = _size;
ULONG countOld = _count;
T * pOldTable = _pTable;
//
// Add the entries from the current table to the new table.
//
_size = sizeNew;
_count = 0;
_pTable = pNewTable;
_cDeleted = 0;
for ( ULONG i = 0; i < sizeOld; i++ )
{
if ( !pOldTable[i].IsFree() )
{
Win4Assert( pOldTable[i].WorkId() != widInvalid );
AddEntry( pOldTable[i] );
}
}
Win4Assert( _count == countOld );
delete [] ((BYTE *) pOldTable);
}
//+---------------------------------------------------------------------------
//
// Class: TWidHashIter
//
// Purpose: Iterator over the TWidHashTable
//
// History: 3-10-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
template <class T> class TWidHashIter
{
public:
TWidHashIter( TWidHashTable<T> & hashTable )
: _hashTable(hashTable)
{
for ( _curr = 0 ; !AtEnd() && _hashTable.GetEntry(_curr).IsFree();
_curr++ )
{
}
}
const T & Get()
{
return _hashTable.GetEntry(_curr);
}
BOOL AtEnd() const
{
Win4Assert( _curr <= _hashTable.Size() );
return _curr == _hashTable.Size();
}
void Reset()
{
_curr = 0;
}
void Next()
{
Win4Assert( !AtEnd() );
for ( _curr++ ; !AtEnd() && _hashTable.GetEntry(_curr).IsFree();
_curr++ )
{
}
}
private:
TWidHashTable<T> & _hashTable;
ULONG _curr;
};