From 3e66504e0942bc1acc3c67fead4142218d19dd12 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sun, 10 Dec 2023 22:58:20 +0100 Subject: [PATCH] RecyclableLongList.Compatibility.List.FindAll() implemented. --- ...ecyclableLongListCompatibilityListTests.cs | 47 +++---- ...yclableLongList.Compatibility.List.Find.cs | 131 ++++++++++++++---- 2 files changed, 126 insertions(+), 52 deletions(-) diff --git a/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs b/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs index ee518ea..a730b40 100644 --- a/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs +++ b/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs @@ -391,30 +391,6 @@ namespace Recyclable.CollectionsTests } } - [Theory] - [MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))] - public void FindAllShouldReturnCorrectIndexes(int itemsCount) - { - const int ExpectedItem = 1; - - // Prepare - var testData = Enumerable.Range(0, itemsCount).Select(value => value % 2 == 0 ? ExpectedItem : -ExpectedItem); - using var list = new RecyclableLongList(testData, initialCapacity: itemsCount); - - // Act - using var actual = list.FindAll(x => x == ExpectedItem); - - // Validate - var expectedData = testData.ToList().FindAll(x => x == ExpectedItem); - - _ = actual.Count.Should().Be(expectedData.Count); - _ = actual.Should().Equal(expectedData); - if (expectedData.Count > 0) - { - _ = actual.Should().AllSatisfy(x => x.Should().Be(ExpectedItem)).And.Equal(expectedData); - } - } - [Theory] [MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))] public void FindAllShouldNotFindNonExistingItems(int itemsCount) @@ -435,6 +411,29 @@ namespace Recyclable.CollectionsTests _ = foundItems.Should().Equal(expectedData); } + [Theory] + [MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))] + public void FindAllShouldReturnCorrectIndexes(int itemsCount) + { + const int ExpectedItem = 1; + + // Prepare + var testData = Enumerable.Range(0, itemsCount).Select(value => value % 2 == 0 ? ExpectedItem : -ExpectedItem); + using var list = new RecyclableLongList(testData, initialCapacity: itemsCount); + var expectedData = testData.ToList().FindAll(x => x == ExpectedItem); + + // Act + using var actual = list.FindAll(x => x == ExpectedItem); + + // Validate + _ = actual.Count.Should().Be(expectedData.Count); + _ = actual.Should().Equal(expectedData); + if (expectedData.Count > 0) + { + _ = actual.Should().AllSatisfy(x => x.Should().Be(ExpectedItem)).And.Equal(expectedData); + } + } + [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] public void FindIndexShouldReturnCorrectIndexes(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) diff --git a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs index 62c8883..54e9e6e 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs @@ -1,5 +1,7 @@ // Ignore Spelling: zzz +using System.Numerics; + namespace Recyclable.Collections { #pragma warning disable IDE1006 // This intentional so that it shows at the end of IntelliSense list @@ -81,41 +83,114 @@ namespace Recyclable.Collections throw new NotImplementedException(); } - public static RecyclableList FindAll(this RecyclableLongList list, Predicate match) + public static RecyclableLongList FindAll(this RecyclableLongList list, Predicate match) { - //int sourceItemsCount = list._count; - //if (sourceItemsCount == 0) - //{ - // return new(); - //} + if (list._longCount == 0) + { + return new(); + } - //ReadOnlySpan sourceSpan = list._memoryBlock; - //RecyclableList result = new(sourceItemsCount >> 3); - //Span resultSpan = result._memoryBlock; - //int capacity = result._capacity, - // resultItemsCount = 0; + int sourceBlockIndex = 0, + blockSize = list._blockSize, + targetBlockIndex = 0; + var targetCapacity = Math.Max(list._longCount >> 3, RecyclableDefaults.InitialCapacity); + if (!BitOperations.IsPow2(targetCapacity)) + { + targetCapacity = checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)); + } - //for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) - //{ - // if (match(sourceSpan[itemIndex])) - // { - // if (resultItemsCount + 1 > capacity) - // { - // capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1)); - // _ = RecyclableListHelpers.EnsureCapacity(result, resultItemsCount, capacity); - // resultSpan = result._memoryBlock; - // } + RecyclableLongList result = new(blockSize, targetCapacity); + ReadOnlySpan resultMemoryBlocksSpan = result._memoryBlocks; + Span resultMemoryBlockSpan = resultMemoryBlocksSpan[0]; + ReadOnlySpan sourceMemoryBlocksSpan = list._memoryBlocks; + ReadOnlySpan sourceMemoryBlockSpan = sourceMemoryBlocksSpan[0]; + var sourceItemIndex = 0; + int lastBlockWithData = list._lastBlockWithData; + var targetItemIndex = 0; + var fullBlocksItemsCount = 0L; + while (sourceBlockIndex < lastBlockWithData) + { + var item = sourceMemoryBlockSpan[sourceItemIndex]; + if (match(item)) + { + if (fullBlocksItemsCount + targetItemIndex >= targetCapacity) + { + targetCapacity = RecyclableLongList.Helpers.Resize(result, blockSize, result._blockSizePow2BitShift, targetCapacity << 1); + resultMemoryBlocksSpan = new(list._memoryBlocks); + } + + if (targetItemIndex == blockSize) + { + targetItemIndex = 0; + resultMemoryBlockSpan = resultMemoryBlocksSpan[++targetBlockIndex]; + fullBlocksItemsCount += blockSize; + } - // resultSpan[resultItemsCount++] = sourceSpan[itemIndex]; - // } - //} + resultMemoryBlockSpan[targetItemIndex++] = item; + } - //result._capacity = capacity; - //result._count = resultItemsCount; - //return result; + if (sourceItemIndex + 1 == blockSize) + { + sourceItemIndex = 0; + sourceBlockIndex++; + sourceMemoryBlockSpan = sourceMemoryBlocksSpan[sourceBlockIndex]; - throw new NotImplementedException(); + if (sourceBlockIndex == lastBlockWithData) + { + break; + } + } + else + { + sourceItemIndex++; + } + } + + if (sourceBlockIndex == lastBlockWithData) + { + // We're re-using another variable for better performance + lastBlockWithData = list._nextItemIndex > 0 ? list._nextItemIndex : blockSize; + while (sourceItemIndex < lastBlockWithData) + { + var item = sourceMemoryBlockSpan[sourceItemIndex++]; + if (match(item)) + { + if (fullBlocksItemsCount + targetItemIndex >= targetCapacity) + { + targetCapacity = RecyclableLongList.Helpers.Resize(result, blockSize, result._blockSizePow2BitShift, targetCapacity << 1); + resultMemoryBlocksSpan = new(list._memoryBlocks); + } + + if (targetItemIndex == blockSize) + { + targetItemIndex = 0; + resultMemoryBlockSpan = resultMemoryBlocksSpan[++targetBlockIndex]; + fullBlocksItemsCount += blockSize; + } + + resultMemoryBlockSpan[targetItemIndex++] = item; + } + } + } + + if (targetItemIndex == blockSize) + { + result._nextItemBlockIndex = targetBlockIndex + 1; + result._nextItemIndex = 0; + result._lastBlockWithData = targetBlockIndex; + } + else + { + result._nextItemBlockIndex = targetBlockIndex; + result._nextItemIndex = targetItemIndex; + result._lastBlockWithData = targetBlockIndex; + } + + result._longCount = fullBlocksItemsCount + targetItemIndex; + result._capacity = targetCapacity; + return result; } + public static RecyclableList FindAllIndexes(this RecyclableLongList list, Predicate match) { //int sourceItemsCount = list._count;