From b21b80e222d2a4eb1fedfb1562eedf2e70f08aa2 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Mon, 20 Mar 2023 20:29:50 +0100 Subject: [PATCH 1/3] RecyclableList modified to respect the removal of blocks. RecyclableList.IndexOf fixed to work in various combinations. RecyclableList tests extended. --- Recyclable.Collections/RecyclableList.cs | 50 ++++++++++++------- .../RecyclableListTests.cs | 22 +++++++- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index 97e6556..14af33d 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -22,9 +22,6 @@ namespace Recyclable.Collections protected ArrayPool _blockArrayPool; protected T[][] _memoryBlocks; - public int BlockSize => _blockSize; - public int BlockCount => _memoryBlocks.Length; - private long _capacity; public long Capacity { @@ -43,6 +40,7 @@ namespace Recyclable.Collections set => _longCount = value; } + public int BlockCount => (int)(_capacity / _blockSize); public int LastBlockIndex => _lastBlockIndex; public int NextItemIndex => _nextItemIndex; @@ -98,7 +96,7 @@ namespace Recyclable.Collections long capacity = _capacity; long available = capacity - copied; - Span memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length); + Span memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount); Span blockArraySpan; var memoryBlocksCount = memoryBlocksSpan.Length; while (true) @@ -172,7 +170,7 @@ namespace Recyclable.Collections blockSize = _blockSize; } - var memoryBlocksCount = _memoryBlocks.Length; + var memoryBlocksCount = BlockCount; memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx], 0, blockSize); foreach (var item in source) @@ -199,9 +197,9 @@ namespace Recyclable.Collections protected static long DoIndexOf(T item, in T[][] memoryBlocks, int lastBlockIndex, int blockSize, int nextItemIndex) { - Span memoryBlocksSpan = new(memoryBlocks); - int itemIndex; - for (var blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++) + Span memoryBlocksSpan = new(memoryBlocks, 0, lastBlockIndex + 1); + int itemIndex, blockIndex; + for (blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++) { itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize); if (itemIndex >= 0) @@ -210,18 +208,28 @@ namespace Recyclable.Collections } } - itemIndex = Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, nextItemIndex); - return itemIndex >= 0 ? itemIndex + (lastBlockIndex * blockSize) : itemIndex; + if (blockIndex == lastBlockIndex) + { + 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)] - protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, long newCapacity, ArrayPool memoryBlocksPool, ArrayPool blockArrayPool) + protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, long oldCapacity, long newCapacity, ArrayPool memoryBlocksPool, ArrayPool blockArrayPool) { const int sourceBlockCount = 0, requiredBlockCount = 1, uninitializedBlocksCount = 2, i = 3; Span localInts = stackalloc int[4] { - source?.Length ?? 0, // sourceBlockCount + (int)(oldCapacity / minBlockSize), // sourceBlockCount (int)(newCapacity / minBlockSize) + (newCapacity % minBlockSize > 0 ? 1 : 0), // requiredBlockCount 0, // uninitializedBlocksCount 0, // i @@ -297,7 +305,7 @@ namespace Recyclable.Collections newCapacity = requestedCapacity; } - memory = SetNewLength(memory, _blockSize, newCapacity, _memoryBlocksPool, _blockArrayPool); + memory = SetNewLength(memory, _blockSize, _capacity, newCapacity, _memoryBlocksPool, _blockArrayPool); if (newCapacity > 0 && _capacity == 0) { _blockSize = memory[0].Length; @@ -318,7 +326,7 @@ namespace Recyclable.Collections if (expectedItemsCount > 0) { - _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); + _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); if (_memoryBlocks.Length > 0) { minBlockSize = _memoryBlocks[0].Length; @@ -342,7 +350,7 @@ namespace Recyclable.Collections _blockArrayPool = blockArrayPool; if (expectedItemsCount > 0) { - _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); + _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); if (_memoryBlocks.Length > 0) { minBlockSize = _memoryBlocks[0].Length; @@ -425,8 +433,12 @@ namespace Recyclable.Collections targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize); } + if (itemsSpan.Length > 0) + { + itemsSpan.CopyTo(targetBlockArraySpan); + } + _nextItemIndex = itemsSpan.Length; - itemsSpan.CopyTo(targetBlockArraySpan); _longCount = targetCapacity; _lastBlockIndex = targetBlockIndex; } @@ -685,7 +697,7 @@ namespace Recyclable.Collections { if (_blockSize >= RecyclableDefaults.MinPooledArrayLength) { - Span memoryBlocksSpan = new(_memoryBlocks); + Span memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount); int memoryBlocksCount = memoryBlocksSpan.Length; ArrayPool blockArrayPool = _blockArrayPool; for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++) @@ -735,14 +747,14 @@ namespace Recyclable.Collections ? ItemNotFoundIndex : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) ? 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 long LongIndexOf(T item) => _longCount == 0 ? ItemNotFoundIndex : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) ? 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) { diff --git a/Recyclable.CollectionsTests/RecyclableListTests.cs b/Recyclable.CollectionsTests/RecyclableListTests.cs index 56c518f..9521781 100644 --- a/Recyclable.CollectionsTests/RecyclableListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableListTests.cs @@ -77,8 +77,13 @@ namespace Recyclable.CollectionsTests [Theory] [MemberData(nameof(RecyclableListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableListTestData))] - public void InitializeSortClearShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { + if (itemsCount == 0) + { + return; + } + // Act using RecyclableList list = new(testData, minBlockSize: targetBlockSize); list.QuickSort(); @@ -86,13 +91,26 @@ namespace Recyclable.CollectionsTests // Validate _ = list.LongCount.Should().Be(itemsCount); _ = list.Should().Equal(testData); - _ = testData.Should().AllSatisfy(expected => list.Contains(expected)); // Act list.Clear(); // Validate _ = 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] From b1e6c587c3f21c0eff783cb24f59bac529dfeafd Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Mon, 20 Mar 2023 20:29:50 +0100 Subject: [PATCH 2/3] RecyclableList modified to respect the removal of blocks. RecyclableList.IndexOf fixed to work in various combinations. RecyclableList tests extended. --- Recyclable.Collections/RecyclableList.cs | 51 ++++++++++++------- .../RecyclableListTests.cs | 22 +++++++- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index 97e6556..a441572 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -22,9 +22,6 @@ namespace Recyclable.Collections protected ArrayPool _blockArrayPool; protected T[][] _memoryBlocks; - public int BlockSize => _blockSize; - public int BlockCount => _memoryBlocks.Length; - private long _capacity; public long Capacity { @@ -43,6 +40,8 @@ namespace Recyclable.Collections set => _longCount = value; } + public int BlockCount => (int)(_capacity / _blockSize); + public int BlockSize => _blockSize; public int LastBlockIndex => _lastBlockIndex; public int NextItemIndex => _nextItemIndex; @@ -98,7 +97,7 @@ namespace Recyclable.Collections long capacity = _capacity; long available = capacity - copied; - Span memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length); + Span memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount); Span blockArraySpan; var memoryBlocksCount = memoryBlocksSpan.Length; while (true) @@ -172,7 +171,7 @@ namespace Recyclable.Collections blockSize = _blockSize; } - var memoryBlocksCount = _memoryBlocks.Length; + var memoryBlocksCount = BlockCount; memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx], 0, blockSize); 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) { - Span memoryBlocksSpan = new(memoryBlocks); - int itemIndex; - for (var blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++) + Span memoryBlocksSpan = new(memoryBlocks, 0, lastBlockIndex + 1); + int itemIndex, blockIndex; + for (blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++) { itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize); if (itemIndex >= 0) @@ -210,18 +209,28 @@ namespace Recyclable.Collections } } - itemIndex = Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, nextItemIndex); - return itemIndex >= 0 ? itemIndex + (lastBlockIndex * blockSize) : itemIndex; + if (blockIndex == lastBlockIndex) + { + 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)] - protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, long newCapacity, ArrayPool memoryBlocksPool, ArrayPool blockArrayPool) + protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, long oldCapacity, long newCapacity, ArrayPool memoryBlocksPool, ArrayPool blockArrayPool) { const int sourceBlockCount = 0, requiredBlockCount = 1, uninitializedBlocksCount = 2, i = 3; Span localInts = stackalloc int[4] { - source?.Length ?? 0, // sourceBlockCount + (int)(oldCapacity / minBlockSize), // sourceBlockCount (int)(newCapacity / minBlockSize) + (newCapacity % minBlockSize > 0 ? 1 : 0), // requiredBlockCount 0, // uninitializedBlocksCount 0, // i @@ -297,7 +306,7 @@ namespace Recyclable.Collections newCapacity = requestedCapacity; } - memory = SetNewLength(memory, _blockSize, newCapacity, _memoryBlocksPool, _blockArrayPool); + memory = SetNewLength(memory, _blockSize, _capacity, newCapacity, _memoryBlocksPool, _blockArrayPool); if (newCapacity > 0 && _capacity == 0) { _blockSize = memory[0].Length; @@ -318,7 +327,7 @@ namespace Recyclable.Collections if (expectedItemsCount > 0) { - _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); + _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); if (_memoryBlocks.Length > 0) { minBlockSize = _memoryBlocks[0].Length; @@ -342,7 +351,7 @@ namespace Recyclable.Collections _blockArrayPool = blockArrayPool; if (expectedItemsCount > 0) { - _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); + _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); if (_memoryBlocks.Length > 0) { minBlockSize = _memoryBlocks[0].Length; @@ -425,8 +434,12 @@ namespace Recyclable.Collections targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize); } + if (itemsSpan.Length > 0) + { + itemsSpan.CopyTo(targetBlockArraySpan); + } + _nextItemIndex = itemsSpan.Length; - itemsSpan.CopyTo(targetBlockArraySpan); _longCount = targetCapacity; _lastBlockIndex = targetBlockIndex; } @@ -685,7 +698,7 @@ namespace Recyclable.Collections { if (_blockSize >= RecyclableDefaults.MinPooledArrayLength) { - Span memoryBlocksSpan = new(_memoryBlocks); + Span memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount); int memoryBlocksCount = memoryBlocksSpan.Length; ArrayPool blockArrayPool = _blockArrayPool; for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++) @@ -735,14 +748,14 @@ namespace Recyclable.Collections ? ItemNotFoundIndex : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) ? 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 long LongIndexOf(T item) => _longCount == 0 ? ItemNotFoundIndex : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) ? 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) { diff --git a/Recyclable.CollectionsTests/RecyclableListTests.cs b/Recyclable.CollectionsTests/RecyclableListTests.cs index 56c518f..9521781 100644 --- a/Recyclable.CollectionsTests/RecyclableListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableListTests.cs @@ -77,8 +77,13 @@ namespace Recyclable.CollectionsTests [Theory] [MemberData(nameof(RecyclableListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableListTestData))] - public void InitializeSortClearShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { + if (itemsCount == 0) + { + return; + } + // Act using RecyclableList list = new(testData, minBlockSize: targetBlockSize); list.QuickSort(); @@ -86,13 +91,26 @@ namespace Recyclable.CollectionsTests // Validate _ = list.LongCount.Should().Be(itemsCount); _ = list.Should().Equal(testData); - _ = testData.Should().AllSatisfy(expected => list.Contains(expected)); // Act list.Clear(); // Validate _ = 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] From b91c4476eb234225a029d902db9599b38a2eca04 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Mon, 20 Mar 2023 23:46:50 +0100 Subject: [PATCH 3/3] RecyclableList.BlockCount renamed => RecyclableList.ReservedBlockCount. Fixed to always include the last block. RecyclableList.LastTakenBlockIndex property added for some scenarios. RecyclableList.IndexOf & .LongIndexOf fixed to use the correct index. RecyclableArrayList.AddRange(RecyclableList items) fixed to use the correct index. Some test cases excluded, where needed. --- Recyclable.Collections/RecyclableArrayList.cs | 2 +- Recyclable.Collections/RecyclableList.cs | 16 ++++++++-------- Recyclable.Collections/RecyclableStack.cs | 2 +- .../RecyclableListTests.cs | 19 +++++++++++++++++-- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Recyclable.Collections/RecyclableArrayList.cs b/Recyclable.Collections/RecyclableArrayList.cs index 7b83164..0d91bd0 100644 --- a/Recyclable.Collections/RecyclableArrayList.cs +++ b/Recyclable.Collections/RecyclableArrayList.cs @@ -279,7 +279,7 @@ namespace Recyclable.Collections itemsSpan = new(items.MemoryBlocks[blockIndex], 0, (int)sourceItemsCount); itemsSpan.CopyTo(targetSpan); } - else if (blockIndex < items.BlockCount) + else if (blockIndex <= items.LastTakenBlockIndex) { itemsSpan = new(items.MemoryBlocks[blockIndex], 0, items.NextItemIndex); itemsSpan.CopyTo(targetSpan); diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index a441572..aac2350 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -31,16 +31,16 @@ namespace Recyclable.Collections public int Count => checked((int)_longCount); public bool IsReadOnly { get; } + public int LastTakenBlockIndex => _lastBlockIndex - (_nextItemIndex > 0 ? 0 : 1); protected long _longCount; - public long LongCount { get => _longCount; set => _longCount = value; } - public int BlockCount => (int)(_capacity / _blockSize); + public int ReservedBlockCount => (int)(_capacity / _blockSize) + (_capacity % _blockSize > 0 ? 1 : 0); public int BlockSize => _blockSize; public int LastBlockIndex => _lastBlockIndex; public int NextItemIndex => _nextItemIndex; @@ -70,7 +70,7 @@ namespace Recyclable.Collections T[][] memoryBlocks = list._memoryBlocks; if (blockSize >= RecyclableDefaults.MinPooledArrayLength) { - blockArrayPool.Return(memoryBlocks[list.BlockCount - 1]); + blockArrayPool.Return(memoryBlocks[list.ReservedBlockCount - 1]); } list._capacity -= blockSize; @@ -97,7 +97,7 @@ namespace Recyclable.Collections long capacity = _capacity; long available = capacity - copied; - Span memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount); + Span memoryBlocksSpan = new(_memoryBlocks, 0, ReservedBlockCount); Span blockArraySpan; var memoryBlocksCount = memoryBlocksSpan.Length; while (true) @@ -171,7 +171,7 @@ namespace Recyclable.Collections blockSize = _blockSize; } - var memoryBlocksCount = BlockCount; + var memoryBlocksCount = ReservedBlockCount; memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx], 0, blockSize); foreach (var item in source) @@ -698,7 +698,7 @@ namespace Recyclable.Collections { if (_blockSize >= RecyclableDefaults.MinPooledArrayLength) { - Span memoryBlocksSpan = new(_memoryBlocks, 0, BlockCount); + Span memoryBlocksSpan = new(_memoryBlocks, 0, ReservedBlockCount); int memoryBlocksCount = memoryBlocksSpan.Length; ArrayPool blockArrayPool = _blockArrayPool; for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++) @@ -748,14 +748,14 @@ namespace Recyclable.Collections ? ItemNotFoundIndex : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) ? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount) - : (int)DoIndexOf(item, _memoryBlocks, BlockCount - 1, _blockSize, _nextItemIndex); + : (int)DoIndexOf(item, _memoryBlocks, LastTakenBlockIndex, _blockSize, _nextItemIndex); public void Insert(int index, T item) => throw new NotSupportedException(); public long LongIndexOf(T item) => _longCount == 0 ? ItemNotFoundIndex : _lastBlockIndex == 0 || (_lastBlockIndex == 1 && _nextItemIndex == 0) ? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount) - : DoIndexOf(item, _memoryBlocks, BlockCount - 1, _blockSize, _nextItemIndex); + : DoIndexOf(item, _memoryBlocks, LastTakenBlockIndex, _blockSize, _nextItemIndex); public bool Remove(T item) { diff --git a/Recyclable.Collections/RecyclableStack.cs b/Recyclable.Collections/RecyclableStack.cs index c915341..fb67715 100644 --- a/Recyclable.Collections/RecyclableStack.cs +++ b/Recyclable.Collections/RecyclableStack.cs @@ -49,7 +49,7 @@ namespace Recyclable.Collections LongCount--; if ((List.Capacity * _blockSize) - LongCount == _blockSize) { - List.RemoveBlock(List.BlockCount - 1); + List.RemoveBlock(List.ReservedBlockCount - 1); } return toRemove; diff --git a/Recyclable.CollectionsTests/RecyclableListTests.cs b/Recyclable.CollectionsTests/RecyclableListTests.cs index 9521781..4bc2cca 100644 --- a/Recyclable.CollectionsTests/RecyclableListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableListTests.cs @@ -291,8 +291,13 @@ namespace Recyclable.CollectionsTests [Theory] [MemberData(nameof(RecyclableListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableListTestData))] - public void RemoveShouldRaiseNotSupportedException(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + public void RemoveShouldRaiseNotSupportedExceptionWhenNotLastItem(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { + if (itemsCount is 0 or 1) + { + return; + } + // Prepare using var list = new RecyclableList(testData, minBlockSize: targetBlockSize); @@ -302,8 +307,13 @@ namespace Recyclable.CollectionsTests [Theory] [MemberData(nameof(RecyclableListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableListTestData))] - public void RemoveAtShouldRaiseNotSupportedException(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + public void RemoveAtShouldRaiseNotSupportedExceptionWhenNotLastItem(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { + if (itemsCount is 0 or 1) + { + return; + } + // Prepare using var list = new RecyclableList(testData, minBlockSize: targetBlockSize); @@ -530,6 +540,11 @@ namespace Recyclable.CollectionsTests [MemberData(nameof(RecyclableListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableListTestData))] public void LongIndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { + if (itemsCount == 0) + { + return; + } + // Prepare using var list = new RecyclableList(testData, minBlockSize: targetBlockSize); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for");