From f6640deaf1271f8a7453d4275e518cef6ff12820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Tue, 16 Sep 2025 13:52:42 +0200 Subject: [PATCH] Add IReadOnlyCollection AddRange overload --- .../RecyclablePriorityQueue.AddRange.cs | 51 ++++++++++++++++++ .../RecyclablePriorityQueueTests.cs | 52 +++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs b/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs index 44bdefd..205d7c2 100644 --- a/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs +++ b/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs @@ -1108,6 +1108,53 @@ namespace Recyclable.Collections } } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclablePriorityQueue queue, IReadOnlyCollection items) + { + int count = items.Count; + if (count == 0) + { + return; + } + + int startIndex = queue._size; + int newCount = startIndex + count; + EnsureCapacity(queue, newCount); + + int index = startIndex; + foreach (T item in items) + { + queue._heap[index++] = item; + } + queue._size = newCount; + + int half = newCount >> 1; + for (int i = half - 1; i >= 0; i--) + { + int idx = i; + T current = queue._heap[idx]; + while (idx < half) + { + int child = (idx << 1) + 1; + int right = child + 1; + if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0) + { + child = right; + } + + if (queue._comparer.Compare(queue._heap[child], current) >= 0) + { + break; + } + + queue._heap[idx] = queue._heap[child]; + idx = child; + } + + queue._heap[idx] = current; + } + } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static IEnumerator AddRange(this RecyclablePriorityQueue queue, IEnumerable source) { @@ -1178,6 +1225,10 @@ namespace Recyclable.Collections { AddRange(queue, readOnlyList); } + else if (items is IReadOnlyCollection readOnlyCollection) + { + AddRange(queue, readOnlyCollection); + } else { foreach (T item in items) diff --git a/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs b/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs index ca701ba..d6866ff 100644 --- a/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs +++ b/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Recyclable.Collections; +using System.Collections; namespace Recyclable.CollectionsTests { @@ -406,6 +407,57 @@ namespace Recyclable.CollectionsTests _ = result.Should().Equal(expected); } + [Fact] + public void AddRangeReadOnlyCollectionShouldAddItemsInSortedOrder() + { + var source = new TestReadOnlyCollection(_testData); + using var queue = new RecyclablePriorityQueue(); + + queue.AddRange(source); + + var result = new List(); + while (queue.LongCount > 0) + { + result.Add(queue.Dequeue()); + } + + _ = result.Should().Equal(_testData.Order()); + } + + [Fact] + public void AddRangeReadOnlyCollectionShouldNotOverrideItems() + { + var source = new TestReadOnlyCollection(_testData); + using var queue = new RecyclablePriorityQueue(); + + queue.AddRange(source); + queue.AddRange(source); + + var expected = _testData.Concat(_testData).Order().ToArray(); + var result = new List(); + while (queue.LongCount > 0) + { + result.Add(queue.Dequeue()); + } + + _ = result.Should().Equal(expected); + } + + private sealed class TestReadOnlyCollection : IReadOnlyCollection + { + private readonly T[] _items; + + public TestReadOnlyCollection(T[] items) + { + _items = items; + } + + public int Count => _items.Length; + + public IEnumerator GetEnumerator() => ((IEnumerable)_items).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); + } } }