-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Reuse HashHelpers for BinaryFormatter objectholder hashes #25509
Changes from all commits
3212106
a3d2616
06daf0e
7bba89b
af85154
b7c32d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -153,6 +153,9 @@ private struct bucket | |
| private IEqualityComparer _keycomparer; | ||
| private Object _syncRoot; | ||
|
|
||
| private static ConditionalWeakTable<object, SerializationInfo> s_serializationInfoTable; | ||
| private static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable => LazyInitializer.EnsureInitialized(ref s_serializationInfoTable); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In CoreLib we use Interlocked.CompareExchange instead. @jkotas any preference here? internal static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable
{
get
{
if (s_serializationInfoTable == null)
Interlocked.CompareExchange(ref s_serializationInfoTable, new ConditionalWeakTable<object, SerializationInfo>(), null);
return s_serializationInfoTable;
}
}
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not have preference.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks :) I'm fine with using LazyInitializer here as the serialization code path isn't highly perf related. |
||
|
|
||
| [Obsolete("Please use EqualityComparer property.")] | ||
| protected IHashCodeProvider hcp | ||
| { | ||
|
|
@@ -380,7 +383,7 @@ 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); | ||
| SerializationInfoTable.Add(this, info); | ||
| } | ||
|
|
||
| // ?InitHash? is basically an implementation of classic DoubleHashing (see http://en.wikipedia.org/wiki/Double_hashing) | ||
|
|
@@ -1172,7 +1175,7 @@ public virtual void OnDeserialization(Object sender) | |
| } | ||
|
|
||
| SerializationInfo siInfo; | ||
| HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); | ||
| SerializationInfoTable.TryGetValue(this, out siInfo); | ||
|
|
||
| if (siInfo == null) | ||
| { | ||
|
|
@@ -1254,7 +1257,7 @@ public virtual void OnDeserialization(Object sender) | |
|
|
||
| _version = siInfo.GetInt32(VersionName); | ||
|
|
||
| HashHelpers.SerializationInfoTable.Remove(this); | ||
| SerializationInfoTable.Remove(this); | ||
| } | ||
|
|
||
| // Implements a Collection for the keys of a hashtable. An instance of this | ||
|
|
@@ -1640,84 +1643,4 @@ public KeyValuePairs[] Items | |
| } | ||
| } | ||
| } | ||
|
|
||
| internal static class HashHelpers | ||
| { | ||
| // 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) % 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 encountering 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; | ||
|
|
||
| private static ConditionalWeakTable<object, SerializationInfo> s_serializationInfoTable; | ||
| public static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable => LazyInitializer.EnsureInitialized(ref s_serializationInfoTable); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,9 @@ | |
| <resheader name="writer"> | ||
| <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||
| </resheader> | ||
| <data name="Arg_HTCapacityOverflow" xml:space="preserve"> | ||
| <value>Capacity overflowed and went negative.</value> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like "Cannot add more than {0} objects." might be more useful/user oriented? They don't care whether we use signed ints.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't address this one as we now should never hit this message unless the array entirely overflows. |
||
| </data> | ||
| <data name="Serialization_NonSerType" xml:space="preserve"> | ||
| <value>Type '{0}' in Assembly '{1}' is not marked as serializable.</value> | ||
| </data> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a comment explaining why we don't just grow the table... since apparently it wasn't obvious to us
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added.