Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ public static FrozenHashTable Create(Span<int> hashCodes, bool hashCodesAreUniqu
// - bucketStarts: initially filled with all -1s, the ith element stores the index
// into hashCodes of the head element of that bucket's chain.
// - nexts: the ith element stores the index of the next item in the chain.
// Use long to check for overflow before allocating - very large collections can overflow int.
#if NET
if ((long)numBuckets + hashCodes.Length > Array.MaxLength)
#else
if ((long)numBuckets + hashCodes.Length > 0x7FFFFFC7)
#endif
{
throw new OutOfMemoryException();
}

int[] arrayPoolBuckets = ArrayPool<int>.Shared.Rent(numBuckets + hashCodes.Length);
Span<int> bucketStarts = arrayPoolBuckets.AsSpan(0, numBuckets);
Span<int> nexts = arrayPoolBuckets.AsSpan(numBuckets, hashCodes.Length);
Expand Down Expand Up @@ -174,7 +184,20 @@ private static int CalcNumBuckets(ReadOnlySpan<int> hashCodes, bool hashCodesAre

// Based on our observations, in more than 99.5% of cases the number of buckets that meets our criteria is
// at least twice as big as the number of unique hash codes.
int minNumBuckets = uniqueCodesCount * 2;
// Use long to avoid integer overflow when uniqueCodesCount is large (> ~1 billion).
long minNumBuckets = (long)uniqueCodesCount * 2;

// If the minimum bucket count combined with hash codes exceeds array length limits,
// skip the expensive collision-counting loop below — any bucket count it finds
// would cause Create to fail. Fall back to the next prime above uniqueCodesCount.
#if NET
if (minNumBuckets + hashCodes.Length > Array.MaxLength)
#else
if (minNumBuckets + hashCodes.Length > 0x7FFFFFC7)
#endif
{
return HashHelpers.GetPrime(uniqueCodesCount);
}

// In our precomputed primes table, find the index of the smallest prime that's at least as large as our number of
// hash codes. If there are more codes than in our precomputed primes table, which accommodates millions of values,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ protected static long NextLong(Random random)

public class FrozenDictionary_Generic_Tests_int_int : FrozenDictionary_Generic_Tests_base_for_numbers<int>
{
protected override int Next(Random random) => random.Next();
protected override int Next(Random random) => random.Next();
}

public class FrozenDictionary_Generic_Tests_uint_uint : FrozenDictionary_Generic_Tests_base_for_numbers<uint>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,14 @@ protected override ulong CreateT(int seed)
ulong lo = unchecked((ulong)rand.Next());
return (hi << 32) | lo;
}

[OuterLoop("Takes several seconds")]
[Theory]
[InlineData(8_000_000)]
public void CreateHugeSet_Success(int largeCount)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dictionary one has AllowVeryLargeSizes. Set doesn't have that variable, but it's redundant anyway as in neither case is the fixture subclassed, and the purpose of this I guess is to limit executions of slow tests.

Copy link
Member

@danmoseley danmoseley Mar 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these comments are by Dan.

{
GenericISetFactory(largeCount);
}
}

public class FrozenSet_Generic_Tests_int : FrozenSet_Generic_Tests<int>
Expand Down
Loading