//+--------------------------------------------------------------------------- // // 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 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 const TWID_INIT_HASH_SIZE = 31; const MEASURE_OF_SLACK = 3; template 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 TWidHashTable::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 TWidHashTable::~TWidHashTable() { if ( 0 != _size ) { Win4Assert( 0 != _pTable ); delete [] ((BYTE *)_pTable); } } template T & TWidHashTable::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 BOOL TWidHashTable::_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 BOOL TWidHashTable::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 BOOL TWidHashTable::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 void TWidHashTable::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 ULONG TWidHashTable::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 void TWidHashTable::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 void TWidHashTable::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 BOOL TWidHashTable::_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 ULONG TWidHashTable::_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 T * TWidHashTable::_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 void TWidHashTable::_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 void TWidHashTable::_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 TWidHashIter { public: TWidHashIter( TWidHashTable & 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 & _hashTable; ULONG _curr; };