RecyclableList.AddRange(IEnumerable) made similar in behavior / optimizations to RecyclableArrayList. Variables named made similar.
This commit is contained in:
1 parent
7c78b6f87e
commit
f94e91c45d
1 file changed
+163
-30
@@ -11,6 +11,7 @@ namespace Recyclable.Collections
|
|||||||
private static readonly ArrayPool<T> _blockArrayPool = ArrayPool<T>.Create();
|
private static readonly ArrayPool<T> _blockArrayPool = ArrayPool<T>.Create();
|
||||||
private static readonly T[][] _emptyBlockArray = new T[0][];
|
private static readonly T[][] _emptyBlockArray = new T[0][];
|
||||||
private static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default;
|
private static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default;
|
||||||
|
|
||||||
private readonly int _blockSize;
|
private readonly int _blockSize;
|
||||||
protected T[][] _memoryBlocks;
|
protected T[][] _memoryBlocks;
|
||||||
|
|
||||||
@@ -57,36 +58,47 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
protected static T[][] SetNewLength(in T[][]? source, in int blockSize, in long newSize)
|
protected static T[][] SetNewLength(in T[][]? source, in int blockSize, in long newCapacity)
|
||||||
{
|
{
|
||||||
ArrayPool<T[]> arrayPool = _arrayPool;
|
ArrayPool<T[]> arrayPool = _arrayPool;
|
||||||
ArrayPool<T> blockArrayPool = _blockArrayPool;
|
ArrayPool<T> blockArrayPool = _blockArrayPool;
|
||||||
var sourceBlockCount = source?.Length ?? 0;
|
var sourceBlockCount = source?.Length ?? 0;
|
||||||
int requiredBlockCount = (int)(newSize / blockSize) + (newSize % blockSize > 0 ? 1 : 0);
|
int requiredBlockCount = (int)(newCapacity / blockSize) + (newCapacity % blockSize > 0 ? 1 : 0);
|
||||||
if (requiredBlockCount == sourceBlockCount)
|
if (requiredBlockCount == sourceBlockCount)
|
||||||
{
|
{
|
||||||
return source!;
|
return source!;
|
||||||
}
|
}
|
||||||
|
//else if (requiredBlockCount < sourceBlockCount)
|
||||||
|
//{
|
||||||
|
// requiredBlockCount = requiredBlockCount > 0 ? requiredBlockCount : 1;
|
||||||
|
// while (requiredBlockCount < sourceBlockCount)
|
||||||
|
// {
|
||||||
|
// requiredBlockCount *= 2;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
Span<T[]> newArraySpan;
|
Span<T[]> newArraySpan;
|
||||||
T[][] newMemoryBlocks = requiredBlockCount.RentArrayFromPool(arrayPool);
|
T[][] newMemoryBlocks = requiredBlockCount.RentArrayFromPool(arrayPool);
|
||||||
switch (requiredBlockCount >= sourceBlockCount)
|
switch (requiredBlockCount >= sourceBlockCount)
|
||||||
{
|
{
|
||||||
case true:
|
case true:
|
||||||
if (sourceBlockCount > 0)
|
switch (sourceBlockCount > 0)
|
||||||
{
|
{
|
||||||
Memory<T[]> sourceMemory = new(source);
|
case true:
|
||||||
Memory<T[]> newArrayMemory = new(newMemoryBlocks);
|
Memory<T[]> sourceMemory = new(source);
|
||||||
sourceMemory.CopyTo(newArrayMemory);
|
Memory<T[]> newArrayMemory = new(newMemoryBlocks);
|
||||||
source!.ReturnToPool(arrayPool);
|
sourceMemory.CopyTo(newArrayMemory);
|
||||||
newArraySpan = new Span<T[]>(newMemoryBlocks)[sourceBlockCount..];
|
source!.ReturnToPool(arrayPool);
|
||||||
}
|
newArraySpan = new Span<T[]>(newMemoryBlocks)[sourceBlockCount..];
|
||||||
else
|
break;
|
||||||
{
|
|
||||||
newArraySpan = new Span<T[]>(newMemoryBlocks);
|
case false:
|
||||||
|
newArraySpan = new Span<T[]>(newMemoryBlocks);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < newArraySpan.Length; i++)
|
int uninitializedBlocksCount = newArraySpan.Length;
|
||||||
|
for (int i = 0; i < uninitializedBlocksCount; i++)
|
||||||
{
|
{
|
||||||
newArraySpan[i] = blockSize.RentArrayFromPool(blockArrayPool);
|
newArraySpan[i] = blockSize.RentArrayFromPool(blockArrayPool);
|
||||||
}
|
}
|
||||||
@@ -98,9 +110,10 @@ namespace Recyclable.Collections
|
|||||||
newArraySpan = new Span<T[]>(newMemoryBlocks);
|
newArraySpan = new Span<T[]>(newMemoryBlocks);
|
||||||
sourceSpan.CopyTo(newArraySpan);
|
sourceSpan.CopyTo(newArraySpan);
|
||||||
sourceSpan = new Span<T[]>(source)[newArraySpan.Length..];
|
sourceSpan = new Span<T[]>(source)[newArraySpan.Length..];
|
||||||
for (int i = 0; i < sourceSpan.Length; i++)
|
int sourceLength = sourceSpan.Length;
|
||||||
|
for (int i = 0; i < sourceLength; i++)
|
||||||
{
|
{
|
||||||
sourceSpan[i].ReturnToPool(_blockArrayPool);
|
sourceSpan[i].ReturnToPool(blockArrayPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
source!.ReturnToPool(arrayPool);
|
source!.ReturnToPool(arrayPool);
|
||||||
@@ -174,7 +187,7 @@ namespace Recyclable.Collections
|
|||||||
public T this[long index]
|
public T this[long index]
|
||||||
{
|
{
|
||||||
get => _memoryBlocks[(int)(index / _blockSize)][index % _blockSize];
|
get => _memoryBlocks[(int)(index / _blockSize)][index % _blockSize];
|
||||||
set => new Span<T>(_memoryBlocks[(int)(index / _blockSize)])[(int)index % _blockSize] = value;
|
set => new Span<T>(_memoryBlocks[(int)(index / _blockSize)])[(int)(index % _blockSize)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T this[int index]
|
public T this[int index]
|
||||||
@@ -193,7 +206,7 @@ namespace Recyclable.Collections
|
|||||||
_ = EnsureCapacity(requiredCapacity);
|
_ = EnsureCapacity(requiredCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
_memoryBlocks[(int)(oldCount / blockSize)][oldCount % blockSize] = item;
|
_memoryBlocks[(int)(oldCount / blockSize)][(int)(oldCount % blockSize)] = item;
|
||||||
_longCount++;
|
_longCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,15 +223,21 @@ namespace Recyclable.Collections
|
|||||||
int blockSize = _blockSize;
|
int blockSize = _blockSize;
|
||||||
int targetItemIdx = (int)(oldLongCount % blockSize);
|
int targetItemIdx = (int)(oldLongCount % blockSize);
|
||||||
int targetBlockIdx = (int)(oldLongCount / blockSize) + (targetItemIdx > 0 ? 1 : 0);
|
int targetBlockIdx = (int)(oldLongCount / blockSize) + (targetItemIdx > 0 ? 1 : 0);
|
||||||
var memoryBlockSpan = new Span<T[]>(_memoryBlocks);
|
var memoryBlocksSpan = new Span<T[]>(_memoryBlocks);
|
||||||
var targetBlockSpan = new Span<T>(memoryBlockSpan[targetBlockIdx]);
|
var memoryBlocksCount = memoryBlocksSpan.Length;
|
||||||
for (long i = 0; i < sourceItemsCount; i++)
|
var targetBlockSpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
|
for (var i = 0L; i < sourceItemsCount; i++)
|
||||||
{
|
{
|
||||||
targetBlockSpan[targetItemIdx++] = items[i];
|
targetBlockSpan[targetItemIdx++] = items[i];
|
||||||
if (targetItemIdx == blockSize)
|
if (targetItemIdx == blockSize)
|
||||||
{
|
{
|
||||||
targetBlockIdx++;
|
targetBlockIdx++;
|
||||||
targetBlockSpan = new Span<T>(memoryBlockSpan[targetBlockIdx]);
|
if (targetBlockIdx == memoryBlocksCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetBlockSpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
targetItemIdx = 0;
|
targetItemIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,15 +258,21 @@ namespace Recyclable.Collections
|
|||||||
int blockSize = _blockSize;
|
int blockSize = _blockSize;
|
||||||
int targetItemIdx = (int)(oldLongCount % blockSize);
|
int targetItemIdx = (int)(oldLongCount % blockSize);
|
||||||
int targetBlockIdx = (int)(oldLongCount / blockSize) + (targetItemIdx > 0 ? 1 : 0);
|
int targetBlockIdx = (int)(oldLongCount / blockSize) + (targetItemIdx > 0 ? 1 : 0);
|
||||||
var memoryBlockSpan = new Span<T[]>(_memoryBlocks);
|
var memoryBlocksSpan = new Span<T[]>(_memoryBlocks);
|
||||||
var targetBlockSpan = new Span<T>(memoryBlockSpan[targetBlockIdx]);
|
var memoryBlocksCount = memoryBlocksSpan.Length;
|
||||||
|
var targetBlockSpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
for (int i = 0; i < sourceItemsCount; i++)
|
for (int i = 0; i < sourceItemsCount; i++)
|
||||||
{
|
{
|
||||||
targetBlockSpan[targetItemIdx++] = items[i];
|
targetBlockSpan[targetItemIdx++] = items[i];
|
||||||
if (targetItemIdx == blockSize)
|
if (targetItemIdx == blockSize)
|
||||||
{
|
{
|
||||||
targetBlockIdx++;
|
targetBlockIdx++;
|
||||||
targetBlockSpan = new Span<T>(memoryBlockSpan[targetBlockIdx]);
|
if (targetBlockIdx == memoryBlocksCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetBlockSpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
targetItemIdx = 0;
|
targetItemIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,15 +293,21 @@ namespace Recyclable.Collections
|
|||||||
int blockSize = _blockSize;
|
int blockSize = _blockSize;
|
||||||
int targetItemIdx = (int)(oldLongCount % blockSize);
|
int targetItemIdx = (int)(oldLongCount % blockSize);
|
||||||
int targetBlockIdx = (int)(oldLongCount / blockSize) + (targetItemIdx > 0 ? 1 : 0);
|
int targetBlockIdx = (int)(oldLongCount / blockSize) + (targetItemIdx > 0 ? 1 : 0);
|
||||||
var memoryBlockSpan = new Span<T[]>(_memoryBlocks);
|
var memoryBlocksSpan = new Span<T[]>(_memoryBlocks);
|
||||||
var targetBlockSpan = new Span<T>(memoryBlockSpan[targetBlockIdx]);
|
var memoryBlocksCount = memoryBlocksSpan.Length;
|
||||||
|
var targetBlockSpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
for (int i = 0; i < sourceItemsCount; i++)
|
for (int i = 0; i < sourceItemsCount; i++)
|
||||||
{
|
{
|
||||||
targetBlockSpan[targetItemIdx++] = items[i];
|
targetBlockSpan[targetItemIdx++] = items[i];
|
||||||
if (targetItemIdx == blockSize)
|
if (targetItemIdx == blockSize)
|
||||||
{
|
{
|
||||||
targetBlockIdx++;
|
targetBlockIdx++;
|
||||||
targetBlockSpan = new Span<T>(memoryBlockSpan[targetBlockIdx]);
|
if (targetBlockIdx == memoryBlocksCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetBlockSpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
targetItemIdx = 0;
|
targetItemIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,12 +315,114 @@ namespace Recyclable.Collections
|
|||||||
_longCount = targetCapacity;
|
_longCount = targetCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddRange(RecyclableList<T> destination, IEnumerable<T> source)
|
private void AddRange(RecyclableList<T> destination, IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
foreach (var item in source)
|
if (source is T[] sourceArray)
|
||||||
{
|
{
|
||||||
destination.Add(item);
|
AddRange(sourceArray);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (source is List<T> sourceList)
|
||||||
|
{
|
||||||
|
AddRange(sourceList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source is RecyclableArrayList<T> sourceRecyclableArrayList)
|
||||||
|
{
|
||||||
|
AddRange(sourceRecyclableArrayList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source is IList<T> sourceIList)
|
||||||
|
{
|
||||||
|
AddRange(sourceIList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long capacity = _capacity;
|
||||||
|
long oldLongCount = _longCount;
|
||||||
|
int blockSize = _blockSize;
|
||||||
|
int targetItemIdx = (int)(oldLongCount % blockSize);
|
||||||
|
int targetBlockIdx = (int)(targetItemIdx / blockSize) + (targetItemIdx > 0 ? 1 : 0);
|
||||||
|
|
||||||
|
Span<T[]> memoryBlocksSpan;
|
||||||
|
Span<T> blockArraySpan;
|
||||||
|
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||||
|
{
|
||||||
|
_ = EnsureCapacity(targetItemIdx + requiredAdditionalCapacity);
|
||||||
|
memoryBlocksSpan = new(_memoryBlocks);
|
||||||
|
var memoryBlocksCount = memoryBlocksSpan.Length;
|
||||||
|
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
blockArraySpan[targetItemIdx++] = item;
|
||||||
|
if (targetItemIdx == blockSize)
|
||||||
|
{
|
||||||
|
targetBlockIdx++;
|
||||||
|
if (targetBlockIdx == memoryBlocksCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetItemIdx = 0;
|
||||||
|
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_longCount = targetItemIdx;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long i;
|
||||||
|
using var enumerator = source.GetEnumerator();
|
||||||
|
|
||||||
|
if (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
long available = capacity - targetItemIdx;
|
||||||
|
memoryBlocksSpan = new(_memoryBlocks);
|
||||||
|
var memoryBlocksCount = memoryBlocksSpan.Length;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (targetItemIdx + growByCount > capacity)
|
||||||
|
{
|
||||||
|
capacity = EnsureCapacity(capacity + growByCount);
|
||||||
|
memoryBlocksSpan = new(_memoryBlocks);
|
||||||
|
memoryBlocksCount = memoryBlocksSpan.Length;
|
||||||
|
available = capacity - targetItemIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockArraySpan = memoryBlocksSpan[targetBlockIdx];
|
||||||
|
for (i = 0; i < available; i++)
|
||||||
|
{
|
||||||
|
blockArraySpan[targetItemIdx++] = enumerator.Current;
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetItemIdx == blockSize)
|
||||||
|
{
|
||||||
|
targetBlockIdx++;
|
||||||
|
if (targetBlockIdx == memoryBlocksCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetItemIdx = 0;
|
||||||
|
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < available)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_longCount = targetItemIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
|||||||
Reference in new issue
Block a user