Move Dictionary to shared CoreLib partition (#14795)
authorJan Kotas <jkotas@microsoft.com>
Thu, 2 Nov 2017 16:22:19 +0000 (09:22 -0700)
committerGitHub <noreply@github.com>
Thu, 2 Nov 2017 16:22:19 +0000 (09:22 -0700)
src/mscorlib/System.Private.CoreLib.csproj
src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
src/mscorlib/shared/System/Collections/Generic/Dictionary.cs [moved from src/mscorlib/src/System/Collections/Generic/Dictionary.cs with 95% similarity]
src/mscorlib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs [new file with mode: 0644]
src/mscorlib/shared/System/Collections/HashHelpers.cs [new file with mode: 0644]
src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
src/mscorlib/src/System/Collections/Hashtable.cs

index c78274e..72368f0 100644 (file)
     <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" />
index 90a656f..5b7629f 100644 (file)
@@ -53,6 +53,7 @@
     <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" />
@@ -67,7 +68,9 @@
     <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" />
@@ -2,31 +2,14 @@
 // 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>
@@ -51,7 +34,7 @@ namespace System.Collections.Generic
     [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
@@ -71,13 +54,13 @@ namespace System.Collections.Generic
         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) { }
 
@@ -132,9 +115,7 @@ namespace System.Collections.Generic
             }
         }
 
-        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)
@@ -152,9 +133,9 @@ namespace System.Collections.Generic
 
         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);
         }
 
@@ -355,12 +336,14 @@ namespace System.Collections.Generic
             {
                 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>[]));
             }
@@ -402,7 +385,7 @@ namespace System.Collections.Generic
 
             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)
@@ -423,9 +406,9 @@ namespace System.Collections.Generic
 
                     return false;
                 }
-
                 collisionCount++;
             }
+
             int index;
             if (freeCount > 0)
             {
@@ -454,7 +437,7 @@ namespace System.Collections.Generic
             // 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);
@@ -463,17 +446,15 @@ namespace System.Collections.Generic
             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;
             }
 
@@ -526,6 +507,7 @@ namespace System.Collections.Generic
             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++)
@@ -536,6 +518,7 @@ namespace System.Collections.Generic
                     }
                 }
             }
+
             for (int i = 0; i < count; i++)
             {
                 if (newEntries[i].hashCode >= 0)
@@ -545,6 +528,7 @@ namespace System.Collections.Generic
                     newBuckets[bucket] = i;
                 }
             }
+
             buckets = newBuckets;
             entries = newEntries;
         }
@@ -672,6 +656,7 @@ namespace System.Collections.Generic
             value = default(TValue);
             return false;
         }
+
         public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None);
 
         bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
@@ -1170,7 +1155,7 @@ namespace System.Collections.Generic
                 get { return false; }
             }
 
-            Object ICollection.SyncRoot
+            object ICollection.SyncRoot
             {
                 get { return ((ICollection)dictionary).SyncRoot; }
             }
@@ -1225,7 +1210,7 @@ namespace System.Collections.Generic
                     }
                 }
 
-                Object System.Collections.IEnumerator.Current
+                object System.Collections.IEnumerator.Current
                 {
                     get
                     {
@@ -1396,7 +1381,7 @@ namespace System.Collections.Generic
                 get { return false; }
             }
 
-            Object ICollection.SyncRoot
+            object ICollection.SyncRoot
             {
                 get { return ((ICollection)dictionary).SyncRoot; }
             }
@@ -1450,7 +1435,7 @@ namespace System.Collections.Generic
                     }
                 }
 
-                Object System.Collections.IEnumerator.Current
+                object System.Collections.IEnumerator.Current
                 {
                     get
                     {
diff --git a/src/mscorlib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/mscorlib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs
new file mode 100644 (file)
index 0000000..ef44fef
--- /dev/null
@@ -0,0 +1,38 @@
+// 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>));
+        }
+    }
+} 
diff --git a/src/mscorlib/shared/System/Collections/HashHelpers.cs b/src/mscorlib/shared/System/Collections/HashHelpers.cs
new file mode 100644 (file)
index 0000000..49cff85
--- /dev/null
@@ -0,0 +1,108 @@
+// 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;
+            }
+        }
+    }
+}
index 05297b0..57b63eb 100644 (file)
@@ -269,56 +269,6 @@ namespace System.Collections.Generic
             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]
index b890bfb..6a23fde 100644 (file)
@@ -123,7 +123,6 @@ namespace System.Collections
            -- 
         */
 
-        internal const Int32 HashPrime = 101;
         private const Int32 InitialSize = 3;
         private const String LoadFactorName = "LoadFactor";
         private const String VersionName = "Version";
@@ -268,7 +267,7 @@ namespace System.Collections
             // 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;
         }
 
@@ -806,7 +805,7 @@ namespace System.Collections
             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
             {
@@ -1378,102 +1377,4 @@ namespace System.Collections
             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;
-    }
 }