Unit tests extended. Missing .ToRecyclableList() & .ToRecyclableLongList() variants added. Support for all major interfaces finished. README.md updated.
This commit is contained in:
1 parent
9cd2096783
commit
a315ff80d5
8 files changed
+791
-183
No files matched your search
@@ -126,18 +126,19 @@
|
||||
1. ✅ `.Remove` / `.RemoveAt` / `.Clear` / `.Dispose` must clear reference when reference type
|
||||
1. ✅ `.Clear` / `.Dispose` must return arrays to pools
|
||||
1. ✅ `int` / `long` variants of `.Count` / `.LongCount`, `.IndexOf` / `.LongIndexOf`, `.Insert`, `.RemoveAt`, `this[]`
|
||||
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.5
|
||||
1. ✅ Add `.ToRecyclableList` / `.ToRecyclableLongList` variants for all supported collection types
|
||||
1. ✅ `Array`
|
||||
1. ✅ `ICollection`
|
||||
1. ✅ `ICollection<T>`
|
||||
1. ✅ `IEnumerable`
|
||||
1. ✅ `IEnumerable<T>`
|
||||
1. ✅ `IReadOnlyList<T>`
|
||||
1. ✅ `ReadOnlySpan<T>`
|
||||
1. ✅ `RecyclableList`
|
||||
1. ✅ `RecyclableLongList`
|
||||
1. ✅ `Span<T>`
|
||||
1. ✅ `T[]`
|
||||
1. 👉 Release 0.0.5
|
||||
1. 🅿️ Add support for `ulong` indexing
|
||||
1. 🅿️ Convert `_memoryBlocks` to `Array` to allow `ulong` lengths
|
||||
1. 🅿️ Convert block indexes from `int` to `ulong` or `long`
|
||||
|
||||
@@ -1,19 +1,44 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public static class RecyclableListExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this Array values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this ICollection values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this ICollection<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this IEnumerable values) => new(values);
|
||||
|
||||
[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);
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this IReadOnlyList<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 ReadOnlySpan<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this RecyclableList<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this RecyclableLongList<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this Span<T> values) => new(values);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableList<T> ToRecyclableList<T>(this T[] values) => new(values);
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,13 @@ namespace Recyclable.Collections
|
||||
internal ulong _version;
|
||||
public ulong Version => _version;
|
||||
|
||||
private IEnumerator<T> AddRangeEnumerated(IEnumerable<T> source, int growByCount)
|
||||
private void AddRangeEnumerated(IEnumerable<T> items, int growByCount)
|
||||
{
|
||||
int targetItemIndex = _count;
|
||||
Span<T> memorySpan;
|
||||
|
||||
int i;
|
||||
var enumerator = source.GetEnumerator();
|
||||
var enumerator = items.GetEnumerator();
|
||||
|
||||
int capacity = _capacity;
|
||||
memorySpan = new(_memoryBlock);
|
||||
@@ -55,15 +55,14 @@ namespace Recyclable.Collections
|
||||
|
||||
_capacity = capacity;
|
||||
_count = targetItemIndex;
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
private void AddRangeWithKnownCount(IEnumerable<T> source, int currentItemsCount, int requiredAdditionalCapacity)
|
||||
private void AddRangeWithKnownCount(IEnumerable<T> items, int currentItemsCount, int requiredAdditionalCapacity)
|
||||
{
|
||||
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity))));
|
||||
|
||||
Span<T> memorySpan = new(_memoryBlock);
|
||||
foreach (var item in source)
|
||||
foreach (var item in items)
|
||||
{
|
||||
memorySpan[currentItemsCount++] = item;
|
||||
}
|
||||
@@ -93,6 +92,30 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IReadOnlyList<T> items)
|
||||
{
|
||||
int sourceItemsCount = items.Count;
|
||||
if (_capacity < _count + sourceItemsCount)
|
||||
{
|
||||
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + sourceItemsCount))));
|
||||
}
|
||||
|
||||
Span<T> memorySpan = new(_memoryBlock, _count, sourceItemsCount);
|
||||
for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++)
|
||||
{
|
||||
memorySpan[sourceItemIndex] = items[sourceItemIndex];
|
||||
}
|
||||
|
||||
_count += sourceItemsCount;
|
||||
}
|
||||
|
||||
public RecyclableList(in Array source)
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(RecyclableList<T> source)
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
@@ -121,6 +144,13 @@ namespace Recyclable.Collections
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(IReadOnlyList<T> source)
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(List<T> source)
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
@@ -128,13 +158,38 @@ namespace Recyclable.Collections
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(IList<T> source)
|
||||
public RecyclableList(ICollection source)
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(ICollection<T> source)
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(IEnumerable source, int initialCapacity = RecyclableDefaults.InitialCapacity)
|
||||
{
|
||||
if (initialCapacity >= RecyclableDefaults.InitialCapacity)
|
||||
{
|
||||
_memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
||||
? RecyclableArrayPool<T>.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity)))
|
||||
: new T[initialCapacity];
|
||||
_capacity = _memoryBlock.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
|
||||
}
|
||||
|
||||
AddRange(source, RecyclableDefaults.MinPooledArrayLength);
|
||||
}
|
||||
|
||||
public RecyclableList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity)
|
||||
{
|
||||
if (initialCapacity >= RecyclableDefaults.InitialCapacity)
|
||||
@@ -153,6 +208,13 @@ namespace Recyclable.Collections
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableList(Span<T> source)
|
||||
{
|
||||
_capacity = RecyclableDefaults.InitialCapacity;
|
||||
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -197,6 +259,19 @@ namespace Recyclable.Collections
|
||||
_version++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(in Array items)
|
||||
{
|
||||
if (_capacity < _count + items.Length)
|
||||
{
|
||||
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Length))));
|
||||
}
|
||||
|
||||
Array.Copy(items, items.GetLowerBound(0), _memoryBlock, _count, items.LongLength);
|
||||
_count += items.Length;
|
||||
_version++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(in T[] items)
|
||||
{
|
||||
@@ -223,6 +298,19 @@ namespace Recyclable.Collections
|
||||
_version++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(Span<T> items)
|
||||
{
|
||||
if (_capacity < _count + items.Length)
|
||||
{
|
||||
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Length))));
|
||||
}
|
||||
|
||||
items.CopyTo(new Span<T>(_memoryBlock, _count, items.Length));
|
||||
_count += items.Length;
|
||||
_version++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(List<T> items)
|
||||
{
|
||||
@@ -237,7 +325,20 @@ namespace Recyclable.Collections
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(IList<T> items)
|
||||
public void AddRange(ICollection items)
|
||||
{
|
||||
if (_capacity < _count + items.Count)
|
||||
{
|
||||
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Count))));
|
||||
}
|
||||
|
||||
items.CopyTo(_memoryBlock, _count);
|
||||
_count += items.Count;
|
||||
_version++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(ICollection<T> items)
|
||||
{
|
||||
if (_capacity < _count + items.Count)
|
||||
{
|
||||
@@ -313,24 +414,79 @@ namespace Recyclable.Collections
|
||||
_version++;
|
||||
}
|
||||
|
||||
public IEnumerator AddRange(IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||
{
|
||||
int targetItemIndex = _count;
|
||||
Span<T> memorySpan;
|
||||
|
||||
int i;
|
||||
var enumerator = source.GetEnumerator();
|
||||
|
||||
int capacity = _capacity;
|
||||
memorySpan = new(_memoryBlock);
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
int available = capacity - targetItemIndex;
|
||||
do
|
||||
{
|
||||
if (targetItemIndex + growByCount > capacity)
|
||||
{
|
||||
capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
|
||||
memorySpan = new(_memoryBlock);
|
||||
available = capacity - targetItemIndex;
|
||||
}
|
||||
|
||||
for (i = 0; i < available; i++)
|
||||
{
|
||||
memorySpan[targetItemIndex++] = (T)enumerator.Current;
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (i >= available);
|
||||
}
|
||||
|
||||
_capacity = capacity;
|
||||
_count = targetItemIndex;
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddRange(IEnumerable<T> items, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||
{
|
||||
if (items is T[] sourceArray)
|
||||
if (items is RecyclableList<T> sourceRecyclableList)
|
||||
{
|
||||
AddRange(sourceRecyclableList);
|
||||
}
|
||||
else if (items is RecyclableLongList<T> sourceRecyclableLongList)
|
||||
{
|
||||
AddRange(sourceRecyclableLongList);
|
||||
}
|
||||
else if (items is T[] sourceArray)
|
||||
{
|
||||
AddRange(sourceArray);
|
||||
}
|
||||
else if (items is Array sourceArrayWithObjects)
|
||||
{
|
||||
AddRange(sourceArrayWithObjects);
|
||||
}
|
||||
else if (items is List<T> sourceList)
|
||||
{
|
||||
AddRange(sourceList);
|
||||
}
|
||||
else if (items is RecyclableList<T> sourceRecyclableList)
|
||||
else if (items is ICollection<T> sourceICollection)
|
||||
{
|
||||
AddRange(sourceRecyclableList);
|
||||
AddRange(sourceICollection);
|
||||
}
|
||||
else if (items is IList<T> sourceIList)
|
||||
else if (items is ICollection sourceICollectionWithObjects)
|
||||
{
|
||||
AddRange(sourceIList);
|
||||
AddRange(sourceICollectionWithObjects);
|
||||
}
|
||||
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
|
||||
{
|
||||
AddRange(sourceIReadOnlyList);
|
||||
}
|
||||
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
|
||||
{
|
||||
|
||||
@@ -1,19 +1,44 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
{
|
||||
public static class RecyclableLongListExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this Array values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this Array values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this IList<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this ICollection values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this List<T> values, int blockSize = RecyclableDefaults.BlockSize) => new(values, blockSize);
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this ICollection<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this IEnumerable values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize: minBlockSize);
|
||||
|
||||
[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 RecyclableLongList<T> ToRecyclableLongList<T>(this IReadOnlyList<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this List<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this ReadOnlySpan<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this RecyclableList<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this RecyclableLongList<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this Span<T> values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this T[] values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||
}
|
||||
}
|
||||
@@ -60,12 +60,12 @@ namespace Recyclable.Collections
|
||||
public int ReservedBlockCount => _reservedBlockCount;
|
||||
public ulong Version => _version;
|
||||
|
||||
private void AddRangeEnumerated(IEnumerable<T> source)
|
||||
private void AddRangeEnumerated(IEnumerable<T> items)
|
||||
{
|
||||
long i, copied = _longCount;
|
||||
int blockSize = _blockSize, targetItemIdx = _nextItemIndex, targetBlockIdx = _nextItemBlockIndex;
|
||||
|
||||
using IEnumerator<T> enumerator = source.GetEnumerator();
|
||||
using IEnumerator<T> enumerator = items.GetEnumerator();
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
return;
|
||||
@@ -134,7 +134,7 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRangeWithKnownCount(IEnumerable<T> source, int requiredAdditionalCapacity)
|
||||
private void AddRangeWithKnownCount(IEnumerable<T> items, int requiredAdditionalCapacity)
|
||||
{
|
||||
long copied = _longCount;
|
||||
int blockSize = _blockSize;
|
||||
@@ -158,7 +158,7 @@ namespace Recyclable.Collections
|
||||
var memoryBlocksCount = _reservedBlockCount;
|
||||
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
|
||||
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
|
||||
foreach (var item in source)
|
||||
foreach (var item in items)
|
||||
{
|
||||
blockArraySpan[targetItemIdx++] = item;
|
||||
if (targetItemIdx == blockSize)
|
||||
@@ -199,139 +199,126 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
public RecyclableLongList(in T[] source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
public RecyclableLongList(in T[] source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
if (expectedItemsCount > 0)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)expectedItemsCount.Value)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_blockSizeMinus1 = _blockSize - 1;
|
||||
_memoryBlocks = _emptyMemoryBlocksArray;
|
||||
}
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(in Array source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
public RecyclableLongList(in Array source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
if (expectedItemsCount > 0)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)expectedItemsCount.Value)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_blockSizeMinus1 = _blockSize - 1;
|
||||
_memoryBlocks = _emptyMemoryBlocksArray;
|
||||
}
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(ReadOnlySpan<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
public RecyclableLongList(IReadOnlyList<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
if (expectedItemsCount > 0)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)expectedItemsCount.Value)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_blockSizeMinus1 = _blockSize - 1;
|
||||
_memoryBlocks = _emptyMemoryBlocksArray;
|
||||
}
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(RecyclableList<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
public RecyclableLongList(ReadOnlySpan<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
if (expectedItemsCount > 0)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)expectedItemsCount.Value)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_blockSizeMinus1 = _blockSize - 1;
|
||||
_memoryBlocks = _emptyMemoryBlocksArray;
|
||||
}
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(RecyclableLongList<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
public RecyclableLongList(RecyclableList<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
if (expectedItemsCount > 0)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)expectedItemsCount.Value)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_blockSizeMinus1 = _blockSize - 1;
|
||||
_memoryBlocks = _emptyMemoryBlocksArray;
|
||||
}
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(List<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
public RecyclableLongList(RecyclableLongList<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
if (expectedItemsCount > 0)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)expectedItemsCount.Value)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_blockSizeMinus1 = _blockSize - 1;
|
||||
_memoryBlocks = _emptyMemoryBlocksArray;
|
||||
}
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(IList<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
public RecyclableLongList(Span<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(List<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(ICollection source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(ICollection<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
|
||||
{
|
||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
|
||||
AddRange(source);
|
||||
}
|
||||
|
||||
public RecyclableLongList(IEnumerable source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)));
|
||||
if (expectedItemsCount > 0)
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)expectedItemsCount.Value)));
|
||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((int)BitOperations.RoundUpToPowerOf2((uint)expectedItemsCount.Value)));
|
||||
_blockSizeMinus1 = minBlockSize - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.SetupBlockArrayPooling(this, minBlockSize);
|
||||
_blockSizeMinus1 = _blockSize - 1;
|
||||
_memoryBlocks = _emptyMemoryBlocksArray;
|
||||
}
|
||||
@@ -423,7 +410,7 @@ namespace Recyclable.Collections
|
||||
targetBlockIndex = _nextItemBlockIndex;
|
||||
|
||||
long fullBlockItemsCount = items.LongLength - blockSize,
|
||||
sourceItemIndex = blockSize - _nextItemIndex;
|
||||
sourceItemIndex = Math.Min(blockSize - _nextItemIndex, items.LongLength);
|
||||
|
||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, _reservedBlockCount);
|
||||
|
||||
@@ -439,11 +426,25 @@ namespace Recyclable.Collections
|
||||
if ((blockSize = (int)(items.LongLength - sourceItemIndex)) > 0)
|
||||
{
|
||||
Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex], 0, blockSize);
|
||||
_lastBlockWithData = targetBlockIndex;
|
||||
_nextItemBlockIndex = targetBlockIndex;
|
||||
_nextItemIndex = blockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastBlockWithData = targetBlockIndex - 1;
|
||||
if (_nextItemIndex + sourceItemIndex >= _blockSize)
|
||||
{
|
||||
_nextItemBlockIndex = targetBlockIndex;
|
||||
_nextItemIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextItemIndex += (int)sourceItemIndex;
|
||||
}
|
||||
}
|
||||
|
||||
_nextItemIndex = blockSize;
|
||||
_longCount = targetCapacity;
|
||||
_lastBlockWithData = targetBlockIndex;
|
||||
_version++;
|
||||
}
|
||||
|
||||
@@ -690,6 +691,62 @@ namespace Recyclable.Collections
|
||||
_version++;
|
||||
}
|
||||
|
||||
public void AddRange(Span<T> items)
|
||||
{
|
||||
if (items.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long targetCapacity = _longCount + items.Length;
|
||||
if (_capacity < targetCapacity)
|
||||
{
|
||||
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||
}
|
||||
|
||||
int blockSize = _blockSize,
|
||||
targetBlockIndex = _nextItemBlockIndex,
|
||||
memoryBlockCount = _reservedBlockCount;
|
||||
|
||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
|
||||
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
|
||||
while (items.Length >= targetBlockArraySpan.Length)
|
||||
{
|
||||
items[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
|
||||
items = items[targetBlockArraySpan.Length..];
|
||||
if (items.Length == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
targetBlockIndex++;
|
||||
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
|
||||
}
|
||||
|
||||
if (items.Length > 0)
|
||||
{
|
||||
items.CopyTo(targetBlockArraySpan);
|
||||
if (_nextItemBlockIndex != targetBlockIndex)
|
||||
{
|
||||
_nextItemIndex = items.Length;
|
||||
_nextItemBlockIndex = targetBlockIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextItemIndex += items.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextItemBlockIndex = targetBlockIndex + 1;
|
||||
_nextItemIndex = 0;
|
||||
}
|
||||
|
||||
_longCount = targetCapacity;
|
||||
_lastBlockWithData = targetBlockIndex;
|
||||
_version++;
|
||||
}
|
||||
|
||||
public void AddRange(List<T> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
@@ -738,7 +795,7 @@ namespace Recyclable.Collections
|
||||
_version++;
|
||||
}
|
||||
|
||||
public void AddRange(IList<T> items)
|
||||
public void AddRange(ICollection items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
@@ -811,27 +868,186 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(ICollection<T> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long targetCapacity = _longCount + items.Count;
|
||||
if (_capacity < targetCapacity)
|
||||
{
|
||||
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||
}
|
||||
|
||||
int blockSize = _blockSize,
|
||||
targetBlockIndex = _nextItemBlockIndex,
|
||||
memoryBlockCount = _reservedBlockCount;
|
||||
|
||||
// We're better off to temporarily copy it to a fixed array,
|
||||
// than copying them item by item. We may run into OOMs, though.
|
||||
T[] itemsBuffer = items.Count >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool<T>.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count];
|
||||
try
|
||||
{
|
||||
items.CopyTo(itemsBuffer, 0);
|
||||
|
||||
Span<T> itemsSpan = new(itemsBuffer, 0, items.Count);
|
||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
|
||||
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
|
||||
while (targetBlockArraySpan.Length <= itemsSpan.Length)
|
||||
{
|
||||
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
|
||||
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
|
||||
targetBlockIndex++;
|
||||
if (targetBlockIndex == memoryBlockCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
|
||||
}
|
||||
|
||||
if (itemsSpan.Length > 0)
|
||||
{
|
||||
itemsSpan.CopyTo(targetBlockArraySpan);
|
||||
if (_nextItemBlockIndex != targetBlockIndex)
|
||||
{
|
||||
_nextItemIndex = itemsSpan.Length;
|
||||
_nextItemBlockIndex = targetBlockIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextItemIndex += itemsSpan.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextItemIndex = 0;
|
||||
}
|
||||
|
||||
_longCount = targetCapacity;
|
||||
_nextItemBlockIndex = targetBlockIndex;
|
||||
_lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (items.Count >= RecyclableDefaults.MinPooledArrayLength)
|
||||
{
|
||||
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, _needsClearing);
|
||||
}
|
||||
|
||||
_version++;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable source)
|
||||
{
|
||||
long i, copied = _longCount;
|
||||
int blockSize = _blockSize, targetItemIdx = _nextItemIndex, targetBlockIdx = _nextItemBlockIndex;
|
||||
|
||||
IEnumerator enumerator = source.GetEnumerator();
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long capacity = _capacity;
|
||||
long available = capacity - copied;
|
||||
|
||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, _reservedBlockCount);
|
||||
Span<T> blockArraySpan;
|
||||
var memoryBlocksCount = memoryBlocksSpan.Length;
|
||||
while (true)
|
||||
{
|
||||
if (copied + blockSize > capacity)
|
||||
{
|
||||
capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
|
||||
memoryBlocksCount = _reservedBlockCount;
|
||||
if (blockSize != _blockSize)
|
||||
{
|
||||
blockSize = _blockSize;
|
||||
}
|
||||
|
||||
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
|
||||
available = capacity - copied;
|
||||
}
|
||||
|
||||
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
|
||||
for (i = 1; i <= available; i++)
|
||||
{
|
||||
blockArraySpan[targetItemIdx++] = (T)enumerator.Current;
|
||||
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
_lastBlockWithData = targetBlockIdx;
|
||||
_longCount += copied + i - _longCount;
|
||||
if (targetItemIdx < blockSize)
|
||||
{
|
||||
_nextItemBlockIndex = targetBlockIdx;
|
||||
_nextItemIndex = targetItemIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nextItemBlockIndex = targetBlockIdx + 1;
|
||||
_nextItemIndex = 0;
|
||||
}
|
||||
|
||||
_capacity = capacity;
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetItemIdx == blockSize)
|
||||
{
|
||||
targetBlockIdx++;
|
||||
targetItemIdx = 0;
|
||||
|
||||
if (targetBlockIdx == memoryBlocksCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
|
||||
}
|
||||
}
|
||||
|
||||
copied += available;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<T> items)
|
||||
{
|
||||
if (items is RecyclableLongList<T> sourceRecyclableLongList)
|
||||
{
|
||||
AddRange(sourceRecyclableLongList);
|
||||
}
|
||||
else if (items is RecyclableList<T> sourceRecyclableList)
|
||||
if (items is RecyclableList<T> sourceRecyclableList)
|
||||
{
|
||||
AddRange(sourceRecyclableList);
|
||||
}
|
||||
else if (items is RecyclableLongList<T> sourceRecyclableLongList)
|
||||
{
|
||||
AddRange(sourceRecyclableLongList);
|
||||
}
|
||||
else if (items is T[] sourceArray)
|
||||
{
|
||||
AddRange(sourceArray);
|
||||
}
|
||||
else if (items is Array sourceArrayWithObjects)
|
||||
{
|
||||
AddRange(sourceArrayWithObjects);
|
||||
}
|
||||
else if (items is List<T> sourceList)
|
||||
{
|
||||
AddRange(sourceList);
|
||||
}
|
||||
else if (items is IList<T> sourceIList)
|
||||
else if (items is ICollection<T> sourceICollection)
|
||||
{
|
||||
AddRange(sourceIList);
|
||||
AddRange(sourceICollection);
|
||||
}
|
||||
else if (items is ICollection sourceICollectionWithObjects)
|
||||
{
|
||||
AddRange(sourceICollectionWithObjects);
|
||||
}
|
||||
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
|
||||
{
|
||||
AddRange(sourceIReadOnlyList);
|
||||
}
|
||||
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||
{
|
||||
@@ -845,6 +1061,52 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IReadOnlyList<T> items)
|
||||
{
|
||||
long copied = _longCount;
|
||||
int blockSize = _blockSize;
|
||||
int targetItemIdx = _nextItemIndex;
|
||||
int targetBlockIdx = _nextItemBlockIndex;
|
||||
|
||||
Span<T[]> memoryBlocksSpan;
|
||||
Span<T> blockArraySpan;
|
||||
long sourceItemsCount = items.Count;
|
||||
long requiredCapacity = copied + sourceItemsCount;
|
||||
if (requiredCapacity == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_capacity < requiredCapacity)
|
||||
{
|
||||
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
|
||||
blockSize = _blockSize;
|
||||
}
|
||||
|
||||
var memoryBlocksCount = _reservedBlockCount;
|
||||
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
|
||||
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
|
||||
for(var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||
{
|
||||
blockArraySpan[targetItemIdx++] = items[itemIndex];
|
||||
if (targetItemIdx == blockSize)
|
||||
{
|
||||
targetItemIdx = 0;
|
||||
if (++targetBlockIdx == memoryBlocksCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
_longCount = requiredCapacity;
|
||||
_nextItemBlockIndex = targetBlockIdx;
|
||||
_nextItemIndex = targetItemIdx;
|
||||
_lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (_longCount == 0)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using Recyclable.Collections;
|
||||
using Recyclable.Collections.Linq;
|
||||
using System.Collections;
|
||||
|
||||
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
|
||||
|
||||
@@ -29,10 +30,34 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((Array)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IEnumerable)testData);
|
||||
}
|
||||
else if (testCase.Contains("IReadOnlyList<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("ReadOnlySpan<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testCase.Contains("Span<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new Span<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
@@ -76,10 +101,34 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((Array)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IEnumerable)testData);
|
||||
}
|
||||
else if (testCase.Contains("IReadOnlyList<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("ReadOnlySpan<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testCase.Contains("Span<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new Span<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
@@ -123,12 +172,43 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((Array)testData);
|
||||
list.AddRange((Array)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection)testData);
|
||||
list.AddRange((ICollection)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IEnumerable)testData);
|
||||
list.AddRange((IEnumerable)testData);
|
||||
}
|
||||
else if (testCase.Contains("IReadOnlyList<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("ReadOnlySpan<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var readOnlySpan = new ReadOnlySpan<long>((long[])testData);
|
||||
list.AddRange(readOnlySpan);
|
||||
list.AddRange(readOnlySpan);
|
||||
}
|
||||
else if (testCase.Contains("Span<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var readOnlySpan = new Span<long>((long[])testData);
|
||||
list.AddRange(readOnlySpan);
|
||||
list.AddRange(readOnlySpan);
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MoreLinq;
|
||||
using Open.Numeric.Primes;
|
||||
using Recyclable.Collections;
|
||||
using System.Collections;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Recyclable.CollectionsTests
|
||||
@@ -78,23 +79,21 @@ namespace Recyclable.CollectionsTests
|
||||
{
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
IDisposable disposable;
|
||||
|
||||
switch (refType)
|
||||
{
|
||||
case false:
|
||||
long[] testData = CreateTestData(itemsCount).ToArray();
|
||||
|
||||
yield return new object[] { $"int[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"ReadOnlySpan[{itemsCount}]", testData, itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"List<int>(itemsCount: {itemsCount}", testData.ToList(), itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"RecyclableList<int>(itemsCount: {itemsCount})", disposable = testData.ToRecyclableList(), itemsCount };
|
||||
//disposable.Dispose();
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
yield return new object[] { $"Array[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"ICollection[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"ICollection<T>[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"IEnumerable[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"IEnumerable<T>[{itemsCount}] without non-enumerated count", new EnumerableWithoutCount<long>(testData), itemsCount };
|
||||
yield return new object[] { $"IList<T>[{itemsCount}]", new CustomIList<long>(testData), itemsCount };
|
||||
yield return new object[] { $"IReadOnlyList<T>[{itemsCount}])", testData, itemsCount };
|
||||
yield return new object[] { $"List<T>[{itemsCount}]", testData.ToList(), itemsCount };
|
||||
yield return new object[] { $"ReadOnlySpan<T>[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"RecyclableList<T>[{itemsCount}]", testData.ToRecyclableList(), itemsCount };
|
||||
|
||||
foreach (var sourceBlockSize in BlockSizeVariants)
|
||||
{
|
||||
@@ -105,39 +104,29 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
$"RecyclableLongList<int>(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})",
|
||||
disposable = testData.ToRecyclableLongList(sourceBlockSize),
|
||||
$"RecyclableLongList<T>[itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize}]",
|
||||
testData.ToRecyclableLongList(sourceBlockSize),
|
||||
itemsCount
|
||||
};
|
||||
|
||||
//disposable.Dispose();
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
}
|
||||
|
||||
yield return new object[] { $"IList<int>(itemsCount: {itemsCount})", new CustomIList<long>(testData), itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"IEnumerable<int>(itemsCount: {itemsCount}) with non-enumerated count", testData, itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"IEnumerable<int>(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount<long>(testData), itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"Span<T>[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"T[{itemsCount}]", testData, itemsCount };
|
||||
break;
|
||||
|
||||
case true:
|
||||
IEnumerable<object> testRefData = CreateRefTestData(itemsCount).ToArray();
|
||||
|
||||
yield return new object[] { $"int[{itemsCount}]", testRefData, itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"List<int>(itemsCount: {itemsCount}", testRefData.ToList(), itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"RecyclableList<int>(itemsCount: {itemsCount})", disposable = testRefData.ToRecyclableList(), itemsCount };
|
||||
|
||||
//disposable.Dispose();
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
yield return new object[] { $"Array[{itemsCount}]", testRefData, itemsCount };
|
||||
yield return new object[] { $"ICollection(itemsCount: {itemsCount}", testRefData, itemsCount };
|
||||
yield return new object[] { $"ICollection<T>(itemsCount: {itemsCount}", testRefData, itemsCount };
|
||||
yield return new object[] { $"IEnumerable(itemsCount: {itemsCount})", testRefData, itemsCount };
|
||||
yield return new object[] { $"IEnumerable<T>(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount<object>(testRefData), itemsCount };
|
||||
yield return new object[] { $"IList<T>(itemsCount: {itemsCount})", new CustomIList<object>(testRefData), itemsCount };
|
||||
yield return new object[] { $"IReadOnlyList<T>(itemsCount: {itemsCount})", testRefData, itemsCount };
|
||||
yield return new object[] { $"List<T>(itemsCount: {itemsCount}", testRefData.ToList(), itemsCount };
|
||||
yield return new object[] { $"ReadOnlySpan<T>[{itemsCount}]", testRefData, itemsCount };
|
||||
yield return new object[] { $"RecyclableList<T>(itemsCount: {itemsCount})", testRefData.ToRecyclableList(), itemsCount };
|
||||
|
||||
foreach (var sourceBlockSize in BlockSizeVariants)
|
||||
{
|
||||
@@ -148,24 +137,14 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
$"RecyclableLongList<int>(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})",
|
||||
disposable = testRefData.ToRecyclableLongList(sourceBlockSize),
|
||||
$"RecyclableLongList<T>(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})",
|
||||
testRefData.ToRecyclableLongList(sourceBlockSize),
|
||||
itemsCount
|
||||
};
|
||||
|
||||
//disposable.Dispose();
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
}
|
||||
|
||||
yield return new object[] { $"IList<int>(itemsCount: {itemsCount})", new CustomIList<object>(testRefData), itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"IEnumerable<int>(itemsCount: {itemsCount}) with non-enumerated count", testRefData, itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"IEnumerable<int>(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount<object>(testRefData), itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"Span<T>[{itemsCount}]", testRefData, itemsCount };
|
||||
yield return new object[] { $"T[{itemsCount}]", testRefData, itemsCount };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using Recyclable.Collections;
|
||||
using Recyclable.Collections.Linq;
|
||||
using System.Collections;
|
||||
using System.Numerics;
|
||||
|
||||
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
|
||||
@@ -41,10 +42,34 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((Array)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IEnumerable)testData);
|
||||
}
|
||||
else if (testCase.Contains("IReadOnlyList<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("ReadOnlySpan<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testCase.Contains("Span<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new Span<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
@@ -90,10 +115,34 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>();
|
||||
|
||||
// Act
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((Array)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IEnumerable)testData);
|
||||
}
|
||||
else if (testCase.Contains("IReadOnlyList<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("ReadOnlySpan<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testCase.Contains("Span<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new Span<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
@@ -138,12 +187,43 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((Array)testData);
|
||||
list.AddRange((Array)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection)testData);
|
||||
list.AddRange((ICollection)testData);
|
||||
}
|
||||
else if (testCase.Contains("ICollection<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
list.AddRange((ICollection<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IEnumerable)testData);
|
||||
list.AddRange((IEnumerable)testData);
|
||||
}
|
||||
else if (testCase.Contains("IReadOnlyList<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
list.AddRange((IReadOnlyList<long>)testData);
|
||||
}
|
||||
else if (testCase.Contains("ReadOnlySpan<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var readOnlySpan = new ReadOnlySpan<long>((long[])testData);
|
||||
list.AddRange(readOnlySpan);
|
||||
list.AddRange(readOnlySpan);
|
||||
}
|
||||
else if (testCase.Contains("Span<T>[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var readOnlySpan = new Span<long>((long[])testData);
|
||||
list.AddRange(readOnlySpan);
|
||||
list.AddRange(readOnlySpan);
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
|
||||
Reference in new issue
Block a user