From a80db94533712dcc0fe59497abd4c0402cc8e99b Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sat, 5 Aug 2023 17:05:29 +0200 Subject: [PATCH] RecyclableLongList.Insert implemented. --- .../RecyclableLongList.Helpers.cs | 32 ++++++++++++++++ Recyclable.Collections/RecyclableLongList.cs | 38 ++++++++++++++++++- .../RecyclableLongListTests.cs | 13 ++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/Recyclable.Collections/RecyclableLongList.Helpers.cs b/Recyclable.Collections/RecyclableLongList.Helpers.cs index 764d2bf..ecd5f25 100644 --- a/Recyclable.Collections/RecyclableLongList.Helpers.cs +++ b/Recyclable.Collections/RecyclableLongList.Helpers.cs @@ -90,6 +90,38 @@ namespace Recyclable.Collections } } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static void MakeRoomAndSet(RecyclableLongList list, long startingItemIndex, T item) + { + T[] currentBlock; + T[][] memoryBlocks = list._memoryBlocks; + int blockSizeMinus1 = list._blockSizeMinus1, + targetItemIndex = (int)(startingItemIndex & blockSizeMinus1), + targetBlockIndex = (int)(startingItemIndex >> list._blockSizePow2BitShift) + (targetItemIndex != 0 ? 1 : 0), + lastTakenBlockIndex = list._lastBlockWithData; + + if (list._nextItemIndex == 0) + { + new Span(memoryBlocks[lastTakenBlockIndex + 1])[0] = memoryBlocks[lastTakenBlockIndex][blockSizeMinus1]; + } + + int currentBlockIndex = lastTakenBlockIndex; + while (currentBlockIndex > targetBlockIndex) + { + new Span(currentBlock = memoryBlocks[currentBlockIndex--], 0, blockSizeMinus1) + .CopyTo(new Span(currentBlock, 1, blockSizeMinus1)); + new Span(currentBlock)[0] = memoryBlocks[currentBlockIndex][blockSizeMinus1]; + } + + if (lastTakenBlockIndex >= 0) + { + new Span(currentBlock = memoryBlocks[targetBlockIndex], targetItemIndex, blockSizeMinus1 - targetItemIndex) + .CopyTo(new Span(currentBlock, targetItemIndex + 1, blockSizeMinus1 - targetItemIndex)); + } + + new Span(memoryBlocks[targetBlockIndex])[targetItemIndex] = item; + } + public static void CopyTo(T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, T[] destinationArray, int destinationArrayIndex) { if (itemsCount <= 0) diff --git a/Recyclable.Collections/RecyclableLongList.cs b/Recyclable.Collections/RecyclableLongList.cs index a398916..5aa8da0 100644 --- a/Recyclable.Collections/RecyclableLongList.cs +++ b/Recyclable.Collections/RecyclableLongList.cs @@ -835,7 +835,43 @@ namespace Recyclable.Collections } } - public void Insert(int index, T item) => throw new NotSupportedException(); + public void Insert(int index, T item) + { + if (index > _longCount++ || index < 0) + { + _longCount--; + Helpers.ThrowIndexOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range. Expected value between 0 and {_longCount}"); + } + + if (_capacity < _longCount) + { + _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount))); + } + + if (_longCount > 1) + { + Helpers.MakeRoomAndSet(this, index, item); + } + else + { + new Span(_memoryBlocks[0])[0] = item; + } + + if (_nextItemIndex < _blockSizeMinus1) + { + if (++_nextItemIndex == 1) + { + _lastBlockWithData++; + } + } + else + { + _nextItemIndex = 0; + _nextItemBlockIndex++; + } + + _version++; + } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public long LongIndexOf(T item) diff --git a/Recyclable.CollectionsTests/RecyclableLongListTests.cs b/Recyclable.CollectionsTests/RecyclableLongListTests.cs index 9423742..7720fde 100644 --- a/Recyclable.CollectionsTests/RecyclableLongListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableLongListTests.cs @@ -513,10 +513,19 @@ namespace Recyclable.CollectionsTests public void InsertAtTheBeginningShouldMoveItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize); + using var list = new RecyclableLongList(); // Act - _ = Assert.Throws(() => list.Insert(0, -1)); + foreach (var item in testData) + { + list.Insert(0, item); + _ = list.Count.Should().Be((int)item); + } + + // Validate + _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); + _ = list.Count.Should().Be((int)itemsCount); + _ = list.Should().ContainInConsecutiveOrder(testData.Reverse()); } [Theory]