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. 🅿️ `Remove`
|
||||||
1. 🅿️ `RemoveAt`
|
1. 🅿️ `RemoveAt`
|
||||||
1. 🅿️ `RemoveBlock`
|
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. 🅿️ Release 0.0.3-beta
|
||||||
1. 🅿️ Add support for `ulong` indexing
|
1. 🅿️ Add support for `ulong` indexing
|
||||||
1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths
|
1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths
|
||||||
@@ -155,8 +166,8 @@
|
|||||||
1. 🅿️ `RecyclableLongListExtensions.CopyTo`
|
1. 🅿️ `RecyclableLongListExtensions.CopyTo`
|
||||||
1. 🅿️ Cleanup
|
1. 🅿️ Cleanup
|
||||||
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
||||||
1. 🅿️ Cleanup `RecyclableLongListExtensions`
|
1. ✅ Cleanup `RecyclableLongListExtensions`
|
||||||
1. 🅿️ `ListExtensions`
|
1. ✅ `ListExtensions`
|
||||||
1. 🅿️ `MathUtils`
|
1. 🅿️ `MathUtils`
|
||||||
1. 🅿️ Optimize
|
1. 🅿️ Optimize
|
||||||
1. 🅿️ `ListExtensions`
|
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;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T item) => _longCount == 0
|
public int IndexOf(T item) => _longCount == 0
|
||||||
@@ -847,7 +847,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
DoRemoveAt(index);
|
DoRemoveAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||||
|
|
||||||
~RecyclableLongListV1()
|
~RecyclableLongListV1()
|
||||||
{
|
{
|
||||||
@@ -1675,9 +1675,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T item) => _longCount == 0
|
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()
|
~RecyclableLongListV2()
|
||||||
{
|
{
|
||||||
@@ -2638,9 +2638,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T item) => _longCount == 0
|
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()
|
~RecyclableLongListV3()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1068,9 +1068,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T item) => _longCount == 0
|
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()
|
~RecyclableLongListIndexOfV1()
|
||||||
{
|
{
|
||||||
@@ -2213,9 +2213,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T item) => _longCount == 0
|
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()
|
~RecyclableLongListIndexOfV2()
|
||||||
{
|
{
|
||||||
@@ -3330,9 +3330,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T item) => _longCount == 0
|
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()
|
~RecyclableLongListIndexOfV3()
|
||||||
{
|
{
|
||||||
@@ -4484,9 +4484,9 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T item) => _longCount == 0
|
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()
|
~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
|
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 ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
|
||||||
private static readonly bool _defaultIsNull = default(T) == null;
|
private static readonly bool _defaultIsNull = default(T) == null;
|
||||||
@@ -517,124 +517,13 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
_capacity = 0;
|
_capacity = 0;
|
||||||
_memoryBlock.ReturnToPool(_arrayPool, NeedsClearing);
|
if (_count is >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
{
|
||||||
|
_arrayPool.Return(_memoryBlock, NeedsClearing);
|
||||||
|
}
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
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
|
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[]> _defaultMemoryBlocksPool = ArrayPool<T[]>.Create();
|
||||||
private static readonly ArrayPool<T> _defaultBlockArrayPool = 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;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public int IndexOf(T item)
|
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()
|
~RecyclableLongList()
|
||||||
{
|
{
|
||||||
@@ -1318,120 +1318,5 @@ namespace Recyclable.Collections
|
|||||||
GC.SuppressFinalize(this);
|
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()
|
public void AddBlock()
|
||||||
{
|
{
|
||||||
T[] newArray = BlockSize.RentArrayFromPool<T>(_arrayPool);
|
T[] newArray = (BlockSize >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
? _arrayPool.Rent(BlockSize)
|
||||||
|
: new T[BlockSize];
|
||||||
|
|
||||||
Memory.Add(newArray);
|
Memory.Add(newArray);
|
||||||
Capacity += BlockSize;
|
Capacity += BlockSize;
|
||||||
}
|
}
|
||||||
@@ -168,13 +171,13 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(T item) => Memory.Contains(item);
|
public bool Contains(T item) => RecyclableList<T>.RecyclableListHelpers.Contains(Memory, item);
|
||||||
public void CopyTo(T[] array, int arrayIndex) => Memory.CopyTo(RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
public void CopyTo(T[] array, int arrayIndex) => RecyclableList<T>.RecyclableListHelpers.CopyTo(Memory, RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
||||||
public IEnumerable<T> Enumerate() => Memory.Enumerate(BlockSize, LongCount);
|
public IEnumerable<T> Enumerate() => RecyclableList<T>.RecyclableListHelpers.Enumerate(Memory, BlockSize, LongCount);
|
||||||
public IEnumerator<T> GetEnumerator() => Memory.Enumerate(BlockSize, LongCount).GetEnumerator();
|
public IEnumerator<T> GetEnumerator() => RecyclableList<T>.RecyclableListHelpers.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||||
IEnumerator IEnumerable.GetEnumerator() => Memory.Enumerate(BlockSize, LongCount).GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => RecyclableList<T>.RecyclableListHelpers.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||||
public int IndexOf(T item) => (int)LongIndexOf(item);
|
public int IndexOf(T item) => (int)GetRelativeIndex(RecyclableList<T>.RecyclableListHelpers.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
||||||
public long LongIndexOf(T item) => GetRelativeIndex(Memory.LongIndexOf(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 Insert(int index, T item) => throw new NotSupportedException();
|
||||||
public void RemoveAt(int index) => throw new NotSupportedException();
|
public void RemoveAt(int index) => throw new NotSupportedException();
|
||||||
public bool TryDequeue(out T? item) => TryDequeue(this, out item);
|
public bool TryDequeue(out T? item) => TryDequeue(this, out item);
|
||||||
@@ -183,7 +186,11 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Memory[index].ReturnToPool(_arrayPool, NeedsClearing);
|
if (Memory[index].LongLength is > RecyclableDefaults.MinPooledArrayLength and <= int.MaxValue)
|
||||||
|
{
|
||||||
|
_arrayPool.Return(Memory[index], NeedsClearing);
|
||||||
|
}
|
||||||
|
|
||||||
Memory.RemoveAt(index);
|
Memory.RemoveAt(index);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
BeginUpdate();
|
BeginUpdate();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.QuickSort(_comparer, _randomNumberGenerator);
|
RecyclableList<(TKey Key, TValue Value)>.RecyclableListHelpers.QuickSort(this, _comparer, _randomNumberGenerator);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ namespace Recyclable.CollectionsTests
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
using RecyclableList<long> list = new(testData);
|
using RecyclableList<long> list = new(testData);
|
||||||
list.QuickSort();
|
RecyclableList<long>.RecyclableListHelpers.QuickSort(list);
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
_ = list.Count.Should().Be((int)itemsCount);
|
_ = list.Count.Should().Be((int)itemsCount);
|
||||||
|
|||||||
@@ -461,7 +461,7 @@ namespace Recyclable.CollectionsTests
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
using RecyclableLongList<long> list = new(testData, minBlockSize: targetBlockSize);
|
using RecyclableLongList<long> list = new(testData, minBlockSize: targetBlockSize);
|
||||||
list.QuickSort();
|
RecyclableList<long>.RecyclableListHelpers.QuickSort(list);
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
_ = list.LongCount.Should().Be(itemsCount);
|
_ = list.LongCount.Should().Be(itemsCount);
|
||||||
|
|||||||
Reference in new issue
Block a user