Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==++==
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- // ==--==
- /*============================================================
- **
- ** Class: Hashtable
- **
- ** <OWNER>[....]</OWNER>
- **
- **
- ** Purpose: Hash table implementation
- **
- **
- ===========================================================*/
- namespace System.Collections {
- using System;
- using System.Runtime;
- using System.Runtime.Serialization;
- using System.Security.Permissions;
- using System.Diagnostics;
- using System.Threading;
- using System.Runtime.CompilerServices;
- using System.Runtime.ConstrainedExecution;
- using System.Diagnostics.Contracts;
- using System.Security.Cryptography;
- // The Hashtable class represents a dictionary of associated keys and values
- // with constant lookup time.
- //
- // Objects used as keys in a hashtable must implement the GetHashCode
- // and Equals methods (or they can rely on the default implementations
- // inherited from Object if key equality is simply reference
- // equality). Furthermore, the GetHashCode and Equals methods of
- // a key object must produce the same results given the same parameters for the
- // entire time the key is present in the hashtable. In practical terms, this
- // means that key objects should be immutable, at least for the time they are
- // used as keys in a hashtable.
- //
- // When entries are added to a hashtable, they are placed into
- // buckets based on the hashcode of their keys. Subsequent lookups of
- // keys will use the hashcode of the keys to only search a particular bucket,
- // thus substantially reducing the number of key comparisons required to find
- // an entry. A hashtable's maximum load factor, which can be specified
- // when the hashtable is instantiated, determines the maximum ratio of
- // hashtable entries to hashtable buckets. Smaller load factors cause faster
- // average lookup times at the cost of increased memory consumption. The
- // default maximum load factor of 1.0 generally provides the best balance
- // between speed and size. As entries are added to a hashtable, the hashtable's
- // actual load factor increases, and when the actual load factor reaches the
- // maximum load factor value, the number of buckets in the hashtable is
- // automatically increased by approximately a factor of two (to be precise, the
- // number of hashtable buckets is increased to the smallest prime number that
- // is larger than twice the current number of hashtable buckets).
- //
- // Each object provides their own hash function, accessed by calling
- // GetHashCode(). However, one can write their own object
- // implementing IEqualityComparer and pass it to a constructor on
- // the Hashtable. That hash function (and the equals method on the
- // IEqualityComparer) would be used for all objects in the table.
- //
- // Changes since V1 during Whidbey:
- // *) Deprecated IHashCodeProvider, use IEqualityComparer instead. This will
- // allow better performance for objects where equality checking can be
- // done much faster than establishing an ordering between two objects,
- // such as an ordinal string equality check.
- //
- [DebuggerTypeProxy(typeof(System.Collections.Hashtable.HashtableDebugView))]
- [DebuggerDisplay("Count = {Count}")]
- [System.Runtime.InteropServices.ComVisible(true)]
- [Serializable]
- public class Hashtable : IDictionary, ISerializable, IDeserializationCallback, ICloneable {
- /*
- Implementation Notes:
- The generic Dictionary was copied from Hashtable's source - any bug
- fixes here probably need to be made to the generic Dictionary as well.
- This Hashtable uses double hashing. There are hashsize buckets in the
- table, and each bucket can contain 0 or 1 element. We a bit to mark
- whether there's been a collision when we inserted multiple elements
- (ie, an inserted item was hashed at least a second time and we probed
- this bucket, but it was already in use). Using the collision bit, we
- can terminate lookups & removes for elements that aren't in the hash
- table more quickly. We steal the most significant bit from the hash code
- to store the collision bit.
- Our hash function is of the following form:
- h(key, n) = h1(key) + n*h2(key)
- where n is the number of times we've hit a collided bucket and rehashed
- (on this particular lookup). Here are our hash functions:
- h1(key) = GetHash(key); // default implementation calls key.GetHashCode();
- h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1));
- The h1 can return any number. h2 must return a number between 1 and
- hashsize - 1 that is relatively prime to hashsize (not a problem if
- hashsize is prime). (Knuth's Art of Computer Programming, Vol. 3, p. 528-9)
- If this is true, then we are guaranteed to visit every bucket in exactly
- hashsize probes, since the least common multiple of hashsize and h2(key)
- will be hashsize * h2(key). (This is the first number where adding h2 to
- h1 mod hashsize will be 0 and we will search the same bucket twice).
- We previously used a different h2(key, n) that was not constant. That is a
- horrifically bad idea, unless you can prove that series will never produce
- any identical numbers that overlap when you mod them by hashsize, for all
- subranges from i to i+hashsize, for all i. It's not worth investigating,
- since there was no clear benefit from using that hash function, and it was
- broken.
- For efficiency reasons, we've implemented this by storing h1 and h2 in a
- temporary, and setting a variable called seed equal to h1. We do a probe,
- and if we collided, we simply add h2 to seed each time through the loop.
- A good test for h2() is to subclass Hashtable, provide your own implementation
- of GetHash() that returns a constant, then add many items to the hash table.
- Make sure Count equals the number of items you inserted.
- Note that when we remove an item from the hash table, we set the key
- equal to buckets, if there was a collision in this bucket. Otherwise
- we'd either wipe out the collision bit, or we'd still have an item in
- the hash table.
- --
- */
- internal const Int32 HashPrime = 101;
- private const Int32 InitialSize = 3;
- private const String LoadFactorName = "LoadFactor";
- private const String VersionName = "Version";
- private const String ComparerName = "Comparer";
- private const String HashCodeProviderName = "HashCodeProvider";
- private const String HashSizeName = "HashSize"; // Must save buckets.Length
- private const String KeysName = "Keys";
- private const String ValuesName = "Values";
- private const String KeyComparerName = "KeyComparer";
- // Deleted entries have their key set to buckets
- // The hash table data.
- // This cannot be serialised
- private struct bucket {
- public Object key;
- public Object val;
- public int hash_coll; // Store hash code; sign bit means there was a collision.
- }
- private bucket[] buckets;
- // The total number of entries in the hash table.
- private int count;
- // The total number of collision bits set in the hashtable
- private int occupancy;
- private int loadsize;
- private float loadFactor;
- private volatile int version;
- private volatile bool isWriterInProgress;
- private ICollection keys;
- private ICollection values;
- private IEqualityComparer _keycomparer;
- private Object _syncRoot;
- [Obsolete("Please use EqualityComparer property.")]
- protected IHashCodeProvider hcp
- {
- get
- {
- if( _keycomparer is CompatibleComparer) {
- return ((CompatibleComparer)_keycomparer).HashCodeProvider;
- }
- else if( _keycomparer == null) {
- return null;
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- set
- {
- if (_keycomparer is CompatibleComparer) {
- CompatibleComparer keyComparer = (CompatibleComparer)_keycomparer;
- _keycomparer = new CompatibleComparer(keyComparer.Comparer, value);
- }
- else if( _keycomparer == null) {
- _keycomparer = new CompatibleComparer((IComparer)null, value);
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- }
- [Obsolete("Please use KeyComparer properties.")]
- protected IComparer comparer
- {
- get
- {
- if( _keycomparer is CompatibleComparer) {
- return ((CompatibleComparer)_keycomparer).Comparer;
- }
- else if( _keycomparer == null) {
- return null;
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- set
- {
- if (_keycomparer is CompatibleComparer) {
- CompatibleComparer keyComparer = (CompatibleComparer)_keycomparer;
- _keycomparer = new CompatibleComparer(value, keyComparer.HashCodeProvider);
- }
- else if( _keycomparer == null) {
- _keycomparer = new CompatibleComparer(value, (IHashCodeProvider)null);
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- }
- protected IEqualityComparer EqualityComparer
- {
- get
- {
- return _keycomparer;
- }
- }
- // Note: this constructor is a bogus constructor that does nothing
- // and is for use only with SyncHashtable.
- internal Hashtable( bool trash )
- {
- }
- // Constructs a new hashtable. The hashtable is created with an initial
- // capacity of zero and a load factor of 1.0.
- public Hashtable() : this(0, 1.0f) {
- }
- // Constructs a new hashtable with the given initial capacity and a load
- // factor of 1.0. The capacity argument serves as an indication of
- // the number of entries the hashtable will contain. When this number (or
- // an approximation) is known, specifying it in the constructor can
- // eliminate a number of resizing operations that would otherwise be
- // performed when elements are added to the hashtable.
- //
- public Hashtable(int capacity) : this(capacity, 1.0f) {
- }
- // Constructs a new hashtable with the given initial capacity and load
- // factor. The capacity argument serves as an indication of the
- // number of entries the hashtable will contain. When this number (or an
- // approximation) is known, specifying it in the constructor can eliminate
- // a number of resizing operations that would otherwise be performed when
- // elements are added to the hashtable. The loadFactor argument
- // indicates the maximum ratio of hashtable entries to hashtable buckets.
- // Smaller load factors cause faster average lookup times at the cost of
- // increased memory consumption. A load factor of 1.0 generally provides
- // the best balance between speed and size.
- //
- public Hashtable(int capacity, float loadFactor) {
- if (capacity < 0)
- throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (!(loadFactor >= 0.1f && loadFactor <= 1.0f))
- throw new ArgumentOutOfRangeException("loadFactor", Environment.GetResourceString("ArgumentOutOfRange_HashtableLoadFactor", .1, 1.0));
- Contract.EndContractBlock();
- // Based on perf work, .72 is the optimal load factor for this table.
- this.loadFactor = 0.72f * loadFactor;
- double rawsize = capacity / this.loadFactor;
- if (rawsize > Int32.MaxValue)
- throw new ArgumentException(Environment.GetResourceString("Arg_HTCapacityOverflow"));
- // Avoid awfully small sizes
- int hashsize = (rawsize > InitialSize) ? HashHelpers.GetPrime((int)rawsize) : InitialSize;
- buckets = new bucket[hashsize];
- loadsize = (int)(this.loadFactor * hashsize);
- isWriterInProgress = false;
- // Based on the current algorithm, loadsize must be less than hashsize.
- Contract.Assert( loadsize < hashsize, "Invalid hashtable loadsize!");
- }
- // Constructs a new hashtable with the given initial capacity and load
- // factor. The capacity argument serves as an indication of the
- // number of entries the hashtable will contain. When this number (or an
- // approximation) is known, specifying it in the constructor can eliminate
- // a number of resizing operations that would otherwise be performed when
- // elements are added to the hashtable. The loadFactor argument
- // indicates the maximum ratio of hashtable entries to hashtable buckets.
- // Smaller load factors cause faster average lookup times at the cost of
- // increased memory consumption. A load factor of 1.0 generally provides
- // the best balance between speed and size. The hcp argument
- // is used to specify an Object that will provide hash codes for all
- // the Objects in the table. Using this, you can in effect override
- // GetHashCode() on each Object using your own hash function. The
- // comparer argument will let you specify a custom function for
- // comparing keys. By specifying user-defined objects for hcp
- // and comparer, users could make a hash table using strings
- // as keys do case-insensitive lookups.
- //
- [Obsolete("Please use Hashtable(int, float, IEqualityComparer) instead.")]
- public Hashtable(int capacity, float loadFactor, IHashCodeProvider hcp, IComparer comparer) : this(capacity, loadFactor) {
- if (hcp == null && comparer == null) {
- this._keycomparer = null;
- }
- else {
- this._keycomparer = new CompatibleComparer(comparer,hcp);
- }
- }
- public Hashtable(int capacity, float loadFactor, IEqualityComparer equalityComparer) : this(capacity, loadFactor) {
- this._keycomparer = equalityComparer;
- }
- // Constructs a new hashtable using a custom hash function
- // and a custom comparison function for keys. This will enable scenarios
- // such as doing lookups with case-insensitive strings.
- //
- [Obsolete("Please use Hashtable(IEqualityComparer) instead.")]
- public Hashtable(IHashCodeProvider hcp, IComparer comparer) : this(0, 1.0f, hcp, comparer) {
- }
- public Hashtable(IEqualityComparer equalityComparer) : this(0, 1.0f, equalityComparer) {
- }
- // Constructs a new hashtable using a custom hash function
- // and a custom comparison function for keys. This will enable scenarios
- // such as doing lookups with case-insensitive strings.
- //
- [Obsolete("Please use Hashtable(int, IEqualityComparer) instead.")]
- public Hashtable(int capacity, IHashCodeProvider hcp, IComparer comparer)
- : this(capacity, 1.0f, hcp, comparer) {
- }
- public Hashtable(int capacity, IEqualityComparer equalityComparer)
- : this(capacity, 1.0f, equalityComparer) {
- }
- // Constructs a new hashtable containing a copy of the entries in the given
- // dictionary. The hashtable is created with a load factor of 1.0.
- //
- public Hashtable(IDictionary d) : this(d, 1.0f) {
- }
- // Constructs a new hashtable containing a copy of the entries in the given
- // dictionary. The hashtable is created with the given load factor.
- //
- public Hashtable(IDictionary d, float loadFactor)
- : this(d, loadFactor, (IEqualityComparer)null) {
- }
- [Obsolete("Please use Hashtable(IDictionary, IEqualityComparer) instead.")]
- public Hashtable(IDictionary d, IHashCodeProvider hcp, IComparer comparer)
- : this(d, 1.0f, hcp, comparer) {
- }
- public Hashtable(IDictionary d, IEqualityComparer equalityComparer)
- : this(d, 1.0f, equalityComparer) {
- }
- [Obsolete("Please use Hashtable(IDictionary, float, IEqualityComparer) instead.")]
- public Hashtable(IDictionary d, float loadFactor, IHashCodeProvider hcp, IComparer comparer)
- : this((d != null ? d.Count : 0), loadFactor, hcp, comparer) {
- if (d==null)
- throw new ArgumentNullException("d", Environment.GetResourceString("ArgumentNull_Dictionary"));
- Contract.EndContractBlock();
- IDictionaryEnumerator e = d.GetEnumerator();
- while (e.MoveNext()) Add(e.Key, e.Value);
- }
- public Hashtable(IDictionary d, float loadFactor, IEqualityComparer equalityComparer)
- : this((d != null ? d.Count : 0), loadFactor, equalityComparer) {
- if (d==null)
- throw new ArgumentNullException("d", Environment.GetResourceString("ArgumentNull_Dictionary"));
- Contract.EndContractBlock();
- IDictionaryEnumerator e = d.GetEnumerator();
- while (e.MoveNext()) Add(e.Key, e.Value);
- }
- protected Hashtable(SerializationInfo info, StreamingContext context) {
- //We can't do anything with the keys and values until the entire graph has been deserialized
- //and we have a reasonable estimate that GetHashCode is not going to fail. For the time being,
- //we'll just cache this. The graph is not valid until OnDeserialization has been called.
- HashHelpers.SerializationInfoTable.Add(this, info);
- }
- // ‘InitHash’ is basically an implementation of classic DoubleHashing (see http://en.wikipedia.org/wiki/Double_hashing)
- //
- // 1) The only ‘correctness’ requirement is that the ‘increment’ used to probe
- // a. Be non-zero
- // b. Be relatively prime to the table size ‘hashSize’. (This is needed to insure you probe all entries in the table before you ‘wrap’ and visit entries already probed)
- // 2) Because we choose table sizes to be primes, we just need to insure that the increment is 0 < incr < hashSize
- //
- // Thus this function would work: Incr = 1 + (seed % (hashSize-1))
- //
- // While this works well for ‘uniformly distributed’ keys, in practice, non-uniformity is common.
- // In particular in practice we can see ‘mostly sequential’ where you get long clusters of keys that ‘pack’.
- // To avoid bad behavior you want it to be the case that the increment is ‘large’ even for ‘small’ values (because small
- // values tend to happen more in practice). Thus we multiply ‘seed’ by a number that will make these small values
- // bigger (and not hurt large values). We picked HashPrime (101) because it was prime, and if ‘hashSize-1’ is not a multiple of HashPrime
- // (enforced in GetPrime), then incr has the potential of being every value from 1 to hashSize-1. The choice was largely arbitrary.
- //
- // Computes the hash function: H(key, i) = h1(key) + i*h2(key, hashSize).
- // The out parameter seed is h1(key), while the out parameter
- // incr is h2(key, hashSize). Callers of this function should
- // add incr each time through a loop.
- private uint InitHash(Object key, int hashsize, out uint seed, out uint incr) {
- // Hashcode must be positive. Also, we must not use the sign bit, since
- // that is used for the collision bit.
- uint hashcode = (uint) GetHash(key) & 0x7FFFFFFF;
- seed = (uint) hashcode;
- // Restriction: incr MUST be between 1 and hashsize - 1, inclusive for
- // the modular arithmetic to work correctly. This guarantees you'll
- // visit every bucket in the table exactly once within hashsize
- // iterations. Violate this and it'll cause obscure bugs forever.
- // If you change this calculation for h2(key), update putEntry too!
- incr = (uint)(1 + ((seed * HashPrime) % ((uint)hashsize - 1)));
- return hashcode;
- }
- // Adds an entry with the given key and value to this hashtable. An
- // ArgumentException is thrown if the key is null or if the key is already
- // present in the hashtable.
- //
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual void Add(Object key, Object value) {
- Insert(key, value, true);
- }
- // Removes all entries from this hashtable.
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- public virtual void Clear() {
- Contract.Assert(!isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
- if (count == 0 && occupancy == 0)
- return;
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- for (int i = 0; i < buckets.Length; i++){
- buckets[i].hash_coll = 0;
- buckets[i].key = null;
- buckets[i].val = null;
- }
- count = 0;
- occupancy = 0;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- }
- // Clone returns a virtually identical copy of this hash table. This does
- // a shallow copy - the Objects in the table aren't cloned, only the references
- // to those Objects.
- public virtual Object Clone()
- {
- bucket[] lbuckets = buckets;
- Hashtable ht = new Hashtable(count,_keycomparer);
- ht.version = version;
- ht.loadFactor = loadFactor;
- ht.count = 0;
- int bucket = lbuckets.Length;
- while (bucket > 0) {
- bucket--;
- Object keyv = lbuckets[bucket].key;
- if ((keyv!= null) && (keyv != lbuckets)) {
- ht[keyv] = lbuckets[bucket].val;
- }
- }
- return ht;
- }
- // Checks if this hashtable contains the given key.
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual bool Contains(Object key) {
- return ContainsKey(key);
- }
- // Checks if this hashtable contains an entry with the given key. This is
- // an O(1) operation.
- //
- public virtual bool ContainsKey(Object key) {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- uint seed;
- uint incr;
- // Take a snapshot of buckets, in case another thread resizes table
- bucket[] lbuckets = buckets;
- uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
- int ntry = 0;
- bucket b;
- int bucketNumber = (int) (seed % (uint)lbuckets.Length);
- do {
- b = lbuckets[bucketNumber];
- if (b.key == null) {
- return false;
- }
- if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (b.key, key))
- return true;
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)lbuckets.Length);
- } while (b.hash_coll < 0 && ++ntry < lbuckets.Length);
- return false;
- }
- // Checks if this hashtable contains an entry with the given value. The
- // values of the entries of the hashtable are compared to the given value
- // using the Object.Equals method. This method performs a linear
- // search and is thus be substantially slower than the ContainsKey
- // method.
- //
- public virtual bool ContainsValue(Object value) {
- if (value == null) {
- for (int i = buckets.Length; --i >= 0;) {
- if (buckets[i].key != null && buckets[i].key != buckets && buckets[i].val == null)
- return true;
- }
- }
- else {
- for (int i = buckets.Length; --i >= 0;) {
- Object val = buckets[i].val;
- if (val!=null && val.Equals(value)) return true;
- }
- }
- return false;
- }
- // Copies the keys of this hashtable to a given array starting at a given
- // index. This method is used by the implementation of the CopyTo method in
- // the KeyCollection class.
- private void CopyKeys(Array array, int arrayIndex) {
- Contract.Requires(array != null);
- Contract.Requires(array.Rank == 1);
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- array.SetValue(keyv, arrayIndex++);
- }
- }
- }
- // Copies the keys of this hashtable to a given array starting at a given
- // index. This method is used by the implementation of the CopyTo method in
- // the KeyCollection class.
- private void CopyEntries(Array array, int arrayIndex) {
- Contract.Requires(array != null);
- Contract.Requires(array.Rank == 1);
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- DictionaryEntry entry = new DictionaryEntry(keyv,lbuckets[i].val);
- array.SetValue(entry, arrayIndex++);
- }
- }
- }
- // Copies the values in this hash table to an array at
- // a given index. Note that this only copies values, and not keys.
- public virtual void CopyTo(Array array, int arrayIndex)
- {
- if (array == null)
- throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Array"));
- if (array.Rank != 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
- if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (array.Length - arrayIndex < Count)
- throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
- Contract.EndContractBlock();
- CopyEntries(array, arrayIndex);
- }
- // Copies the values in this Hashtable to an KeyValuePairs array.
- // KeyValuePairs is different from Dictionary Entry in that it has special
- // debugger attributes on its fields.
- internal virtual KeyValuePairs[] ToKeyValuePairsArray() {
- KeyValuePairs[] array = new KeyValuePairs[count];
- int index = 0;
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- array[index++] = new KeyValuePairs(keyv,lbuckets[i].val);
- }
- }
- return array;
- }
- // Copies the values of this hashtable to a given array starting at a given
- // index. This method is used by the implementation of the CopyTo method in
- // the ValueCollection class.
- private void CopyValues(Array array, int arrayIndex) {
- Contract.Requires(array != null);
- Contract.Requires(array.Rank == 1);
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- array.SetValue(lbuckets[i].val, arrayIndex++);
- }
- }
- }
- // Returns the value associated with the given key. If an entry with the
- // given key is not found, the returned value is null.
- //
- public virtual Object this[Object key] {
- get {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- uint seed;
- uint incr;
- // Take a snapshot of buckets, in case another thread does a resize
- bucket[] lbuckets = buckets;
- uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
- int ntry = 0;
- bucket b;
- int bucketNumber = (int) (seed % (uint)lbuckets.Length);
- do
- {
- int currentversion;
- // A read operation on hashtable has three steps:
- // (1) calculate the hash and find the slot number.
- // (2) compare the hashcode, if equal, go to step 3. Otherwise end.
- // (3) compare the key, if equal, go to step 4. Otherwise end.
- // (4) return the value contained in the bucket.
- // After step 3 and before step 4. A writer can kick in a remove the old item and add a new one
- // in the same bukcet. So in the reader we need to check if the hash table is modified during above steps.
- //
- // Writers (Insert, Remove, Clear) will set 'isWriterInProgress' flag before it starts modifying
- // the hashtable and will ckear the flag when it is done. When the flag is cleared, the 'version'
- // will be increased. We will repeat the reading if a writer is in progress or done with the modification
- // during the read.
- //
- // Our memory model guarantee if we pick up the change in bucket from another processor,
- // we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
- //
- int spinCount = 0;
- do {
- // this is violate read, following memory accesses can not be moved ahead of it.
- currentversion = version;
- b = lbuckets[bucketNumber];
- // The contention between reader and writer shouldn't happen frequently.
- // But just in case this will burn CPU, yield the control of CPU if we spinned a few times.
- // 8 is just a random number I pick.
- if( (++spinCount) % 8 == 0 ) {
- Thread.Sleep(1); // 1 means we are yeilding control to all threads, including low-priority ones.
- }
- } while ( isWriterInProgress || (currentversion != version) );
- if (b.key == null) {
- return null;
- }
- if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (b.key, key))
- return b.val;
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)lbuckets.Length);
- } while (b.hash_coll < 0 && ++ntry < lbuckets.Length);
- return null;
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- set {
- Insert(key, value, false);
- }
- }
- // Increases the bucket count of this hashtable. This method is called from
- // the Insert method when the actual load factor of the hashtable reaches
- // the upper limit specified when the hashtable was constructed. The number
- // of buckets in the hashtable is increased to the smallest prime number
- // that is larger than twice the current number of buckets, and the entries
- // in the hashtable are redistributed into the new buckets using the cached
- // hashcodes.
- private void expand() {
- int rawsize = HashHelpers.ExpandPrime(buckets.Length);
- rehash(rawsize, false);
- }
- // We occationally need to rehash the table to clean up the collision bits.
- private void rehash() {
- rehash( buckets.Length, false );
- }
- private void UpdateVersion() {
- // Version might become negative when version is Int32.MaxValue, but the oddity will be still be correct.
- // So we don't need to special case this.
- version++;
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
- private void rehash( int newsize, bool forceNewHashCode ) {
- // reset occupancy
- occupancy=0;
- // Don't replace any internal state until we've finished adding to the
- // new bucket[]. This serves two purposes:
- // 1) Allow concurrent readers to see valid hashtable contents
- // at all times
- // 2) Protect against an OutOfMemoryException while allocating this
- // new bucket[].
- bucket[] newBuckets = new bucket[newsize];
- // rehash table into new buckets
- int nb;
- for (nb = 0; nb < buckets.Length; nb++){
- bucket oldb = buckets[nb];
- if ((oldb.key != null) && (oldb.key != buckets)) {
- int hashcode = ((forceNewHashCode ? GetHash(oldb.key) : oldb.hash_coll) & 0x7FFFFFFF);
- putEntry(newBuckets, oldb.key, oldb.val, hashcode);
- }
- }
- // New bucket[] is good to go - replace buckets and other internal state.
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets = newBuckets;
- loadsize = (int)(loadFactor * newsize);
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- // minimun size of hashtable is 3 now and maximum loadFactor is 0.72 now.
- Contract.Assert(loadsize < newsize, "Our current implementaion means this is not possible.");
- return;
- }
- // Returns an enumerator for this hashtable.
- // If modifications made to the hashtable while an enumeration is
- // in progress, the MoveNext and Current methods of the
- // enumerator will throw an exception.
- //
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- IEnumerator IEnumerable.GetEnumerator() {
- return new HashtableEnumerator(this, HashtableEnumerator.DictEntry);
- }
- // Returns a dictionary enumerator for this hashtable.
- // If modifications made to the hashtable while an enumeration is
- // in progress, the MoveNext and Current methods of the
- // enumerator will throw an exception.
- //
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual IDictionaryEnumerator GetEnumerator() {
- return new HashtableEnumerator(this, HashtableEnumerator.DictEntry);
- }
- // Internal method to get the hash code for an Object. This will call
- // GetHashCode() on each object if you haven't provided an IHashCodeProvider
- // instance. Otherwise, it calls hcp.GetHashCode(obj).
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- protected virtual int GetHash(Object key)
- {
- if (_keycomparer != null)
- return _keycomparer.GetHashCode(key);
- return key.GetHashCode();
- }
- // Is this Hashtable read-only?
- public virtual bool IsReadOnly {
- get { return false; }
- }
- public virtual bool IsFixedSize {
- get { return false; }
- }
- // Is this Hashtable synchronized? See SyncRoot property
- public virtual bool IsSynchronized {
- get { return false; }
- }
- // Internal method to compare two keys. If you have provided an IComparer
- // instance in the constructor, this method will call comparer.Compare(item, key).
- // Otherwise, it will call item.Equals(key).
- //
- protected virtual bool KeyEquals(Object item, Object key)
- {
- Contract.Assert(key != null, "key can't be null here!");
- if( Object.ReferenceEquals(buckets, item)) {
- return false;
- }
- if (Object.ReferenceEquals(item,key))
- return true;
- if (_keycomparer != null)
- return _keycomparer.Equals(item, key);
- return item == null ? false : item.Equals(key);
- }
- // Returns a collection representing the keys of this hashtable. The order
- // in which the returned collection represents the keys is unspecified, but
- // it is guaranteed to be buckets = newBuckets; the same order in which a collection returned by
- // GetValues represents the values of the hashtable.
- //
- // The returned collection is live in the sense that any changes
- // to the hash table are reflected in this collection. It is not
- // a static copy of all the keys in the hash table.
- //
- public virtual ICollection Keys {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (keys == null) keys = new KeyCollection(this);
- return keys;
- }
- }
- // Returns a collection representing the values of this hashtable. The
- // order in which the returned collection represents the values is
- // unspecified, but it is guaranteed to be the same order in which a
- // collection returned by GetKeys represents the keys of the
- // hashtable.
- //
- // The returned collection is live in the sense that any changes
- // to the hash table are reflected in this collection. It is not
- // a static copy of all the keys in the hash table.
- //
- public virtual ICollection Values {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (values == null) values = new ValueCollection(this);
- return values;
- }
- }
- // Inserts an entry into this hashtable. This method is called from the Set
- // and Add methods. If the add parameter is true and the given key already
- // exists in the hashtable, an exception is thrown.
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
- private void Insert (Object key, Object nvalue, bool add) {
- // @
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- if (count >= loadsize) {
- expand();
- }
- else if(occupancy > loadsize && count > 100) {
- rehash();
- }
- uint seed;
- uint incr;
- // Assume we only have one thread writing concurrently. Modify
- // buckets to contain new data, as long as we insert in the right order.
- uint hashcode = InitHash(key, buckets.Length, out seed, out incr);
- int ntry = 0;
- int emptySlotNumber = -1; // We use the empty slot number to cache the first empty slot. We chose to reuse slots
- // create by remove that have the collision bit set over using up new slots.
- int bucketNumber = (int) (seed % (uint)buckets.Length);
- do {
- // Set emptySlot number to current bucket if it is the first available bucket that we have seen
- // that once contained an entry and also has had a collision.
- // We need to search this entire collision chain because we have to ensure that there are no
- // duplicate entries in the table.
- if (emptySlotNumber == -1 && (buckets[bucketNumber].key == buckets) && (buckets[bucketNumber].hash_coll < 0))//(((buckets[bucketNumber].hash_coll & unchecked(0x80000000))!=0)))
- emptySlotNumber = bucketNumber;
- // Insert the key/value pair into this bucket if this bucket is empty and has never contained an entry
- // OR
- // This bucket once contained an entry but there has never been a collision
- if ((buckets[bucketNumber].key == null) ||
- (buckets[bucketNumber].key == buckets && ((buckets[bucketNumber].hash_coll & unchecked(0x80000000))==0))) {
- // If we have found an available bucket that has never had a collision, but we've seen an available
- // bucket in the past that has the collision bit set, use the previous bucket instead
- if (emptySlotNumber != -1) // Reuse slot
- bucketNumber = emptySlotNumber;
- // We pretty much have to insert in this order. Don't set hash
- // code until the value & key are set appropriately.
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets[bucketNumber].val = nvalue;
- buckets[bucketNumber].key = key;
- buckets[bucketNumber].hash_coll |= (int) hashcode;
- count++;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- #if FEATURE_RANDOMIZED_STRING_HASHING
- if(ntry > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
- {
- // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
- // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
- if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
- {
- _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
- rehash(buckets.Length, true);
- }
- }
- #endif
- return;
- }
- // The current bucket is in use
- // OR
- // it is available, but has had the collision bit set and we have already found an available bucket
- if (((buckets[bucketNumber].hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (buckets[bucketNumber].key, key)) {
- if (add) {
- throw new ArgumentException(Environment.GetResourceString("Argument_AddingDuplicate__", buckets[bucketNumber].key, key));
- }
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets[bucketNumber].val = nvalue;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- #if FEATURE_RANDOMIZED_STRING_HASHING
- if(ntry > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
- {
- // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
- // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
- if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
- {
- _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
- rehash(buckets.Length, true);
- }
- }
- #endif
- return;
- }
- // The current bucket is full, and we have therefore collided. We need to set the collision bit
- // UNLESS
- // we have remembered an available slot previously.
- if (emptySlotNumber == -1) {// We don't need to set the collision bit here since we already have an empty slot
- if( buckets[bucketNumber].hash_coll >= 0 ) {
- buckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
- occupancy++;
- }
- }
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)buckets.Length);
- } while (++ntry < buckets.Length);
- // This code is here if and only if there were no buckets without a collision bit set in the entire table
- if (emptySlotNumber != -1)
- {
- // We pretty much have to insert in this order. Don't set hash
- // code until the value & key are set appropriately.
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets[emptySlotNumber].val = nvalue;
- buckets[emptySlotNumber].key = key;
- buckets[emptySlotNumber].hash_coll |= (int) hashcode;
- count++;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- #if FEATURE_RANDOMIZED_STRING_HASHING
- if(buckets.Length > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
- {
- // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
- // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
- if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
- {
- _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
- rehash(buckets.Length, true);
- }
- }
- #endif
- return;
- }
- // If you see this assert, make sure load factor & count are reasonable.
- // Then verify that our double hash function (h2, described at top of file)
- // meets the requirements described above. You should never see this assert.
- Contract.Assert(false, "hash table insert failed! Load factor too high, or our double hashing function is incorrect.");
- throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HashInsertFailed"));
- }
- private void putEntry (bucket[] newBuckets, Object key, Object nvalue, int hashcode)
- {
- Contract.Assert(hashcode >= 0, "hashcode >= 0"); // make sure collision bit (sign bit) wasn't set.
- uint seed = (uint) hashcode;
- uint incr = (uint)(1 + ((seed * HashPrime) % ((uint)newBuckets.Length - 1)));
- int bucketNumber = (int) (seed % (uint)newBuckets.Length);
- do {
- if ((newBuckets[bucketNumber].key == null) || (newBuckets[bucketNumber].key == buckets)) {
- newBuckets[bucketNumber].val = nvalue;
- newBuckets[bucketNumber].key = key;
- newBuckets[bucketNumber].hash_coll |= hashcode;
- return;
- }
- if( newBuckets[bucketNumber].hash_coll >= 0 ) {
- newBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
- occupancy++;
- }
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)newBuckets.Length);
- } while (true);
- }
- // Removes an entry from this hashtable. If an entry with the specified
- // key exists in the hashtable, it is removed. An ArgumentException is
- // thrown if the key is null.
- //
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
- public virtual void Remove(Object key) {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- Contract.Assert(!isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
- uint seed;
- uint incr;
- // Assuming only one concurrent writer, write directly into buckets.
- uint hashcode = InitHash(key, buckets.Length, out seed, out incr);
- int ntry = 0;
- bucket b;
- int bn = (int) (seed % (uint)buckets.Length); // bucketNumber
- do {
- b = buckets[bn];
- if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (b.key, key)) {
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- // Clear hash_coll field, then key, then value
- buckets[bn].hash_coll &= unchecked((int)0x80000000);
- if (buckets[bn].hash_coll != 0) {
- buckets[bn].key = buckets;
- }
- else {
- buckets[bn].key = null;
- }
- buckets[bn].val = null; // Free object references sooner & simplify ContainsValue.
- count--;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- return;
- }
- bn = (int) (((long)bn + incr)% (uint)buckets.Length);
- } while (b.hash_coll < 0 && ++ntry < buckets.Length);
- //throw new ArgumentException(Environment.GetResourceString("Arg_RemoveArgNotFound"));
- }
- // Returns the object to synchronize on for this hash table.
- public virtual Object SyncRoot {
- get {
- if( _syncRoot == null) {
- System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
- }
- return _syncRoot;
- }
- }
- // Returns the number of associations in this hashtable.
- //
- public virtual int Count {
- get { return count; }
- }
- // Returns a thread-safe wrapper for a Hashtable.
- //
- [HostProtection(Synchronization=true)]
- public static Hashtable Synchronized(Hashtable table) {
- if (table==null)
- throw new ArgumentNullException("table");
- Contract.EndContractBlock();
- return new SyncHashtable(table);
- }
- //
- // The ISerializable Implementation
- //
- [System.Security.SecurityCritical]
- public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
- if (info==null) {
- throw new ArgumentNullException("info");
- }
- Contract.EndContractBlock();
- // This is imperfect - it only works well if all other writes are
- // also using our synchronized wrapper. But it's still a good idea.
- lock (SyncRoot) {
- // This method hasn't been fully tweaked to be safe for a concurrent writer.
- int oldVersion = version;
- info.AddValue(LoadFactorName, loadFactor);
- info.AddValue(VersionName, version);
- //
- // We need to maintain serialization compatibility with Everett and RTM.
- // If the comparer is null or a compatible comparer, serialize Hashtable
- // in a format that can be deserialized on Everett and RTM.
- //
- // Also, if the Hashtable is using randomized hashing, serialize the old
- // view of the _keycomparer so perevious frameworks don't see the new types
- #pragma warning disable 618
- #if FEATURE_RANDOMIZED_STRING_HASHING
- IEqualityComparer keyComparerForSerilization = (IEqualityComparer) HashHelpers.GetEqualityComparerForSerialization(_keycomparer);
- #else
- IEqualityComparer keyComparerForSerilization = _keycomparer;
- #endif
- if( keyComparerForSerilization == null) {
- info.AddValue(ComparerName, null,typeof(IComparer));
- info.AddValue(HashCodeProviderName, null, typeof(IHashCodeProvider));
- }
- else if(keyComparerForSerilization is CompatibleComparer) {
- CompatibleComparer c = keyComparerForSerilization as CompatibleComparer;
- info.AddValue(ComparerName, c.Comparer, typeof(IComparer));
- info.AddValue(HashCodeProviderName, c.HashCodeProvider, typeof(IHashCodeProvider));
- }
- else {
- info.AddValue(KeyComparerName, keyComparerForSerilization, typeof(IEqualityComparer));
- }
- #pragma warning restore 618
- info.AddValue(HashSizeName, buckets.Length); //This is the length of the bucket array.
- Object [] serKeys = new Object[count];
- Object [] serValues = new Object[count];
- CopyKeys(serKeys, 0);
- CopyValues(serValues,0);
- info.AddValue(KeysName, serKeys, typeof(Object[]));
- info.AddValue(ValuesName, serValues, typeof(Object[]));
- // Explicitly check to see if anyone changed the Hashtable while we
- // were serializing it. That's a ---- in their code.
- if (version != oldVersion)
- throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- }
- }
- //
- // DeserializationEvent Listener
- //
- public virtual void OnDeserialization(Object sender) {
- if (buckets!=null) {
- // Somebody had a dependency on this hashtable and fixed us up before the ObjectManager got to it.
- return;
- }
- SerializationInfo siInfo;
- HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
- if (siInfo==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_InvalidOnDeser"));
- }
- int hashsize = 0;
- IComparer c = null;
- #pragma warning disable 618
- IHashCodeProvider hcp = null;
- #pragma warning restore 618
- Object [] serKeys = null;
- Object [] serValues = null;
- SerializationInfoEnumerator enumerator = siInfo.GetEnumerator();
- while( enumerator.MoveNext())
- {
- switch( enumerator.Name)
- {
- case LoadFactorName:
- loadFactor = siInfo.GetSingle(LoadFactorName);
- break;
- case HashSizeName:
- hashsize = siInfo.GetInt32(HashSizeName);
- break;
- case KeyComparerName:
- _keycomparer = (IEqualityComparer)siInfo.GetValue(KeyComparerName, typeof(IEqualityComparer));
- break;
- case ComparerName:
- c = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer));
- break;
- case HashCodeProviderName:
- #pragma warning disable 618
- hcp = (IHashCodeProvider)siInfo.GetValue(HashCodeProviderName, typeof(IHashCodeProvider));
- #pragma warning restore 618
- break;
- case KeysName:
- serKeys = (Object[])siInfo.GetValue(KeysName, typeof(Object[]));
- break;
- case ValuesName:
- serValues = (Object[])siInfo.GetValue(ValuesName, typeof(Object[]));
- break;
- }
- }
- loadsize = (int)(loadFactor*hashsize);
- // V1 object doesn't has _keycomparer field.
- if ( (_keycomparer == null) && ( (c != null) || (hcp != null) ) ){
- _keycomparer = new CompatibleComparer(c,hcp);
- }
- buckets = new bucket[hashsize];
- if (serKeys==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_MissingKeys"));
- }
- if (serValues==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_MissingValues"));
- }
- if (serKeys.Length!=serValues.Length) {
- throw new SerializationException(Environment.GetResourceString("Serialization_KeyValueDifferentSizes"));
- }
- for (int i=0; i<serKeys.Length; i++) {
- if (serKeys[i]==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_NullKey"));
- }
- Insert(serKeys[i], serValues[i], true);
- }
- version = siInfo.GetInt32(VersionName);
- HashHelpers.SerializationInfoTable.Remove(this);
- }
- // Implements a Collection for the keys of a hashtable. An instance of this
- // class is created by the GetKeys method of a hashtable.
- [Serializable]
- private class KeyCollection : ICollection
- {
- private Hashtable _hashtable;
- internal KeyCollection(Hashtable hashtable) {
- _hashtable = hashtable;
- }
- public virtual void CopyTo(Array array, int arrayIndex) {
- if (array==null)
- throw new ArgumentNullException("array");
- if (array.Rank != 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
- if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- Contract.EndContractBlock();
- if (array.Length - arrayIndex < _hashtable.count)
- throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
- _hashtable.CopyKeys(array, arrayIndex);
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual IEnumerator GetEnumerator() {
- return new HashtableEnumerator(_hashtable, HashtableEnumerator.Keys);
- }
- public virtual bool IsSynchronized {
- get { return _hashtable.IsSynchronized; }
- }
- public virtual Object SyncRoot {
- get { return _hashtable.SyncRoot; }
- }
- public virtual int Count {
- get { return _hashtable.count; }
- }
- }
- // Implements a Collection for the values of a hashtable. An instance of
- // this class is created by the GetValues method of a hashtable.
- [Serializable]
- private class ValueCollection : ICollection
- {
- private Hashtable _hashtable;
- internal ValueCollection(Hashtable hashtable) {
- _hashtable = hashtable;
- }
- public virtual void CopyTo(Array array, int arrayIndex) {
- if (array==null)
- throw new ArgumentNullException("array");
- if (array.Rank != 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
- if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- Contract.EndContractBlock();
- if (array.Length - arrayIndex < _hashtable.count)
- throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
- _hashtable.CopyValues(array, arrayIndex);
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual IEnumerator GetEnumerator() {
- return new HashtableEnumerator(_hashtable, HashtableEnumerator.Values);
- }
- public virtual bool IsSynchronized {
- get { return _hashtable.IsSynchronized; }
- }
- public virtual Object SyncRoot {
- get { return _hashtable.SyncRoot; }
- }
- public virtual int Count {
- get { return _hashtable.count; }
- }
- }
- // Synchronized wrapper for hashtable
- [Serializable]
- private class SyncHashtable : Hashtable, IEnumerable
- {
- protected Hashtable _table;
- internal SyncHashtable(Hashtable table) : base(false) {
- _table = table;
- }
- internal SyncHashtable(SerializationInfo info, StreamingContext context) : base (info, context) {
- _table = (Hashtable)info.GetValue("ParentTable", typeof(Hashtable));
- if (_table==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_InsufficientState"));
- }
- }
- /*================================GetObjectData=================================
- **Action: Return a serialization info containing a reference to _table. We need
- ** to implement this because our parent HT does and we don't want to actually
- ** serialize all of it's values (just a reference to the table, which will then
- ** be serialized separately.)
- **Returns: void
- **Arguments: info -- the SerializationInfo into which to store the data.
- ** context -- the StreamingContext for the current serialization (ignored)
- **Exceptions: ArgumentNullException if info is null.
- ==============================================================================*/
- [System.Security.SecurityCritical] // auto-generated
- public override void GetObjectData(SerializationInfo info, StreamingContext context) {
- if (info==null) {
- throw new ArgumentNullException("info");
- }
- Contract.EndContractBlock();
- // Our serialization code hasn't been fully tweaked to be safe
- // for a concurrent writer.
- lock (_table.SyncRoot) {
- info.AddValue("ParentTable", _table, typeof(Hashtable));
- }
- }
- public override int Count {
- get { return _table.Count; }
- }
- public override bool IsReadOnly {
- get { return _table.IsReadOnly; }
- }
- public override bool IsFixedSize {
- get { return _table.IsFixedSize; }
- }
- public override bool IsSynchronized {
- get { return true; }
- }
- public override Object this[Object key] {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- return _table[key];
- }
- set {
- lock(_table.SyncRoot) {
- _table[key] = value;
- }
- }
- }
- public override Object SyncRoot {
- get { return _table.SyncRoot; }
- }
- public override void Add(Object key, Object value) {
- lock(_table.SyncRoot) {
- _table.Add(key, value);
- }
- }
- public override void Clear() {
- lock(_table.SyncRoot) {
- _table.Clear();
- }
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public override bool Contains(Object key) {
- return _table.Contains(key);
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public override bool ContainsKey(Object key) {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- return _table.ContainsKey(key);
- }
- public override bool ContainsValue(Object key) {
- lock(_table.SyncRoot) {
- return _table.ContainsValue(key);
- }
- }
- public override void CopyTo(Array array, int arrayIndex) {
- lock (_table.SyncRoot) {
- _table.CopyTo(array, arrayIndex);
- }
- }
- public override Object Clone() {
- lock (_table.SyncRoot) {
- return Hashtable.Synchronized((Hashtable)_table.Clone());
- }
- }
- IEnumerator IEnumerable.GetEnumerator() {
- return _table.GetEnumerator();
- }
- public override IDictionaryEnumerator GetEnumerator() {
- return _table.GetEnumerator();
- }
- public override ICollection Keys {
- get {
- lock(_table.SyncRoot) {
- return _table.Keys;
- }
- }
- }
- public override ICollection Values {
- get {
- lock(_table.SyncRoot) {
- return _table.Values;
- }
- }
- }
- public override void Remove(Object key) {
- lock(_table.SyncRoot) {
- _table.Remove(key);
- }
- }
- /*==============================OnDeserialization===============================
- **Action: Does nothing. We have to implement this because our parent HT implements it,
- ** but it doesn't do anything meaningful. The real work will be done when we
- ** call OnDeserialization on our parent table.
- **Returns: void
- **Arguments: None
- **Exceptions: None
- ==============================================================================*/
- public override void OnDeserialization(Object sender) {
- return;
- }
- internal override KeyValuePairs[] ToKeyValuePairsArray() {
- return _table.ToKeyValuePairsArray();
- }
- }
- // Implements an enumerator for a hashtable. The enumerator uses the
- // internal version number of the hashtabke to ensure that no modifications
- // are made to the hashtable while an enumeration is in progress.
- [Serializable]
- private class HashtableEnumerator : IDictionaryEnumerator, ICloneable
- {
- private Hashtable hashtable;
- private int bucket;
- private int version;
- private bool current;
- private int getObjectRetType; // What should GetObject return?
- private Object currentKey;
- private Object currentValue;
- internal const int Keys = 1;
- internal const int Values = 2;
- internal const int DictEntry = 3;
- internal HashtableEnumerator(Hashtable hashtable, int getObjRetType) {
- this.hashtable = hashtable;
- bucket = hashtable.buckets.Length;
- version = hashtable.version;
- current = false;
- getObjectRetType = getObjRetType;
- }
- public Object Clone() {
- return MemberwiseClone();
- }
- public virtual Object Key {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
- return currentKey;
- }
- }
- public virtual bool MoveNext() {
- if (version != hashtable.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- while (bucket > 0) {
- bucket--;
- Object keyv = hashtable.buckets[bucket].key;
- if ((keyv!= null) && (keyv != hashtable.buckets)) {
- currentKey = keyv;
- currentValue = hashtable.buckets[bucket].val;
- current = true;
- return true;
- }
- }
- current = false;
- return false;
- }
- public virtual DictionaryEntry Entry {
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
- return new DictionaryEntry(currentKey, currentValue);
- }
- }
- public virtual Object Current {
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
- if (getObjectRetType==Keys)
- return currentKey;
- else if (getObjectRetType==Values)
- return currentValue;
- else
- return new DictionaryEntry(currentKey, currentValue);
- }
- }
- public virtual Object Value {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
- return currentValue;
- }
- }
- public virtual void Reset() {
- if (version != hashtable.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- current = false;
- bucket = hashtable.buckets.Length;
- currentKey = null;
- currentValue = null;
- }
- }
- // internal debug view class for hashtable
- internal class HashtableDebugView {
- private Hashtable hashtable;
- public HashtableDebugView( Hashtable hashtable) {
- if( hashtable == null) {
- throw new ArgumentNullException( "hashtable");
- }
- Contract.EndContractBlock();
- this.hashtable = hashtable;
- }
- [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
- public KeyValuePairs[] Items {
- get {
- return hashtable.ToKeyValuePairsArray();
- }
- }
- }
- }
- [FriendAccessAllowed]
- internal static class HashHelpers
- {
- #if FEATURE_RANDOMIZED_STRING_HASHING
- public const int HashCollisionThreshold = 100;
- public static bool s_UseRandomizedStringHashing = String.UseRandomizedHashing();
- #endif
- // Table of prime numbers to use as hash table sizes.
- // A typical resize algorithm would pick the smallest prime number in this array
- // that is larger than twice the previous capacity.
- // Suppose our Hashtable currently has capacity x and enough elements are added
- // such that a resize needs to occur. Resizing first computes 2x then finds the
- // first prime in the table greater than 2x, i.e. if primes are ordered
- // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n.
- // Doubling is important for preserving the asymptotic complexity of the
- // hashtable operations such as add. Having a prime guarantees that double
- // hashing does not lead to infinite loops. IE, your hash function will be
- // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime.
- public static readonly int[] primes = {
- 3, 7, 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};
- // Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo
- // object until OnDeserialization is called.
- private static ConditionalWeakTable<object, SerializationInfo> s_SerializationInfoTable;
- internal static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable
- {
- get
- {
- if(s_SerializationInfoTable == null)
- {
- ConditionalWeakTable<object, SerializationInfo> newTable = new ConditionalWeakTable<object, SerializationInfo>();
- Interlocked.CompareExchange(ref s_SerializationInfoTable, newTable, null);
- }
- return s_SerializationInfoTable;
- }
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- public static bool IsPrime(int candidate)
- {
- if ((candidate & 1) != 0)
- {
- int limit = (int)Math.Sqrt (candidate);
- for (int divisor = 3; divisor <= limit; divisor+=2)
- {
- if ((candidate % divisor) == 0)
- return false;
- }
- return true;
- }
- return (candidate == 2);
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- public static int GetPrime(int min)
- {
- if (min < 0)
- throw new ArgumentException(Environment.GetResourceString("Arg_HTCapacityOverflow"));
- Contract.EndContractBlock();
- for (int i = 0; i < primes.Length; i++)
- {
- int prime = primes[i];
- if (prime >= min) return prime;
- }
- //outside of our predefined table.
- //compute the hard way.
- for (int i = (min | 1); i < Int32.MaxValue;i+=2)
- {
- if (IsPrime(i) && ((i - 1) % Hashtable.HashPrime != 0))
- return i;
- }
- return min;
- }
- public static int GetMinPrime()
- {
- return primes[0];
- }
- // Returns size of hashtable to grow to.
- public static int ExpandPrime(int oldSize)
- {
- int newSize = 2 * oldSize;
- // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
- // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
- if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
- {
- Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
- return MaxPrimeArrayLength;
- }
- return GetPrime(newSize);
- }
- // This is the maximum prime smaller than Array.MaxArrayLength
- public const int MaxPrimeArrayLength = 0x7FEFFFFD;
- #if FEATURE_RANDOMIZED_STRING_HASHING
- public static bool IsWellKnownEqualityComparer(object comparer)
- {
- return (comparer == null || comparer == System.Collections.Generic.EqualityComparer<string>.Default || comparer is IWellKnownStringEqualityComparer);
- }
- public static IEqualityComparer GetRandomizedEqualityComparer(object comparer)
- {
- Contract.Assert(comparer == null || comparer == System.Collections.Generic.EqualityComparer<string>.Default || comparer is IWellKnownStringEqualityComparer);
- if(comparer == null) {
- return new System.Collections.Generic.RandomizedObjectEqualityComparer();
- }
- if(comparer == System.Collections.Generic.EqualityComparer<string>.Default) {
- return new System.Collections.Generic.RandomizedStringEqualityComparer();
- }
- IWellKnownStringEqualityComparer cmp = comparer as IWellKnownStringEqualityComparer;
- if(cmp != null) {
- return cmp.GetRandomizedEqualityComparer();
- }
- Contract.Assert(false, "Missing case in GetRandomizedEqualityComparer!");
- return null;
- }
- public static object GetEqualityComparerForSerialization(object comparer)
- {
- if(comparer == null)
- {
- return null;
- }
- IWellKnownStringEqualityComparer cmp = comparer as IWellKnownStringEqualityComparer;
- if(cmp != null)
- {
- return cmp.GetEqualityComparerForSerialization();
- }
- return comparer;
- }
- private const int bufferSize = 1024;
- private static RandomNumberGenerator rng;
- private static byte[] data;
- private static int currentIndex = bufferSize;
- private static readonly object lockObj = new Object();
- internal static long GetEntropy()
- {
- lock(lockObj) {
- long ret;
- if(currentIndex == bufferSize)
- {
- if(null == rng)
- {
- rng = RandomNumberGenerator.Create();
- data = new byte[bufferSize];
- Contract.Assert(bufferSize % 8 == 0, "We increment our current index by 8, so our buffer size must be a multiple of 8");
- }
- rng.GetBytes(data);
- currentIndex = 0;
- }
- ret = BitConverter.ToInt64(data, currentIndex);
- currentIndex += 8;
- return ret;
- }
- }
- #endif // FEATURE_RANDOMIZED_STRING_HASHING
- }
- }
- // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
- // ==++==
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- // ==--==
- /*============================================================
- **
- ** Class: Hashtable
- **
- ** <OWNER>[....]</OWNER>
- **
- **
- ** Purpose: Hash table implementation
- **
- **
- ===========================================================*/
- namespace System.Collections {
- using System;
- using System.Runtime;
- using System.Runtime.Serialization;
- using System.Security.Permissions;
- using System.Diagnostics;
- using System.Threading;
- using System.Runtime.CompilerServices;
- using System.Runtime.ConstrainedExecution;
- using System.Diagnostics.Contracts;
- using System.Security.Cryptography;
- // The Hashtable class represents a dictionary of associated keys and values
- // with constant lookup time.
- //
- // Objects used as keys in a hashtable must implement the GetHashCode
- // and Equals methods (or they can rely on the default implementations
- // inherited from Object if key equality is simply reference
- // equality). Furthermore, the GetHashCode and Equals methods of
- // a key object must produce the same results given the same parameters for the
- // entire time the key is present in the hashtable. In practical terms, this
- // means that key objects should be immutable, at least for the time they are
- // used as keys in a hashtable.
- //
- // When entries are added to a hashtable, they are placed into
- // buckets based on the hashcode of their keys. Subsequent lookups of
- // keys will use the hashcode of the keys to only search a particular bucket,
- // thus substantially reducing the number of key comparisons required to find
- // an entry. A hashtable's maximum load factor, which can be specified
- // when the hashtable is instantiated, determines the maximum ratio of
- // hashtable entries to hashtable buckets. Smaller load factors cause faster
- // average lookup times at the cost of increased memory consumption. The
- // default maximum load factor of 1.0 generally provides the best balance
- // between speed and size. As entries are added to a hashtable, the hashtable's
- // actual load factor increases, and when the actual load factor reaches the
- // maximum load factor value, the number of buckets in the hashtable is
- // automatically increased by approximately a factor of two (to be precise, the
- // number of hashtable buckets is increased to the smallest prime number that
- // is larger than twice the current number of hashtable buckets).
- //
- // Each object provides their own hash function, accessed by calling
- // GetHashCode(). However, one can write their own object
- // implementing IEqualityComparer and pass it to a constructor on
- // the Hashtable. That hash function (and the equals method on the
- // IEqualityComparer) would be used for all objects in the table.
- //
- // Changes since V1 during Whidbey:
- // *) Deprecated IHashCodeProvider, use IEqualityComparer instead. This will
- // allow better performance for objects where equality checking can be
- // done much faster than establishing an ordering between two objects,
- // such as an ordinal string equality check.
- //
- [DebuggerTypeProxy(typeof(System.Collections.Hashtable.HashtableDebugView))]
- [DebuggerDisplay("Count = {Count}")]
- [System.Runtime.InteropServices.ComVisible(true)]
- [Serializable]
- public class Hashtable : IDictionary, ISerializable, IDeserializationCallback, ICloneable {
- /*
- Implementation Notes:
- The generic Dictionary was copied from Hashtable's source - any bug
- fixes here probably need to be made to the generic Dictionary as well.
- This Hashtable uses double hashing. There are hashsize buckets in the
- table, and each bucket can contain 0 or 1 element. We a bit to mark
- whether there's been a collision when we inserted multiple elements
- (ie, an inserted item was hashed at least a second time and we probed
- this bucket, but it was already in use). Using the collision bit, we
- can terminate lookups & removes for elements that aren't in the hash
- table more quickly. We steal the most significant bit from the hash code
- to store the collision bit.
- Our hash function is of the following form:
- h(key, n) = h1(key) + n*h2(key)
- where n is the number of times we've hit a collided bucket and rehashed
- (on this particular lookup). Here are our hash functions:
- h1(key) = GetHash(key); // default implementation calls key.GetHashCode();
- h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1));
- The h1 can return any number. h2 must return a number between 1 and
- hashsize - 1 that is relatively prime to hashsize (not a problem if
- hashsize is prime). (Knuth's Art of Computer Programming, Vol. 3, p. 528-9)
- If this is true, then we are guaranteed to visit every bucket in exactly
- hashsize probes, since the least common multiple of hashsize and h2(key)
- will be hashsize * h2(key). (This is the first number where adding h2 to
- h1 mod hashsize will be 0 and we will search the same bucket twice).
- We previously used a different h2(key, n) that was not constant. That is a
- horrifically bad idea, unless you can prove that series will never produce
- any identical numbers that overlap when you mod them by hashsize, for all
- subranges from i to i+hashsize, for all i. It's not worth investigating,
- since there was no clear benefit from using that hash function, and it was
- broken.
- For efficiency reasons, we've implemented this by storing h1 and h2 in a
- temporary, and setting a variable called seed equal to h1. We do a probe,
- and if we collided, we simply add h2 to seed each time through the loop.
- A good test for h2() is to subclass Hashtable, provide your own implementation
- of GetHash() that returns a constant, then add many items to the hash table.
- Make sure Count equals the number of items you inserted.
- Note that when we remove an item from the hash table, we set the key
- equal to buckets, if there was a collision in this bucket. Otherwise
- we'd either wipe out the collision bit, or we'd still have an item in
- the hash table.
- --
- */
- internal const Int32 HashPrime = 101;
- private const Int32 InitialSize = 3;
- private const String LoadFactorName = "LoadFactor";
- private const String VersionName = "Version";
- private const String ComparerName = "Comparer";
- private const String HashCodeProviderName = "HashCodeProvider";
- private const String HashSizeName = "HashSize"; // Must save buckets.Length
- private const String KeysName = "Keys";
- private const String ValuesName = "Values";
- private const String KeyComparerName = "KeyComparer";
- // Deleted entries have their key set to buckets
- // The hash table data.
- // This cannot be serialised
- private struct bucket {
- public Object key;
- public Object val;
- public int hash_coll; // Store hash code; sign bit means there was a collision.
- }
- private bucket[] buckets;
- // The total number of entries in the hash table.
- private int count;
- // The total number of collision bits set in the hashtable
- private int occupancy;
- private int loadsize;
- private float loadFactor;
- private volatile int version;
- private volatile bool isWriterInProgress;
- private ICollection keys;
- private ICollection values;
- private IEqualityComparer _keycomparer;
- private Object _syncRoot;
- [Obsolete("Please use EqualityComparer property.")]
- protected IHashCodeProvider hcp
- {
- get
- {
- if( _keycomparer is CompatibleComparer) {
- return ((CompatibleComparer)_keycomparer).HashCodeProvider;
- }
- else if( _keycomparer == null) {
- return null;
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- set
- {
- if (_keycomparer is CompatibleComparer) {
- CompatibleComparer keyComparer = (CompatibleComparer)_keycomparer;
- _keycomparer = new CompatibleComparer(keyComparer.Comparer, value);
- }
- else if( _keycomparer == null) {
- _keycomparer = new CompatibleComparer((IComparer)null, value);
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- }
- [Obsolete("Please use KeyComparer properties.")]
- protected IComparer comparer
- {
- get
- {
- if( _keycomparer is CompatibleComparer) {
- return ((CompatibleComparer)_keycomparer).Comparer;
- }
- else if( _keycomparer == null) {
- return null;
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- set
- {
- if (_keycomparer is CompatibleComparer) {
- CompatibleComparer keyComparer = (CompatibleComparer)_keycomparer;
- _keycomparer = new CompatibleComparer(value, keyComparer.HashCodeProvider);
- }
- else if( _keycomparer == null) {
- _keycomparer = new CompatibleComparer(value, (IHashCodeProvider)null);
- }
- else {
- throw new ArgumentException(Environment.GetResourceString("Arg_CannotMixComparisonInfrastructure"));
- }
- }
- }
- protected IEqualityComparer EqualityComparer
- {
- get
- {
- return _keycomparer;
- }
- }
- // Note: this constructor is a bogus constructor that does nothing
- // and is for use only with SyncHashtable.
- internal Hashtable( bool trash )
- {
- }
- // Constructs a new hashtable. The hashtable is created with an initial
- // capacity of zero and a load factor of 1.0.
- public Hashtable() : this(0, 1.0f) {
- }
- // Constructs a new hashtable with the given initial capacity and a load
- // factor of 1.0. The capacity argument serves as an indication of
- // the number of entries the hashtable will contain. When this number (or
- // an approximation) is known, specifying it in the constructor can
- // eliminate a number of resizing operations that would otherwise be
- // performed when elements are added to the hashtable.
- //
- public Hashtable(int capacity) : this(capacity, 1.0f) {
- }
- // Constructs a new hashtable with the given initial capacity and load
- // factor. The capacity argument serves as an indication of the
- // number of entries the hashtable will contain. When this number (or an
- // approximation) is known, specifying it in the constructor can eliminate
- // a number of resizing operations that would otherwise be performed when
- // elements are added to the hashtable. The loadFactor argument
- // indicates the maximum ratio of hashtable entries to hashtable buckets.
- // Smaller load factors cause faster average lookup times at the cost of
- // increased memory consumption. A load factor of 1.0 generally provides
- // the best balance between speed and size.
- //
- public Hashtable(int capacity, float loadFactor) {
- if (capacity < 0)
- throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (!(loadFactor >= 0.1f && loadFactor <= 1.0f))
- throw new ArgumentOutOfRangeException("loadFactor", Environment.GetResourceString("ArgumentOutOfRange_HashtableLoadFactor", .1, 1.0));
- Contract.EndContractBlock();
- // Based on perf work, .72 is the optimal load factor for this table.
- this.loadFactor = 0.72f * loadFactor;
- double rawsize = capacity / this.loadFactor;
- if (rawsize > Int32.MaxValue)
- throw new ArgumentException(Environment.GetResourceString("Arg_HTCapacityOverflow"));
- // Avoid awfully small sizes
- int hashsize = (rawsize > InitialSize) ? HashHelpers.GetPrime((int)rawsize) : InitialSize;
- buckets = new bucket[hashsize];
- loadsize = (int)(this.loadFactor * hashsize);
- isWriterInProgress = false;
- // Based on the current algorithm, loadsize must be less than hashsize.
- Contract.Assert( loadsize < hashsize, "Invalid hashtable loadsize!");
- }
- // Constructs a new hashtable with the given initial capacity and load
- // factor. The capacity argument serves as an indication of the
- // number of entries the hashtable will contain. When this number (or an
- // approximation) is known, specifying it in the constructor can eliminate
- // a number of resizing operations that would otherwise be performed when
- // elements are added to the hashtable. The loadFactor argument
- // indicates the maximum ratio of hashtable entries to hashtable buckets.
- // Smaller load factors cause faster average lookup times at the cost of
- // increased memory consumption. A load factor of 1.0 generally provides
- // the best balance between speed and size. The hcp argument
- // is used to specify an Object that will provide hash codes for all
- // the Objects in the table. Using this, you can in effect override
- // GetHashCode() on each Object using your own hash function. The
- // comparer argument will let you specify a custom function for
- // comparing keys. By specifying user-defined objects for hcp
- // and comparer, users could make a hash table using strings
- // as keys do case-insensitive lookups.
- //
- [Obsolete("Please use Hashtable(int, float, IEqualityComparer) instead.")]
- public Hashtable(int capacity, float loadFactor, IHashCodeProvider hcp, IComparer comparer) : this(capacity, loadFactor) {
- if (hcp == null && comparer == null) {
- this._keycomparer = null;
- }
- else {
- this._keycomparer = new CompatibleComparer(comparer,hcp);
- }
- }
- public Hashtable(int capacity, float loadFactor, IEqualityComparer equalityComparer) : this(capacity, loadFactor) {
- this._keycomparer = equalityComparer;
- }
- // Constructs a new hashtable using a custom hash function
- // and a custom comparison function for keys. This will enable scenarios
- // such as doing lookups with case-insensitive strings.
- //
- [Obsolete("Please use Hashtable(IEqualityComparer) instead.")]
- public Hashtable(IHashCodeProvider hcp, IComparer comparer) : this(0, 1.0f, hcp, comparer) {
- }
- public Hashtable(IEqualityComparer equalityComparer) : this(0, 1.0f, equalityComparer) {
- }
- // Constructs a new hashtable using a custom hash function
- // and a custom comparison function for keys. This will enable scenarios
- // such as doing lookups with case-insensitive strings.
- //
- [Obsolete("Please use Hashtable(int, IEqualityComparer) instead.")]
- public Hashtable(int capacity, IHashCodeProvider hcp, IComparer comparer)
- : this(capacity, 1.0f, hcp, comparer) {
- }
- public Hashtable(int capacity, IEqualityComparer equalityComparer)
- : this(capacity, 1.0f, equalityComparer) {
- }
- // Constructs a new hashtable containing a copy of the entries in the given
- // dictionary. The hashtable is created with a load factor of 1.0.
- //
- public Hashtable(IDictionary d) : this(d, 1.0f) {
- }
- // Constructs a new hashtable containing a copy of the entries in the given
- // dictionary. The hashtable is created with the given load factor.
- //
- public Hashtable(IDictionary d, float loadFactor)
- : this(d, loadFactor, (IEqualityComparer)null) {
- }
- [Obsolete("Please use Hashtable(IDictionary, IEqualityComparer) instead.")]
- public Hashtable(IDictionary d, IHashCodeProvider hcp, IComparer comparer)
- : this(d, 1.0f, hcp, comparer) {
- }
- public Hashtable(IDictionary d, IEqualityComparer equalityComparer)
- : this(d, 1.0f, equalityComparer) {
- }
- [Obsolete("Please use Hashtable(IDictionary, float, IEqualityComparer) instead.")]
- public Hashtable(IDictionary d, float loadFactor, IHashCodeProvider hcp, IComparer comparer)
- : this((d != null ? d.Count : 0), loadFactor, hcp, comparer) {
- if (d==null)
- throw new ArgumentNullException("d", Environment.GetResourceString("ArgumentNull_Dictionary"));
- Contract.EndContractBlock();
- IDictionaryEnumerator e = d.GetEnumerator();
- while (e.MoveNext()) Add(e.Key, e.Value);
- }
- public Hashtable(IDictionary d, float loadFactor, IEqualityComparer equalityComparer)
- : this((d != null ? d.Count : 0), loadFactor, equalityComparer) {
- if (d==null)
- throw new ArgumentNullException("d", Environment.GetResourceString("ArgumentNull_Dictionary"));
- Contract.EndContractBlock();
- IDictionaryEnumerator e = d.GetEnumerator();
- while (e.MoveNext()) Add(e.Key, e.Value);
- }
- protected Hashtable(SerializationInfo info, StreamingContext context) {
- //We can't do anything with the keys and values until the entire graph has been deserialized
- //and we have a reasonable estimate that GetHashCode is not going to fail. For the time being,
- //we'll just cache this. The graph is not valid until OnDeserialization has been called.
- HashHelpers.SerializationInfoTable.Add(this, info);
- }
- // ‘InitHash’ is basically an implementation of classic DoubleHashing (see http://en.wikipedia.org/wiki/Double_hashing)
- //
- // 1) The only ‘correctness’ requirement is that the ‘increment’ used to probe
- // a. Be non-zero
- // b. Be relatively prime to the table size ‘hashSize’. (This is needed to insure you probe all entries in the table before you ‘wrap’ and visit entries already probed)
- // 2) Because we choose table sizes to be primes, we just need to insure that the increment is 0 < incr < hashSize
- //
- // Thus this function would work: Incr = 1 + (seed % (hashSize-1))
- //
- // While this works well for ‘uniformly distributed’ keys, in practice, non-uniformity is common.
- // In particular in practice we can see ‘mostly sequential’ where you get long clusters of keys that ‘pack’.
- // To avoid bad behavior you want it to be the case that the increment is ‘large’ even for ‘small’ values (because small
- // values tend to happen more in practice). Thus we multiply ‘seed’ by a number that will make these small values
- // bigger (and not hurt large values). We picked HashPrime (101) because it was prime, and if ‘hashSize-1’ is not a multiple of HashPrime
- // (enforced in GetPrime), then incr has the potential of being every value from 1 to hashSize-1. The choice was largely arbitrary.
- //
- // Computes the hash function: H(key, i) = h1(key) + i*h2(key, hashSize).
- // The out parameter seed is h1(key), while the out parameter
- // incr is h2(key, hashSize). Callers of this function should
- // add incr each time through a loop.
- private uint InitHash(Object key, int hashsize, out uint seed, out uint incr) {
- // Hashcode must be positive. Also, we must not use the sign bit, since
- // that is used for the collision bit.
- uint hashcode = (uint) GetHash(key) & 0x7FFFFFFF;
- seed = (uint) hashcode;
- // Restriction: incr MUST be between 1 and hashsize - 1, inclusive for
- // the modular arithmetic to work correctly. This guarantees you'll
- // visit every bucket in the table exactly once within hashsize
- // iterations. Violate this and it'll cause obscure bugs forever.
- // If you change this calculation for h2(key), update putEntry too!
- incr = (uint)(1 + ((seed * HashPrime) % ((uint)hashsize - 1)));
- return hashcode;
- }
- // Adds an entry with the given key and value to this hashtable. An
- // ArgumentException is thrown if the key is null or if the key is already
- // present in the hashtable.
- //
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual void Add(Object key, Object value) {
- Insert(key, value, true);
- }
- // Removes all entries from this hashtable.
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- public virtual void Clear() {
- Contract.Assert(!isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
- if (count == 0 && occupancy == 0)
- return;
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- for (int i = 0; i < buckets.Length; i++){
- buckets[i].hash_coll = 0;
- buckets[i].key = null;
- buckets[i].val = null;
- }
- count = 0;
- occupancy = 0;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- }
- // Clone returns a virtually identical copy of this hash table. This does
- // a shallow copy - the Objects in the table aren't cloned, only the references
- // to those Objects.
- public virtual Object Clone()
- {
- bucket[] lbuckets = buckets;
- Hashtable ht = new Hashtable(count,_keycomparer);
- ht.version = version;
- ht.loadFactor = loadFactor;
- ht.count = 0;
- int bucket = lbuckets.Length;
- while (bucket > 0) {
- bucket--;
- Object keyv = lbuckets[bucket].key;
- if ((keyv!= null) && (keyv != lbuckets)) {
- ht[keyv] = lbuckets[bucket].val;
- }
- }
- return ht;
- }
- // Checks if this hashtable contains the given key.
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual bool Contains(Object key) {
- return ContainsKey(key);
- }
- // Checks if this hashtable contains an entry with the given key. This is
- // an O(1) operation.
- //
- public virtual bool ContainsKey(Object key) {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- uint seed;
- uint incr;
- // Take a snapshot of buckets, in case another thread resizes table
- bucket[] lbuckets = buckets;
- uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
- int ntry = 0;
- bucket b;
- int bucketNumber = (int) (seed % (uint)lbuckets.Length);
- do {
- b = lbuckets[bucketNumber];
- if (b.key == null) {
- return false;
- }
- if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (b.key, key))
- return true;
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)lbuckets.Length);
- } while (b.hash_coll < 0 && ++ntry < lbuckets.Length);
- return false;
- }
- // Checks if this hashtable contains an entry with the given value. The
- // values of the entries of the hashtable are compared to the given value
- // using the Object.Equals method. This method performs a linear
- // search and is thus be substantially slower than the ContainsKey
- // method.
- //
- public virtual bool ContainsValue(Object value) {
- if (value == null) {
- for (int i = buckets.Length; --i >= 0;) {
- if (buckets[i].key != null && buckets[i].key != buckets && buckets[i].val == null)
- return true;
- }
- }
- else {
- for (int i = buckets.Length; --i >= 0;) {
- Object val = buckets[i].val;
- if (val!=null && val.Equals(value)) return true;
- }
- }
- return false;
- }
- // Copies the keys of this hashtable to a given array starting at a given
- // index. This method is used by the implementation of the CopyTo method in
- // the KeyCollection class.
- private void CopyKeys(Array array, int arrayIndex) {
- Contract.Requires(array != null);
- Contract.Requires(array.Rank == 1);
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- array.SetValue(keyv, arrayIndex++);
- }
- }
- }
- // Copies the keys of this hashtable to a given array starting at a given
- // index. This method is used by the implementation of the CopyTo method in
- // the KeyCollection class.
- private void CopyEntries(Array array, int arrayIndex) {
- Contract.Requires(array != null);
- Contract.Requires(array.Rank == 1);
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- DictionaryEntry entry = new DictionaryEntry(keyv,lbuckets[i].val);
- array.SetValue(entry, arrayIndex++);
- }
- }
- }
- // Copies the values in this hash table to an array at
- // a given index. Note that this only copies values, and not keys.
- public virtual void CopyTo(Array array, int arrayIndex)
- {
- if (array == null)
- throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Array"));
- if (array.Rank != 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
- if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (array.Length - arrayIndex < Count)
- throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
- Contract.EndContractBlock();
- CopyEntries(array, arrayIndex);
- }
- // Copies the values in this Hashtable to an KeyValuePairs array.
- // KeyValuePairs is different from Dictionary Entry in that it has special
- // debugger attributes on its fields.
- internal virtual KeyValuePairs[] ToKeyValuePairsArray() {
- KeyValuePairs[] array = new KeyValuePairs[count];
- int index = 0;
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- array[index++] = new KeyValuePairs(keyv,lbuckets[i].val);
- }
- }
- return array;
- }
- // Copies the values of this hashtable to a given array starting at a given
- // index. This method is used by the implementation of the CopyTo method in
- // the ValueCollection class.
- private void CopyValues(Array array, int arrayIndex) {
- Contract.Requires(array != null);
- Contract.Requires(array.Rank == 1);
- bucket[] lbuckets = buckets;
- for (int i = lbuckets.Length; --i >= 0;) {
- Object keyv = lbuckets[i].key;
- if ((keyv != null) && (keyv != buckets)){
- array.SetValue(lbuckets[i].val, arrayIndex++);
- }
- }
- }
- // Returns the value associated with the given key. If an entry with the
- // given key is not found, the returned value is null.
- //
- public virtual Object this[Object key] {
- get {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- uint seed;
- uint incr;
- // Take a snapshot of buckets, in case another thread does a resize
- bucket[] lbuckets = buckets;
- uint hashcode = InitHash(key, lbuckets.Length, out seed, out incr);
- int ntry = 0;
- bucket b;
- int bucketNumber = (int) (seed % (uint)lbuckets.Length);
- do
- {
- int currentversion;
- // A read operation on hashtable has three steps:
- // (1) calculate the hash and find the slot number.
- // (2) compare the hashcode, if equal, go to step 3. Otherwise end.
- // (3) compare the key, if equal, go to step 4. Otherwise end.
- // (4) return the value contained in the bucket.
- // After step 3 and before step 4. A writer can kick in a remove the old item and add a new one
- // in the same bukcet. So in the reader we need to check if the hash table is modified during above steps.
- //
- // Writers (Insert, Remove, Clear) will set 'isWriterInProgress' flag before it starts modifying
- // the hashtable and will ckear the flag when it is done. When the flag is cleared, the 'version'
- // will be increased. We will repeat the reading if a writer is in progress or done with the modification
- // during the read.
- //
- // Our memory model guarantee if we pick up the change in bucket from another processor,
- // we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
- //
- int spinCount = 0;
- do {
- // this is violate read, following memory accesses can not be moved ahead of it.
- currentversion = version;
- b = lbuckets[bucketNumber];
- // The contention between reader and writer shouldn't happen frequently.
- // But just in case this will burn CPU, yield the control of CPU if we spinned a few times.
- // 8 is just a random number I pick.
- if( (++spinCount) % 8 == 0 ) {
- Thread.Sleep(1); // 1 means we are yeilding control to all threads, including low-priority ones.
- }
- } while ( isWriterInProgress || (currentversion != version) );
- if (b.key == null) {
- return null;
- }
- if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (b.key, key))
- return b.val;
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)lbuckets.Length);
- } while (b.hash_coll < 0 && ++ntry < lbuckets.Length);
- return null;
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- set {
- Insert(key, value, false);
- }
- }
- // Increases the bucket count of this hashtable. This method is called from
- // the Insert method when the actual load factor of the hashtable reaches
- // the upper limit specified when the hashtable was constructed. The number
- // of buckets in the hashtable is increased to the smallest prime number
- // that is larger than twice the current number of buckets, and the entries
- // in the hashtable are redistributed into the new buckets using the cached
- // hashcodes.
- private void expand() {
- int rawsize = HashHelpers.ExpandPrime(buckets.Length);
- rehash(rawsize, false);
- }
- // We occationally need to rehash the table to clean up the collision bits.
- private void rehash() {
- rehash( buckets.Length, false );
- }
- private void UpdateVersion() {
- // Version might become negative when version is Int32.MaxValue, but the oddity will be still be correct.
- // So we don't need to special case this.
- version++;
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
- private void rehash( int newsize, bool forceNewHashCode ) {
- // reset occupancy
- occupancy=0;
- // Don't replace any internal state until we've finished adding to the
- // new bucket[]. This serves two purposes:
- // 1) Allow concurrent readers to see valid hashtable contents
- // at all times
- // 2) Protect against an OutOfMemoryException while allocating this
- // new bucket[].
- bucket[] newBuckets = new bucket[newsize];
- // rehash table into new buckets
- int nb;
- for (nb = 0; nb < buckets.Length; nb++){
- bucket oldb = buckets[nb];
- if ((oldb.key != null) && (oldb.key != buckets)) {
- int hashcode = ((forceNewHashCode ? GetHash(oldb.key) : oldb.hash_coll) & 0x7FFFFFFF);
- putEntry(newBuckets, oldb.key, oldb.val, hashcode);
- }
- }
- // New bucket[] is good to go - replace buckets and other internal state.
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets = newBuckets;
- loadsize = (int)(loadFactor * newsize);
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- // minimun size of hashtable is 3 now and maximum loadFactor is 0.72 now.
- Contract.Assert(loadsize < newsize, "Our current implementaion means this is not possible.");
- return;
- }
- // Returns an enumerator for this hashtable.
- // If modifications made to the hashtable while an enumeration is
- // in progress, the MoveNext and Current methods of the
- // enumerator will throw an exception.
- //
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- IEnumerator IEnumerable.GetEnumerator() {
- return new HashtableEnumerator(this, HashtableEnumerator.DictEntry);
- }
- // Returns a dictionary enumerator for this hashtable.
- // If modifications made to the hashtable while an enumeration is
- // in progress, the MoveNext and Current methods of the
- // enumerator will throw an exception.
- //
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual IDictionaryEnumerator GetEnumerator() {
- return new HashtableEnumerator(this, HashtableEnumerator.DictEntry);
- }
- // Internal method to get the hash code for an Object. This will call
- // GetHashCode() on each object if you haven't provided an IHashCodeProvider
- // instance. Otherwise, it calls hcp.GetHashCode(obj).
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- protected virtual int GetHash(Object key)
- {
- if (_keycomparer != null)
- return _keycomparer.GetHashCode(key);
- return key.GetHashCode();
- }
- // Is this Hashtable read-only?
- public virtual bool IsReadOnly {
- get { return false; }
- }
- public virtual bool IsFixedSize {
- get { return false; }
- }
- // Is this Hashtable synchronized? See SyncRoot property
- public virtual bool IsSynchronized {
- get { return false; }
- }
- // Internal method to compare two keys. If you have provided an IComparer
- // instance in the constructor, this method will call comparer.Compare(item, key).
- // Otherwise, it will call item.Equals(key).
- //
- protected virtual bool KeyEquals(Object item, Object key)
- {
- Contract.Assert(key != null, "key can't be null here!");
- if( Object.ReferenceEquals(buckets, item)) {
- return false;
- }
- if (Object.ReferenceEquals(item,key))
- return true;
- if (_keycomparer != null)
- return _keycomparer.Equals(item, key);
- return item == null ? false : item.Equals(key);
- }
- // Returns a collection representing the keys of this hashtable. The order
- // in which the returned collection represents the keys is unspecified, but
- // it is guaranteed to be buckets = newBuckets; the same order in which a collection returned by
- // GetValues represents the values of the hashtable.
- //
- // The returned collection is live in the sense that any changes
- // to the hash table are reflected in this collection. It is not
- // a static copy of all the keys in the hash table.
- //
- public virtual ICollection Keys {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (keys == null) keys = new KeyCollection(this);
- return keys;
- }
- }
- // Returns a collection representing the values of this hashtable. The
- // order in which the returned collection represents the values is
- // unspecified, but it is guaranteed to be the same order in which a
- // collection returned by GetKeys represents the keys of the
- // hashtable.
- //
- // The returned collection is live in the sense that any changes
- // to the hash table are reflected in this collection. It is not
- // a static copy of all the keys in the hash table.
- //
- public virtual ICollection Values {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (values == null) values = new ValueCollection(this);
- return values;
- }
- }
- // Inserts an entry into this hashtable. This method is called from the Set
- // and Add methods. If the add parameter is true and the given key already
- // exists in the hashtable, an exception is thrown.
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
- private void Insert (Object key, Object nvalue, bool add) {
- // @
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- if (count >= loadsize) {
- expand();
- }
- else if(occupancy > loadsize && count > 100) {
- rehash();
- }
- uint seed;
- uint incr;
- // Assume we only have one thread writing concurrently. Modify
- // buckets to contain new data, as long as we insert in the right order.
- uint hashcode = InitHash(key, buckets.Length, out seed, out incr);
- int ntry = 0;
- int emptySlotNumber = -1; // We use the empty slot number to cache the first empty slot. We chose to reuse slots
- // create by remove that have the collision bit set over using up new slots.
- int bucketNumber = (int) (seed % (uint)buckets.Length);
- do {
- // Set emptySlot number to current bucket if it is the first available bucket that we have seen
- // that once contained an entry and also has had a collision.
- // We need to search this entire collision chain because we have to ensure that there are no
- // duplicate entries in the table.
- if (emptySlotNumber == -1 && (buckets[bucketNumber].key == buckets) && (buckets[bucketNumber].hash_coll < 0))//(((buckets[bucketNumber].hash_coll & unchecked(0x80000000))!=0)))
- emptySlotNumber = bucketNumber;
- // Insert the key/value pair into this bucket if this bucket is empty and has never contained an entry
- // OR
- // This bucket once contained an entry but there has never been a collision
- if ((buckets[bucketNumber].key == null) ||
- (buckets[bucketNumber].key == buckets && ((buckets[bucketNumber].hash_coll & unchecked(0x80000000))==0))) {
- // If we have found an available bucket that has never had a collision, but we've seen an available
- // bucket in the past that has the collision bit set, use the previous bucket instead
- if (emptySlotNumber != -1) // Reuse slot
- bucketNumber = emptySlotNumber;
- // We pretty much have to insert in this order. Don't set hash
- // code until the value & key are set appropriately.
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets[bucketNumber].val = nvalue;
- buckets[bucketNumber].key = key;
- buckets[bucketNumber].hash_coll |= (int) hashcode;
- count++;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- #if FEATURE_RANDOMIZED_STRING_HASHING
- if(ntry > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
- {
- // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
- // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
- if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
- {
- _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
- rehash(buckets.Length, true);
- }
- }
- #endif
- return;
- }
- // The current bucket is in use
- // OR
- // it is available, but has had the collision bit set and we have already found an available bucket
- if (((buckets[bucketNumber].hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (buckets[bucketNumber].key, key)) {
- if (add) {
- throw new ArgumentException(Environment.GetResourceString("Argument_AddingDuplicate__", buckets[bucketNumber].key, key));
- }
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets[bucketNumber].val = nvalue;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- #if FEATURE_RANDOMIZED_STRING_HASHING
- if(ntry > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
- {
- // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
- // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
- if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
- {
- _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
- rehash(buckets.Length, true);
- }
- }
- #endif
- return;
- }
- // The current bucket is full, and we have therefore collided. We need to set the collision bit
- // UNLESS
- // we have remembered an available slot previously.
- if (emptySlotNumber == -1) {// We don't need to set the collision bit here since we already have an empty slot
- if( buckets[bucketNumber].hash_coll >= 0 ) {
- buckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
- occupancy++;
- }
- }
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)buckets.Length);
- } while (++ntry < buckets.Length);
- // This code is here if and only if there were no buckets without a collision bit set in the entire table
- if (emptySlotNumber != -1)
- {
- // We pretty much have to insert in this order. Don't set hash
- // code until the value & key are set appropriately.
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- buckets[emptySlotNumber].val = nvalue;
- buckets[emptySlotNumber].key = key;
- buckets[emptySlotNumber].hash_coll |= (int) hashcode;
- count++;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- #if FEATURE_RANDOMIZED_STRING_HASHING
- if(buckets.Length > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(_keycomparer))
- {
- // PERF: We don't want to rehash if _keycomparer is already a RandomizedObjectEqualityComparer since in some
- // cases there may not be any strings in the hashtable and we wouldn't get any mixing.
- if(_keycomparer == null || !(_keycomparer is System.Collections.Generic.RandomizedObjectEqualityComparer))
- {
- _keycomparer = HashHelpers.GetRandomizedEqualityComparer(_keycomparer);
- rehash(buckets.Length, true);
- }
- }
- #endif
- return;
- }
- // If you see this assert, make sure load factor & count are reasonable.
- // Then verify that our double hash function (h2, described at top of file)
- // meets the requirements described above. You should never see this assert.
- Contract.Assert(false, "hash table insert failed! Load factor too high, or our double hashing function is incorrect.");
- throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HashInsertFailed"));
- }
- private void putEntry (bucket[] newBuckets, Object key, Object nvalue, int hashcode)
- {
- Contract.Assert(hashcode >= 0, "hashcode >= 0"); // make sure collision bit (sign bit) wasn't set.
- uint seed = (uint) hashcode;
- uint incr = (uint)(1 + ((seed * HashPrime) % ((uint)newBuckets.Length - 1)));
- int bucketNumber = (int) (seed % (uint)newBuckets.Length);
- do {
- if ((newBuckets[bucketNumber].key == null) || (newBuckets[bucketNumber].key == buckets)) {
- newBuckets[bucketNumber].val = nvalue;
- newBuckets[bucketNumber].key = key;
- newBuckets[bucketNumber].hash_coll |= hashcode;
- return;
- }
- if( newBuckets[bucketNumber].hash_coll >= 0 ) {
- newBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
- occupancy++;
- }
- bucketNumber = (int) (((long)bucketNumber + incr)% (uint)newBuckets.Length);
- } while (true);
- }
- // Removes an entry from this hashtable. If an entry with the specified
- // key exists in the hashtable, it is removed. An ArgumentException is
- // thrown if the key is null.
- //
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
- public virtual void Remove(Object key) {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- Contract.Assert(!isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
- uint seed;
- uint incr;
- // Assuming only one concurrent writer, write directly into buckets.
- uint hashcode = InitHash(key, buckets.Length, out seed, out incr);
- int ntry = 0;
- bucket b;
- int bn = (int) (seed % (uint)buckets.Length); // bucketNumber
- do {
- b = buckets[bn];
- if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
- KeyEquals (b.key, key)) {
- #if !FEATURE_CORECLR
- Thread.BeginCriticalRegion();
- #endif
- isWriterInProgress = true;
- // Clear hash_coll field, then key, then value
- buckets[bn].hash_coll &= unchecked((int)0x80000000);
- if (buckets[bn].hash_coll != 0) {
- buckets[bn].key = buckets;
- }
- else {
- buckets[bn].key = null;
- }
- buckets[bn].val = null; // Free object references sooner & simplify ContainsValue.
- count--;
- UpdateVersion();
- isWriterInProgress = false;
- #if !FEATURE_CORECLR
- Thread.EndCriticalRegion();
- #endif
- return;
- }
- bn = (int) (((long)bn + incr)% (uint)buckets.Length);
- } while (b.hash_coll < 0 && ++ntry < buckets.Length);
- //throw new ArgumentException(Environment.GetResourceString("Arg_RemoveArgNotFound"));
- }
- // Returns the object to synchronize on for this hash table.
- public virtual Object SyncRoot {
- get {
- if( _syncRoot == null) {
- System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
- }
- return _syncRoot;
- }
- }
- // Returns the number of associations in this hashtable.
- //
- public virtual int Count {
- get { return count; }
- }
- // Returns a thread-safe wrapper for a Hashtable.
- //
- [HostProtection(Synchronization=true)]
- public static Hashtable Synchronized(Hashtable table) {
- if (table==null)
- throw new ArgumentNullException("table");
- Contract.EndContractBlock();
- return new SyncHashtable(table);
- }
- //
- // The ISerializable Implementation
- //
- [System.Security.SecurityCritical]
- public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
- if (info==null) {
- throw new ArgumentNullException("info");
- }
- Contract.EndContractBlock();
- // This is imperfect - it only works well if all other writes are
- // also using our synchronized wrapper. But it's still a good idea.
- lock (SyncRoot) {
- // This method hasn't been fully tweaked to be safe for a concurrent writer.
- int oldVersion = version;
- info.AddValue(LoadFactorName, loadFactor);
- info.AddValue(VersionName, version);
- //
- // We need to maintain serialization compatibility with Everett and RTM.
- // If the comparer is null or a compatible comparer, serialize Hashtable
- // in a format that can be deserialized on Everett and RTM.
- //
- // Also, if the Hashtable is using randomized hashing, serialize the old
- // view of the _keycomparer so perevious frameworks don't see the new types
- #pragma warning disable 618
- #if FEATURE_RANDOMIZED_STRING_HASHING
- IEqualityComparer keyComparerForSerilization = (IEqualityComparer) HashHelpers.GetEqualityComparerForSerialization(_keycomparer);
- #else
- IEqualityComparer keyComparerForSerilization = _keycomparer;
- #endif
- if( keyComparerForSerilization == null) {
- info.AddValue(ComparerName, null,typeof(IComparer));
- info.AddValue(HashCodeProviderName, null, typeof(IHashCodeProvider));
- }
- else if(keyComparerForSerilization is CompatibleComparer) {
- CompatibleComparer c = keyComparerForSerilization as CompatibleComparer;
- info.AddValue(ComparerName, c.Comparer, typeof(IComparer));
- info.AddValue(HashCodeProviderName, c.HashCodeProvider, typeof(IHashCodeProvider));
- }
- else {
- info.AddValue(KeyComparerName, keyComparerForSerilization, typeof(IEqualityComparer));
- }
- #pragma warning restore 618
- info.AddValue(HashSizeName, buckets.Length); //This is the length of the bucket array.
- Object [] serKeys = new Object[count];
- Object [] serValues = new Object[count];
- CopyKeys(serKeys, 0);
- CopyValues(serValues,0);
- info.AddValue(KeysName, serKeys, typeof(Object[]));
- info.AddValue(ValuesName, serValues, typeof(Object[]));
- // Explicitly check to see if anyone changed the Hashtable while we
- // were serializing it. That's a ---- in their code.
- if (version != oldVersion)
- throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- }
- }
- //
- // DeserializationEvent Listener
- //
- public virtual void OnDeserialization(Object sender) {
- if (buckets!=null) {
- // Somebody had a dependency on this hashtable and fixed us up before the ObjectManager got to it.
- return;
- }
- SerializationInfo siInfo;
- HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
- if (siInfo==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_InvalidOnDeser"));
- }
- int hashsize = 0;
- IComparer c = null;
- #pragma warning disable 618
- IHashCodeProvider hcp = null;
- #pragma warning restore 618
- Object [] serKeys = null;
- Object [] serValues = null;
- SerializationInfoEnumerator enumerator = siInfo.GetEnumerator();
- while( enumerator.MoveNext())
- {
- switch( enumerator.Name)
- {
- case LoadFactorName:
- loadFactor = siInfo.GetSingle(LoadFactorName);
- break;
- case HashSizeName:
- hashsize = siInfo.GetInt32(HashSizeName);
- break;
- case KeyComparerName:
- _keycomparer = (IEqualityComparer)siInfo.GetValue(KeyComparerName, typeof(IEqualityComparer));
- break;
- case ComparerName:
- c = (IComparer)siInfo.GetValue(ComparerName, typeof(IComparer));
- break;
- case HashCodeProviderName:
- #pragma warning disable 618
- hcp = (IHashCodeProvider)siInfo.GetValue(HashCodeProviderName, typeof(IHashCodeProvider));
- #pragma warning restore 618
- break;
- case KeysName:
- serKeys = (Object[])siInfo.GetValue(KeysName, typeof(Object[]));
- break;
- case ValuesName:
- serValues = (Object[])siInfo.GetValue(ValuesName, typeof(Object[]));
- break;
- }
- }
- loadsize = (int)(loadFactor*hashsize);
- // V1 object doesn't has _keycomparer field.
- if ( (_keycomparer == null) && ( (c != null) || (hcp != null) ) ){
- _keycomparer = new CompatibleComparer(c,hcp);
- }
- buckets = new bucket[hashsize];
- if (serKeys==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_MissingKeys"));
- }
- if (serValues==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_MissingValues"));
- }
- if (serKeys.Length!=serValues.Length) {
- throw new SerializationException(Environment.GetResourceString("Serialization_KeyValueDifferentSizes"));
- }
- for (int i=0; i<serKeys.Length; i++) {
- if (serKeys[i]==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_NullKey"));
- }
- Insert(serKeys[i], serValues[i], true);
- }
- version = siInfo.GetInt32(VersionName);
- HashHelpers.SerializationInfoTable.Remove(this);
- }
- // Implements a Collection for the keys of a hashtable. An instance of this
- // class is created by the GetKeys method of a hashtable.
- [Serializable]
- private class KeyCollection : ICollection
- {
- private Hashtable _hashtable;
- internal KeyCollection(Hashtable hashtable) {
- _hashtable = hashtable;
- }
- public virtual void CopyTo(Array array, int arrayIndex) {
- if (array==null)
- throw new ArgumentNullException("array");
- if (array.Rank != 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
- if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- Contract.EndContractBlock();
- if (array.Length - arrayIndex < _hashtable.count)
- throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
- _hashtable.CopyKeys(array, arrayIndex);
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual IEnumerator GetEnumerator() {
- return new HashtableEnumerator(_hashtable, HashtableEnumerator.Keys);
- }
- public virtual bool IsSynchronized {
- get { return _hashtable.IsSynchronized; }
- }
- public virtual Object SyncRoot {
- get { return _hashtable.SyncRoot; }
- }
- public virtual int Count {
- get { return _hashtable.count; }
- }
- }
- // Implements a Collection for the values of a hashtable. An instance of
- // this class is created by the GetValues method of a hashtable.
- [Serializable]
- private class ValueCollection : ICollection
- {
- private Hashtable _hashtable;
- internal ValueCollection(Hashtable hashtable) {
- _hashtable = hashtable;
- }
- public virtual void CopyTo(Array array, int arrayIndex) {
- if (array==null)
- throw new ArgumentNullException("array");
- if (array.Rank != 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
- if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException("arrayIndex", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- Contract.EndContractBlock();
- if (array.Length - arrayIndex < _hashtable.count)
- throw new ArgumentException(Environment.GetResourceString("Arg_ArrayPlusOffTooSmall"));
- _hashtable.CopyValues(array, arrayIndex);
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public virtual IEnumerator GetEnumerator() {
- return new HashtableEnumerator(_hashtable, HashtableEnumerator.Values);
- }
- public virtual bool IsSynchronized {
- get { return _hashtable.IsSynchronized; }
- }
- public virtual Object SyncRoot {
- get { return _hashtable.SyncRoot; }
- }
- public virtual int Count {
- get { return _hashtable.count; }
- }
- }
- // Synchronized wrapper for hashtable
- [Serializable]
- private class SyncHashtable : Hashtable, IEnumerable
- {
- protected Hashtable _table;
- internal SyncHashtable(Hashtable table) : base(false) {
- _table = table;
- }
- internal SyncHashtable(SerializationInfo info, StreamingContext context) : base (info, context) {
- _table = (Hashtable)info.GetValue("ParentTable", typeof(Hashtable));
- if (_table==null) {
- throw new SerializationException(Environment.GetResourceString("Serialization_InsufficientState"));
- }
- }
- /*================================GetObjectData=================================
- **Action: Return a serialization info containing a reference to _table. We need
- ** to implement this because our parent HT does and we don't want to actually
- ** serialize all of it's values (just a reference to the table, which will then
- ** be serialized separately.)
- **Returns: void
- **Arguments: info -- the SerializationInfo into which to store the data.
- ** context -- the StreamingContext for the current serialization (ignored)
- **Exceptions: ArgumentNullException if info is null.
- ==============================================================================*/
- [System.Security.SecurityCritical] // auto-generated
- public override void GetObjectData(SerializationInfo info, StreamingContext context) {
- if (info==null) {
- throw new ArgumentNullException("info");
- }
- Contract.EndContractBlock();
- // Our serialization code hasn't been fully tweaked to be safe
- // for a concurrent writer.
- lock (_table.SyncRoot) {
- info.AddValue("ParentTable", _table, typeof(Hashtable));
- }
- }
- public override int Count {
- get { return _table.Count; }
- }
- public override bool IsReadOnly {
- get { return _table.IsReadOnly; }
- }
- public override bool IsFixedSize {
- get { return _table.IsFixedSize; }
- }
- public override bool IsSynchronized {
- get { return true; }
- }
- public override Object this[Object key] {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- return _table[key];
- }
- set {
- lock(_table.SyncRoot) {
- _table[key] = value;
- }
- }
- }
- public override Object SyncRoot {
- get { return _table.SyncRoot; }
- }
- public override void Add(Object key, Object value) {
- lock(_table.SyncRoot) {
- _table.Add(key, value);
- }
- }
- public override void Clear() {
- lock(_table.SyncRoot) {
- _table.Clear();
- }
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public override bool Contains(Object key) {
- return _table.Contains(key);
- }
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- public override bool ContainsKey(Object key) {
- if (key == null) {
- throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
- }
- Contract.EndContractBlock();
- return _table.ContainsKey(key);
- }
- public override bool ContainsValue(Object key) {
- lock(_table.SyncRoot) {
- return _table.ContainsValue(key);
- }
- }
- public override void CopyTo(Array array, int arrayIndex) {
- lock (_table.SyncRoot) {
- _table.CopyTo(array, arrayIndex);
- }
- }
- public override Object Clone() {
- lock (_table.SyncRoot) {
- return Hashtable.Synchronized((Hashtable)_table.Clone());
- }
- }
- IEnumerator IEnumerable.GetEnumerator() {
- return _table.GetEnumerator();
- }
- public override IDictionaryEnumerator GetEnumerator() {
- return _table.GetEnumerator();
- }
- public override ICollection Keys {
- get {
- lock(_table.SyncRoot) {
- return _table.Keys;
- }
- }
- }
- public override ICollection Values {
- get {
- lock(_table.SyncRoot) {
- return _table.Values;
- }
- }
- }
- public override void Remove(Object key) {
- lock(_table.SyncRoot) {
- _table.Remove(key);
- }
- }
- /*==============================OnDeserialization===============================
- **Action: Does nothing. We have to implement this because our parent HT implements it,
- ** but it doesn't do anything meaningful. The real work will be done when we
- ** call OnDeserialization on our parent table.
- **Returns: void
- **Arguments: None
- **Exceptions: None
- ==============================================================================*/
- public override void OnDeserialization(Object sender) {
- return;
- }
- internal override KeyValuePairs[] ToKeyValuePairsArray() {
- return _table.ToKeyValuePairsArray();
- }
- }
- // Implements an enumerator for a hashtable. The enumerator uses the
- // internal version number of the hashtabke to ensure that no modifications
- // are made to the hashtable while an enumeration is in progress.
- [Serializable]
- private class HashtableEnumerator : IDictionaryEnumerator, ICloneable
- {
- private Hashtable hashtable;
- private int bucket;
- private int version;
- private bool current;
- private int getObjectRetType; // What should GetObject return?
- private Object currentKey;
- private Object currentValue;
- internal const int Keys = 1;
- internal const int Values = 2;
- internal const int DictEntry = 3;
- internal HashtableEnumerator(Hashtable hashtable, int getObjRetType) {
- this.hashtable = hashtable;
- bucket = hashtable.buckets.Length;
- version = hashtable.version;
- current = false;
- getObjectRetType = getObjRetType;
- }
- public Object Clone() {
- return MemberwiseClone();
- }
- public virtual Object Key {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
- return currentKey;
- }
- }
- public virtual bool MoveNext() {
- if (version != hashtable.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- while (bucket > 0) {
- bucket--;
- Object keyv = hashtable.buckets[bucket].key;
- if ((keyv!= null) && (keyv != hashtable.buckets)) {
- currentKey = keyv;
- currentValue = hashtable.buckets[bucket].val;
- current = true;
- return true;
- }
- }
- current = false;
- return false;
- }
- public virtual DictionaryEntry Entry {
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
- return new DictionaryEntry(currentKey, currentValue);
- }
- }
- public virtual Object Current {
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
- if (getObjectRetType==Keys)
- return currentKey;
- else if (getObjectRetType==Values)
- return currentValue;
- else
- return new DictionaryEntry(currentKey, currentValue);
- }
- }
- public virtual Object Value {
- #if !FEATURE_CORECLR
- [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
- #endif
- get {
- if (current == false) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumOpCantHappen));
- return currentValue;
- }
- }
- public virtual void Reset() {
- if (version != hashtable.version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- current = false;
- bucket = hashtable.buckets.Length;
- currentKey = null;
- currentValue = null;
- }
- }
- // internal debug view class for hashtable
- internal class HashtableDebugView {
- private Hashtable hashtable;
- public HashtableDebugView( Hashtable hashtable) {
- if( hashtable == null) {
- throw new ArgumentNullException( "hashtable");
- }
- Contract.EndContractBlock();
- this.hashtable = hashtable;
- }
- [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
- public KeyValuePairs[] Items {
- get {
- return hashtable.ToKeyValuePairsArray();
- }
- }
- }
- }
- [FriendAccessAllowed]
- internal static class HashHelpers
- {
- #if FEATURE_RANDOMIZED_STRING_HASHING
- public const int HashCollisionThreshold = 100;
- public static bool s_UseRandomizedStringHashing = String.UseRandomizedHashing();
- #endif
- // Table of prime numbers to use as hash table sizes.
- // A typical resize algorithm would pick the smallest prime number in this array
- // that is larger than twice the previous capacity.
- // Suppose our Hashtable currently has capacity x and enough elements are added
- // such that a resize needs to occur. Resizing first computes 2x then finds the
- // first prime in the table greater than 2x, i.e. if primes are ordered
- // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n.
- // Doubling is important for preserving the asymptotic complexity of the
- // hashtable operations such as add. Having a prime guarantees that double
- // hashing does not lead to infinite loops. IE, your hash function will be
- // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime.
- public static readonly int[] primes = {
- 3, 7, 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};
- // Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo
- // object until OnDeserialization is called.
- private static ConditionalWeakTable<object, SerializationInfo> s_SerializationInfoTable;
- internal static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable
- {
- get
- {
- if(s_SerializationInfoTable == null)
- {
- ConditionalWeakTable<object, SerializationInfo> newTable = new ConditionalWeakTable<object, SerializationInfo>();
- Interlocked.CompareExchange(ref s_SerializationInfoTable, newTable, null);
- }
- return s_SerializationInfoTable;
- }
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- public static bool IsPrime(int candidate)
- {
- if ((candidate & 1) != 0)
- {
- int limit = (int)Math.Sqrt (candidate);
- for (int divisor = 3; divisor <= limit; divisor+=2)
- {
- if ((candidate % divisor) == 0)
- return false;
- }
- return true;
- }
- return (candidate == 2);
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- public static int GetPrime(int min)
- {
- if (min < 0)
- throw new ArgumentException(Environment.GetResourceString("Arg_HTCapacityOverflow"));
- Contract.EndContractBlock();
- for (int i = 0; i < primes.Length; i++)
- {
- int prime = primes[i];
- if (prime >= min) return prime;
- }
- //outside of our predefined table.
- //compute the hard way.
- for (int i = (min | 1); i < Int32.MaxValue;i+=2)
- {
- if (IsPrime(i) && ((i - 1) % Hashtable.HashPrime != 0))
- return i;
- }
- return min;
- }
- public static int GetMinPrime()
- {
- return primes[0];
- }
- // Returns size of hashtable to grow to.
- public static int ExpandPrime(int oldSize)
- {
- int newSize = 2 * oldSize;
- // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
- // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
- if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
- {
- Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
- return MaxPrimeArrayLength;
- }
- return GetPrime(newSize);
- }
- // This is the maximum prime smaller than Array.MaxArrayLength
- public const int MaxPrimeArrayLength = 0x7FEFFFFD;
- #if FEATURE_RANDOMIZED_STRING_HASHING
- public static bool IsWellKnownEqualityComparer(object comparer)
- {
- return (comparer == null || comparer == System.Collections.Generic.EqualityComparer<string>.Default || comparer is IWellKnownStringEqualityComparer);
- }
- public static IEqualityComparer GetRandomizedEqualityComparer(object comparer)
- {
- Contract.Assert(comparer == null || comparer == System.Collections.Generic.EqualityComparer<string>.Default || comparer is IWellKnownStringEqualityComparer);
- if(comparer == null) {
- return new System.Collections.Generic.RandomizedObjectEqualityComparer();
- }
- if(comparer == System.Collections.Generic.EqualityComparer<string>.Default) {
- return new System.Collections.Generic.RandomizedStringEqualityComparer();
- }
- IWellKnownStringEqualityComparer cmp = comparer as IWellKnownStringEqualityComparer;
- if(cmp != null) {
- return cmp.GetRandomizedEqualityComparer();
- }
- Contract.Assert(false, "Missing case in GetRandomizedEqualityComparer!");
- return null;
- }
- public static object GetEqualityComparerForSerialization(object comparer)
- {
- if(comparer == null)
- {
- return null;
- }
- IWellKnownStringEqualityComparer cmp = comparer as IWellKnownStringEqualityComparer;
- if(cmp != null)
- {
- return cmp.GetEqualityComparerForSerialization();
- }
- return comparer;
- }
- private const int bufferSize = 1024;
- private static RandomNumberGenerator rng;
- private static byte[] data;
- private static int currentIndex = bufferSize;
- private static readonly object lockObj = new Object();
- internal static long GetEntropy()
- {
- lock(lockObj) {
- long ret;
- if(currentIndex == bufferSize)
- {
- if(null == rng)
- {
- rng = RandomNumberGenerator.Create();
- data = new byte[bufferSize];
- Contract.Assert(bufferSize % 8 == 0, "We increment our current index by 8, so our buffer size must be a multiple of 8");
- }
- rng.GetBytes(data);
- currentIndex = 0;
- }
- ret = BitConverter.ToInt64(data, currentIndex);
- currentIndex += 8;
- return ret;
- }
- }
- #endif // FEATURE_RANDOMIZED_STRING_HASHING
- }
- }
- // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Add Comment
Please, Sign In to add comment