diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index a862503..3a4a6d1 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -548,7 +548,7 @@ namespace Recyclable.Collections } long capacity = _capacity; - long oldLongCount = _longCount; + long copied = _longCount; int blockSize = _blockSize; int targetItemIdx = _nextItemIndex; int targetBlockIdx = _lastBlockIndex; @@ -557,7 +557,7 @@ namespace Recyclable.Collections Span blockArraySpan; if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) { - long requiredCapacity = oldLongCount + requiredAdditionalCapacity; + long requiredCapacity = copied + requiredAdditionalCapacity; if (capacity < requiredCapacity) { _ = EnsureCapacity(requiredCapacity); @@ -594,51 +594,66 @@ namespace Recyclable.Collections if (enumerator.MoveNext()) { - long available = capacity - targetItemIdx; - memoryBlocksSpan = new(_memoryBlocks); + long available = capacity - copied, + initialCount = copied; + + memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length); var memoryBlocksCount = memoryBlocksSpan.Length; while (true) { - if (targetItemIdx + growByCount > capacity) + if (copied + growByCount > capacity) { capacity = EnsureCapacity(capacity + growByCount); - memoryBlocksSpan = new(_memoryBlocks); - memoryBlocksCount = memoryBlocksSpan.Length; - available = capacity - targetItemIdx; + memoryBlocksCount = _memoryBlocks.Length; + if (blockSize != _blockSize) + { + blockSize = _blockSize; + } + + memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); + available = capacity - copied; } - blockArraySpan = memoryBlocksSpan[targetBlockIdx]; - for (i = 0; i < available; i++) + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize); + for (i = 1; i <= available; i++) { blockArraySpan[targetItemIdx++] = enumerator.Current; + if (!enumerator.MoveNext()) { - break; + copied += i; + if (targetItemIdx < blockSize) + { + _lastBlockIndex = targetBlockIdx; + _nextItemIndex = targetItemIdx; + } + else + { + _lastBlockIndex = targetBlockIdx + 1; + _nextItemIndex = 0; + } + + _longCount += copied - _longCount; + return; } if (targetItemIdx == blockSize) { targetBlockIdx++; + targetItemIdx = 0; + if (targetBlockIdx == memoryBlocksCount) { break; } - targetItemIdx = 0; - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize); } } - if (i < available) - { - break; - } + copied += available; } } - - _longCount = targetItemIdx; - _lastBlockIndex = targetBlockIdx; - _nextItemIndex = targetItemIdx; } public void Clear() diff --git a/Recyclable.CollectionsTests/RecyclableListTests.cs b/Recyclable.CollectionsTests/RecyclableListTests.cs index 8b538cd..8cbac3b 100644 --- a/Recyclable.CollectionsTests/RecyclableListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableListTests.cs @@ -320,7 +320,8 @@ namespace Recyclable.CollectionsTests new object[] { "RecyclableArrayList", _testData.ToRecyclableArrayList() }, new object[] { "RecyclableList", _testData.ToRecyclableList() }, new object[] { "IList", new CustomIList(_testData) }, - new object[] { "IEnumerable", _testData }, + new object[] { "IEnumerable with non-enumerated count", _testData }, + new object[] { "IEnumerable without non-enumerated count", new EnumerableWithoutCount(_testData) } }; public static IEnumerable AddRangeEmptyCollectionTypes => new[] @@ -433,7 +434,8 @@ namespace Recyclable.CollectionsTests // Validate var expectedData = testData.Concat(testData).ToArray(); - _ = list.Capacity.Should().Be(229376L, "when capacity == 0, then we allocate as much memory as needed, only"); + _ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedData.Length) + .And.BeLessThan((int)(expectedData.Length * 1.5)); _ = list.Should().HaveCount(expectedData.Length) .And.ContainInConsecutiveOrder(expectedData) .And.BeEquivalentTo(expectedData);