/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Diagnostics; namespace JetBrains.DataStructures { public class HashtableParams { public const uint maxBucketsPerIndex = 2; static HashtableParams() { Array.Sort( TableSizes ); } // table sizes should be prime & enough far from powers of 2 public static readonly uint[] TableSizes = { 5,11,23,47,97,197,397,797,1597,3203,6421,12853,25717,51437,102877,205759, 411527,823117,1646237,3292489,6584983,13169977,26339969,52679969,105359939, 210719881,421439783,842879579,1685759167, 433,877,1759,3527,7057,14143,28289,56591,113189,226379,452759,905551,1811107, 3622219,7244441,14488931,28977863,57955739,115911563,231823147,463646329,927292699, 1854585413, 953,1907,3821,7643,15287,30577,61169,122347,244703,489407,978821,1957651,3915341, 7830701,15661423,31322867,62645741,125291483,250582987,501165979,1002331963, 2004663929, 1039,2081,4177,8363,16729,33461,66923,133853,267713,535481,1070981,2141977,4283963, 8567929,17135863,34271747,68543509,137087021,274174111,548348231,1096696463, 31,67,137,277,557,1117,2237,4481,8963,17929,35863,71741,143483,286973,573953, 1147921,2295859,4591721,9183457,18366923,36733847,73467739,146935499,293871013, 587742049,1175484103, 599,1201,2411,4831,9677,19373,38747,77509,155027,310081,620171,1240361,2480729, 4961459,9922933,19845871,39691759,79383533,158767069,317534141,635068283,1270136683, 311,631,1277,2557,5119,10243,20507,41017,82037,164089,328213,656429,1312867, 2625761,5251529,10503061,21006137,42012281,84024581,168049163,336098327,672196673, 1344393353, 3,7,17,37,79,163,331,673,1361,2729,5471,10949,21911,43853,87719,175447,350899, 701819,1403641,2807303,5614657,11229331,22458671,44917381,89834777,179669557, 359339171,718678369,1437356741, 43,89,179,359,719,1439,2879,5779,11579,23159,46327,92657,185323,370661,741337, 1482707,2965421,5930887,11861791,23723597,47447201,94894427,189788857,379577741, 759155483,1518310967, 379,761,1523,3049,6101,12203,24407,48817,97649,195311,390647,781301,1562611, 3125257,6250537,12501169,25002389,50004791,100009607,200019221,400038451,800076929, 1600153859 }; public static uint AdjustHashtableSize( uint desiredSize ) { int lo = 0; int hi = TableSizes.Length - 1; while( lo <= hi ) { int i = ( lo + hi ) >> 1; int c = (int)TableSizes[ i ] - (int)desiredSize; if( c == 0 ) { return TableSizes[ i ]; } if( c < 0 ) { lo = i + 1; } else { hi = i - 1; } } return TableSizes[ lo ]; } } /// /// Map of hashable objects to arbitrary objects. /// public class HashMap : IEnumerable { public HashMap() : this( 0 ) {} public HashMap( int initialSize ) { _count = 0; ReHash( _initialSize = HashtableParams.AdjustHashtableSize( (uint) initialSize ) ); } public int Count { get { return (int) _count; } } public bool Contains( object key ) { if( _count == 0 ) return false; uint tableIndex, prevIndex; return SearchCollisions( key, out tableIndex, out prevIndex ) != 0; } public object this [ object key ] { get { if( _count == 0 ) return null; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); return( index == 0 ) ? null : _buckets[ index ].value; } set { Add( key, value ); } } public virtual void Add( object key, object value ) { uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { _buckets[ index ].value = value; } else { uint i = _firstFree; if( i != 0 ) { _firstFree = _buckets[ i ].next; } else { i = _count + 1; if( i == _buckets.Length ) { ReHash( ( ( _size * 13 ) - 7 ) >> 3 ); tableIndex = ( (uint) key.GetHashCode() ) % _size; } } _buckets[ i ].key = key; _buckets[ i ].value = value; _buckets[ i ].next = _hashTable[ tableIndex ]; _hashTable[ tableIndex ] = i; ++_count; #if DEBUG ++_version; #endif } } public void Remove( object key ) { if( _count == 0 ) return; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { if( prevIndex != 0 ) { _buckets[ prevIndex ].next = _buckets[ index ].next; } else { _hashTable[ tableIndex ] = _buckets[ index ].next; } _buckets[ index ].key = null; _buckets[ index ].value = null; _buckets[ index ].next = _firstFree; _firstFree = index; if( --_count == 0 ) { Clear(); } #if DEBUG ++_version; #endif } } public void Clear() { if( _initialSize != _size ) { _count = 0; ReHash( 0 ); } else { if( _count > 0 ) { _count = 0; _firstFree = 0; Array.Clear( _hashTable, 0, _hashTable.Length ); Array.Clear( _buckets, 0, _buckets.Length ); } } } public Entry GetEntry( object key ) { if ( key == null ) { throw new ArgumentNullException( "key" ); } uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); return ( index == 0 ) ? null : new Entry( _buckets, index ); } /// /// Proxy object around a bucket representing key-value pair. /// public class Entry { internal Entry( bucket[] buckets, uint index ) { _buckets = buckets; _index = index; } public object Key { get { return _buckets[ _index ].key; } set { _buckets[ _index ].key = value; } } public object Value { get { return _buckets[ _index ].value; } set { _buckets[ _index ].value = value; } } internal uint _index; internal bucket[] _buckets; } #region IEnumerable Members protected internal class HashMapEnumerator : IEnumerator { public HashMapEnumerator( HashMap map ) { _entry = new Entry( map._buckets, 0 ); _count = map._count; _theMap = map; Reset(); } public object Current { get { #if DEBUG if( _counted == 0 ) { throw new InvalidOperationException( "Enumerator.Current called without calling MoveNext()." ); } if( _savedVersion != _theMap._version ) { throw new InvalidOperationException( "Collection modified while enumeration." ); } #endif return _entry; } } public virtual bool MoveNext() { if( _counted == _count ) { return false; } while( _entry._buckets[ ++_entry._index ].key == null ); ++_counted; return true; } public void Reset() { _entry._index = _counted = 0; #if DEBUG _savedVersion = _theMap._version; #endif } private uint _count; private uint _counted; private Entry _entry; private HashMap _theMap; #if DEBUG private int _savedVersion; #endif } public IEnumerator GetEnumerator() { return new HashMapEnumerator( this ); } #endregion #region implementation details protected internal void ReHash( uint desiredSize ) { if( desiredSize < _initialSize ) { desiredSize = _initialSize; } uint size = HashtableParams.AdjustHashtableSize( desiredSize ); if( size != _size ) { _firstFree = 0; bucket[] oldBuckets = _buckets; _hashTable = new uint[ size ]; _buckets = new bucket[ size * HashtableParams.maxBucketsPerIndex ]; if( _count > 0 ) { for( uint i = 1, j = 0; i < oldBuckets.Length; ++i ) { object key = oldBuckets[ i ].key; if( key != null ) { ++j; _buckets[ j ].key = key; _buckets[ j ].value = oldBuckets[ i ].value; uint hashValue = ( (uint) key.GetHashCode() ) % size; _buckets[ j ].next = _hashTable[ hashValue ]; _hashTable[ hashValue ] = j; } } } #if DEBUG ++_version; #endif _size = size; } } /// /// Returns index of bucket where key is found or zero if not found. /// protected internal uint SearchCollisions( object key, out uint tableIndex, out uint prevIndex ) { prevIndex = 0; uint bucketIndex = _hashTable[ tableIndex = ( (uint) key.GetHashCode() ) % _size ]; if( bucketIndex > 0 && !key.Equals( _buckets[ bucketIndex ].key ) ) { #if DEBUG int savedVersion = _version; #endif _buckets[ 0 ].key = key; do { #if DEBUG if( savedVersion != _version || !key.Equals( _buckets[ 0 ].key ) ) { throw new InvalidOperationException( "Non-serialized usage detected!" ); } #endif bucketIndex = _buckets[ ( prevIndex = bucketIndex ) ].next; } while( !key.Equals( _buckets[ bucketIndex ].key ) ); _buckets[ 0 ].key = null; } return bucketIndex; } protected internal struct bucket { public object key; public object value; public uint next; } protected internal uint _count; protected internal uint _size; protected internal uint _initialSize; protected internal uint _firstFree; protected internal uint[] _hashTable; protected internal bucket[] _buckets; #if DEBUG protected internal int _version; #endif #endregion } /// /// Set of hashable objects /// In contrast to HashMap, in keys aren't mapped to values /// Signatures of some public methods differ from the HashMap's ones /// Indexer is not supported /// public class HashSet : IEnumerable { public HashSet() : this( 0 ) {} public HashSet( int initialSize ) { _count = 0; ReHash( _initialSize = HashtableParams.AdjustHashtableSize( (uint) initialSize ) ); } public HashSet( HashSet other ) : this( 0 ) { foreach( HashSet.Entry e in other ) { Add( e.Key ); } } public HashSet( ICollection collection ) : this( 0 ) { foreach( object item in collection ) { Add( item ); } } public int Count { get { return (int) _count; } } public bool Contains( object key ) { if( _count == 0 ) return false; if ( key == null ) throw new ArgumentNullException( "key" ); uint tableIndex, prevIndex; return SearchCollisions( key, out tableIndex, out prevIndex ) != 0; } public virtual void Add( object key ) { if ( key == null ) throw new ArgumentNullException( "key" ); uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index == 0 ) { uint i = _firstFree; if( i != 0 ) { _firstFree = _buckets[ i ].next; } else { i = _count + 1; if( i == _buckets.Length ) { ReHash( ( ( _size * 13 ) - 7 ) >> 3 ); tableIndex = ( (uint) key.GetHashCode() ) % _size; } } _buckets[ i ].key = key; _buckets[ i ].next = _hashTable[ tableIndex ]; _hashTable[ tableIndex ] = i; ++_count; #if DEBUG ++_version; #endif } } public void Remove( object key ) { if ( key == null ) throw new ArgumentNullException( "key" ); if( _count == 0 ) return; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { if( prevIndex != 0 ) { _buckets[ prevIndex ].next = _buckets[ index ].next; } else { _hashTable[ tableIndex ] = _buckets[ index ].next; } _buckets[ index ].key = null; _buckets[ index ].next = _firstFree; _firstFree = index; if( --_count == 0 ) { Clear(); } #if DEBUG ++_version; #endif } } public object GetKey( object key ) { if ( key == null ) throw new ArgumentNullException( "key" ); if( _count == 0 ) return null; uint tableIndex, prevIndex; uint bucketIndex = SearchCollisions( key, out tableIndex, out prevIndex ); return ( bucketIndex == 0 ) ? null : _buckets[ bucketIndex ].key; } public void Clear() { if( _initialSize != _size ) { _count = 0; ReHash( 0 ); } else { if( _count > 0 ) { _count = 0; _firstFree = 0; Array.Clear( _hashTable, 0, _hashTable.Length ); Array.Clear( _buckets, 0, _buckets.Length ); } } } /// /// Proxy object around a bucket representing key-value pair. /// public class Entry { internal Entry( bucket[] buckets, uint index ) { _buckets = buckets; _index = index; } public object Key { get { return _buckets[ _index ].key; } set { _buckets[ _index ].key = value; } } internal uint _index; internal bucket[] _buckets; } #region IEnumerable Members protected internal class HashSetEnumerator : IEnumerator { public HashSetEnumerator( HashSet set ) { _entry = new Entry( set._buckets, 0 ); _count = set._count; _theSet = set; Reset(); } public object Current { get { #if DEBUG if( _counted == 0 ) { throw new InvalidOperationException( "Enumerator.Current called without calling MoveNext()." ); } if( _savedVersion != _theSet._version ) { throw new InvalidOperationException( "Collection modified while enumeration." ); } #endif return _entry; } } public virtual bool MoveNext() { if( _counted == _count ) { return false; } while( _entry._buckets[ ++_entry._index ].key == null ); ++_counted; return true; } public void Reset() { _entry._index = _counted = 0; #if DEBUG _savedVersion = _theSet._version; #endif } private uint _count; private uint _counted; private Entry _entry; private HashSet _theSet; #if DEBUG private int _savedVersion; #endif } public IEnumerator GetEnumerator() { return new HashSetEnumerator( this ); } #endregion #region implementation details protected internal void ReHash( uint desiredSize ) { if( desiredSize < _initialSize ) { desiredSize = _initialSize; } uint size = HashtableParams.AdjustHashtableSize( desiredSize ); if( size != _size ) { _firstFree = 0; bucket[] oldBuckets = _buckets; _hashTable = new uint[ size ]; _buckets = new bucket[ size * HashtableParams.maxBucketsPerIndex ]; if( _count > 0 ) { for( uint i = 1, j = 0; i < oldBuckets.Length; ++i ) { object key = oldBuckets[ i ].key; if( key != null ) { ++j; _buckets[ j ].key = key; uint hashValue = ( (uint) key.GetHashCode() ) % size; _buckets[ j ].next = _hashTable[ hashValue ]; _hashTable[ hashValue ] = j; } } } #if DEBUG ++_version; #endif _size = size; } } /// /// Returns index of bucket where key is found or zero if not found. /// protected internal uint SearchCollisions( object key, out uint tableIndex, out uint prevIndex ) { prevIndex = 0; uint bucketIndex = _hashTable[ tableIndex = ( (uint) key.GetHashCode() ) % _size ]; if( bucketIndex > 0 && !key.Equals( _buckets[ bucketIndex ].key ) ) { #if DEBUG int savedVersion = _version; #endif _buckets[ 0 ].key = key; do { #if DEBUG if( savedVersion != _version || !key.Equals( _buckets[ 0 ].key ) ) { throw new InvalidOperationException( "Non-serialized usage detected!" ); } #endif bucketIndex = _buckets[ ( prevIndex = bucketIndex ) ].next; } while( !key.Equals( _buckets[ bucketIndex ].key ) ); _buckets[ 0 ].key = null; } return bucketIndex; } protected internal struct bucket { public object key; public uint next; } protected internal uint _count; protected internal uint _size; protected internal uint _initialSize; protected internal uint _firstFree; protected internal uint[] _hashTable; protected internal bucket[] _buckets; #if DEBUG protected internal int _version; #endif #endregion } /// /// IntHashSet is similar to HashSet. /// Never add Int32.MaxValue to a set!!! /// public class IntHashSet : IEnumerable { public IntHashSet() : this( 0 ) {} public IntHashSet( int initialSize ) { _count = 0; ReHash( _initialSize = HashtableParams.AdjustHashtableSize( (uint) initialSize ) ); } public int Count { get { return (int) _count; } } public bool Contains( int key ) { if( _count == 0 ) return false; uint tableIndex, prevIndex; return SearchCollisions( key, out tableIndex, out prevIndex ) != 0; } public virtual void Add( int key ) { uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index == 0 ) { uint i = _firstFree; if( i != 0 ) { _firstFree = _buckets[ i ].next; } else { i = _count + 1; if( i == _buckets.Length ) { ReHash( ( ( _size * 13 ) - 7 ) >> 3 ); tableIndex = ( (uint) key.GetHashCode() ) % _size; } } _buckets[ i ].key = key; _buckets[ i ].next = _hashTable[ tableIndex ]; _hashTable[ tableIndex ] = i; ++_count; #if DEBUG ++_version; #endif } } public void Remove( int key ) { if( _count == 0 ) return; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { if( prevIndex != 0 ) { _buckets[ prevIndex ].next = _buckets[ index ].next; } else { _hashTable[ tableIndex ] = _buckets[ index ].next; } _buckets[ index ].key = Int32.MaxValue; _buckets[ index ].next = _firstFree; _firstFree = index; if( --_count == 0 ) { Clear(); } #if DEBUG ++_version; #endif } } public void Clear() { if( _initialSize != _size ) { _count = 0; ReHash( 0 ); } else { if( _count > 0 ) { _count = 0; _firstFree = 0; Array.Clear( _hashTable, 0, _hashTable.Length ); Array.Clear( _buckets, 0, _buckets.Length ); } } } /// /// Proxy object around a bucket representing key-value pair. /// public class Entry { internal Entry( bucket[] buckets, uint index ) { _buckets = buckets; _index = index; } public int Key { get { return _buckets[ _index ].key; } set { _buckets[ _index ].key = value; } } internal uint _index; internal bucket[] _buckets; } /// /// Compares the bucket indices for the two given keys. /// For a hash-set that sustained only add operations without the remove ones, this comparer corresponds to the order in which the values were added to the hash set. /// public int BucketComparer(int key1, int key2) { uint dummy1, dummy2; uint value1 = SearchCollisions(key1, out dummy1, out dummy2); uint value2 = SearchCollisions(key2, out dummy1, out dummy2); return (int)value1 - (int)value2; } #region IEnumerable Members protected internal class IntHashSetEnumerator : IEnumerator { public IntHashSetEnumerator( IntHashSet set ) { _entry = new Entry( set._buckets, 0 ); _count = set._count; _theSet = set; Reset(); } public object Current { get { #if DEBUG if( _counted == 0 ) { throw new InvalidOperationException( "Enumerator.Current called without calling MoveNext()." ); } if( _savedVersion != _theSet._version ) { throw new InvalidOperationException( "Collection modified while enumeration." ); } #endif return _entry; } } public virtual bool MoveNext() { if( _counted == _count ) { return false; } while( _entry._buckets[ ++_entry._index ].key == Int32.MaxValue ); ++_counted; return true; } public void Reset() { _entry._index = _counted = 0; #if DEBUG _savedVersion = _theSet._version; #endif } private uint _count; private uint _counted; private Entry _entry; private IntHashSet _theSet; #if DEBUG private int _savedVersion; #endif } public IEnumerator GetEnumerator() { return new IntHashSetEnumerator( this ); } #endregion #region implementation details protected internal void ReHash( uint desiredSize ) { if( desiredSize < _initialSize ) { desiredSize = _initialSize; } uint size = HashtableParams.AdjustHashtableSize( desiredSize ); if( size != _size ) { _firstFree = 0; bucket[] oldBuckets = _buckets; _hashTable = new uint[ size ]; _buckets = new bucket[ size * HashtableParams.maxBucketsPerIndex ]; if( _count > 0 ) { for( uint i = 1, j = 0; i < oldBuckets.Length; ++i ) { int key = oldBuckets[ i ].key; if( key != Int32.MaxValue ) { ++j; _buckets[ j ].key = key; uint hashValue = ( (uint) key ) % size; _buckets[ j ].next = _hashTable[ hashValue ]; _hashTable[ hashValue ] = j; } } } #if DEBUG ++_version; #endif _size = size; } } /// /// Returns index of bucket where key is found or zero if not found. /// protected internal uint SearchCollisions( int key, out uint tableIndex, out uint prevIndex ) { prevIndex = 0; uint bucketIndex = _hashTable[ tableIndex = ( (uint) key ) % _size ]; if( bucketIndex > 0 && key != _buckets[ bucketIndex ].key ) { #if DEBUG int savedVersion = _version; #endif _buckets[ 0 ].key = key; do { #if DEBUG if( savedVersion != _version || key != _buckets[ 0 ].key ) { throw new InvalidOperationException( "Non-serialized usage detected!" ); } #endif bucketIndex = _buckets[ ( prevIndex = bucketIndex ) ].next; } while( key != _buckets[ bucketIndex ].key ); } return bucketIndex; } protected internal struct bucket { public int key; public uint next; } protected internal uint _count; protected internal uint _size; protected internal uint _initialSize; protected internal uint _firstFree; protected internal uint[] _hashTable; protected internal bucket[] _buckets; #if DEBUG protected internal int _version; #endif #endregion } /// /// IntHashTable implements non-synchronized hashtable of pairs: int(key), object. /// !!! Do not use Int32.MaxValue as a value for key. /// public class IntHashTable : IEnumerable { public IntHashTable() : this( 0 ) {} public IntHashTable( int initialSize ) { _count = 0; ReHash( _initialSize = HashtableParams.AdjustHashtableSize( (uint) initialSize ) ); } public int Count { get { return (int) _count; } } public bool Contains( int key ) { if( _count == 0 ) return false; uint tableIndex, prevIndex; return SearchCollisions( key, out tableIndex, out prevIndex ) != 0; } public bool ContainsKey( int key ) { uint tableIndex, prevIndex; return SearchCollisions( key, out tableIndex, out prevIndex ) != 0; } public virtual object this [ int key ] { get { if( _count == 0 ) return null; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); return( index == 0 ) ? null : _buckets[ index ].value; } set { Add( key, value ); } } public virtual void Add( int key, object value ) { uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { _buckets[ index ].value = value; } else { uint i = _firstFree; if( i != 0 ) { _firstFree = _buckets[ i ].next; } else { i = _count + 1; if( i == _buckets.Length ) { ReHash( (uint) ( ( ( _hashTable.Length * 13 ) - 7 ) >> 3 ) ); tableIndex = ( (uint) key ) % ( (uint) _hashTable.Length ); i = _count + 1; } } _buckets[ i ].key = key; _buckets[ i ].value = value; _buckets[ i ].next = _hashTable[ tableIndex ]; _hashTable[ tableIndex ] = i; ++_count; #if DEBUG ++_version; #endif } } public void Remove( int key ) { if( _count == 0 ) return; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { if( prevIndex != 0 ) { _buckets[ prevIndex ].next = _buckets[ index ].next; } else { _hashTable[ tableIndex ] = _buckets[ index ].next; } _buckets[ index ].key = Int32.MaxValue; _buckets[ index ].value = null; _buckets[ index ].next = _firstFree; _firstFree = index; if( --_count == 0 ) { Clear(); } #if DEBUG ++_version; #endif } } public void Clear() { if( _initialSize != _hashTable.Length ) { _count = 0; ReHash( 0 ); } else { if( _count > 0 ) { _count = 0; _firstFree = 0; Array.Clear( _hashTable, 0, _hashTable.Length ); Array.Clear( _buckets, 0, _buckets.Length ); } } } public Entry GetEntry( int key ) { if( _count == 0 ) return null; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); return ( index == 0 ) ? null : new Entry( _buckets, index ); } /// /// Proxy object around a bucket representing key-value pair. /// public class Entry { internal Entry( bucket[] buckets, uint index ) { _buckets = buckets; _index = index; } public int Key { get { return _buckets[ _index ].key; } set { _buckets[ _index ].key = value; } } public object Value { [DebuggerStepThrough] get { return _buckets[ _index ].value; } set { _buckets[ _index ].value = value; } } internal uint _index; internal bucket[] _buckets; } #region IEnumerable Members protected internal class IntHashTableEnumerator : IEnumerator { public IntHashTableEnumerator( IntHashTable table ) { _entry = new Entry( table._buckets, 0 ); _count = table._count; _table = table; Reset(); } public object Current { get { #if DEBUG if( _counted == 0 ) { throw new InvalidOperationException( "Enumerator.Current called without calling MoveNext()." ); } if( _savedVersion != _table._version ) { throw new InvalidOperationException( "Collection modified while enumeration." ); } #endif return _entry; } } public virtual bool MoveNext() { if( _counted == _count ) { return false; } while( _entry._buckets[ ++_entry._index ].key == Int32.MaxValue ); ++_counted; return true; } public void Reset() { _entry._index = _counted = 0; #if DEBUG _savedVersion = _table._version; #endif } private uint _count; private uint _counted; private Entry _entry; protected IntHashTable _table; #if DEBUG private int _savedVersion; #endif } public virtual IEnumerator GetEnumerator() { return new IntHashTableEnumerator( this ); } /// /// Returns the size of the memory occupied by the hashtable structures. Does not /// include the memory occupied by the objects stored in the hashtable. /// /// The size of the hashtable structures in bytes. public virtual int EstimateMemorySize() { return 8 + 12 + 4 * _hashTable.Length + 12 * _buckets.Length; } #endregion #region implementation details protected internal virtual void ReHash( uint desiredSize ) { if( desiredSize < _initialSize ) { desiredSize = _initialSize; } uint size = HashtableParams.AdjustHashtableSize( desiredSize ); if( _hashTable == null || size != _hashTable.Length ) { _firstFree = 0; bucket[] oldBuckets = _buckets; _hashTable = new uint[ size ]; _buckets = new bucket[ size * HashtableParams.maxBucketsPerIndex ]; if( _count > 0 ) { for( uint i = 1, j = 0; i < oldBuckets.Length; ++i ) { int key = oldBuckets[ i ].key; if( key != Int32.MaxValue ) { ++j; _buckets[ j ].key = key; _buckets[ j ].value = oldBuckets[ i ].value; uint hashValue = ( (uint) key ) % size; _buckets[ j ].next = _hashTable[ hashValue ]; _hashTable[ hashValue ] = j; } } } #if DEBUG ++_version; #endif } } /// /// Returns index of bucket where key is found or zero if not found. /// protected internal uint SearchCollisions( int key, out uint tableIndex, out uint prevIndex ) { prevIndex = 0; uint bucketIndex = _hashTable[ tableIndex = ( (uint) key ) % ( (uint) _hashTable.Length ) ]; if( bucketIndex > 0 && key != _buckets[ bucketIndex ].key ) { #if DEBUG int savedVersion = _version; #endif _buckets[ 0 ].key = key; do { #if DEBUG if( savedVersion != _version || key != _buckets[ 0 ].key ) { throw new InvalidOperationException( "Non-serialized usage detected!" ); } #endif bucketIndex = _buckets[ ( prevIndex = bucketIndex ) ].next; } while( key != _buckets[ bucketIndex ].key ); } return bucketIndex; } protected internal struct bucket { public int key; public object value; public uint next; } protected internal uint _count; //protected internal uint _size; protected internal uint _initialSize; protected internal uint _firstFree; protected internal uint[] _hashTable; protected internal bucket[] _buckets; #if DEBUG protected internal int _version; #endif #endregion } /// /// IntHashTableOfInt implements non-synchronized hashtable of pairs: int(key), int. /// public class IntHashTableOfInt : IEnumerable { public IntHashTableOfInt() : this( 0 ) {} public IntHashTableOfInt( int initialSize ) { _count = 0; ReHash( _initialSize = HashtableParams.AdjustHashtableSize( (uint) initialSize ) ); } /// /// The value which is returned if the key is not found in the hash. /// public int MissingKeyValue { get { return _missingKeyValue; } set { _missingKeyValue = value; } } public int Count { get { return (int) _count; } } public bool Contains( int key ) { if( _count == 0 ) return false; uint tableIndex, prevIndex; return SearchCollisions( key, out tableIndex, out prevIndex ) != 0; } public bool ContainsKey( int key ) { return Contains( key ); } public virtual int this [ int key ] { get { if( _count == 0 ) return _missingKeyValue; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); return( index == 0 ) ? _missingKeyValue : _buckets[ index ].value; } set { Add( key, value ); } } public virtual void Add( int key, int value ) { uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { _buckets[ index ].value = value; } else { uint i = _firstFree; if( i != 0 ) { _firstFree = _buckets[ i ].next; } else { i = _count + 1; if( i == _buckets.Length ) { ReHash( ( ( _size * 13 ) - 7 ) >> 3 ); tableIndex = ( (uint) key.GetHashCode() ) % _size; } } _buckets[ i ].key = key; _buckets[ i ].value = value; _buckets[ i ].next = _hashTable[ tableIndex ]; _hashTable[ tableIndex ] = i; ++_count; #if DEBUG ++_version; #endif } } public void Remove( int key ) { if( _count == 0 ) return; uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); if( index != 0 ) { if( prevIndex != 0 ) { _buckets[ prevIndex ].next = _buckets[ index ].next; } else { _hashTable[ tableIndex ] = _buckets[ index ].next; } _buckets[ index ].key = _missingKeyValue; _buckets[ index ].next = _firstFree; _firstFree = index; if( --_count == 0 ) { Clear(); } #if DEBUG ++_version; #endif } } public void Clear() { if( _initialSize != _size ) { _count = 0; ReHash( 0 ); } else { if( _count > 0 ) { _count = 0; _firstFree = 0; Array.Clear( _hashTable, 0, _hashTable.Length ); Array.Clear( _buckets, 0, _buckets.Length ); } } } public Entry GetEntry( int key ) { uint tableIndex, prevIndex; uint index = SearchCollisions( key, out tableIndex, out prevIndex ); return ( index == 0 ) ? null : new Entry( _buckets, index ); } /// /// Proxy object around a bucket representing key-value pair. /// public class Entry { internal Entry( bucket[] buckets, uint index ) { _buckets = buckets; _index = index; } public int Key { get { return _buckets[ _index ].key; } set { _buckets[ _index ].key = value; } } public int Value { get { return _buckets[ _index ].value; } set { _buckets[ _index ].value = value; } } internal uint _index; internal bucket[] _buckets; } #region IEnumerable Members protected internal class IntHashTableOfIntEnumerator : IEnumerator { public IntHashTableOfIntEnumerator( IntHashTableOfInt table, int missingKeyValue ) { _entry = new Entry( table._buckets, 0 ); _count = table._count; _missingKeyValue = missingKeyValue; _table = table; Reset(); } public object Current { get { #if DEBUG if( _counted == 0 ) { throw new InvalidOperationException( "Enumerator.Current called without calling MoveNext()." ); } if( _savedVersion != _table._version ) { throw new InvalidOperationException( "Collection modified while enumeration." ); } #endif return _entry; } } public virtual bool MoveNext() { if( _counted == _count ) { return false; } while( _entry._buckets[ ++_entry._index ].key == _missingKeyValue ); ++_counted; return true; } public void Reset() { _entry._index = _counted = 0; #if DEBUG _savedVersion = _table._version; #endif } private uint _count; private uint _counted; private int _missingKeyValue; private Entry _entry; private IntHashTableOfInt _table; #if DEBUG private int _savedVersion; #endif } public virtual IEnumerator GetEnumerator() { return new IntHashTableOfIntEnumerator( this, _missingKeyValue ); } #endregion #region implementation details protected internal void ReHash( uint desiredSize ) { if( desiredSize < _initialSize ) { desiredSize = _initialSize; } uint size = HashtableParams.AdjustHashtableSize( desiredSize ); if( size != _size ) { _firstFree = 0; bucket[] oldBuckets = _buckets; _hashTable = new uint[ size ]; _buckets = new bucket[ size * HashtableParams.maxBucketsPerIndex ]; if( _count > 0 ) { for( uint i = 1, j = 0; i < oldBuckets.Length; ++i ) { int key = oldBuckets[ i ].key; if( key != _missingKeyValue ) { ++j; _buckets[ j ].key = key; _buckets[ j ].value = oldBuckets[ i ].value; uint hashValue = ( (uint) key ) % size; _buckets[ j ].next = _hashTable[ hashValue ]; _hashTable[ hashValue ] = j; } } } #if DEBUG ++_version; #endif _size = size; } } /// /// Returns index of bucket where key is found or zero if not found. /// protected internal uint SearchCollisions( int key, out uint tableIndex, out uint prevIndex ) { prevIndex = 0; uint bucketIndex = _hashTable[ tableIndex = ( (uint) key ) % _size ]; if( bucketIndex > 0 && key != _buckets[ bucketIndex ].key ) { #if DEBUG int savedVersion = _version; #endif _buckets[ 0 ].key = key; do { #if DEBUG if( savedVersion != _version || key != _buckets[ 0 ].key ) { throw new InvalidOperationException( "Non-serialized usage detected!" ); } #endif bucketIndex = _buckets[ ( prevIndex = bucketIndex ) ].next; } while( key != _buckets[ bucketIndex ].key ); } return bucketIndex; } protected internal struct bucket { public int key; public int value; public uint next; } protected internal uint _count; protected internal uint _size; protected internal uint _initialSize; protected internal uint _firstFree; protected internal int _missingKeyValue = Int32.MaxValue; protected internal uint[] _hashTable; protected internal bucket[] _buckets; #if DEBUG protected internal int _version; #endif #endregion } /// /// IntWeakHashTable implements non-synchronized hashtable of pairs: /// key = int, value = weak reference to object /// public class IntWeakHashTable : IntHashTable { public IntWeakHashTable() : base() {} public IntWeakHashTable( int initialSize ) : base( initialSize ) {} public override void Add( int key, object value ) { base.Add( key, new WeakReference( value ) ); } public override object this [int key] { get { object value = base[ key ]; return ( value == null ) ? null : ( (WeakReference)value ).Target; } set { Add( key, value ); } } // enumerator for self-cleaned hashtable should check whether the current object is alive protected internal class IntWeakHashTableEnumerator : IntHashTableEnumerator { public IntWeakHashTableEnumerator( IntWeakHashTable table ) : base( table ) {} public override bool MoveNext() { IntWeakHashTable table = (IntWeakHashTable) _table; while( base.MoveNext() ) { Entry current = (Entry) Current; WeakReference wr = current.Value as WeakReference; if( wr != null && wr.IsAlive ) { return true; } else { if( table.ValueDead != null ) { table.ValueDead( current.Key ); } } } return false; } } public override IEnumerator GetEnumerator() { return new IntWeakHashTableEnumerator( this ); } public delegate void ValueDeadDelegate( int id ); public event ValueDeadDelegate ValueDead; public void Compact() { ArrayList buckets = new ArrayList(); foreach( Entry e in this ) { buckets.Add( _buckets[ e._index ] ); } Clear(); foreach( bucket b in buckets ) { base.Add( b.key, b.value ); } } protected internal override void ReHash( uint desiredSize ) { if( desiredSize < _initialSize ) { desiredSize = _initialSize; } uint size = HashtableParams.AdjustHashtableSize( desiredSize ); if( _hashTable == null || size != _hashTable.Length ) { _firstFree = 0; bucket[] oldBuckets = _buckets; if( _count > 0 ) { uint count = 0; for( int i = 1; i < oldBuckets.Length; ++i ) { int key = oldBuckets[ i ].key; WeakReference value = oldBuckets[ i ].value as WeakReference; if( key != Int32.MaxValue && value != null && value.IsAlive ) { oldBuckets[ ++count ].key = oldBuckets[ i ].key; oldBuckets[ count ].value = oldBuckets[ i ].value; oldBuckets[ count ].next = 0; } } if( count < _hashTable.Length / 2 ) { _count = count; size = (uint) _hashTable.Length; Array.Clear( _hashTable, 0, _hashTable.Length ); for( uint i = 1; i < count + 1; ++i ) { int key = _buckets[ i ].key; uint hashValue = ( (uint) key ) % size; _buckets[ i ].next = _hashTable[ hashValue ]; _hashTable[ hashValue ] = i; } #if DEBUG ++_version; #endif return; } } _hashTable = new uint[ size ]; _buckets = new bucket[ size * HashtableParams.maxBucketsPerIndex ]; if( _count > 0 ) { for( uint i = 1, j = 0; i < oldBuckets.Length; ++i ) { int key = oldBuckets[ i ].key; if( key != Int32.MaxValue ) { ++j; _buckets[ j ].key = key; _buckets[ j ].value = oldBuckets[ i ].value; uint hashValue = ( (uint) key ) % size; _buckets[ j ].next = _hashTable[ hashValue ]; _hashTable[ hashValue ] = j; } } } #if DEBUG ++_version; #endif } } } }