.AddRange methods moved to their own classes, for better method resolution times.

This commit is contained in:
mlemanczyk committed 2023-09-16 21:09:42 +02:00
1 parent 5ee8a8c03f
commit 56f79c4f21
4 files changed
+1174 -1164

No files matched your search

@@ -0,0 +1,322 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Recyclable.Collections
{
public static class zRecyclableListAddRange
{
private static void AddRangeEnumerated<T>(this RecyclableList<T> list, IEnumerable<T> items, int growByCount)
{
int targetItemIndex = list._count;
Span<T> memorySpan;
int i;
var enumerator = items.GetEnumerator();
int capacity = list._capacity;
memorySpan = new(list._memoryBlock);
if (enumerator.MoveNext())
{
int available = capacity - targetItemIndex;
do
{
if (targetItemIndex + growByCount > capacity)
{
capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
memorySpan = new(list._memoryBlock);
available = capacity - targetItemIndex;
}
for (i = 0; i < available; i++)
{
memorySpan[targetItemIndex++] = enumerator.Current;
if (!enumerator.MoveNext())
{
break;
}
}
}
while (i >= available);
}
list._capacity = capacity;
list._count = targetItemIndex;
}
private static void AddRangeWithKnownCount<T>(this RecyclableList<T> list, IEnumerable<T> items, int currentItemsCount, int requiredAdditionalCapacity)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity))));
Span<T> memorySpan = new(list._memoryBlock);
foreach (var item in items)
{
memorySpan[currentItemsCount++] = item;
}
list._count = currentItemsCount;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, in Array items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
Array.Copy(items, items.GetLowerBound(0), list._memoryBlock, list._count, items.LongLength);
list._count += items.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, in T[] items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
new Span<T>(items).CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
list._count += items.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, ReadOnlySpan<T> items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
list._count += items.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, Span<T> items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
list._count += items.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, List<T> items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
items.CopyTo(list._memoryBlock, list._count);
list._count += items.Count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, ICollection items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
items.CopyTo(list._memoryBlock, list._count);
list._count += items.Count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, ICollection<T> items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
items.CopyTo(list._memoryBlock, list._count);
list._count += items.Count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, RecyclableList<T> items)
{
if (list._capacity < list._count + items._count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count))));
}
new Span<T>(items._memoryBlock, 0, items._count).CopyTo(new Span<T>(list._memoryBlock, list._count, items._count));
list._count += items._count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, RecyclableLongList<T> items)
{
if (items._longCount == 0)
{
return;
}
int oldCount = list._count;
long sourceItemsCount = items._longCount;
if (oldCount + sourceItemsCount > int.MaxValue)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
}
int targetCapacity = oldCount + (int)sourceItemsCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
// TODO: Avoid unnecessary range operator - pass as arguments instead.
Span<T> targetSpan = new Span<T>(list._memoryBlock)[oldCount..],
itemsSpan;
int blockIndex,
sourceBlockSize = items._blockSize,
lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1;
for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize);
itemsSpan.CopyTo(targetSpan);
targetSpan = targetSpan[sourceBlockSize..];
}
if (blockIndex == 0)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount);
itemsSpan.CopyTo(targetSpan);
}
else if (blockIndex <= items._lastBlockWithData)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex);
itemsSpan.CopyTo(targetSpan);
}
list._count = targetCapacity;
list._version++;
}
public static IEnumerator AddRange<T>(this RecyclableList<T> list, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
{
int targetItemIndex = list._count;
Span<T> memorySpan;
int i;
var enumerator = source.GetEnumerator();
int capacity = list._capacity;
memorySpan = new(list._memoryBlock);
if (enumerator.MoveNext())
{
int available = capacity - targetItemIndex;
do
{
if (targetItemIndex + growByCount > capacity)
{
capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
memorySpan = new(list._memoryBlock);
available = capacity - targetItemIndex;
}
for (i = 0; i < available; i++)
{
memorySpan[targetItemIndex++] = (T)enumerator.Current;
if (!enumerator.MoveNext())
{
break;
}
}
}
while (i >= available);
}
list._capacity = capacity;
list._count = targetItemIndex;
return enumerator;
}
public static void AddRange<T>(this RecyclableList<T> list, IReadOnlyList<T> items)
{
int sourceItemsCount = items.Count;
if (list._capacity < list._count + sourceItemsCount)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount))));
}
Span<T> memorySpan = new(list._memoryBlock, list._count, sourceItemsCount);
for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++)
{
memorySpan[sourceItemIndex] = items[sourceItemIndex];
}
list._count += sourceItemsCount;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void AddRange<T>(this RecyclableList<T> list, IEnumerable<T> items, int growByCount = RecyclableDefaults.MinPooledArrayLength)
{
if (items is RecyclableList<T> sourceRecyclableList)
{
AddRange(list, sourceRecyclableList);
}
else if (items is RecyclableLongList<T> sourceRecyclableLongList)
{
AddRange(list, sourceRecyclableLongList);
}
else if (items is T[] sourceArray)
{
AddRange(list, sourceArray);
}
else if (items is Array sourceArrayWithObjects)
{
AddRange(list, sourceArrayWithObjects);
}
else if (items is List<T> sourceList)
{
AddRange(list, sourceList);
}
else if (items is ICollection<T> sourceICollection)
{
AddRange(list, sourceICollection);
}
else if (items is ICollection sourceICollectionWithObjects)
{
AddRange(list, sourceICollectionWithObjects);
}
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
{
AddRange(list, sourceIReadOnlyList);
}
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
{
AddRangeWithKnownCount(list, items, list._count, requiredAdditionalCapacity);
list._version++;
}
else
{
AddRangeEnumerated(list, items, growByCount);
list._version++;
}
}
}
}
+12 -321
View File
@@ -19,57 +19,6 @@ namespace Recyclable.Collections
internal ulong _version;
public ulong Version => _version;
private void AddRangeEnumerated(IEnumerable<T> items, int growByCount)
{
int targetItemIndex = _count;
Span<T> memorySpan;
int i;
var enumerator = items.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++] = enumerator.Current;
if (!enumerator.MoveNext())
{
break;
}
}
}
while (i >= available);
}
_capacity = capacity;
_count = targetItemIndex;
}
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 items)
{
memorySpan[currentItemsCount++] = item;
}
_count = currentItemsCount;
}
public RecyclableList()
{
_capacity = RecyclableDefaults.InitialCapacity;
@@ -92,84 +41,67 @@ 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);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(RecyclableList<T> source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(RecyclableLongList<T> source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(ReadOnlySpan<T> source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(in T[] source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(IReadOnlyList<T> source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(List<T> source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(ICollection source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(ICollection<T> source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(IEnumerable source, int initialCapacity = RecyclableDefaults.InitialCapacity)
@@ -187,7 +119,7 @@ namespace Recyclable.Collections
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
}
AddRange(source, RecyclableDefaults.MinPooledArrayLength);
zRecyclableListAddRange.AddRange(this, source, RecyclableDefaults.MinPooledArrayLength);
}
public RecyclableList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity)
@@ -205,14 +137,14 @@ namespace Recyclable.Collections
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
}
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public RecyclableList(Span<T> source)
{
_capacity = RecyclableDefaults.InitialCapacity;
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
AddRange(source);
zRecyclableListAddRange.AddRange(this, source);
}
public T this[int index]
@@ -259,247 +191,6 @@ 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)
{
if (_capacity < _count + items.Length)
{
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Length))));
}
new Span<T>(items).CopyTo(new Span<T>(_memoryBlock, _count, items.Length));
_count += items.Length;
_version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public void AddRange(ReadOnlySpan<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(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)
{
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 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)
{
_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(RecyclableList<T> items)
{
if (_capacity < _count + items._count)
{
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items._count))));
}
new Span<T>(items._memoryBlock, 0, items._count).CopyTo(new Span<T>(_memoryBlock, _count, items._count));
_count += items._count;
_version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public void AddRange(RecyclableLongList<T> items)
{
if (items._longCount == 0)
{
return;
}
int oldCount = _count;
long sourceItemsCount = items._longCount;
if (oldCount + sourceItemsCount > int.MaxValue)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
}
int targetCapacity = oldCount + (int)sourceItemsCount;
if (_capacity < targetCapacity)
{
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
// TODO: Avoid unnecessary range operator - pass as arguments instead.
Span<T> targetSpan = new Span<T>(_memoryBlock)[oldCount..],
itemsSpan;
int blockIndex,
sourceBlockSize = items._blockSize,
lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1;
for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize);
itemsSpan.CopyTo(targetSpan);
targetSpan = targetSpan[sourceBlockSize..];
}
if (blockIndex == 0)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount);
itemsSpan.CopyTo(targetSpan);
}
else if (blockIndex <= items._lastBlockWithData)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex);
itemsSpan.CopyTo(targetSpan);
}
_count = targetCapacity;
_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 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 ICollection<T> sourceICollection)
{
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) && requiredAdditionalCapacity != 0)
{
AddRangeWithKnownCount(items, _count, requiredAdditionalCapacity);
_version++;
}
else
{
AddRangeEnumerated(items, growByCount);
_version++;
}
}
public T[] AsArray => _memoryBlock;
[MethodImpl(MethodImplOptions.NoInlining)]
@@ -0,0 +1,819 @@
using System.Collections;
using System.Numerics;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
public static class zRecyclableLongListAddRange
{
private static void AddRangeEnumerated<T>(this RecyclableLongList<T> targetList, IEnumerable<T> items)
{
using IEnumerator<T> enumerator = items.GetEnumerator();
if (!enumerator.MoveNext())
{
return;
}
int blockSize = targetList._blockSize,
targetBlockIdx = targetList._nextItemBlockIndex,
targetItemIdx = targetList._nextItemIndex;
long capacity = targetList._capacity,
copied = targetList._longCount,
available = capacity - copied,
i;
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> blockArraySpan;
while (true)
{
if (copied + blockSize > capacity)
{
capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
memoryBlocksSpan = new(targetList._memoryBlocks);
available = capacity - copied;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
for (i = 1; i <= available; i++)
{
blockArraySpan[targetItemIdx++] = enumerator.Current;
if (!enumerator.MoveNext())
{
targetList._lastBlockWithData = targetBlockIdx;
targetList._longCount += copied + i - targetList._longCount;
if (targetItemIdx < blockSize)
{
targetList._nextItemBlockIndex = targetBlockIdx;
targetList._nextItemIndex = targetItemIdx;
}
else
{
targetList._nextItemBlockIndex = targetBlockIdx + 1;
targetList._nextItemIndex = 0;
}
targetList._capacity = capacity;
targetList._version++;
return;
}
if (targetItemIdx == blockSize)
{
targetBlockIdx++;
targetItemIdx = 0;
if (i == available)
{
break;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
}
}
copied += available;
}
}
private static void AddRangeWithKnownCount<T>(this RecyclableLongList<T> targetList, IEnumerable<T> items, int requiredAdditionalCapacity)
{
long requiredCapacity = targetList._longCount + requiredAdditionalCapacity;
if (requiredCapacity == 0)
{
return;
}
int blockSize = targetList._blockSize,
targetBlockIdx = targetList._nextItemBlockIndex,
targetItemIdx = targetList._nextItemIndex;
if (targetList._capacity < requiredCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
}
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
foreach (var item in items)
{
blockArraySpan[targetItemIdx++] = item;
if (targetItemIdx == blockSize)
{
targetBlockIdx++;
targetItemIdx = 0;
if (targetBlockIdx == memoryBlocksSpan.Length)
{
break;
}
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
}
}
targetList._longCount = requiredCapacity;
targetList._nextItemBlockIndex = targetBlockIdx;
targetList._nextItemIndex = targetItemIdx;
targetList._lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1);
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, in Array items)
{
if (items.LongLength == 0)
{
return;
}
int blockSize = targetList._blockSize,
targetBlockIndex = targetList._nextItemBlockIndex;
long fullBlockItemsCount = items.LongLength - blockSize,
sourceItemIndex = Math.Min(blockSize - targetList._nextItemIndex, items.LongLength),
targetCapacity = targetList._longCount + items.LongLength;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Array.Copy(items, 0, memoryBlocksSpan[targetBlockIndex++], targetList._nextItemIndex, sourceItemIndex);
while (sourceItemIndex < fullBlockItemsCount)
{
Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex++], 0, blockSize);
sourceItemIndex += blockSize;
}
// We're reusing a variable which is no longer needed. That's to avoid unnecessary
// allocation.
if ((blockSize = (int)(items.LongLength - sourceItemIndex)) > 0)
{
Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex], 0, blockSize);
targetList._lastBlockWithData = targetBlockIndex;
targetList._nextItemBlockIndex = targetBlockIndex;
targetList._nextItemIndex = blockSize;
}
else
{
targetList._lastBlockWithData = targetBlockIndex - 1;
if (targetList._nextItemIndex + sourceItemIndex >= targetList._blockSize)
{
targetList._nextItemBlockIndex = targetBlockIndex;
targetList._nextItemIndex = 0;
}
else
{
targetList._nextItemIndex += (int)sourceItemIndex;
}
}
targetList._longCount = targetCapacity;
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection items)
{
if (items.Count == 0)
{
return;
}
int targetBlockIndex = targetList._nextItemBlockIndex;
long targetCapacity = targetList._longCount + items.Count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
// 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];
items.CopyTo(itemsBuffer, 0);
Span<T> itemsSpan = new(itemsBuffer, 0, items.Count);
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex);
while (targetBlockArraySpan.Length <= itemsSpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (itemsSpan.Length == 0)
{
break;
}
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
if (targetList._nextItemBlockIndex != targetBlockIndex)
{
targetList._nextItemIndex = itemsSpan.Length;
targetList._nextItemBlockIndex = targetBlockIndex;
}
else
{
targetList._nextItemIndex += itemsSpan.Length;
}
}
else
{
targetList._nextItemIndex = 0;
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = targetBlockIndex;
targetList._lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
if (items.Count >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
}
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection<T> items)
{
if (items.Count == 0)
{
return;
}
int targetBlockIndex = targetList._nextItemBlockIndex;
long targetCapacity = targetList._longCount + items.Count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
// 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];
items.CopyTo(itemsBuffer, 0);
Span<T> itemsSpan = new(itemsBuffer, 0, items.Count);
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex);
while (targetBlockArraySpan.Length <= itemsSpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (itemsSpan.Length == 0)
{
break;
}
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
if (targetList._nextItemBlockIndex != targetBlockIndex)
{
targetList._nextItemIndex = itemsSpan.Length;
targetList._nextItemBlockIndex = targetBlockIndex;
}
else
{
targetList._nextItemIndex += itemsSpan.Length;
}
}
else
{
targetList._nextItemIndex = 0;
}
targetList._longCount = targetCapacity;
targetList._nextItemBlockIndex = targetBlockIndex;
targetList._lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
if (items.Count >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
}
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, IEnumerable source)
{
IEnumerator enumerator = source.GetEnumerator();
if (!enumerator.MoveNext())
{
return;
}
int blockSize = targetList._blockSize,
targetBlockIdx = targetList._nextItemBlockIndex,
targetItemIdx = targetList._nextItemIndex;
long capacity = targetList._capacity,
copied = targetList._longCount,
available = capacity - copied,
i;
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> blockArraySpan;
while (true)
{
if (copied + blockSize > capacity)
{
capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
memoryBlocksSpan = new(targetList._memoryBlocks);
available = capacity - copied;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
for (i = 1; i <= available; i++)
{
blockArraySpan[targetItemIdx++] = (T)enumerator.Current;
if (!enumerator.MoveNext())
{
targetList._lastBlockWithData = targetBlockIdx;
targetList._longCount += copied + i - targetList._longCount;
if (targetItemIdx < blockSize)
{
targetList._nextItemBlockIndex = targetBlockIdx;
targetList._nextItemIndex = targetItemIdx;
}
else
{
targetList._nextItemBlockIndex = targetBlockIdx + 1;
targetList._nextItemIndex = 0;
}
targetList._capacity = capacity;
return;
}
if (targetItemIdx == blockSize)
{
targetBlockIdx++;
targetItemIdx = 0;
if (i == available)
{
break;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
}
}
copied += available;
}
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, IEnumerable<T> items)
{
switch (items)
{
case RecyclableList<T> sourceRecyclableList:
targetList.AddRange(sourceRecyclableList);
return;
case RecyclableLongList<T> sourceRecyclableLongList:
targetList.AddRange(sourceRecyclableLongList);
return;
case T[] sourceArray:
targetList.AddRange(sourceArray);
return;
case Array sourceArrayWithObjects:
targetList.AddRange(sourceArrayWithObjects);
return;
case List<T> sourceList:
targetList.AddRange(sourceList);
return;
case ICollection<T> sourceICollection:
targetList.AddRange(sourceICollection);
return;
case ICollection sourceICollectionWithObjects:
targetList.AddRange(sourceICollectionWithObjects);
return;
case IReadOnlyList<T> sourceIReadOnlyList:
targetList.AddRange(sourceIReadOnlyList);
return;
default:
if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
{
targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity);
}
else
{
targetList.AddRangeEnumerated(items);
}
return;
}
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, IReadOnlyList<T> items)
{
long sourceItemsCount = items.Count,
requiredCapacity = targetList._longCount + sourceItemsCount;
if (requiredCapacity == 0)
{
return;
}
int blockSize = targetList._blockSize,
targetBlockIdx = targetList._nextItemBlockIndex,
targetItemIdx = targetList._nextItemIndex;
if (targetList._capacity < requiredCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
}
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
blockArraySpan[targetItemIdx++] = items[itemIndex];
if (targetItemIdx == blockSize)
{
targetItemIdx = 0;
targetBlockIdx++;
if (itemIndex + 1 == sourceItemsCount)
{
break;
}
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
}
}
targetList._longCount = requiredCapacity;
targetList._nextItemBlockIndex = targetBlockIdx;
targetList._nextItemIndex = targetItemIdx;
targetList._lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1);
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, List<T> items)
{
if (items.Count == 0)
{
return;
}
int blockSize = targetList._blockSize,
itemsCount = items.Count,
copied = Math.Min(blockSize - targetList._nextItemIndex, itemsCount),
targetBlockIndex = targetList._nextItemBlockIndex;
long targetCapacity = targetList._longCount + items.Count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
items.CopyTo(0, memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, copied);
if (targetList._nextItemIndex + copied == blockSize)
{
targetBlockIndex++;
targetList._nextItemIndex = 0;
}
else
{
targetList._nextItemIndex += copied;
}
while (blockSize <= itemsCount - copied)
{
items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex++], 0, blockSize);
copied += blockSize;
}
if ((itemsCount - copied < blockSize) && (itemsCount - copied != 0))
{
targetList._nextItemIndex = itemsCount - copied;
items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex], 0, targetList._nextItemIndex);
}
targetList._nextItemBlockIndex = targetBlockIndex;
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex - (targetList._nextItemIndex > 0 ? 0 : 1);
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, ReadOnlySpan<T> items)
{
if (items.Length == 0)
{
return;
}
int targetBlockIndex = targetList._nextItemBlockIndex;
long targetCapacity = targetList._longCount + items.Length;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._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]);
}
if (items.Length > 0)
{
items.CopyTo(targetBlockArraySpan);
if (targetList._nextItemBlockIndex != targetBlockIndex)
{
targetList._nextItemIndex = items.Length;
targetList._nextItemBlockIndex = targetBlockIndex;
}
else
{
targetList._nextItemIndex += items.Length;
}
}
else
{
targetList._nextItemBlockIndex = targetBlockIndex + 1;
targetList._nextItemIndex = 0;
}
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex;
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableList<T> items)
{
if (items._count == 0)
{
return;
}
int targetBlockIndex = targetList._nextItemBlockIndex;
long targetCapacity = targetList._longCount + items._count;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
Span<T> itemsSpan = new(items._memoryBlock, 0, items._count);
Span<T[]> targetMemoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex);
while (itemsSpan.Length >= targetBlockArraySpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (itemsSpan.Length == 0)
{
break;
}
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
targetList._lastBlockWithData = targetBlockIndex;
if (targetList._nextItemBlockIndex != targetBlockIndex)
{
targetList._nextItemIndex = itemsSpan.Length;
targetList._nextItemBlockIndex = targetBlockIndex;
}
else
{
targetList._nextItemIndex += itemsSpan.Length;
}
}
else
{
targetList._lastBlockWithData = targetBlockIndex - 1;
targetList._nextItemBlockIndex = targetBlockIndex;
targetList._nextItemIndex = 0;
}
targetList._longCount = targetCapacity;
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableLongList<T> items)
{
if (items._longCount == 0)
{
return;
}
int sourceBlockIndex = 0,
targetBlockIndex = targetList._nextItemBlockIndex,
toCopy;
long copiedCount = 0,
itemsCount = items._longCount,
targetCapacity = targetList._longCount + itemsCount;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
Span<T[]> sourceMemoryBlocksSpan = new(items._memoryBlocks);
Span<T> itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]);
Span<T[]> targetMemoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex);
while (copiedCount < itemsCount)
{
toCopy = checked((int)Math.Min(itemsCount - copiedCount, itemsSpan.Length));
if (targetBlockArraySpan.Length < toCopy)
{
toCopy = targetBlockArraySpan.Length;
itemsSpan[..toCopy].CopyTo(targetBlockArraySpan);
targetBlockIndex++;
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]);
itemsSpan = itemsSpan[toCopy..];
copiedCount += toCopy;
}
else
{
itemsSpan[..toCopy].CopyTo(targetBlockArraySpan);
targetBlockArraySpan = targetBlockArraySpan[toCopy..];
itemsSpan = itemsSpan[toCopy..];
if (itemsSpan.IsEmpty && sourceBlockIndex + 1 < sourceMemoryBlocksSpan.Length)
{
sourceBlockIndex++;
itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]);
}
copiedCount += toCopy;
if (targetBlockArraySpan.IsEmpty && copiedCount < itemsCount)
{
targetBlockIndex++;
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]);
}
}
}
targetList._lastBlockWithData = targetBlockIndex;
if ((targetCapacity & targetList._blockSizeMinus1) == 0)
{
targetList._nextItemIndex = 0;
targetList._nextItemBlockIndex = targetBlockIndex + 1;
}
else
{
targetList._nextItemIndex = checked((int)(targetCapacity & targetList._blockSizeMinus1));
targetList._nextItemBlockIndex = targetBlockIndex;
}
targetList._longCount = targetCapacity;
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, Span<T> items)
{
if (items.Length == 0)
{
return;
}
int targetBlockIndex = targetList._nextItemBlockIndex;
long targetCapacity = targetList._longCount + items.Length;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._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]);
}
if (items.Length > 0)
{
items.CopyTo(targetBlockArraySpan);
if (targetList._nextItemBlockIndex != targetBlockIndex)
{
targetList._nextItemIndex = items.Length;
targetList._nextItemBlockIndex = targetBlockIndex;
}
else
{
targetList._nextItemIndex += items.Length;
}
}
else
{
targetList._nextItemBlockIndex = targetBlockIndex + 1;
targetList._nextItemIndex = 0;
}
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex;
targetList._version++;
}
public static void AddRange<T>(this RecyclableLongList<T> targetList, in T[] items)
{
if (items.LongLength == 0)
{
return;
}
int targetBlockIndex = targetList._nextItemBlockIndex;
long targetCapacity = targetList._longCount + items.LongLength;
if (targetList._capacity < targetCapacity)
{
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
Span<T> itemsSpan = new(items);
Span<T[]> memoryBlocksSpan = new(targetList._memoryBlocks);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex);
while (itemsSpan.Length >= targetBlockArraySpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
if (itemsSpan.Length == 0)
{
break;
}
targetBlockIndex++;
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
if (targetList._nextItemBlockIndex != targetBlockIndex)
{
targetList._nextItemIndex = itemsSpan.Length;
targetList._nextItemBlockIndex = targetBlockIndex;
}
else
{
targetList._nextItemIndex += itemsSpan.Length;
}
}
else
{
targetList._nextItemBlockIndex = targetBlockIndex + 1;
targetList._nextItemIndex = 0;
}
targetList._longCount = targetCapacity;
targetList._lastBlockWithData = targetBlockIndex;
targetList._version++;
}
}
}
+21 -843
View File
@@ -11,7 +11,7 @@ namespace Recyclable.Collections
public partial class RecyclableLongList<T> : IList<T>, IReadOnlyList<T>, IDisposable
{
private static readonly bool _defaultIsNull = default(T) == null;
private static readonly bool _needsClearing = !typeof(T).IsValueType;
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
internal int _blockSize;
internal byte _blockSizePow2BitShift;
@@ -52,134 +52,17 @@ namespace Recyclable.Collections
public int NextItemIndex => _nextItemIndex;
public ulong Version => _version;
private void AddRangeEnumerated(IEnumerable<T> items)
{
using IEnumerator<T> enumerator = items.GetEnumerator();
if (!enumerator.MoveNext())
public RecyclableLongList(int minBlockSize = RecyclableDefaults.BlockSize, long initialCapacity = RecyclableDefaults.InitialCapacity)
{
if (!BitOperations.IsPow2(minBlockSize))
{
return;
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
}
long i, copied = _longCount;
int blockSize = _blockSize,
targetItemIdx = _nextItemIndex,
targetBlockIdx = _nextItemBlockIndex;
long capacity = _capacity;
long available = capacity - copied;
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
Span<T> blockArraySpan;
while (true)
{
if (copied + blockSize > capacity)
{
capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
memoryBlocksSpan = new(_memoryBlocks);
available = capacity - copied;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
for (i = 1; i <= available; i++)
{
blockArraySpan[targetItemIdx++] = 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 (i == available)
{
break;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
}
}
copied += available;
}
}
private void AddRangeWithKnownCount(IEnumerable<T> items, int requiredAdditionalCapacity)
{
long copied = _longCount;
int blockSize = _blockSize;
int targetItemIdx = _nextItemIndex;
int targetBlockIdx = _nextItemBlockIndex;
long requiredCapacity = copied + requiredAdditionalCapacity;
if (requiredCapacity == 0)
{
return;
}
if (_capacity < requiredCapacity)
{
_capacity = Helpers.Resize(this, blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
blockSize = _blockSize;
}
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
Span<T> blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
foreach (var item in items)
{
blockArraySpan[targetItemIdx++] = item;
if (targetItemIdx == blockSize)
{
targetBlockIdx++;
targetItemIdx = 0;
if (targetBlockIdx == memoryBlocksSpan.Length)
{
break;
}
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
}
}
_longCount = requiredCapacity;
_nextItemBlockIndex = targetBlockIdx;
_nextItemIndex = targetItemIdx;
_lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1);
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public RecyclableLongList(int minBlockSize = RecyclableDefaults.BlockSize, long? initialCapacity = default)
{
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
if (initialCapacity > 0)
{
Helpers.SetupBlockArrayPooling(this, minBlockSize);
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity.Value)));
_blockSizeMinus1 = minBlockSize - 1;
}
else
{
Helpers.SetupBlockArrayPooling(this, minBlockSize);
_blockSizeMinus1 = _blockSize - 1;
_memoryBlocks = _emptyMemoryBlocksArray;
}
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
_capacity = RecyclableLongList<T>.Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
_blockSize = minBlockSize;
_blockSizeMinus1 = minBlockSize - 1;
}
public RecyclableLongList(in T[] source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -190,7 +73,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(in Array source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -201,7 +84,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(IReadOnlyList<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -212,7 +95,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(ReadOnlySpan<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -223,7 +106,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(RecyclableList<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -234,7 +117,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(RecyclableLongList<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -245,7 +128,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(Span<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -256,7 +139,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(List<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -267,7 +150,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(ICollection source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -278,7 +161,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(ICollection<T> source, int minBlockSize = RecyclableDefaults.BlockSize)
@@ -289,7 +172,7 @@ namespace Recyclable.Collections
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count));
_blockSizeMinus1 = minBlockSize - 1;
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(IEnumerable source, int minBlockSize = RecyclableDefaults.BlockSize, long? initialCapacity = default)
@@ -306,7 +189,7 @@ namespace Recyclable.Collections
_memoryBlocks = _emptyMemoryBlocksArray;
}
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public RecyclableLongList(IEnumerable<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? initialCapacity = default)
@@ -323,7 +206,7 @@ namespace Recyclable.Collections
_memoryBlocks = _emptyMemoryBlocksArray;
}
AddRange(source);
zRecyclableLongListAddRange.AddRange(this, source);
}
public T this[long index]
@@ -376,710 +259,6 @@ namespace Recyclable.Collections
_version++;
}
public void AddRange(in Array items)
{
if (items.LongLength == 0)
{
return;
}
long targetCapacity = _longCount + items.LongLength;
if (_capacity < targetCapacity)
{
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
int blockSize = _blockSize,
targetBlockIndex = _nextItemBlockIndex;
long fullBlockItemsCount = items.LongLength - blockSize,
sourceItemIndex = Math.Min(blockSize - _nextItemIndex, items.LongLength);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
Array.Copy(items, 0, memoryBlocksSpan[targetBlockIndex++], _nextItemIndex, sourceItemIndex);
while (sourceItemIndex < fullBlockItemsCount)
{
Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex++], 0, blockSize);
sourceItemIndex += blockSize;
}
// We're reusing a variable which is no longer needed. That's to avoid unnecessary
// allocation.
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;
}
}
_longCount = targetCapacity;
_version++;
}
public void AddRange(in T[] items)
{
if (items.LongLength == 0)
{
return;
}
long targetCapacity = _longCount + items.LongLength;
if (_capacity < targetCapacity)
{
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
int blockSize = _blockSize,
targetBlockIndex = _nextItemBlockIndex;
Span<T> itemsSpan = new(items);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
while (itemsSpan.Length >= targetBlockArraySpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
if (itemsSpan.Length == 0)
{
break;
}
targetBlockIndex++;
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
if (_nextItemBlockIndex != targetBlockIndex)
{
_nextItemIndex = itemsSpan.Length;
_nextItemBlockIndex = targetBlockIndex;
}
else
{
_nextItemIndex += itemsSpan.Length;
}
}
else
{
_nextItemBlockIndex = targetBlockIndex + 1;
_nextItemIndex = 0;
}
_longCount = targetCapacity;
_lastBlockWithData = targetBlockIndex;
_version++;
}
public void AddRange(RecyclableList<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;
Span<T> itemsSpan = new(items._memoryBlock, 0, items._count);
Span<T[]> targetMemoryBlocksSpan = new(_memoryBlocks);
Span<T> targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
while (itemsSpan.Length >= targetBlockArraySpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (itemsSpan.Length == 0)
{
break;
}
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
_lastBlockWithData = targetBlockIndex;
if (_nextItemBlockIndex != targetBlockIndex)
{
_nextItemIndex = itemsSpan.Length;
_nextItemBlockIndex = targetBlockIndex;
}
else
{
_nextItemIndex += itemsSpan.Length;
}
}
else
{
_lastBlockWithData = targetBlockIndex - 1;
_nextItemBlockIndex = targetBlockIndex;
_nextItemIndex = 0;
}
_longCount = targetCapacity;
_version++;
}
public void AddRange(ReadOnlySpan<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;
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
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]);
}
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(RecyclableLongList<T> items)
{
if (items._longCount == 0)
{
return;
}
long itemsCount = items._longCount,
targetCapacity = _longCount + itemsCount;
if (_capacity < targetCapacity)
{
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
}
int blockSize = _blockSize,
sourceBlockIndex = 0,
targetBlockIndex = _nextItemBlockIndex,
toCopy;
Span<T[]> sourceMemoryBlocksSpan = new(items._memoryBlocks);
Span<T> itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]);
Span<T[]> targetMemoryBlocksSpan = new(_memoryBlocks);
Span<T> targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
long copiedCount = 0;
while (copiedCount < itemsCount)
{
toCopy = checked((int)Math.Min(itemsCount - copiedCount, itemsSpan.Length));
if (targetBlockArraySpan.Length < toCopy)
{
toCopy = targetBlockArraySpan.Length;
itemsSpan[..toCopy].CopyTo(targetBlockArraySpan);
targetBlockIndex++;
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]);
itemsSpan = itemsSpan[toCopy..];
copiedCount += toCopy;
}
else
{
itemsSpan[..toCopy].CopyTo(targetBlockArraySpan);
targetBlockArraySpan = targetBlockArraySpan[toCopy..];
itemsSpan = itemsSpan[toCopy..];
if (itemsSpan.IsEmpty && sourceBlockIndex + 1 < sourceMemoryBlocksSpan.Length)
{
sourceBlockIndex++;
itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]);
}
copiedCount += toCopy;
if (targetBlockArraySpan.IsEmpty && copiedCount < itemsCount)
{
targetBlockIndex++;
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]);
}
}
}
_lastBlockWithData = targetBlockIndex;
if ((targetCapacity & _blockSizeMinus1) == 0)
{
_nextItemIndex = 0;
_nextItemBlockIndex = targetBlockIndex + 1;
}
else
{
_nextItemIndex = checked((int)(targetCapacity & _blockSizeMinus1));
_nextItemBlockIndex = targetBlockIndex;
}
_longCount = targetCapacity;
_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;
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
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]);
}
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)
{
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,
itemsCount = items.Count,
copied = Math.Min(blockSize - _nextItemIndex, itemsCount);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
items.CopyTo(0, memoryBlocksSpan[targetBlockIndex], _nextItemIndex, copied);
if (_nextItemIndex + copied == blockSize)
{
targetBlockIndex++;
_nextItemIndex = 0;
}
else
{
_nextItemIndex += copied;
}
while (blockSize <= itemsCount - copied)
{
items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex++], 0, blockSize);
copied += blockSize;
}
if ((itemsCount - copied < blockSize) && (itemsCount - copied != 0))
{
_nextItemIndex = itemsCount - copied;
items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex], 0, _nextItemIndex);
}
_nextItemBlockIndex = targetBlockIndex;
_longCount = targetCapacity;
_lastBlockWithData = targetBlockIndex - (_nextItemIndex > 0 ? 0 : 1);
_version++;
}
public void AddRange(ICollection 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;
// 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);
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 (itemsSpan.Length == 0)
{
break;
}
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]);
}
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(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;
// 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);
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 (itemsSpan.Length == 0)
{
break;
}
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]);
}
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);
Span<T> blockArraySpan;
while (true)
{
if (copied + blockSize > capacity)
{
capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
memoryBlocksSpan = new(_memoryBlocks);
available = capacity - copied;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
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 (i == available)
{
break;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
}
}
copied += available;
}
}
public void AddRange(IEnumerable<T> items)
{
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 ICollection<T> sourceICollection)
{
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))
{
AddRangeWithKnownCount(items, requiredAdditionalCapacity);
_version++;
}
else
{
AddRangeEnumerated(items);
_version++;
}
}
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;
}
memoryBlocksSpan = new(_memoryBlocks);
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
for(var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
blockArraySpan[targetItemIdx++] = items[itemIndex];
if (targetItemIdx == blockSize)
{
targetItemIdx = 0;
targetBlockIdx++;
if (itemIndex + 1 == sourceItemsCount)
{
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)
@@ -1105,7 +284,6 @@ namespace Recyclable.Collections
}
}
// TODO: Do we want to initialize with empty arrays or null refs? Null refs may be faster.
Array.Fill(_memoryBlocks, null);
_capacity = 0;
_nextItemBlockIndex = 0;