//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996-2000 // // File: CHash.hxx // // Contents: Hash table // // Classes: CWidHashTable // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- #include #pragma hdrstop #include "chash.hxx" //+--------------------------------------------------------------------------- // // Member: CWidHashTable::CWidHashTable, public // // Synopsis: Null constructor // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- CWidHashTable::CWidHashTable( BOOL fAggressiveGrowth ) : _size (0), _table(0), _count(0), _fAggressiveGrowth( fAggressiveGrowth ) { Win4Assert( widInvalid == 0xFFFFFFFF ); #ifdef DO_STATS _cMaxChainLen = 0; _cTotalSearches = 0; _cTotalLength = 0; #endif // DO_STATS } //+--------------------------------------------------------------------------- // // Member: CWidHashTable::Init, public // // Synopsis: Initialize hash table // // Arguments: [count] -- Count of used elements // [size] -- Size of [table] // [table] -- Hash table // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- void CWidHashTable::Init( ULONG count, ULONG size, WORKID * table ) { _count = count; _size = size; _table = table; } //Init //+--------------------------------------------------------------------------- // // Member: CWidHashTable::ReInit, public // // Synopsis: "ReInitialize" to empty state. // // Arguments: [count] -- Count of used elements // [size] -- Size of [table] // [table] -- Hash table // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- void CWidHashTable::ReInit(ULONG count, ULONG size, WORKID* table ) { Init( count, size, table ); Win4Assert( widInvalid == 0xFFFFFFFF ); RtlFillMemory( _table, _size * sizeof(_table[0]), 0xFF ); } //ReInit //+--------------------------------------------------------------------------- // // Member: CWidHashTable::GrowSize, public // // Synopsis: Compute new size for table // // Arguments: [count] -- Count of to-be-used elements // // Returns: Suggested new size // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- unsigned CWidHashTable::GrowSize ( unsigned count ) { if ( count < 3 ) return INIT_HASH_SIZE; if ( !_fAggressiveGrowth ) return count * 2 - 1; // Make the hash table as large as possible without eathing too much // memory. 4 seems to work ok. // The hash table is deemed "full" in IsFull if half of the entries // are in use. // This is so aggressive in picking a new size because: // - rehashing is expensive // - walking on hash collisions is expensive // - it's resized mostly on the initial scan, and there are probably // a lot more files to be added soon. unsigned size = count * 4; // make it power of two + 1 // a good approximation of a prime for ( unsigned sizeInit = 1; sizeInit < size; sizeInit *= 2 ) continue; return sizeInit - 1; } //GrowSize //+--------------------------------------------------------------------------- // // Member: CWidHashTable::Add, public // // Synopsis: Add a new entry // // Arguments: [hash] -- Best position for entry // [wid] -- Workid (entry) // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- void CWidHashTable::Add ( unsigned hash, WORKID wid ) { Win4Assert ( !IsFull() ); unsigned cur = hash % _size; unsigned start = cur; unsigned delta = Delta(); // this loop has to terminate // since the table is NOT full! while ( _table[cur] != widInvalid && _table[cur] != widUnused ) { cur = (cur + delta) % _size; if ( cur == start ) // wrapped around { Win4Assert( delta != 1 ); // table is full delta = 1; cur = (cur + 1) % _size; } } _count++; _table[cur] = wid; } //Add //+--------------------------------------------------------------------------- // // Member: CWidHashTable::Remove, public // // Synopsis: Removes entry from table // // Arguments: [hash] -- Best position for entry // [wid] -- Workid (entry) // [fDisableDeletionCheck] -- Should we assert that the deleted // entry must be found ? // // History: 10-Jan-96 KyleP Added header // // Notes: An entry is 'removed' by marking its slot as unused. An // unused slot is like an empty slot, except it does not // terminate a CShortWidList. // //---------------------------------------------------------------------------- void CWidHashTable::Remove( unsigned hash, WORKID wid, BOOL fDisableDeletionCheck ) { // // This assert not true -- IsFull is checked before adding a new item, // so after the add it could be full. If we happen to do a delete at // this point, the table could be full and that's ok. // // Win4Assert ( !IsFull() ); unsigned cur = hash % _size; unsigned start = cur; unsigned delta = Delta(); // this loop has to terminate // since the table is NOT full! while ( _table[cur] != wid && _table[cur] != widInvalid ) { cur = (cur + delta) % _size; if ( cur == start ) // wrapped around { Win4Assert( delta != 1 ); // table is full delta = 1; cur = (cur + 1) % _size; } } if ( !fDisableDeletionCheck ) { Win4Assert( wid == _table[cur] ); } if ( _table[cur] == wid ) { _count--; _table[cur] = widUnused; } } //Remove //+--------------------------------------------------------------------------- // // Member: CShortWidList::CShortWidList, public // // Synopsis: Iterator for closed hash 'bucket'. // // Arguments: [hash] -- Starting position // [hashTable] -- Table // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- CShortWidList::CShortWidList ( unsigned hash, CWidHashTable& hashTable ) : _hashTable(hashTable), _counter(0), _size( hashTable.Size() ) { if (_size == 0) _wid = widInvalid; else { _iCur = hash % _size; _iStart = _iCur; _delta = hashTable.Delta(); _wid = _hashTable[_iCur]; if ( widUnused == _wid ) _wid = NextWorkId(); } } //CShortWidList //+--------------------------------------------------------------------------- // // Member: CShortWidList::NextWorkId, public // // Returns: Next workid in 'bucket', or widInvalid if at end. // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- WORKID CShortWidList::NextWorkId() { _wid = widUnused; do { _counter++; _iCur = ( _iCur + _delta ) % _size; if ( _iCur == _iStart ) // wrapped around { if (_counter >= _size) // looked at all { _wid = widInvalid; break; } else { _delta = 1; // try the fallback delta _iCur = (_iCur + 1) % _size; _wid = _hashTable[_iCur]; } } else { _wid = _hashTable[_iCur]; } } while ( _wid == widUnused ); return _wid; } //NextWorkId //+--------------------------------------------------------------------------- // // Member: CWidHashTable::IsFull, private // // Returns: TRUE if hash table is full (is running out of slack). // // History: 10-Jan-96 KyleP Added header // //---------------------------------------------------------------------------- BOOL CWidHashTable::IsFull() { // If the hash table is greater than or equal to half full, // it's time to rehash. if ( _fAggressiveGrowth ) return ( _count * 2 ) >= _size; return ( _count * 3 / 2 ) >= _size; } //IsFull