RecyclableList modified to respect the removal of blocks.

RecyclableList.IndexOf fixed to work in various combinations.

RecyclableList tests extended.
This commit is contained in:
mlemanczyk committed 2023-03-20 21:48:09 +01:00
1 parent 070549de9e
commit b1e6c587c3
2 files changed
+52 -21

No files matched your search

+32 -19
View File
@@ -22,9 +22,6 @@ namespace Recyclable.Collections
protected ArrayPool<T> _blockArrayPool; protected ArrayPool<T> _blockArrayPool;
protected T[][] _memoryBlocks; protected T[][] _memoryBlocks;
public int BlockSize => _blockSize;
public int BlockCount => _memoryBlocks.Length;
private long _capacity; private long _capacity;
public long Capacity public long Capacity
{ {
@@ -43,6 +40,8 @@ namespace Recyclable.Collections
set => _longCount = value; set => _longCount = value;
} }
public int BlockCount => (int)(_capacity / _blockSize);
public int BlockSize => _blockSize;
public int LastBlockIndex => _lastBlockIndex; public int LastBlockIndex => _lastBlockIndex;
public int NextItemIndex => _nextItemIndex; public int NextItemIndex => _nextItemIndex;
@@ -98,7 +97,7 @@ namespace Recyclable.Collections
long capacity = _capacity; long capacity = _capacity;
long available = capacity - copied; long available = capacity - copied;
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length); Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount);
Span<T> blockArraySpan; Span<T> blockArraySpan;
var memoryBlocksCount = memoryBlocksSpan.Length; var memoryBlocksCount = memoryBlocksSpan.Length;
while (true) while (true)
@@ -172,7 +171,7 @@ namespace Recyclable.Collections
blockSize = _blockSize; blockSize = _blockSize;
} }
var memoryBlocksCount = _memoryBlocks.Length; var memoryBlocksCount = BlockCount;
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx], 0, blockSize); blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
foreach (var item in source) foreach (var item in source)
@@ -199,9 +198,9 @@ namespace Recyclable.Collections
protected static long DoIndexOf(T item, in T[][] memoryBlocks, int lastBlockIndex, int blockSize, int nextItemIndex) protected static long DoIndexOf(T item, in T[][] memoryBlocks, int lastBlockIndex, int blockSize, int nextItemIndex)
{ {
Span<T[]> memoryBlocksSpan = new(memoryBlocks); Span<T[]> memoryBlocksSpan = new(memoryBlocks, 0, lastBlockIndex + 1);
int itemIndex; int itemIndex, blockIndex;
for (var blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++) for (blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++)
{ {
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize); itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize);
if (itemIndex >= 0) if (itemIndex >= 0)
@@ -210,18 +209,28 @@ namespace Recyclable.Collections
} }
} }
itemIndex = Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, nextItemIndex); if (blockIndex == lastBlockIndex)
return itemIndex >= 0 ? itemIndex + (lastBlockIndex * blockSize) : itemIndex; {
itemIndex = nextItemIndex != 0
? Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, nextItemIndex)
: Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, blockSize);
}
else
{
return ItemNotFoundIndex;
}
return itemIndex >= 0 ? itemIndex + (lastBlockIndex * blockSize) : ItemNotFoundIndex;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, long newCapacity, ArrayPool<T[]> memoryBlocksPool, ArrayPool<T> blockArrayPool) protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, long oldCapacity, long newCapacity, ArrayPool<T[]> memoryBlocksPool, ArrayPool<T> blockArrayPool)
{ {
const int sourceBlockCount = 0, requiredBlockCount = 1, uninitializedBlocksCount = 2, i = 3; const int sourceBlockCount = 0, requiredBlockCount = 1, uninitializedBlocksCount = 2, i = 3;
Span<int> localInts = stackalloc int[4] Span<int> localInts = stackalloc int[4]
{ {
source?.Length ?? 0, // sourceBlockCount (int)(oldCapacity / minBlockSize), // sourceBlockCount
(int)(newCapacity / minBlockSize) + (newCapacity % minBlockSize > 0 ? 1 : 0), // requiredBlockCount (int)(newCapacity / minBlockSize) + (newCapacity % minBlockSize > 0 ? 1 : 0), // requiredBlockCount
0, // uninitializedBlocksCount 0, // uninitializedBlocksCount
0, // i 0, // i
@@ -297,7 +306,7 @@ namespace Recyclable.Collections
newCapacity = requestedCapacity; newCapacity = requestedCapacity;
} }
memory = SetNewLength(memory, _blockSize, newCapacity, _memoryBlocksPool, _blockArrayPool); memory = SetNewLength(memory, _blockSize, _capacity, newCapacity, _memoryBlocksPool, _blockArrayPool);
if (newCapacity > 0 && _capacity == 0) if (newCapacity > 0 && _capacity == 0)
{ {
_blockSize = memory[0].Length; _blockSize = memory[0].Length;
@@ -318,7 +327,7 @@ namespace Recyclable.Collections
if (expectedItemsCount > 0) if (expectedItemsCount > 0)
{ {
_memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0) if (_memoryBlocks.Length > 0)
{ {
minBlockSize = _memoryBlocks[0].Length; minBlockSize = _memoryBlocks[0].Length;
@@ -342,7 +351,7 @@ namespace Recyclable.Collections
_blockArrayPool = blockArrayPool; _blockArrayPool = blockArrayPool;
if (expectedItemsCount > 0) if (expectedItemsCount > 0)
{ {
_memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0) if (_memoryBlocks.Length > 0)
{ {
minBlockSize = _memoryBlocks[0].Length; minBlockSize = _memoryBlocks[0].Length;
@@ -425,8 +434,12 @@ namespace Recyclable.Collections
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize); targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
} }
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
}
_nextItemIndex = itemsSpan.Length; _nextItemIndex = itemsSpan.Length;
itemsSpan.CopyTo(targetBlockArraySpan);
_longCount = targetCapacity; _longCount = targetCapacity;
_lastBlockIndex = targetBlockIndex; _lastBlockIndex = targetBlockIndex;
} }
@@ -685,7 +698,7 @@ namespace Recyclable.Collections
{ {
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength) if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{ {
Span<T[]> memoryBlocksSpan = new(_memoryBlocks); Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount);
int memoryBlocksCount = memoryBlocksSpan.Length; int memoryBlocksCount = memoryBlocksSpan.Length;
ArrayPool<T> blockArrayPool = _blockArrayPool; ArrayPool<T> blockArrayPool = _blockArrayPool;
for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++) for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++)
@@ -735,14 +748,14 @@ namespace Recyclable.Collections
? ItemNotFoundIndex ? ItemNotFoundIndex
: _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0)
? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount) ? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount)
: (int)DoIndexOf(item, _memoryBlocks, _lastBlockIndex, _blockSize, _nextItemIndex); : (int)DoIndexOf(item, _memoryBlocks, BlockCount - 1, _blockSize, _nextItemIndex);
public void Insert(int index, T item) => throw new NotSupportedException(); public void Insert(int index, T item) => throw new NotSupportedException();
public long LongIndexOf(T item) => _longCount == 0 public long LongIndexOf(T item) => _longCount == 0
? ItemNotFoundIndex ? ItemNotFoundIndex
: _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0)
? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount) ? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount)
: DoIndexOf(item, _memoryBlocks, _lastBlockIndex, _blockSize, _nextItemIndex); : DoIndexOf(item, _memoryBlocks, BlockCount - 1, _blockSize, _nextItemIndex);
public bool Remove(T item) public bool Remove(T item)
{ {
@@ -77,8 +77,13 @@ namespace Recyclable.CollectionsTests
[Theory] [Theory]
[MemberData(nameof(RecyclableListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableListTestData))] [MemberData(nameof(RecyclableListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableListTestData))]
public void InitializeSortClearShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize) public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
{ {
if (itemsCount == 0)
{
return;
}
// Act // Act
using RecyclableList<long> list = new(testData, minBlockSize: targetBlockSize); using RecyclableList<long> list = new(testData, minBlockSize: targetBlockSize);
list.QuickSort(); list.QuickSort();
@@ -86,13 +91,26 @@ namespace Recyclable.CollectionsTests
// Validate // Validate
_ = list.LongCount.Should().Be(itemsCount); _ = list.LongCount.Should().Be(itemsCount);
_ = list.Should().Equal(testData); _ = list.Should().Equal(testData);
_ = testData.Should().AllSatisfy(expected => list.Contains(expected));
// Act // Act
list.Clear(); list.Clear();
// Validate // Validate
_ = list.LongCount.Should().Be(0); _ = list.LongCount.Should().Be(0);
_ = list.Should().BeEmpty();
// Act
list.AddRange(testData);
// Validate
_ = list.LongCount.Should().Be(itemsCount);
_ = list.Should().ContainInConsecutiveOrder(testData);
// Act
foreach (var item in testData.Reverse())
{
_ = list.Remove(item).Should().BeTrue();
}
} }
[Theory] [Theory]