RecyclableLongList fixes to LastBlockWithData calculations. Tests extended.

This commit is contained in:
mlemanczyk committed 2023-06-10 06:39:30 +02:00
1 parent 79c3eeb33f
commit 5b961c73d1
2 files changed
+37 -9

No files matched your search

+19 -9
View File
@@ -37,7 +37,7 @@ namespace Recyclable.Collections
public int Count => checked((int)_longCount);
public bool IsReadOnly { get; }
private int _lastBlockWithData;
private int _lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
public int LastBlockWithData => _lastBlockWithData;
internal long _longCount;
@@ -50,6 +50,7 @@ namespace Recyclable.Collections
public int ReservedBlockCount => _reservedBlockCount;
public int BlockSize => _blockSize;
public int BlockSizeMinus1 => _blockSizeMinus1;
public byte BlockSizePow2BitShift => _blockSizePow2BitShift;
public int NextItemBlockIndex => _nextItemBlockIndex;
public int NextItemIndex => _nextItemIndex;
@@ -470,10 +471,15 @@ namespace Recyclable.Collections
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex++] = item;
if (_nextItemIndex == _blockSize)
{
if (_nextItemBlockIndex > _lastBlockWithData)
{
_lastBlockWithData++;
}
_nextItemBlockIndex++;
_nextItemIndex = 0;
}
else if (_nextItemIndex == 1)
else if (_nextItemIndex == 1 && _nextItemBlockIndex > _lastBlockWithData)
{
_lastBlockWithData++;
}
@@ -501,28 +507,32 @@ namespace Recyclable.Collections
Span<T> itemsSpan = new(items);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
while (targetBlockArraySpan.Length <= itemsSpan.Length)
while (itemsSpan.Length >= targetBlockArraySpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (targetBlockIndex == memoryBlockCount)
if (itemsSpan.Length == 0)
{
break;
}
targetBlockIndex++;
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
_nextItemBlockIndex = targetBlockIndex;
}
else
{
_nextItemBlockIndex = targetBlockIndex + 1;
}
_nextItemIndex = itemsSpan.Length;
_longCount = targetCapacity;
_nextItemBlockIndex = targetBlockIndex;
_lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
_lastBlockWithData = targetBlockIndex;
}
public void AddRange(RecyclableList<T> items)
@@ -822,7 +832,7 @@ namespace Recyclable.Collections
}
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
int lastBlockIndex = _nextItemBlockIndex;
int lastBlockIndex = _lastBlockWithData;
for (int blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++)
{
if (Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, _blockSize) >= 0)
@@ -831,7 +841,7 @@ namespace Recyclable.Collections
}
}
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
return Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex > 0 ? _nextItemIndex : _blockSize) >= 0;
}
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
@@ -45,6 +45,7 @@ namespace Recyclable.CollectionsTests
// Validate
int expectedItemsCount = testData.Count();
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedItemsCount, "when capacity == 0, then we should allocate as much memory as needed, only");
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / list.BlockSize) - (itemsCount % list.BlockSize != 0 ? 0 : 1));
_ = list.LongCount.Should().Be(expectedItemsCount);
_ = list.Should().ContainInConsecutiveOrder(testData);
}
@@ -88,6 +89,7 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.Capacity.Should().Be(0);
_ = list.LastBlockWithData.Should().Be(-1);
_ = list.LongCount.Should().Be(0);
_ = list.Should().BeEmpty();
}
@@ -144,6 +146,7 @@ namespace Recyclable.CollectionsTests
}
_ = list.LongCount.Should().Be(expectedData.Length);
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(expectedData);
}
@@ -163,6 +166,7 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.LongCount.Should().Be(itemsCount * 2);
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData);
}
@@ -181,6 +185,7 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.LongCount.Should().Be(itemsCount, "we added so many items");
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / list.BlockSize) - (itemsCount % list.BlockSize == 0 ? 1 : 0));
_ = list.Should().ContainInConsecutiveOrder(testData);
}
@@ -213,6 +218,7 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.LongCount.Should().Be(itemsCount * 2);
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData.Concat(testData));
}
@@ -572,11 +578,13 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.Count.Should().Be((int)(itemsCount - deleted));
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
}
_ = list.Should().BeEmpty();
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
}
[Theory]
@@ -594,11 +602,13 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.Count.Should().Be((int)(itemsCount - deleted));
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData.Take((int)(itemsCount - deleted)));
}
_ = list.Should().BeEmpty();
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
}
[Theory]
@@ -616,8 +626,13 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.Count.Should().Be((int)(itemsCount - deleted));
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
}
_ = list.Should().BeEmpty();
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
}
[Theory]
@@ -634,11 +649,14 @@ namespace Recyclable.CollectionsTests
_ = list.Remove(item).Should().BeTrue($"we search for {item} which should exist");
removedCount++;
var expectedList = expectedData[0..((int)itemsCount - removedCount)];
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.LongCount.Should().Be(expectedList.Length);
_ = list.Should().ContainInConsecutiveOrder(expectedList);
}
_ = list.Should().BeEmpty();
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
_ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex);
}
}
}