RecyclableLongListExtensions & ListExtensions cleaned up
This commit is contained in:
1 parent
45edb9d8fb
commit
c5406b04b7
17 files changed
+640
-682
No files matched your search
@@ -93,6 +93,17 @@
|
||||
1. 🅿️ `Remove`
|
||||
1. 🅿️ `RemoveAt`
|
||||
1. 🅿️ `RemoveBlock`
|
||||
1. 🅿️ Add `.ToRecyclableList` / `.ToRecyclableLongList` variants for all supported collection types
|
||||
1. 🅿️ `RecyclableList`
|
||||
1. 🅿️ `RecyclableLongList`
|
||||
1. 🅿️ `IList<T>`
|
||||
1. 🅿️ `ICollection<T>`
|
||||
1. 🅿️ `IEnumerable<T>`
|
||||
1. 🅿️ `IEnumerable`
|
||||
1. 🅿️ `IReadOnlyList<T>`
|
||||
1. 🅿️ `IReadOnlyCollection<T>`
|
||||
1. 🅿️ `IList`
|
||||
1. 🅿️ `ICollection`
|
||||
1. 🅿️ Release 0.0.3-beta
|
||||
1. 🅿️ Add support for `ulong` indexing
|
||||
1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths
|
||||
@@ -155,8 +166,8 @@
|
||||
1. 🅿️ `RecyclableLongListExtensions.CopyTo`
|
||||
1. 🅿️ Cleanup
|
||||
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
||||
1. 🅿️ Cleanup `RecyclableLongListExtensions`
|
||||
1. 🅿️ `ListExtensions`
|
||||
1. ✅ Cleanup `RecyclableLongListExtensions`
|
||||
1. ✅ `ListExtensions`
|
||||
1. 🅿️ `MathUtils`
|
||||
1. 🅿️ Optimize
|
||||
1. 🅿️ `ListExtensions`
|
||||
|
||||
+9
-9
@@ -775,9 +775,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T item) => _longCount == 0
|
||||
@@ -847,7 +847,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
DoRemoveAt(index);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongListV1()
|
||||
{
|
||||
@@ -1675,9 +1675,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T item) => _longCount == 0
|
||||
@@ -1816,7 +1816,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongListV2()
|
||||
{
|
||||
@@ -2638,9 +2638,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T item) => _longCount == 0
|
||||
@@ -2779,7 +2779,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongListV3()
|
||||
{
|
||||
|
||||
@@ -1068,9 +1068,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T item) => _longCount == 0
|
||||
@@ -1217,7 +1217,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongListIndexOfV1()
|
||||
{
|
||||
@@ -2213,9 +2213,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T item) => _longCount == 0
|
||||
@@ -2362,7 +2362,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongListIndexOfV2()
|
||||
{
|
||||
@@ -3330,9 +3330,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T item) => _longCount == 0
|
||||
@@ -3476,7 +3476,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongListIndexOfV3()
|
||||
{
|
||||
@@ -4484,9 +4484,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(T item) => _longCount == 0
|
||||
@@ -4630,7 +4630,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongListIndexOfV4()
|
||||
{
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
// The source code was taken from https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
|
||||
// It was only beautified for .Net 6.0+.
|
||||
internal static class ListExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Contains<T>(this RecyclableList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CopyTo<T>(this RecyclableList<T[]> arrays, long startingIndex, int blockSize, int lastBlockSize, T[] destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
Span<T> arrayMemory = destinationArray.AsSpan();
|
||||
arrayMemory = arrayMemory[destinationArrayIndex..];
|
||||
int startingArrayIndex = (int)(startingIndex / blockSize);
|
||||
startingIndex %= blockSize;
|
||||
if (arrays.Count > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[0].AsSpan();
|
||||
var maxToCopy = (int)Math.Min(blockSize - startingIndex, arrayMemory.Length);
|
||||
if (arrays.Count == 1)
|
||||
{
|
||||
maxToCopy = (int)Math.Min(maxToCopy, lastBlockSize);
|
||||
}
|
||||
|
||||
sourceMemory = sourceMemory[(int)startingIndex..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
for (var arrayIdx = startingArrayIndex + 1; arrayIdx < arrays.Count - 1 && arrayMemory.Length > 0; arrayIdx++)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[arrayIdx].AsSpan();
|
||||
var maxToCopy = Math.Min(blockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
if (arrays.Count > 1 && arrayMemory.Length > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[^1].AsSpan();
|
||||
var maxToCopy = Math.Min(lastBlockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IEnumerable<T> Enumerate<T>(this RecyclableList<T[]> arrays, int chunkSize, long totalCount)
|
||||
{
|
||||
long currentCount = 0;
|
||||
for (var arrayIdx = 0; (arrayIdx < arrays.Count) && (currentCount < totalCount); arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
currentCount += Math.Min(chunkSize, totalCount);
|
||||
switch (currentCount < totalCount)
|
||||
{
|
||||
case true:
|
||||
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
var partialCount = (int)(totalCount % chunkSize);
|
||||
int maxCount = partialCount > 0 ? partialCount : chunkSize;
|
||||
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void InsertionSort<T>(this IList<T> values, int startIndex, int endIndex, IComparer<T> comparer)
|
||||
{
|
||||
var left = startIndex;
|
||||
while (left < endIndex)
|
||||
{
|
||||
int right = left;
|
||||
T? temp = values[++left];
|
||||
while (right >= startIndex && 0 < comparer.Compare(values[right], temp))
|
||||
{
|
||||
values[right + 1] = values[right--];
|
||||
}
|
||||
|
||||
values[right + 1] = temp;
|
||||
}
|
||||
}
|
||||
public static void InsertionSort<T>(this IList<T> values, IComparer<T> comparer)
|
||||
{
|
||||
values.InsertionSort(0, values.Count - 1, comparer);
|
||||
}
|
||||
public static void InsertionSort<T>(this IList<T> values)
|
||||
{
|
||||
values.InsertionSort(Comparer<T>.Default);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LongIndexOf<T>(this RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
|
||||
{
|
||||
for (var arrayIdx = 0; arrayIdx < arrays.Count; arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
Span<T> arrayMemory = array.AsSpan();
|
||||
for (int memoryIdx = 0; memoryIdx < arrayMemory.Length; memoryIdx++)
|
||||
{
|
||||
if (comparer.Equals(arrayMemory[memoryIdx], item))
|
||||
{
|
||||
return (arrayIdx * (long)blockSize) + memoryIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void QuickSort<T>(this IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
var range = (startIndex, endIndex);
|
||||
var stack = new Stack<(int, int)>();
|
||||
|
||||
do
|
||||
{
|
||||
startIndex = range.startIndex;
|
||||
endIndex = range.endIndex;
|
||||
|
||||
if (endIndex - startIndex + 1 < 31)
|
||||
{
|
||||
values.InsertionSort(startIndex, endIndex, comparer);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var pivot = values.SampleMedian(startIndex, endIndex, comparer, randomNumberGenerator);
|
||||
var left = startIndex;
|
||||
var right = endIndex;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
while (0 > comparer.Compare(values[left], pivot))
|
||||
{
|
||||
left++;
|
||||
}
|
||||
|
||||
while (0 > comparer.Compare(pivot, values[right]))
|
||||
{
|
||||
right--;
|
||||
}
|
||||
|
||||
if (left <= right)
|
||||
{
|
||||
values.Swap(left++, right--);
|
||||
}
|
||||
}
|
||||
|
||||
if (startIndex < right)
|
||||
{
|
||||
stack.Push((startIndex, right));
|
||||
}
|
||||
|
||||
if (left < endIndex)
|
||||
{
|
||||
stack.Push((left, endIndex));
|
||||
}
|
||||
}
|
||||
while (stack.TryPop(out range));
|
||||
}
|
||||
public static void QuickSort<T>(this IList<T> values, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
|
||||
=> values.QuickSort(0, values.Count - 1, comparer, randomNumberGenerator);
|
||||
|
||||
public static void QuickSort<T>(this IList<T> values)
|
||||
=> values.QuickSort(Comparer<T>.Default, new SystemRandomNumberGenerator());
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static T SampleMedian<T>(this IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
var left = randomNumberGenerator.NextInt32(startIndex, endIndex);
|
||||
var middle = randomNumberGenerator.NextInt32(startIndex, endIndex);
|
||||
var right = randomNumberGenerator.NextInt32(startIndex, endIndex);
|
||||
|
||||
if (0 > comparer.Compare(values[right], values[left]))
|
||||
{
|
||||
values.Swap(right, left);
|
||||
}
|
||||
|
||||
if (0 > comparer.Compare(values[middle], values[left]))
|
||||
{
|
||||
values.Swap(middle, left);
|
||||
}
|
||||
|
||||
if (0 > comparer.Compare(values[right], values[middle]))
|
||||
{
|
||||
values.Swap(right, middle);
|
||||
}
|
||||
|
||||
return values[middle];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Swap<T>(this IList<T> values, int xIndex, int yIndex)
|
||||
=> (values[yIndex], values[xIndex]) = (values[xIndex], values[yIndex]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public static class RecyclableListExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this IEnumerable<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this T[] values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this List<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this RecyclableList<T> values) => new(values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public partial class RecyclableList<T>
|
||||
{
|
||||
internal static class RecyclableListHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Contains<T>(RecyclableList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CopyTo(RecyclableList<T[]> arrays, long startingIndex, int blockSize, int lastBlockSize, T[] destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
Span<T> arrayMemory = destinationArray.AsSpan();
|
||||
arrayMemory = arrayMemory[destinationArrayIndex..];
|
||||
int startingArrayIndex = (int)(startingIndex / blockSize);
|
||||
startingIndex %= blockSize;
|
||||
if (arrays.Count > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[0].AsSpan();
|
||||
var maxToCopy = (int)Math.Min(blockSize - startingIndex, arrayMemory.Length);
|
||||
if (arrays.Count == 1)
|
||||
{
|
||||
maxToCopy = (int)Math.Min(maxToCopy, lastBlockSize);
|
||||
}
|
||||
|
||||
sourceMemory = sourceMemory[(int)startingIndex..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
for (var arrayIdx = startingArrayIndex + 1; arrayIdx < arrays.Count - 1 && arrayMemory.Length > 0; arrayIdx++)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[arrayIdx].AsSpan();
|
||||
var maxToCopy = Math.Min(blockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
arrayMemory = arrayMemory[maxToCopy..];
|
||||
}
|
||||
|
||||
if (arrays.Count > 1 && arrayMemory.Length > 0)
|
||||
{
|
||||
ReadOnlySpan<T> sourceMemory = arrays[^1].AsSpan();
|
||||
var maxToCopy = Math.Min(lastBlockSize, arrayMemory.Length);
|
||||
sourceMemory = sourceMemory[..maxToCopy];
|
||||
sourceMemory.CopyTo(arrayMemory);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IEnumerable<T> Enumerate(RecyclableList<T[]> arrays, int chunkSize, long totalCount)
|
||||
{
|
||||
long currentCount = 0;
|
||||
for (var arrayIdx = 0; (arrayIdx < arrays.Count) && (currentCount < totalCount); arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
currentCount += Math.Min(chunkSize, totalCount);
|
||||
switch (currentCount < totalCount)
|
||||
{
|
||||
case true:
|
||||
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
var partialCount = (int)(totalCount % chunkSize);
|
||||
int maxCount = partialCount > 0 ? partialCount : chunkSize;
|
||||
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void InsertionSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer)
|
||||
{
|
||||
var left = startIndex;
|
||||
while (left < endIndex)
|
||||
{
|
||||
int right = left;
|
||||
T? temp = values[++left];
|
||||
while (right >= startIndex && 0 < comparer.Compare(values[right], temp))
|
||||
{
|
||||
values[right + 1] = values[right--];
|
||||
}
|
||||
|
||||
values[right + 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public static void InsertionSort(IList<T> values, IComparer<T> comparer)
|
||||
{
|
||||
InsertionSort(values, 0, values.Count - 1, comparer);
|
||||
}
|
||||
|
||||
public static void InsertionSort(IList<T> values)
|
||||
{
|
||||
InsertionSort(values, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LongIndexOf(RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
|
||||
{
|
||||
for (var arrayIdx = 0; arrayIdx < arrays.Count; arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
Span<T> arrayMemory = array.AsSpan();
|
||||
for (int memoryIdx = 0; memoryIdx < arrayMemory.Length; memoryIdx++)
|
||||
{
|
||||
if (comparer.Equals(arrayMemory[memoryIdx], item))
|
||||
{
|
||||
return (arrayIdx * (long)blockSize) + memoryIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The source code was taken from https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
|
||||
// It was only beautified for .Net 6.0+.
|
||||
public static void QuickSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
var range = (startIndex, endIndex);
|
||||
var stack = new Stack<(int, int)>();
|
||||
|
||||
do
|
||||
{
|
||||
startIndex = range.startIndex;
|
||||
endIndex = range.endIndex;
|
||||
|
||||
if (endIndex - startIndex + 1 < 31)
|
||||
{
|
||||
InsertionSort(values, startIndex, endIndex, comparer);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var pivot = SampleMedian(values, startIndex, endIndex, comparer, randomNumberGenerator);
|
||||
var left = startIndex;
|
||||
var right = endIndex;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
while (0 > comparer.Compare(values[left], pivot))
|
||||
{
|
||||
left++;
|
||||
}
|
||||
|
||||
while (0 > comparer.Compare(pivot, values[right]))
|
||||
{
|
||||
right--;
|
||||
}
|
||||
|
||||
if (left <= right)
|
||||
{
|
||||
Swap(values, left++, right--);
|
||||
}
|
||||
}
|
||||
|
||||
if (startIndex < right)
|
||||
{
|
||||
stack.Push((startIndex, right));
|
||||
}
|
||||
|
||||
if (left < endIndex)
|
||||
{
|
||||
stack.Push((left, endIndex));
|
||||
}
|
||||
}
|
||||
while (stack.TryPop(out range));
|
||||
}
|
||||
public static void QuickSort(IList<T> values, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
|
||||
=> QuickSort(values, 0, values.Count - 1, comparer, randomNumberGenerator);
|
||||
|
||||
public static void QuickSort(IList<T> values)
|
||||
=> QuickSort(values, Comparer<T>.Default, new SystemRandomNumberGenerator());
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static T SampleMedian(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
var left = randomNumberGenerator.NextInt32(startIndex, endIndex);
|
||||
var middle = randomNumberGenerator.NextInt32(startIndex, endIndex);
|
||||
var right = randomNumberGenerator.NextInt32(startIndex, endIndex);
|
||||
|
||||
if (0 > comparer.Compare(values[right], values[left]))
|
||||
{
|
||||
Swap(values, right, left);
|
||||
}
|
||||
|
||||
if (0 > comparer.Compare(values[middle], values[left]))
|
||||
{
|
||||
Swap(values, middle, left);
|
||||
}
|
||||
|
||||
if (0 > comparer.Compare(values[right], values[middle]))
|
||||
{
|
||||
Swap(values, right, middle);
|
||||
}
|
||||
|
||||
return values[middle];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Swap(IList<T> values, int xIndex, int yIndex)
|
||||
=> (values[yIndex], values[xIndex]) = (values[xIndex], values[yIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public partial class RecyclableList<T> : IList
|
||||
{
|
||||
public bool IsFixedSize => false;
|
||||
public bool IsSynchronized => false;
|
||||
|
||||
private object? _syncRoot;
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_syncRoot == null)
|
||||
{
|
||||
_ = Interlocked.CompareExchange(ref _syncRoot, new object(), null);
|
||||
}
|
||||
|
||||
return _syncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
object? IList.this[int index]
|
||||
{
|
||||
get => _memoryBlock[index];
|
||||
|
||||
set
|
||||
{
|
||||
if (value is T typedValue)
|
||||
{
|
||||
new Span<T>(_memoryBlock)[index] = typedValue;
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
new Span<T>(_memoryBlock)[index] = default;
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Add(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Add(typeValue);
|
||||
return _count - 1;
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
Add(null);
|
||||
return _count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
return RecyclableDefaults.ItemNotFoundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(object? value) => value is T typeValue
|
||||
? Contains(typeValue)
|
||||
#nullable disable
|
||||
: value == null && _defaultIsNull && Contains(default);
|
||||
#nullable restore
|
||||
|
||||
public int IndexOf(object? value) => value is T typeValue
|
||||
? IndexOf(typeValue)
|
||||
: value == null && _defaultIsNull
|
||||
#nullable disable
|
||||
? IndexOf(default)
|
||||
#nullable restore
|
||||
: RecyclableDefaults.ItemNotFoundIndex;
|
||||
|
||||
public void Insert(int index, object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Insert(index, typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
Insert(index, default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
_ = Remove(typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
_ = Remove(default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index) => Array.Copy(_memoryBlock, 0, array, index, _count);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IList, IDisposable
|
||||
public partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
||||
{
|
||||
private static readonly ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
|
||||
private static readonly bool _defaultIsNull = default(T) == null;
|
||||
@@ -517,124 +517,13 @@ namespace Recyclable.Collections
|
||||
{
|
||||
Clear();
|
||||
_capacity = 0;
|
||||
_memoryBlock.ReturnToPool(_arrayPool, NeedsClearing);
|
||||
if (_count is >= RecyclableDefaults.MinPooledArrayLength)
|
||||
{
|
||||
_arrayPool.Return(_memoryBlock, NeedsClearing);
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
#region IList implementation
|
||||
|
||||
public bool IsFixedSize => false;
|
||||
public bool IsSynchronized => false;
|
||||
|
||||
private object? _syncRoot;
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_syncRoot == null)
|
||||
{
|
||||
_ = Interlocked.CompareExchange(ref _syncRoot, new object(), null);
|
||||
}
|
||||
|
||||
return _syncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
object? IList.this[int index]
|
||||
{
|
||||
get => _memoryBlock[index];
|
||||
|
||||
set
|
||||
{
|
||||
if (value is T typedValue)
|
||||
{
|
||||
new Span<T>(_memoryBlock)[index] = typedValue;
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
new Span<T>(_memoryBlock)[index] = default;
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Add(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Add(typeValue);
|
||||
return _count - 1;
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
Add(null);
|
||||
return _count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
return RecyclableDefaults.ItemNotFoundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(object? value) => value is T typeValue
|
||||
? Contains(typeValue)
|
||||
#nullable disable
|
||||
: value == null && _defaultIsNull && Contains(default);
|
||||
#nullable restore
|
||||
|
||||
public int IndexOf(object? value) => value is T typeValue
|
||||
? IndexOf(typeValue)
|
||||
: value == null && _defaultIsNull
|
||||
#nullable disable
|
||||
? IndexOf(default)
|
||||
#nullable restore
|
||||
: RecyclableDefaults.ItemNotFoundIndex;
|
||||
|
||||
public void Insert(int index, object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Insert(index, typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
Insert(index, default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
_ = Remove( typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
_ = Remove(default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index) => Array.Copy(_memoryBlock, 0, array, index, _count);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public static class RecyclableLongListExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this IList<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this List<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this IEnumerable<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize: minBlockSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public partial class RecyclableLongList<T>
|
||||
{
|
||||
internal class RecyclableLongListHelpers
|
||||
{
|
||||
public static void CopyTo(T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, T[] destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
if (itemsCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<T> sourceBlockMemory;
|
||||
Span<T> destinationArrayMemory;
|
||||
int startingBlockIndex = (int)(startingIndex / blockSize);
|
||||
int lastBlockIndex = (int)((itemsCount / blockSize) + (itemsCount % blockSize > 0 ? 1 : 0)) - 1;
|
||||
Span<T[]> sourceMemoryBlocksSpan = new(sourceMemoryBlocks, startingBlockIndex, lastBlockIndex + 1);
|
||||
destinationArrayMemory = new Span<T>(destinationArray, destinationArrayIndex, (int)Math.Min(destinationArray.Length - destinationArrayIndex, itemsCount));
|
||||
int memoryBlockIndex;
|
||||
for (memoryBlockIndex = 0; memoryBlockIndex < lastBlockIndex; memoryBlockIndex++)
|
||||
{
|
||||
sourceBlockMemory = new(sourceMemoryBlocksSpan[memoryBlockIndex], 0, blockSize);
|
||||
sourceBlockMemory.CopyTo(destinationArrayMemory);
|
||||
destinationArrayMemory = destinationArrayMemory[blockSize..];
|
||||
}
|
||||
|
||||
if (itemsCount - (lastBlockIndex * blockSize) > 0)
|
||||
{
|
||||
sourceBlockMemory = new(sourceMemoryBlocksSpan[lastBlockIndex], 0, (int)(itemsCount - (lastBlockIndex * blockSize)));
|
||||
sourceBlockMemory.CopyTo(destinationArrayMemory);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyTo(T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, Array destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
if (itemsCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int startingBlockIndex = (int)(startingIndex / blockSize);
|
||||
int lastBlockIndex = (int)((itemsCount / blockSize) + (itemsCount % blockSize > 0 ? 1 : 0)) - 1;
|
||||
|
||||
var destinationIndex = destinationArrayIndex;
|
||||
Span<T[]> sourceMemoryBlocksSpan = new(sourceMemoryBlocks, startingBlockIndex, lastBlockIndex + 1);
|
||||
for (int memoryBlockIndex = 0; memoryBlockIndex < lastBlockIndex; memoryBlockIndex++)
|
||||
{
|
||||
Array.ConstrainedCopy(sourceMemoryBlocksSpan[memoryBlockIndex], 0, destinationArray, destinationIndex, blockSize);
|
||||
destinationIndex += blockSize;
|
||||
}
|
||||
|
||||
if (itemsCount - destinationIndex > 0)
|
||||
{
|
||||
Array.ConstrainedCopy(sourceMemoryBlocksSpan[lastBlockIndex], 0, destinationArray, destinationIndex, (int)(itemsCount - destinationIndex));
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Enumerate(T[][] arrays, int chunkSize, long totalCount)
|
||||
{
|
||||
long currentCount = 0;
|
||||
for (var arrayIdx = 0; arrayIdx < arrays.Length && currentCount < totalCount; arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
currentCount += chunkSize;
|
||||
switch (currentCount < totalCount)
|
||||
{
|
||||
case true:
|
||||
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
var partialCount = (int)(totalCount % chunkSize);
|
||||
int maxCount = partialCount > 0 ? partialCount : chunkSize;
|
||||
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public partial class RecyclableLongList<T> : IList
|
||||
{
|
||||
public bool IsFixedSize => false;
|
||||
public bool IsSynchronized => false;
|
||||
|
||||
private object? _syncRoot;
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_syncRoot == null)
|
||||
{
|
||||
_ = Interlocked.CompareExchange(ref _syncRoot, new object(), null);
|
||||
}
|
||||
|
||||
return _syncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
object? IList.this[int index]
|
||||
{
|
||||
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
||||
|
||||
set
|
||||
{
|
||||
if (value is T typedValue)
|
||||
{
|
||||
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = typedValue;
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = default;
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Add(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Add(typeValue);
|
||||
return (int)(_longCount - 1);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
Add(null);
|
||||
return (int)(_longCount - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
return RecyclableDefaults.ItemNotFoundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(object? value) => value is T typeValue
|
||||
? Contains(typeValue)
|
||||
#nullable disable
|
||||
: value == null && _defaultIsNull && Contains(default);
|
||||
#nullable restore
|
||||
|
||||
public int IndexOf(object? value) => value is T typeValue
|
||||
? IndexOf(typeValue)
|
||||
: value == null && _defaultIsNull
|
||||
#nullable disable
|
||||
? IndexOf(default)
|
||||
#nullable restore
|
||||
: RecyclableDefaults.ItemNotFoundIndex;
|
||||
|
||||
public void Insert(int index, object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Insert(index, typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
Insert(index, default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
_ = Remove(typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
_ = Remove(default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, index);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using Recyclable.Collections.Pools;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public partial class RecyclableLongList<T> : IList<T>, IReadOnlyList<T>, IList, IDisposable
|
||||
public partial class RecyclableLongList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
||||
{
|
||||
private static readonly ArrayPool<T[]> _defaultMemoryBlocksPool = ArrayPool<T[]>.Create();
|
||||
private static readonly ArrayPool<T> _defaultBlockArrayPool = ArrayPool<T>.Create();
|
||||
@@ -1073,9 +1073,9 @@ namespace Recyclable.Collections
|
||||
return Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex > 0 ? _nextItemIndex : _blockSize) >= 0;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public int IndexOf(T item)
|
||||
@@ -1296,7 +1296,7 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
|
||||
~RecyclableLongList()
|
||||
{
|
||||
@@ -1318,120 +1318,5 @@ namespace Recyclable.Collections
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
#region IList implementation
|
||||
|
||||
public bool IsFixedSize => false;
|
||||
public bool IsSynchronized => false;
|
||||
|
||||
private object? _syncRoot;
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_syncRoot == null)
|
||||
{
|
||||
_ = Interlocked.CompareExchange(ref _syncRoot, new object(), null);
|
||||
}
|
||||
|
||||
return _syncRoot;
|
||||
}
|
||||
}
|
||||
|
||||
object? IList.this[int index]
|
||||
{
|
||||
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
||||
|
||||
set
|
||||
{
|
||||
if (value is T typedValue)
|
||||
{
|
||||
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = typedValue;
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = default;
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Add(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Add(typeValue);
|
||||
return (int)(_longCount - 1);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
Add(null);
|
||||
return (int)(_longCount - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. { (value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.") }");
|
||||
return RecyclableDefaults.ItemNotFoundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(object? value) => value is T typeValue
|
||||
? Contains(typeValue)
|
||||
#nullable disable
|
||||
: value == null && _defaultIsNull && Contains(default);
|
||||
#nullable restore
|
||||
|
||||
public int IndexOf(object? value) => value is T typeValue
|
||||
? IndexOf(typeValue)
|
||||
: value == null && _defaultIsNull
|
||||
#nullable disable
|
||||
? IndexOf(default)
|
||||
#nullable restore
|
||||
: RecyclableDefaults.ItemNotFoundIndex;
|
||||
|
||||
public void Insert(int index, object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
Insert(index, typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
Insert(index, default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(object? value)
|
||||
{
|
||||
if (value is T typeValue)
|
||||
{
|
||||
_ = Remove(typeValue);
|
||||
}
|
||||
else if (value == null && _defaultIsNull)
|
||||
{
|
||||
#nullable disable
|
||||
_ = Remove(default);
|
||||
#nullable restore
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, index);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
internal static class RecyclableLongListExtensions
|
||||
{
|
||||
private const int _minPooledArraySize = RecyclableDefaults.MinPooledArrayLength;
|
||||
|
||||
public static void CopyTo<T>(this T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, T[] destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
if (itemsCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<T> sourceBlockMemory;
|
||||
Span<T> destinationArrayMemory;
|
||||
int startingBlockIndex = (int)(startingIndex / blockSize);
|
||||
int lastBlockIndex = (int)((itemsCount / blockSize) + (itemsCount % blockSize > 0 ? 1 : 0)) - 1;
|
||||
Span<T[]> sourceMemoryBlocksSpan = new(sourceMemoryBlocks, startingBlockIndex, lastBlockIndex + 1);
|
||||
destinationArrayMemory = new Span<T>(destinationArray, destinationArrayIndex, (int)Math.Min(destinationArray.Length - destinationArrayIndex, itemsCount));
|
||||
int memoryBlockIndex;
|
||||
for (memoryBlockIndex = 0; memoryBlockIndex < lastBlockIndex; memoryBlockIndex++)
|
||||
{
|
||||
sourceBlockMemory = new(sourceMemoryBlocksSpan[memoryBlockIndex], 0, blockSize);
|
||||
sourceBlockMemory.CopyTo(destinationArrayMemory);
|
||||
destinationArrayMemory = destinationArrayMemory[blockSize..];
|
||||
}
|
||||
|
||||
if (itemsCount - (lastBlockIndex * blockSize) > 0)
|
||||
{
|
||||
sourceBlockMemory = new(sourceMemoryBlocksSpan[lastBlockIndex], 0, (int)(itemsCount - (lastBlockIndex * blockSize)));
|
||||
sourceBlockMemory.CopyTo(destinationArrayMemory);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CopyTo<T>(this T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, Array destinationArray, int destinationArrayIndex)
|
||||
{
|
||||
if (itemsCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int startingBlockIndex = (int)(startingIndex / blockSize);
|
||||
int lastBlockIndex = (int)((itemsCount / blockSize) + (itemsCount % blockSize > 0 ? 1 : 0)) - 1;
|
||||
|
||||
var destinationIndex = destinationArrayIndex;
|
||||
Span<T[]> sourceMemoryBlocksSpan = new(sourceMemoryBlocks, startingBlockIndex, lastBlockIndex + 1);
|
||||
for (int memoryBlockIndex = 0; memoryBlockIndex < lastBlockIndex; memoryBlockIndex++)
|
||||
{
|
||||
Array.ConstrainedCopy(sourceMemoryBlocksSpan[memoryBlockIndex], 0, destinationArray, destinationIndex, blockSize);
|
||||
destinationIndex += blockSize;
|
||||
}
|
||||
|
||||
if (itemsCount - destinationIndex > 0)
|
||||
{
|
||||
Array.ConstrainedCopy(sourceMemoryBlocksSpan[lastBlockIndex], 0, destinationArray, destinationIndex, (int)(itemsCount - destinationIndex));
|
||||
}
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public static bool Contains<T>(this RecyclableLongList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
||||
|
||||
public static IEnumerable<T> Enumerate<T>(this T[][] arrays, int chunkSize, long totalCount)
|
||||
{
|
||||
long currentCount = 0;
|
||||
for (var arrayIdx = 0; arrayIdx < arrays.Length && currentCount < totalCount; arrayIdx++)
|
||||
{
|
||||
var array = arrays[arrayIdx];
|
||||
currentCount += chunkSize;
|
||||
switch (currentCount < totalCount)
|
||||
{
|
||||
case true:
|
||||
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
var partialCount = (int)(totalCount % chunkSize);
|
||||
int maxCount = partialCount > 0 ? partialCount : chunkSize;
|
||||
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
|
||||
{
|
||||
yield return array[valueIdx];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public static T LimitTo<T>(this T value, T maxValue, IComparer<T> comparer)
|
||||
// => comparer.Compare(value, maxValue) > 0 ? maxValue : value;
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LongIndexOf<T>(this RecyclableLongList<T> arrays, long itemsCount, T itemToFind, IEqualityComparer<T> comparer)
|
||||
{
|
||||
for (var itemIdx = 0L; itemIdx < itemsCount; itemIdx++)
|
||||
{
|
||||
var item = arrays[itemIdx];
|
||||
if (comparer.Equals(itemToFind, item))
|
||||
{
|
||||
return itemIdx;
|
||||
}
|
||||
|
||||
itemIdx++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static long LongIndexOf<T>(this T[][] arrays, long itemsCount, T itemToFind, IEqualityComparer<T> comparer)
|
||||
{
|
||||
var arraysSpan = new Span<T[]>(arrays);
|
||||
var arraysCount = arraysSpan.Length;
|
||||
for (int arrayIdx = 0, scannedCount = 0; arrayIdx < arraysCount; arrayIdx++)
|
||||
{
|
||||
var itemsSpan = new Span<T>(arraysSpan[arrayIdx]);
|
||||
var arrayItemsCount = itemsSpan.Length;
|
||||
for (var itemIdx = 0; itemIdx < arrayItemsCount; itemIdx++)
|
||||
{
|
||||
if (comparer.Equals(itemToFind, itemsSpan[itemIdx]))
|
||||
{
|
||||
return scannedCount;
|
||||
}
|
||||
|
||||
scannedCount++;
|
||||
if (scannedCount == itemsCount)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this IList<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this List<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this IEnumerable<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this T[] values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this List<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this RecyclableList<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this IEnumerable<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize: minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T[] RentArrayFromPool<T>(this int minSize, ArrayPool<T> arrayPool) => (minSize >= _minPooledArraySize)
|
||||
? arrayPool.Rent(minSize)
|
||||
: new T[minSize];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T[] RentArrayFromPool<T>(this long minSize, ArrayPool<T> arrayPool) => (minSize is >= _minPooledArraySize and <= int.MaxValue)
|
||||
? arrayPool.Rent((int)minSize)
|
||||
: new T[minSize];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ReturnToPool<T>(this T[] array, ArrayPool<T> arrayPool, bool needsClearing)
|
||||
{
|
||||
if (array.LongLength is > _minPooledArraySize and <= int.MaxValue)
|
||||
{
|
||||
arrayPool.Return(array, needsClearing);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long LimitTo(this long value, int limit)
|
||||
=> (value <= limit) ? value : limit;
|
||||
|
||||
public static int IndexOf<T>(this T[] memory, int itemCount, T? itemToFind, IEqualityComparer<T> equalityComparer)
|
||||
{
|
||||
for (var itemIdx = 0; itemIdx < itemCount; itemIdx++)
|
||||
{
|
||||
var item = memory[itemIdx];
|
||||
if (equalityComparer.Equals(item, itemToFind))
|
||||
{
|
||||
return itemIdx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,10 @@ namespace Recyclable.Collections
|
||||
|
||||
public void AddBlock()
|
||||
{
|
||||
T[] newArray = BlockSize.RentArrayFromPool<T>(_arrayPool);
|
||||
T[] newArray = (BlockSize >= RecyclableDefaults.MinPooledArrayLength)
|
||||
? _arrayPool.Rent(BlockSize)
|
||||
: new T[BlockSize];
|
||||
|
||||
Memory.Add(newArray);
|
||||
Capacity += BlockSize;
|
||||
}
|
||||
@@ -168,13 +171,13 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(T item) => Memory.Contains(item);
|
||||
public void CopyTo(T[] array, int arrayIndex) => Memory.CopyTo(RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
||||
public IEnumerable<T> Enumerate() => Memory.Enumerate(BlockSize, LongCount);
|
||||
public IEnumerator<T> GetEnumerator() => Memory.Enumerate(BlockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => Memory.Enumerate(BlockSize, LongCount).GetEnumerator();
|
||||
public int IndexOf(T item) => (int)LongIndexOf(item);
|
||||
public long LongIndexOf(T item) => GetRelativeIndex(Memory.LongIndexOf(BlockSize, item, _equalityComparer));
|
||||
public bool Contains(T item) => RecyclableList<T>.RecyclableListHelpers.Contains(Memory, item);
|
||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableList<T>.RecyclableListHelpers.CopyTo(Memory, RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
||||
public IEnumerable<T> Enumerate() => RecyclableList<T>.RecyclableListHelpers.Enumerate(Memory, BlockSize, LongCount);
|
||||
public IEnumerator<T> GetEnumerator() => RecyclableList<T>.RecyclableListHelpers.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableList<T>.RecyclableListHelpers.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||
public int IndexOf(T item) => (int)GetRelativeIndex(RecyclableList<T>.RecyclableListHelpers.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
||||
public long LongIndexOf(T item) => GetRelativeIndex(RecyclableList<T>.RecyclableListHelpers.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
||||
public void Insert(int index, T item) => throw new NotSupportedException();
|
||||
public void RemoveAt(int index) => throw new NotSupportedException();
|
||||
public bool TryDequeue(out T? item) => TryDequeue(this, out item);
|
||||
@@ -183,7 +186,11 @@ namespace Recyclable.Collections
|
||||
{
|
||||
try
|
||||
{
|
||||
Memory[index].ReturnToPool(_arrayPool, NeedsClearing);
|
||||
if (Memory[index].LongLength is > RecyclableDefaults.MinPooledArrayLength and <= int.MaxValue)
|
||||
{
|
||||
_arrayPool.Return(Memory[index], NeedsClearing);
|
||||
}
|
||||
|
||||
Memory.RemoveAt(index);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
BeginUpdate();
|
||||
try
|
||||
{
|
||||
this.QuickSort(_comparer, _randomNumberGenerator);
|
||||
RecyclableList<(TKey Key, TValue Value)>.RecyclableListHelpers.QuickSort(this, _comparer, _randomNumberGenerator);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -419,7 +419,7 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
// Act
|
||||
using RecyclableList<long> list = new(testData);
|
||||
list.QuickSort();
|
||||
RecyclableList<long>.RecyclableListHelpers.QuickSort(list);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
|
||||
@@ -461,7 +461,7 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
// Act
|
||||
using RecyclableLongList<long> list = new(testData, minBlockSize: targetBlockSize);
|
||||
list.QuickSort();
|
||||
RecyclableList<long>.RecyclableListHelpers.QuickSort(list);
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
|
||||
Reference in new issue
Block a user