Add additional AddRange overloads for hash set
This commit is contained in:
1 parent
1dc6c479ae
commit
3dea509958
2 files changed
+269
No files matched your search
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -262,6 +263,90 @@ namespace Recyclable.Collections
|
|||||||
set._count = 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)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
internal static void AddRange<T>(this RecyclableHashSet<T> set, List<T> items)
|
internal static void AddRange<T>(this RecyclableHashSet<T> set, List<T> items)
|
||||||
where T : notnull
|
where T : notnull
|
||||||
@@ -326,6 +411,154 @@ namespace Recyclable.Collections
|
|||||||
set._count = 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)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
internal static void AddRange<T>(this RecyclableHashSet<T> set, RecyclableList<T> items)
|
internal static void AddRange<T>(this RecyclableHashSet<T> set, RecyclableList<T> items)
|
||||||
where T : notnull
|
where T : notnull
|
||||||
|
|||||||
@@ -161,5 +161,41 @@ namespace Recyclable.CollectionsTests
|
|||||||
_ = set.Should().Contain(new[] { 1, 2, 3 });
|
_ = set.Should().Contain(new[] { 1, 2, 3 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeShouldAddItemsFromArray()
|
||||||
|
{
|
||||||
|
using var set = new RecyclableHashSet<int>();
|
||||||
|
Array array = new int[] { 1, 2, 3 };
|
||||||
|
|
||||||
|
set.AddRange(array);
|
||||||
|
|
||||||
|
_ = set.Count.Should().Be(3);
|
||||||
|
_ = set.Should().Contain(new[] { 1, 2, 3 });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeShouldAddItemsFromICollection()
|
||||||
|
{
|
||||||
|
using var set = new RecyclableHashSet<int>();
|
||||||
|
System.Collections.ArrayList list = new System.Collections.ArrayList { 1, 2, 3 };
|
||||||
|
|
||||||
|
set.AddRange(list);
|
||||||
|
|
||||||
|
_ = set.Count.Should().Be(3);
|
||||||
|
_ = set.Should().Contain(new[] { 1, 2, 3 });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeShouldAddItemsFromGenericICollection()
|
||||||
|
{
|
||||||
|
using var set = new RecyclableHashSet<int>();
|
||||||
|
ICollection<int> list = new List<int> { 1, 2, 3 };
|
||||||
|
|
||||||
|
set.AddRange(list);
|
||||||
|
|
||||||
|
_ = set.Count.Should().Be(3);
|
||||||
|
_ = set.Should().Contain(new[] { 1, 2, 3 });
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user