using System; using System.Collections; namespace System.util.collections { /// /// A HashTable with iterators /// public class k_HashTable : IMap { #region static helper functions private readonly static int[] mk_Primes = { 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 }; private static bool IsPrime(int ai_Number) { if ((ai_Number & 1) == 0) return (ai_Number == 2); int li_Max = (int)Math.Sqrt(ai_Number); for (int li_Div=3; li_Div < li_Max; li_Div+=2) { if ((ai_Number % li_Div) == 0) return false; } return true; } private static int FindPrimeGreater(int ai_Min) { if (ai_Min < 0) throw new ArgumentException("k_HashTable capacity overflow."); // do binary search lookup in primes array int li_Pos = Array.BinarySearch(mk_Primes, ai_Min); if (li_Pos >= 0) return mk_Primes[li_Pos]; li_Pos = ~li_Pos; if (li_Pos < mk_Primes.Length) return mk_Primes[li_Pos]; // ai_Min is greater than highest number in mk_Primes for (int i = (ai_Min|1); i <= Int32.MaxValue; i+=2) { if (IsPrime(i)) return i; } return ai_Min; } #endregion #region Bucket Structure private struct r_Bucket { public object mk_Key; public object mk_Value; public int mi_HashCode; // MSB (sign bit) indicates a collision. } #endregion #region k_BucketIterator Implementation private class k_BucketIterator : k_Iterator { private readonly k_HashTable mk_Table; private int mi_Index; public k_BucketIterator(k_HashTable ak_Table, int ai_Index) { mk_Table = ak_Table; mi_Index = -1; if (ai_Index >= 0) mi_Index = FindNext(ai_Index-1); } public override object Current { get { if (mi_Index < 0 || mk_Table.mk_Buckets[mi_Index].mk_Key == null) throw new k_InvalidPositionException(); r_Bucket lr_Bucket = mk_Table.mk_Buckets[mi_Index]; return new DictionaryEntry(lr_Bucket.mk_Key, lr_Bucket.mk_Value); } set { if (mi_Index < 0 || mk_Table.mk_Buckets[mi_Index].mk_Key == null) throw new k_InvalidPositionException(); DictionaryEntry lr_Entry = (DictionaryEntry)value; r_Bucket lr_Bucket = mk_Table.mk_Buckets[mi_Index]; if (mk_Table.mk_Comparer.Compare(lr_Entry.Key, lr_Bucket.mk_Key) != 0) throw new ArgumentException("Key values must not be changed."); mk_Table.mk_Buckets[mi_Index].mk_Value = lr_Entry.Value; } } public override void Move(int ai_Count) { int li_NewIndex = mi_Index; if (ai_Count > 0) { while (ai_Count-- > 0) { if (li_NewIndex < 0) throw new InvalidOperationException("Tried to moved beyond end element."); li_NewIndex = FindNext(li_NewIndex); } } else { while (ai_Count++ < 0) { if (li_NewIndex < 0) li_NewIndex = FindPrev(mk_Table.mk_Buckets.Length); else li_NewIndex = FindPrev(li_NewIndex); if (li_NewIndex < 0) throw new InvalidOperationException("Tried to move before first element."); } } mi_Index = li_NewIndex; } public override int Distance(k_Iterator ak_Iter) { k_BucketIterator lk_Iter = ak_Iter as k_BucketIterator; if (lk_Iter == null || !object.ReferenceEquals(lk_Iter.Collection, this.Collection)) throw new ArgumentException("Cannot determine distance of iterators belonging to different collections."); k_Iterator lk_End = mk_Table.End; int li_IndexDiff; if (this != lk_End && ak_Iter != lk_End) li_IndexDiff = mi_Index - lk_Iter.mi_Index; else li_IndexDiff = (this == lk_End) ? 1 : -1; // 1 is also fine when both are End if (li_IndexDiff < 0) { int li_Diff = 0; k_Iterator lk_Bck = this.Clone(); for (; lk_Bck != ak_Iter && lk_Bck != lk_End; lk_Bck.Next()) --li_Diff; if (lk_Bck == ak_Iter) return li_Diff; } else { int li_Diff = 0; k_Iterator lk_Fwd = ak_Iter.Clone(); for (; lk_Fwd != this && lk_Fwd != lk_End; lk_Fwd.Next()) ++li_Diff; if (lk_Fwd == this) return li_Diff; } throw new Exception("Inconsistent state. Concurrency?"); } public override object Collection { get { return mk_Table; } } public override bool Equals(object ak_Obj) { k_BucketIterator lk_Iter = ak_Obj as k_BucketIterator; if (lk_Iter == null) return false; return (mi_Index == lk_Iter.mi_Index && object.ReferenceEquals(mk_Table, lk_Iter.mk_Table)); } public override int GetHashCode() { return mk_Table.GetHashCode() ^ mi_Index; } public override k_Iterator Clone() { return new k_BucketIterator(mk_Table, mi_Index); } private int FindPrev(int ai_Index) { --ai_Index; r_Bucket[] lk_Buckets = mk_Table.mk_Buckets; while (ai_Index >= 0 && lk_Buckets[ai_Index].mk_Key == null) --ai_Index; if (ai_Index < -1) return -1; return ai_Index; } private int FindNext(int ai_Index) { ++ai_Index; r_Bucket[] lk_Buckets = mk_Table.mk_Buckets; while (ai_Index < lk_Buckets.Length && lk_Buckets[ai_Index].mk_Key == null) ++ai_Index; if (ai_Index >= lk_Buckets.Length) return -1; return ai_Index; } internal int Index { get { return mi_Index; } } } private class k_PinnedBucketIterator : k_BucketIterator { public k_PinnedBucketIterator(k_HashTable ak_Table, int ai_Index) : base(ak_Table, ai_Index) { } public override void Move(int ai_Count) { throw new k_IteratorPinnedException(); } } #endregion private IHashCodeProvider mk_HashProvider; private IComparer mk_Comparer; private double md_LoadFactor; private int mi_GrowSize; private r_Bucket[] mk_Buckets; private int mi_Count; private readonly k_Iterator mk_End; public k_HashTable() : this(0, 0.72) { } public k_HashTable(int ai_Capacity, double ad_LoadFactor) : this(ai_Capacity, ad_LoadFactor, null, null) { } public k_HashTable(int ai_Capacity, double ad_LoadFactor, IHashCodeProvider ak_HashProvider, IComparer ak_Comparer) { if (ad_LoadFactor <= .0 || ad_LoadFactor > 1.0) throw new ArgumentException("Load factor must be greater than .0 and smaller or equal to 1.0", "ad_LoadFactor"); md_LoadFactor = ad_LoadFactor; double ld_Size = ai_Capacity/ad_LoadFactor; if (ld_Size > int.MaxValue) throw new ArgumentException("k_HashTable overflow"); int li_TableSize = FindPrimeGreater((int)ld_Size); mk_Buckets = new r_Bucket[li_TableSize]; mi_GrowSize = (md_LoadFactor < 1.0) ? (int)(md_LoadFactor * li_TableSize) : li_TableSize-1; mk_HashProvider = ak_HashProvider; mk_Comparer = ak_Comparer; mk_End = new k_PinnedBucketIterator(this, -1); } // IContainer Members public k_Iterator Begin { get { if (mi_Count == 0) return mk_End; return new k_PinnedBucketIterator(this, 0); } } public k_Iterator End { get { return mk_End; } } public bool IsEmpty { get { return (mi_Count == 0); } } public k_Iterator Find(object ak_Value) { DictionaryEntry lr_Item = (DictionaryEntry)ak_Value; int li_Index = FindBucket(lr_Item.Key); if (li_Index < 0 || !object.Equals(mk_Buckets[li_Index].mk_Value, lr_Item.Value)) return this.End; return new k_BucketIterator(this, li_Index); } public k_Iterator Erase(k_Iterator ak_Where) { //System.Diagnostics.Debug.Assert(object.ReferenceEquals(this, ak_Where.Collection), "Iterator does not belong to this collection."); k_Iterator lk_Successor = ak_Where + 1; EmptyBucket(((k_BucketIterator)ak_Where).Index); return lk_Successor; } public k_Iterator Erase(k_Iterator ak_First, k_Iterator ak_Last) { if (ak_First == this.Begin && ak_Last == this.End) { Clear(); return ak_Last.Clone(); } k_Iterator lk_Current = ak_First; while (lk_Current != ak_Last) lk_Current = Erase(lk_Current); return lk_Current; } // IMap Members public k_Iterator FindKey(object ak_Key) { return new k_BucketIterator(this, FindBucket(ak_Key)); } public void Add(DictionaryEntry ar_Item) { Add(ar_Item.Key, ar_Item.Value); } public void Insert(k_Iterator ak_SrcBegin, k_Iterator ak_SrcEnd) { for (k_Iterator lk_Iter = ak_SrcBegin.Clone(); lk_Iter != ak_SrcEnd; lk_Iter.Next()) Add((DictionaryEntry)lk_Iter.Current); } #region IDictionary Members public void Add(object ak_Key, object ak_Value) { SetValue(ak_Key, ak_Value, true); } public void Clear() { if (mi_Count == 0) return; for (int i=0; i < mk_Buckets.Length; ++i) mk_Buckets[i] = new r_Bucket(); mi_Count = 0; } public bool Contains(object ak_Key) { return (FindBucket(ak_Key) >= 0); } public void Remove(object ak_Key) { EmptyBucket(FindBucket(ak_Key)); } public IDictionaryEnumerator GetEnumerator() { return new k_IteratorDictEnumerator(this.Begin, this.End); } public bool IsReadOnly { get { return false; } } public bool IsFixedSize { get { return false; } } public object this[object ak_Key] { get { int li_Index = FindBucket(ak_Key); if (li_Index < 0) return null; return mk_Buckets[li_Index].mk_Value; } set { SetValue(ak_Key, value, false); } } public ICollection Keys { get { int i = 0; object[] lk_Keys = new object[mi_Count]; foreach (DictionaryEntry lr_Entry in this) lk_Keys[i++] = lr_Entry.Key; return lk_Keys; } } public ICollection Values { get { int i=0; object[] lk_Values = new object[mi_Count]; foreach (DictionaryEntry lr_Entry in this) lk_Values[i++] = lr_Entry.Value; return lk_Values; } } #endregion #region ICollection Members public void CopyTo(Array ak_Array, int ai_Index) { foreach (DictionaryEntry lr_Entry in this) ak_Array.SetValue(lr_Entry, ai_Index++); } public int Count { get { return mi_Count; } } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return this; } } #endregion #region IEnumerable Members IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new k_IteratorEnumerator(this.Begin, this.End); } #endregion #region ICloneable Members public object Clone() { k_HashTable lk_Clone = new k_HashTable(this.Count, md_LoadFactor, mk_HashProvider, mk_Comparer); int i = mk_Buckets.Length; while (i-- > 0) { object lk_Key = mk_Buckets[i].mk_Key; if (lk_Key != null) lk_Clone[lk_Key] = mk_Buckets[i].mk_Value; } return lk_Clone; } #endregion private void EmptyBucket(int ai_Index) { if (ai_Index < 0 || ai_Index >= mk_Buckets.Length) return; if (mk_Buckets[ai_Index].mk_Key == null) throw new InvalidOperationException("Key was removed earlier."); mk_Buckets[ai_Index].mi_HashCode &= unchecked((int)0x80000000); mk_Buckets[ai_Index].mk_Key = null; mk_Buckets[ai_Index].mk_Value = null; --mi_Count; } private int FindBucket(object ak_Key) { if (ak_Key == null) throw new ArgumentException("Key must not be null.", "ak_Key"); uint lui_BucketCount = (uint)mk_Buckets.Length; uint lui_Increment; uint lui_HashCode = ComputeHashAndStep(ak_Key, out lui_Increment); uint lui_Walker = lui_HashCode % lui_BucketCount; r_Bucket lr_Bucket; do { int li_Index = (int)lui_Walker; lr_Bucket = mk_Buckets[li_Index]; if (lr_Bucket.mk_Key == null && lr_Bucket.mi_HashCode >= 0) break; // stop on empty non-duplicate if ((lr_Bucket.mi_HashCode & 0x7fffffff) == lui_HashCode && EqualsHelper(lr_Bucket.mk_Key, ak_Key)) return li_Index; lui_Walker += lui_Increment; lui_Walker %= lui_BucketCount; } while (lr_Bucket.mi_HashCode < 0 && lui_Walker != lui_HashCode); return -1; // not found } private void SetValue(object ak_Key, object ak_Value, bool ab_Add) { if (mi_Count >= mi_GrowSize) ExpandBucketsArray(); uint lui_BucketCount = (uint)mk_Buckets.Length; uint lui_Increment; uint lui_HashCode = ComputeHashAndStep(ak_Key, out lui_Increment); r_Bucket lr_Bucket; int li_Free = -1; uint lui_Walker = lui_HashCode % lui_BucketCount; do { int li_Index = (int)lui_Walker; lr_Bucket = mk_Buckets[li_Index]; if (li_Free < 0 && lr_Bucket.mk_Key == null && lr_Bucket.mi_HashCode < 0) li_Free = li_Index; if (lr_Bucket.mk_Key == null && (lr_Bucket.mi_HashCode & unchecked(0x80000000)) == 0) { if (li_Free >= 0) li_Index = li_Free; mk_Buckets[li_Index].mk_Key = ak_Key; mk_Buckets[li_Index].mk_Value = ak_Value; mk_Buckets[li_Index].mi_HashCode |= (int)lui_HashCode; ++mi_Count; return; } if ((lr_Bucket.mi_HashCode & 0x7fffffff) == lui_HashCode && EqualsHelper(lr_Bucket.mk_Key, ak_Key)) { if (ab_Add) throw new ArgumentException("duplicate key"); mk_Buckets[li_Index].mk_Value = ak_Value; return; } // mark all as dupes as long as we have not found a free bucket if (li_Free == -1) mk_Buckets[li_Index].mi_HashCode |= unchecked((int)0x80000000); lui_Walker += lui_Increment; lui_Walker %= lui_BucketCount; } while (lui_Walker != lui_HashCode); if (li_Free == -1) throw new InvalidOperationException("Corrupted hash table. Insert failed."); mk_Buckets[li_Free].mk_Key = ak_Key; mk_Buckets[li_Free].mk_Value = ak_Value; mk_Buckets[li_Free].mi_HashCode |= (int)lui_HashCode; ++mi_Count; } private static void InternalExpandInsert(r_Bucket[] ak_Buckets, r_Bucket ar_Bucket) { ar_Bucket.mi_HashCode &= 0x7fffffff; uint lui_BucketCount = (uint)ak_Buckets.Length; uint lui_Increment = (uint)(1 + ((((uint)ar_Bucket.mi_HashCode >> 5) + 1) % (lui_BucketCount - 1))); uint lui_Walker = (uint)ar_Bucket.mi_HashCode % lui_BucketCount; for (;;) { int li_Index = (int)lui_Walker; if (ak_Buckets[li_Index].mk_Key == null) { ak_Buckets[li_Index] = ar_Bucket; return; } // since current bucket is occupied mark it as duplicate ak_Buckets[li_Index].mi_HashCode |= unchecked((int)0x80000000); lui_Walker += lui_Increment; lui_Walker %= lui_BucketCount; } } private void ExpandBucketsArray() { int li_NewSize = FindPrimeGreater(mk_Buckets.Length * 2); r_Bucket[] lk_Buckets = new r_Bucket[li_NewSize]; foreach (r_Bucket lr_Bucket in mk_Buckets) { if (lr_Bucket.mk_Key == null) continue; InternalExpandInsert(lk_Buckets, lr_Bucket); } mk_Buckets = lk_Buckets; mi_GrowSize = (md_LoadFactor < 1.0) ? (int)(md_LoadFactor * li_NewSize) : li_NewSize-1; } private uint ComputeHashAndStep(object ak_Key, out uint aui_Increment) { // mask the sign bit (our collision indicator) uint lui_HashCode = (uint)GetHashHelper(ak_Key) & 0x7fffffff; // calc increment value relatively prime to mk_Buckets.Length aui_Increment = (uint)(1 + (((lui_HashCode >> 5) + 1) % ((uint)mk_Buckets.Length - 1))); return lui_HashCode; } private int GetHashHelper(object ak_Key) { if (mk_HashProvider != null) return mk_HashProvider.GetHashCode(ak_Key); return ak_Key.GetHashCode(); } private bool EqualsHelper(object ak_ObjA, object ak_ObjB) { if (mk_Comparer != null) return (mk_Comparer.Compare(ak_ObjA, ak_ObjB) == 0); return Object.Equals(ak_ObjA, ak_ObjB); } } }