Optimize sorted-list and read-only list AddRange

This commit is contained in:
Marek Lemańczyk committed 2025-07-17 00:21:45 +02:00
1 parent aacfed4e7e
commit 56a5c3375a
2 files changed
+137 -4

No files matched your search

@@ -798,19 +798,97 @@ namespace Recyclable.Collections
internal static void AddRange<TKey, TValue>(this RecyclableStack<(TKey Key, TValue Value)> stack, RecyclableSortedList<TKey, TValue> items)
where TKey : notnull
{
for (int i = 0; i < items._count; i++)
int count = items._count;
if (count == 0)
{
stack.Push((items._keys[i], items._values[i]));
return;
}
EnsureCapacity(stack, stack._count + count);
RecyclableArrayPoolChunk<(TKey Key, TValue Value)> chunk = stack._current;
while (chunk.Previous != null && chunk.Previous.Index < chunk.Previous.Value.Length)
{
chunk = chunk.Previous;
}
int index = chunk.Index;
int chunkLength = chunk.Value.Length;
int copied = 0;
TKey[] keys = items._keys;
TValue[] values = items._values;
while (copied < count)
{
int toCopy = Math.Min(chunkLength - index, count - copied);
var destination = chunk.Value;
int local = copied;
for (int i = 0; i < toCopy; i++)
{
destination[index + i] = (keys[local + i], values[local + i]);
}
copied += toCopy;
index += toCopy;
if (index == chunkLength && copied < count)
{
chunk.Index = index;
chunk = chunk.Next!;
index = 0;
chunkLength = chunk.Value.Length;
}
}
chunk.Index = index;
stack._count += count;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
internal static void AddRange<T>(this RecyclableStack<T> stack, IReadOnlyList<T> items)
{
for (int i = 0; i < items.Count; i++)
int count = items.Count;
if (count == 0)
{
stack.Push(items[i]);
return;
}
EnsureCapacity(stack, stack._count + count);
RecyclableArrayPoolChunk<T> chunk = stack._current;
while (chunk.Previous != null && chunk.Previous.Index < chunk.Previous.Value.Length)
{
chunk = chunk.Previous;
}
int index = chunk.Index;
int chunkLength = chunk.Value.Length;
int copied = 0;
while (copied < count)
{
int toCopy = Math.Min(chunkLength - index, count - copied);
var destination = chunk.Value;
int local = copied;
for (int i = 0; i < toCopy; i++)
{
destination[index + i] = items[local + i];
}
copied += toCopy;
index += toCopy;
if (index == chunkLength && copied < count)
{
chunk.Index = index;
chunk = chunk.Next!;
index = 0;
chunkLength = chunk.Value.Length;
}
}
chunk.Index = index;
stack._count += count;
}
internal static IEnumerator AddRange<T>(this RecyclableStack<T> stack, IEnumerable source)
@@ -703,5 +703,60 @@ namespace Recyclable.CollectionsTests
_ = stack.Should().Equal(expected);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
public void AddRangeShouldAddSortedListItemsInCorrectOrder(int itemsCount)
{
long[] data = RecyclableLongListTestData.CreateTestData(itemsCount).ToArray();
using var source = new RecyclableSortedList<int, long>();
for (int i = 0; i < data.Length; i++)
{
source.Add(i, data[i]);
}
using var stack = new RecyclableStack<(int Key, long Value)>();
stack.AddRange(source);
List<(int Key, long Value)> expected = new(itemsCount);
for (int i = 0; i < data.Length; i++)
{
expected.Add((i, data[i]));
}
expected.Reverse();
_ = stack.Count.Should().Be(itemsCount);
_ = stack.Should().Equal(expected);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
public void AddRangeShouldNotOverrideItemsFromSortedList(int itemsCount)
{
long[] data = RecyclableLongListTestData.CreateTestData(itemsCount).ToArray();
using var source = new RecyclableSortedList<int, long>();
for (int i = 0; i < data.Length; i++)
{
source.Add(i, data[i]);
}
using var stack = new RecyclableStack<(int Key, long Value)>();
stack.AddRange(source);
stack.AddRange(source);
List<(int Key, long Value)> expected = new(itemsCount * 2);
for (int i = 0; i < data.Length; i++)
{
expected.Add((i, data[i]));
}
for (int i = 0; i < data.Length; i++)
{
expected.Add((i, data[i]));
}
expected.Reverse();
_ = stack.Count.Should().Be(itemsCount * 2);
_ = stack.Should().Equal(expected);
}
}
}