.AddRange methods moved to their own classes, for better method resolution times.
This commit is contained in:
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user