<Compile Include="$(BclSourcesRoot)\System\Nullable.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\Comparer.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\ComparerHelpers.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Collections\Generic\Dictionary.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\EqualityComparer.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\ArraySortHelper.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\ObjectModel\ReadOnlyDictionary.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\CharEnumerator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\CLSCompliantAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\DictionaryEntry.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\Dictionary.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\ICollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\ICollectionDebugView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\IComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\IReadOnlyList.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\KeyNotFoundException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\KeyValuePair.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\NonRandomizedStringEqualityComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\List.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\HashHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\ICollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\IComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\IDictionary.cs" />
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-/*============================================================
-**
-**
-**
-**
-** Purpose: Generic hash table implementation
-**
-** #DictionaryVersusHashtableThreadSafety
-** Hashtable has multiple reader/single writer (MR/SW) thread safety built into
-** certain methods and properties, whereas Dictionary doesn't. If you're
-** converting framework code that formerly used Hashtable to Dictionary, it's
-** important to consider whether callers may have taken a dependence on MR/SW
-** thread safety. If a reader writer lock is available, then that may be used
-** with a Dictionary to get the same thread safety guarantee.
-**
-===========================================================*/
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
namespace System.Collections.Generic
{
- using System;
- using System.Collections;
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- using System.Runtime.Serialization;
-
/// <summary>
/// Used internally to control behavior of insertion into a <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
[DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>, ISerializable, IDeserializationCallback
{
private struct Entry
private IEqualityComparer<TKey> comparer;
private KeyCollection keys;
private ValueCollection values;
- private Object _syncRoot;
+ private object _syncRoot;
// constants for serialization
- private const String VersionName = "Version"; // Do not rename (binary serialization)
- private const String HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length
- private const String KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization)
- private const String ComparerName = "Comparer"; // Do not rename (binary serialization)
+ private const string VersionName = "Version"; // Do not rename (binary serialization)
+ private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length
+ private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization)
+ private const string ComparerName = "Comparer"; // Do not rename (binary serialization)
public Dictionary() : this(0, null) { }
}
}
- public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) :
- this(collection, null)
- { }
+ public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : this(collection, null) { }
public Dictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) :
this((collection as ICollection<KeyValuePair<TKey, TValue>>)?.Count ?? 0, comparer)
protected Dictionary(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 resonable 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.
+ // We can't do anything with the keys and values until the entire graph has been deserialized
+ // and we have a resonable 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);
}
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info);
}
+
info.AddValue(VersionName, version);
info.AddValue(ComparerName, comparer, typeof(IEqualityComparer<TKey>));
- info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); //This is the length of the bucket array.
+ info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); // This is the length of the bucket array
+
if (buckets != null)
{
- KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[Count];
+ var array = new KeyValuePair<TKey, TValue>[Count];
CopyTo(array, 0);
info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair<TKey, TValue>[]));
}
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
- int targetBucket = hashCode % buckets.Length;
+ int targetBucket = hashCode % buckets.Length;
int collisionCount = 0;
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next)
return false;
}
-
collisionCount++;
}
+
int index;
if (freeCount > 0)
{
// If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
// i.e. EqualityComparer<string>.Default.
- if (collisionCount > HashHelpers.HashCollisionThreshold && comparer == NonRandomizedStringEqualityComparer.Default)
+ if (collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer)
{
comparer = (IEqualityComparer<TKey>)EqualityComparer<string>.Default;
Resize(entries.Length, true);
return true;
}
- public virtual void OnDeserialization(Object sender)
+ public virtual void OnDeserialization(object sender)
{
SerializationInfo siInfo;
HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo);
if (siInfo == null)
{
- // It might be necessary to call OnDeserialization from a container if the container object also implements
- // OnDeserialization. However, remoting will call OnDeserialization again.
// We can return immediately if this function is called twice.
- // Note we set remove the serialization info from the table at the end of this method.
+ // Note we remove the serialization info from the table at the end of this method.
return;
}
for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1;
Entry[] newEntries = new Entry[newSize];
Array.Copy(entries, 0, newEntries, 0, count);
+
if (forceNewHashCodes)
{
for (int i = 0; i < count; i++)
}
}
}
+
for (int i = 0; i < count; i++)
{
if (newEntries[i].hashCode >= 0)
newBuckets[bucket] = i;
}
}
+
buckets = newBuckets;
entries = newEntries;
}
value = default(TValue);
return false;
}
+
public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None);
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
get { return false; }
}
- Object ICollection.SyncRoot
+ object ICollection.SyncRoot
{
get { return ((ICollection)dictionary).SyncRoot; }
}
}
}
- Object System.Collections.IEnumerator.Current
+ object System.Collections.IEnumerator.Current
{
get
{
get { return false; }
}
- Object ICollection.SyncRoot
+ object ICollection.SyncRoot
{
get { return ((ICollection)dictionary).SyncRoot; }
}
}
}
- Object System.Collections.IEnumerator.Current
+ object System.Collections.IEnumerator.Current
{
get
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.Serialization;
+
+namespace System.Collections.Generic
+{
+ // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary<string,...>
+ // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which
+ // keeps the performance not affected till we hit collision threshold and then we switch to the comparer which is using
+ // randomized string hashing.
+ [Serializable] // Required for compatibility with .NET Core 2.0 as we exposed the NonRandomizedStringEqualityComparer inside the serialization blob
+#if CORECLR
+ internal
+#else
+ public
+#endif
+ sealed class NonRandomizedStringEqualityComparer : EqualityComparer<string>, ISerializable
+ {
+ internal static new IEqualityComparer<string> Default { get; } = new NonRandomizedStringEqualityComparer();
+
+ private NonRandomizedStringEqualityComparer() { }
+
+ // This is used by the serialization engine.
+ private NonRandomizedStringEqualityComparer(SerializationInfo information, StreamingContext context) { }
+
+ public sealed override bool Equals(string x, string y) => string.Equals(x, y);
+
+ public sealed override int GetHashCode(string obj) => obj?.GetLegacyNonRandomizedHashCode() ?? 0;
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // We are doing this to stay compatible with .NET Framework.
+ info.SetType(typeof(GenericEqualityComparer<string>));
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Threading;
+
+namespace System.Collections
+{
+ internal static class HashHelpers
+ {
+ public const int HashCollisionThreshold = 100;
+
+ public const int HashPrime = 101;
+
+ // 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};
+
+ 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);
+ }
+
+ public static int GetPrime(int min)
+ {
+ if (min < 0)
+ throw new ArgumentException(SR.Arg_HTCapacityOverflow);
+
+ 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) % HashPrime != 0))
+ return i;
+ }
+ return min;
+ }
+
+ // 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)
+ {
+ Debug.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;
+
+
+ // 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)
+ Interlocked.CompareExchange(ref s_serializationInfoTable, new ConditionalWeakTable<object, SerializationInfo>(), null);
+
+ return s_serializationInfoTable;
+ }
+ }
+ }
+}
GetType().GetHashCode();
}
- // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which
- // keeps the performance unaffected till we hit collision threshold and then we switch to the comparer which is using
- // randomized string hashing GenericEqualityComparer<string>
- // We are keeping serialization support here to support deserialization of .NET Core 2.0 serialization payloads with this type in it.
- [Serializable]
- internal sealed class NonRandomizedStringEqualityComparer : EqualityComparer<string>, ISerializable
- {
- private static IEqualityComparer<string> s_nonRandomizedComparer;
-
- private NonRandomizedStringEqualityComparer() { }
-
- // This is used by the serialization engine.
- private NonRandomizedStringEqualityComparer(SerializationInfo information, StreamingContext context) { }
-
- internal static new IEqualityComparer<string> Default
- {
- get
- {
- if (s_nonRandomizedComparer == null)
- {
- s_nonRandomizedComparer = new NonRandomizedStringEqualityComparer();
- }
- return s_nonRandomizedComparer;
- }
- }
-
- public override bool Equals(string x, string y)
- {
- if (x != null)
- {
- if (y != null) return x.Equals(y);
- return false;
- }
- if (y != null) return false;
- return true;
- }
-
- public override int GetHashCode(string obj)
- {
- if (obj == null) return 0;
- return obj.GetLegacyNonRandomizedHashCode();
- }
-
- public void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- // We are doing this to stay compatible with .NET Framework.
- info.SetType(typeof(GenericEqualityComparer<string>));
- }
- }
-
// Performance of IndexOf on byte array is very important for some scenarios.
// We will call the C runtime function memchr, which is optimized.
[Serializable]
--
*/
- internal const Int32 HashPrime = 101;
private const Int32 InitialSize = 3;
private const String LoadFactorName = "LoadFactor";
private const String VersionName = "Version";
// 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)));
+ incr = (uint)(1 + ((seed * HashHelpers.HashPrime) % ((uint)hashsize - 1)));
return hashcode;
}
Debug.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)));
+ uint incr = (uint)(1 + ((seed * HashHelpers.HashPrime) % ((uint)newBuckets.Length - 1)));
int bucketNumber = (int)(seed % (uint)newBuckets.Length);
do
{
private Hashtable hashtable;
}
}
-
- [FriendAccessAllowed]
- internal static class HashHelpers
- {
- public const int HashCollisionThreshold = 100;
-
- // 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;
- }
- }
-
- 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);
- }
-
- public static int GetPrime(int min)
- {
- if (min < 0)
- throw new ArgumentException(SR.Arg_HTCapacityOverflow);
-
- 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;
- }
-
- // 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)
- {
- Debug.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;
- }
}