Files
Recyclable.Collections/Recyclable.Collections/RecyclableHashSet.AddRange.cs
T

1081 lines
35 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Numerics;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
internal static class zRecyclableHashSetAddRange
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ref RecyclableHashSet<T>.Entry GetEntry<T>(RecyclableHashSet<T> set, int index)
where T : notnull
{
return ref set._entries[index >> set._blockShift][index & set._blockSizeMinus1];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void EnsureCapacity<T>(RecyclableHashSet<T> set, int min)
where T : notnull
{
int requiredBlocks = (min + set._blockSizeMinus1) >> set._blockShift;
if (set._entries.Length < requiredBlocks)
{
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<RecyclableHashSet<T>.Entry[]>.RentShared(requiredBlocks)
: new RecyclableHashSet<T>.Entry[requiredBlocks][];
Array.Copy(set._entries, newBlocks, set._entries.Length);
if (set._entries.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<RecyclableHashSet<T>.Entry[]>.ReturnShared(set._entries, false);
}
set._entries = newBlocks;
}
for (int i = 0; i < requiredBlocks; i++)
{
if (set._entries[i] == null)
{
set._entries[i] = set._blockSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<RecyclableHashSet<T>.Entry>.RentShared(set._blockSize)
: new RecyclableHashSet<T>.Entry[set._blockSize];
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ResizeBuckets<T>(RecyclableHashSet<T> set, int newSize)
where T : notnull
{
int[] newBuckets = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<int>.RentShared(newSize)
: new int[newSize];
Array.Fill(newBuckets, -1);
for (int i = 0; i < set._count; i++)
{
ref var entry = ref GetEntry(set, i);
int bucket = entry.HashCode & (newSize - 1);
entry.Next = newBuckets[bucket];
newBuckets[bucket] = i;
}
if (set._buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<int>.ReturnShared(set._buckets, false);
}
set._buckets = newBuckets;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, ReadOnlySpan<T> items)
where T : notnull
{
int count = items.Length;
if (count == 0)
{
return;
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
for (int i = 0; i < count; i++)
{
T value = items[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, Span<T> items)
where T : notnull
{
int count = items.Length;
if (count == 0)
{
return;
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
for (int i = 0; i < count; i++)
{
T value = items[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, T[] items)
where T : notnull
{
int count = items.Length;
if (count == 0)
{
return;
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
for (int i = 0; i < count; i++)
{
T value = items[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, in Array items)
where T : notnull
{
int count = items.Length;
if (count == 0)
{
return;
}
T[] buffer;
int lower = items.GetLowerBound(0);
if (items is T[] array && lower == 0)
{
buffer = array;
}
else
{
buffer = count >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)count)))
: new T[count];
Array.Copy(items, lower, buffer, 0, count);
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
for (int i = 0; i < count; i++)
{
T value = buffer[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
if (!ReferenceEquals(buffer, items) && count >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(buffer, RecyclableHashSet<T>._needsClearing);
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, List<T> items)
where T : notnull
{
int count = items.Count;
if (count == 0)
{
return;
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
Span<T> span = CollectionsMarshal.AsSpan(items);
for (int i = 0; i < count; i++)
{
T value = span[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, ICollection items)
where T : notnull
{
int count = items.Count;
if (count == 0)
{
return;
}
T[] buffer = count >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)count)))
: new T[count];
items.CopyTo(buffer, 0);
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
for (int i = 0; i < count; i++)
{
T value = buffer[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
if (count >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(buffer, RecyclableHashSet<T>._needsClearing);
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, ICollection<T> items)
where T : notnull
{
int count = items.Count;
if (count == 0)
{
return;
}
T[] buffer = count >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)count)))
: new T[count];
items.CopyTo(buffer, 0);
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
for (int i = 0; i < count; i++)
{
T value = buffer[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
if (count >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(buffer, RecyclableHashSet<T>._needsClearing);
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, RecyclableList<T> items)
where T : notnull
{
int count = items._count;
if (count == 0)
{
return;
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
T[] source = items._memoryBlock;
for (int i = 0; i < count; i++)
{
T value = source[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, RecyclableHashSet<T> items)
where T : notnull
{
int count = items._count;
if (count == 0)
{
return;
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
var entries = items._entries;
int shift = items._blockShift,
mask = items._blockSizeMinus1;
for (int i = 0; i < count; i++)
{
T value = entries[i >> shift][i & mask].Value;
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, RecyclableLongList<T> items)
where T : notnull
{
long longCount = items._longCount;
if (longCount == 0)
{
return;
}
if (longCount > int.MaxValue - set._count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items exceeds {int.MaxValue}");
}
int count = (int)longCount;
int fullBlocks = (int)(longCount >> items._blockSizePow2BitShift);
int lastBlockLength = (int)(longCount & items._blockSizeMinus1);
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
var blocks = items._memoryBlocks;
int blockSize = items._blockSize;
for (int block = 0; block < fullBlocks; block++)
{
var source = blocks[block];
for (int j = 0; j < blockSize; j++)
{
T value = source[j];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
}
if (lastBlockLength > 0)
{
var source = blocks[fullBlocks];
for (int j = 0; j < lastBlockLength; j++)
{
T value = source[j];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, RecyclableStack<T> items)
where T : notnull
{
long longCount = items._count;
if (longCount == 0)
{
return;
}
if (longCount > int.MaxValue - set._count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items exceeds {int.MaxValue}");
}
int count = (int)longCount;
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
var sourceChunk = items._current;
while (sourceChunk.Previous != null)
{
sourceChunk = sourceChunk.Previous;
}
while (sourceChunk != null)
{
var source = sourceChunk.Value;
int sourceCount = sourceChunk.Index;
for (int i = 0; i < sourceCount; i++)
{
T value = source[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int idx = set._buckets[bucket];
bool exists = false;
while (idx >= 0)
{
ref var check = ref GetEntry(set, idx);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
idx = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
sourceChunk = sourceChunk.Next!;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, RecyclableQueue<T> items)
where T : notnull
{
long longCount = items._count;
if (longCount == 0)
{
return;
}
if (longCount > int.MaxValue - set._count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items exceeds {int.MaxValue}");
}
int count = (int)longCount;
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
var chunk = items._head;
while (chunk != null)
{
int idx = chunk.Bottom;
int top = chunk.Top;
var source = chunk.Value;
while (idx < top)
{
T value = source[idx++];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int checkIndex = set._buckets[bucket];
bool exists = false;
while (checkIndex >= 0)
{
ref var check = ref GetEntry(set, checkIndex);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
checkIndex = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
chunk = chunk.Next;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, IReadOnlyList<T> items)
where T : notnull
{
int count = items.Count;
if (count == 0)
{
return;
}
int requiredCount = set._count + count;
EnsureCapacity(set, requiredCount);
int bucketsLength = set._buckets.Length;
while (requiredCount >= (bucketsLength * 3) / 4)
{
bucketsLength <<= 1;
}
if (bucketsLength != set._buckets.Length)
{
ResizeBuckets(set, bucketsLength);
}
int bucketMask = set._buckets.Length - 1;
int insertIndex = set._count;
for (int i = 0; i < count; i++)
{
T value = items[i];
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
insertIndex++;
}
set._count = insertIndex;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableHashSet<T> set, IEnumerable<T> items)
where T : notnull
{
foreach (T value in items)
{
int requiredCount = set._count + 1;
if (requiredCount > set._entries.Length * set._blockSize)
{
EnsureCapacity(set, requiredCount);
}
if (set._count >= (set._buckets.Length * 3) / 4)
{
ResizeBuckets(set, set._buckets.Length << 1);
}
int hash = value?.GetHashCode() & int.MaxValue ?? 0;
int bucketMask = set._buckets.Length - 1;
int bucket = hash & bucketMask;
int index = set._buckets[bucket];
bool exists = false;
while (index >= 0)
{
ref var check = ref GetEntry(set, index);
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, value))
{
exists = true;
break;
}
index = check.Next;
}
if (exists)
{
continue;
}
int insertIndex = set._count++;
ref var entry = ref GetEntry(set, insertIndex);
entry.HashCode = hash;
entry.Value = value!;
entry.Next = set._buckets[bucket];
set._buckets[bucket] = insertIndex;
}
}
}
}