Add more AddRange overloads for RecyclableHashSet

This commit is contained in:
Marek Lemańczyk committed 2025-07-17 15:36:49 +02:00
1 parent 12b01c32ba
commit 1dc6c479ae
2 files changed
+311

No files matched your search

@@ -454,6 +454,281 @@ namespace Recyclable.Collections
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;
}
@@ -125,5 +125,41 @@ namespace Recyclable.CollectionsTests
_ = set.Should().Contain(new[] { 1, 2, 3 });
}
[Fact]
public void AddRangeShouldAddItemsFromLongList()
{
using var set = new RecyclableHashSet<int>();
using var list = new RecyclableLongList<int>(new[] { 1, 2, 3 });
set.AddRange(list);
_ = set.Count.Should().Be(3);
_ = set.Should().Contain(new[] { 1, 2, 3 });
}
[Fact]
public void AddRangeShouldAddItemsFromStack()
{
using var set = new RecyclableHashSet<int>();
using var stack = new RecyclableStack<int>(new[] { 1, 2, 3 });
set.AddRange(stack);
_ = set.Count.Should().Be(3);
_ = set.Should().Contain(new[] { 1, 2, 3 });
}
[Fact]
public void AddRangeShouldAddItemsFromQueue()
{
using var set = new RecyclableHashSet<int>();
using var queue = new RecyclableQueue<int>(new[] { 1, 2, 3 });
set.AddRange(queue);
_ = set.Count.Should().Be(3);
_ = set.Should().Contain(new[] { 1, 2, 3 });
}
}
}