-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Refresh and update RetrievableEntryHashset #8989
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a293a5d
793f68b
103b4df
bd49a53
f9133a8
7b68241
91c654b
2b46138
8b56f2a
edea820
e043bcd
b9b7df9
e6b01e3
205f168
ca376ab
3106365
1d6de36
fe2eee7
b606f97
684f5a4
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 |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Runtime.CompilerServices; | ||
| using System.Runtime.Serialization; | ||
| // Used by Hashtable and Dictionary's SerializationInfo .ctor's to store the SerializationInfo | ||
| // object until OnDeserialization is called. | ||
| using System.Threading; | ||
|
|
||
| namespace System.Collections | ||
| { | ||
| internal static partial class HashHelpers | ||
| { | ||
| private static ConditionalWeakTable<object, SerializationInfo>? s_serializationInfoTable; | ||
|
|
||
| public static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable | ||
| { | ||
| get | ||
| { | ||
| if (s_serializationInfoTable == null) | ||
| { | ||
| _ = Interlocked.CompareExchange(ref s_serializationInfoTable, new ConditionalWeakTable<object, SerializationInfo>(), null); | ||
| } | ||
|
|
||
| return s_serializationInfoTable; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,40 +1,41 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Diagnostics; | ||
| #if !SILVERLIGHT | ||
| #if FEATURE_CONSTRAINED_EXECUTION | ||
| using System.Runtime.ConstrainedExecution; | ||
| #endif | ||
| #endif | ||
| using System.Runtime.CompilerServices; | ||
|
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. basically verbatim from runtime copy |
||
|
|
||
| #nullable disable | ||
|
|
||
| namespace Microsoft.Build.Collections | ||
| namespace System.Collections | ||
| { | ||
| /// <summary> | ||
| /// Duplicated because internal to mscorlib | ||
| /// </summary> | ||
| internal static class HashHelpers | ||
| internal static partial class HashHelpers | ||
| { | ||
| // Table of prime numbers to use as hash table sizes. | ||
| // The entry used for capacity is the smallest prime number in this array | ||
| // that is larger than twice the previous capacity. | ||
| // This is the maximum prime smaller than Array.MaxLength. | ||
| public const int MaxPrimeArrayLength = 0x7FFFFFC3; | ||
|
|
||
| public const int HashPrime = 101; | ||
|
|
||
| internal static readonly int[] primes = { | ||
| // 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. | ||
| // We prefer the low computation costs of higher prime numbers over the increased | ||
| // memory allocation of a fixed prime number i.e. when right sizing a HashSet. | ||
| internal static ReadOnlySpan<int> Primes => new int[] | ||
| { | ||
| 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}; | ||
|
|
||
| #if !SILVERLIGHT | ||
| #if FEATURE_CONSTRAINED_EXECUTION | ||
| [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] | ||
| #endif | ||
| #endif | ||
| internal static bool IsPrime(int candidate) | ||
| 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 | ||
| }; | ||
|
|
||
| public static bool IsPrime(int candidate) | ||
| { | ||
| if ((candidate & 1) != 0) | ||
| { | ||
|
|
@@ -46,61 +47,76 @@ internal static bool IsPrime(int candidate) | |
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| return candidate == 2; | ||
| } | ||
|
|
||
| #if !SILVERLIGHT | ||
| #if FEATURE_CONSTRAINED_EXECUTION | ||
| [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] | ||
| #endif | ||
| #endif | ||
| internal static int GetPrime(int min) | ||
| public static int GetPrime(int min) | ||
| { | ||
| Debug.Assert(min >= 0, "min less than zero; handle overflow checking before calling HashHelpers"); | ||
| if (min < 0) | ||
| { | ||
| throw new ArgumentException(); | ||
| } | ||
|
|
||
| for (int i = 0; i < primes.Length; i++) | ||
| foreach (int prime in Primes) | ||
| { | ||
| 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) | ||
| // Outside of our predefined table. Compute the hard way. | ||
| for (int i = min | 1; i < int.MaxValue; i += 2) | ||
| { | ||
| if (IsPrime(i)) | ||
| if (IsPrime(i) && ((i - 1) % HashPrime != 0)) | ||
| { | ||
| return i; | ||
| } | ||
| } | ||
| return min; | ||
| } | ||
|
|
||
| internal static int GetMinPrime() | ||
| { | ||
| return primes[0]; | ||
| return min; | ||
| } | ||
|
|
||
| // Returns size of hashtable to grow to. | ||
| internal static int ExpandPrime(int oldSize) | ||
| 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. | ||
| // 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) | ||
| if ((uint)newSize > MaxPrimeArrayLength && oldSize < MaxPrimeArrayLength) | ||
| { | ||
| Debug.Assert(GetPrime(MaxPrimeArrayLength) == MaxPrimeArrayLength, "Invalid MaxPrimeArrayLength"); | ||
| return MaxPrimeArrayLength; | ||
| } | ||
|
|
||
| return GetPrime(newSize); | ||
| } | ||
|
|
||
| // This is the maximum prime smaller than Array.MaxArrayLength | ||
| internal const int MaxPrimeArrayLength = 0x7FEFFFFD; | ||
| /// <summary>Returns approximate reciprocal of the divisor: ceil(2**64 / divisor).</summary> | ||
| /// <remarks>This should only be used on 64-bit.</remarks> | ||
|
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. this comment here is what I was referring to. it works on 32 bit, IIRC this is here because on 32 bit the multiply below is less efficient . @stephentoub there's no correctness issue here, right? they don't have the benefit of #if BIT64 in this project. My assumption is that the vast majority of their use is 64 bit.
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.
Correct |
||
| public static ulong GetFastModMultiplier(uint divisor) => | ||
| (ulong.MaxValue / divisor) + 1; | ||
|
|
||
| /// <summary>Performs a mod operation using the multiplier pre-computed with <see cref="GetFastModMultiplier"/>.</summary> | ||
| /// <remarks>This should only be used on 64-bit.</remarks> | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public static uint FastMod(uint value, uint divisor, ulong multiplier) | ||
| { | ||
| // We use modified Daniel Lemire's fastmod algorithm, | ||
| // which allows to avoid the long multiplication if the divisor is less than 2**31. | ||
| Debug.Assert(divisor <= int.MaxValue); | ||
|
|
||
| // This is equivalent of (uint)Math.BigMul(multiplier * value, divisor, out _). This version | ||
| // is faster than BigMul currently because we only need the high bits. | ||
| uint highbits = (uint)(((((multiplier * value) >> 32) + 1) * divisor) >> 32); | ||
|
|
||
| Debug.Assert(highbits == value % divisor); | ||
| return highbits; | ||
| } | ||
| } | ||
| } | ||
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.
from Hashset, untested here. And hopefully going away soon with your use of BinaryFormatter.