diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs index a664683..9c74f0b 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs @@ -1,10 +1,12 @@ using Collections.Benchmarks.Core; -namespace Recyclable.Collections.Benchmarks +namespace Recyclable.Collections.Compatibility.List.Benchmarks { - public class BinarySearchBenchmarks : CollectionsBenchmarksBase + public class BinarySearchBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks { - public void Array_BinarySearch_BestAndWorstCases() + public override string MethodNameSuffix => "_BinarySearch"; + + public void Array_BinarySearch() { var data = TestObjects; var list = TestObjects; @@ -16,7 +18,7 @@ namespace Recyclable.Collections.Benchmarks } } - public void List_BinarySearch_BestAndWorstCases() + public void List_BinarySearch() { var data = TestObjects; var list = TestObjectsAsList; @@ -28,7 +30,7 @@ namespace Recyclable.Collections.Benchmarks } } - public void PooledList_BinarySearch_BestAndWorstCases() + public void PooledList_BinarySearch() { var data = TestObjects; var list = TestObjectsAsPooledList; @@ -40,7 +42,7 @@ namespace Recyclable.Collections.Benchmarks } } - public void RecyclableList_BinarySearch_BestAndWorstCases() + public void RecyclableList_BinarySearch() { var data = TestObjects; var list = TestObjectsAsRecyclableList; @@ -52,7 +54,7 @@ namespace Recyclable.Collections.Benchmarks } } - // public void RecyclableLongList_BinarySearch_BestAndWorstCases() + // public void RecyclableLongList_BinarySearch() // { // var data = TestObjects; // var list = TestObjectsAsRecyclableLongList; diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs index b9e9a60..26780bb 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs @@ -1,9 +1,11 @@ using Collections.Benchmarks.Core; -namespace Recyclable.Collections.Benchmarks +namespace Recyclable.Collections.Compatibility.List.Benchmarks { - public class ConvertAllBenchmarks : CollectionsBenchmarksBase + public class ConvertAllBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks { + public override string MethodNameSuffix => "_ConvertAll"; + public void Array_ConvertAll() { var data = TestObjects; diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs index ee6158f..34db6bb 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs @@ -1,9 +1,11 @@ using Collections.Benchmarks.Core; -namespace Recyclable.Collections.Benchmarks +namespace Recyclable.Collections.Compatibility.List.Benchmarks { - public partial class RecyclableCollectionsCompatibilityListBenchmarks + public class ExistsBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks { + public override string MethodNameSuffix => "_Exists_BestAndWorstCases"; + public void Array_Exists_BestAndWorstCases() { var data = TestObjects; diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs index 1d13950..87a3a7e 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs @@ -1,9 +1,11 @@ using Collections.Benchmarks.Core; -namespace Recyclable.Collections.Benchmarks +namespace Recyclable.Collections.Compatibility.List.Benchmarks { - public partial class RecyclableCollectionsCompatibilityListBenchmarks + public class FindAllBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks { + public override string MethodNameSuffix => "_FindAll_BestAndWorstCases"; + public void Array_FindAll_BestAndWorstCases() { var data = TestObjects; diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs index 648cab9..9038b0e 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs @@ -1,9 +1,11 @@ using Collections.Benchmarks.Core; -namespace Recyclable.Collections.Benchmarks +namespace Recyclable.Collections.Compatibility.List.Benchmarks { - public partial class RecyclableCollectionsCompatibilityListBenchmarks : CollectionsBenchmarksBase + public class FindLastIndexBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks { + public override string MethodNameSuffix => "_FindLastIndex_BestAndWorstCases"; + public void Array_FindLastIndex_BestAndWorstCases() { var data = TestObjects; diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs index 5b89d10..02d9c40 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs @@ -1,9 +1,11 @@ using Collections.Benchmarks.Core; -namespace Recyclable.Collections.Benchmarks +namespace Recyclable.Collections.Compatibility.List.Benchmarks { - public partial class RecyclableCollectionsCompatibilityListBenchmarks + public class FindLastBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks { + public override string MethodNameSuffix => "_FindLast_BestAndWorstCases"; + public void Array_FindLast_BestAndWorstCases() { var data = TestObjects; diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs index 028e1a6..fd81d08 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs @@ -1,9 +1,11 @@ using Collections.Benchmarks.Core; -namespace Recyclable.Collections.Benchmarks +namespace Recyclable.Collections.Compatibility.List.Benchmarks { - public partial class RecyclableCollectionsCompatibilityListBenchmarks + public class FindBenchmarks: RecyclableCollectionsCompatibilityListBenchmarks { + public override string MethodNameSuffix => "_Find_BestAndWorstCases"; + public void Array_Find_BestAndWorstCases() { var data = TestObjects; diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs index 3751555..ee58af5 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs @@ -1,2 +1,25 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; + +namespace Recyclable.Collections.Compatibility.List.Benchmarks +{ + public static class Program + { + static IConfig BenchmarkConfig { get; } = ManualConfig.Create(DefaultConfig.Instance) + .WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary); + + static void Main() + { + BenchmarkRunner.Run(new[] + { + typeof(BinarySearchBenchmarks), + typeof(ConvertAllBenchmarks), + typeof(ExistsBenchmarks), + typeof(FindBenchmarks), + typeof(FindAllBenchmarks), + typeof(FindLastBenchmarks), + typeof(FindLastIndexBenchmarks) + }, BenchmarkConfig); + } + } +} \ No newline at end of file diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj b/Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj index 4fbb469..1fca1aa 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj @@ -1,15 +1,19 @@ - + Exe - net6.0 + net6.0;net7.0 enable enable - - + + + + + + diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/RecyclableCollectionsCompatibilityListBenchmarks.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/RecyclableCollectionsCompatibilityListBenchmarks.cs new file mode 100644 index 0000000..e9616c6 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/RecyclableCollectionsCompatibilityListBenchmarks.cs @@ -0,0 +1,23 @@ +using BenchmarkDotNet.Attributes; +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Compatibility.List.Benchmarks +{ + + public partial class RecyclableCollectionsCompatibilityListBenchmarks : DataDrivenBenchmarksBase + { + [Params(CollectionsBenchmarksSource.Array)] + public override CollectionsBenchmarksSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } + + [Params( + CollectionsBenchmarksSource.List, + CollectionsBenchmarksSource.PooledList, + CollectionsBenchmarksSource.RecyclableList + )] + public override CollectionsBenchmarksSource DataType { get => base.DataType; set => base.DataType = value; } + + //[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 70, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)] + [Params(RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinItemsCountForParallelization)] + public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; } + } +} diff --git a/Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj b/Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj index d8e4e38..fbc8c9b 100644 --- a/Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj +++ b/Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj @@ -12,8 +12,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -25,4 +25,8 @@ + + + + diff --git a/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs b/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs index 730e4b9..da1d55b 100644 --- a/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs +++ b/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs @@ -3,6 +3,7 @@ using Recyclable.Collections; using Recyclable.Collections.TestData; using Recyclable.Collections.Pools; using System.Numerics; +using System.Collections; #pragma warning disable xUnit1026, RCS1163, IDE0039, IDE0060, RCS1235 @@ -201,6 +202,27 @@ namespace Recyclable.CollectionsTests } } + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindIndexShouldNotAnythingWhenRangeExcludesItem(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.FindIndex((int)itemIndex + 1, (int)(itemsCount - itemIndex - 1), item => item == expectedItem); + + // Act + var actual = list.FindIndex((int)itemIndex + 1, (int)(itemsCount - itemIndex - 1), item => item == expectedItem); + + // Validate + _ = actual.Should().Be(-1).And.Be(expected); + } + } + [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] public void FindIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) @@ -475,6 +497,82 @@ namespace Recyclable.CollectionsTests } } + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void InsertRangeShouldAddItemsInTheRightPosition(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + var expectedData = testData.Reverse().ToArray(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableList(testData.Reverse(), itemsCount); + var expected = expectedData.ToList(); + expected.InsertRange((int)itemIndex, testData); + + if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (Array)testData); + } + else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (ICollection)testData); + } + else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (ICollection)testData); + } + else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (IEnumerable)testData); + } + else if (testCase.Contains("IReadOnlyList[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (IReadOnlyList)testData); + } + else if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, new ReadOnlySpan((long[])testData)); + } + else if (testCase.Contains("Span[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, new Span((long[])testData)); + } + else if (testData is long[] testDataArray) + { + list.InsertRange((int)itemIndex, testDataArray); + } + else if (testData is List testDataList) + { + list.InsertRange((int)itemIndex, testDataList); + } + else if (testData is RecyclableList testDataRecyclableList) + { + list.InsertRange((int)itemIndex, testDataRecyclableList); + } + else if (testData is RecyclableLongList testDataRecyclableLongList) + { + list.InsertRange((int)itemIndex, testDataRecyclableLongList); + } + else if (testData is IList testDataIList) + { + list.InsertRange((int)itemIndex, testDataIList); + } + else if (testData is IEnumerable testDataIEnumerable) + { + list.InsertRange((int)itemIndex, testDataIEnumerable); + } + else + { + throw new InvalidCastException("Unknown type of test data"); + } + + // Validate + list.Count.Should().Be(itemsCount << 1); + _ = list.Should().Equal(expected); + } + } + [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] public void LastIndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) @@ -564,7 +662,62 @@ namespace Recyclable.CollectionsTests expectedItems.InsertRange((int)itemIndex, testData); // Act - list.InsertRange((int)itemIndex, testData); + if (testCase.Contains("Array[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (Array)testData); + } + else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (ICollection)testData); + } + else if (testCase.Contains("ICollection[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (ICollection)testData); + } + else if (testCase.Contains("IEnumerable[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (IEnumerable)testData); + } + else if (testCase.Contains("IReadOnlyList[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, (IReadOnlyList)testData); + } + else if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, new ReadOnlySpan((long[])testData)); + } + else if (testCase.Contains("Span[", StringComparison.OrdinalIgnoreCase)) + { + list.InsertRange((int)itemIndex, new Span((long[])testData)); + } + else if (testData is long[] testDataArray) + { + list.InsertRange((int)itemIndex, testDataArray); + } + else if (testData is List testDataList) + { + list.InsertRange((int)itemIndex, testDataList); + } + else if (testData is RecyclableList testDataRecyclableList) + { + list.InsertRange((int)itemIndex, testDataRecyclableList); + } + else if (testData is RecyclableLongList testDataRecyclableLongList) + { + list.InsertRange((int)itemIndex, testDataRecyclableLongList); + } + else if (testData is IList testDataIList) + { + list.InsertRange((int)itemIndex, testDataIList); + } + else if (testData is IEnumerable testDataIEnumerable) + { + list.InsertRange((int)itemIndex, testDataIEnumerable); + } + else + { + throw new InvalidCastException("Unknown type of test data"); + } // Validate _ = list[(int)itemIndex].Should().Be(item); diff --git a/Recyclable.Collections.Compatibility.List.sln b/Recyclable.Collections.Compatibility.List.sln index 837756c..2daf82b 100644 --- a/Recyclable.Collections.Compatibility.List.sln +++ b/Recyclable.Collections.Compatibility.List.sln @@ -7,26 +7,40 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Comp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Compatibility.List.Tests", "Recyclable.Collections.Compatibility.List.Tests\Recyclable.Collections.Compatibility.List.Tests.csproj", "{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Recyclable.Collections.Compatibility.List.Benchmarks", "Recyclable.Collections.Compatibility.List.Benchmarks\Recyclable.Collections.Compatibility.List.Benchmarks.csproj", "{A1414948-E61B-4AE1-ACAF-33A8BB85041E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Compatibility.List.Benchmarks", "Recyclable.Collections.Compatibility.List.Benchmarks\Recyclable.Collections.Compatibility.List.Benchmarks.csproj", "{A1414948-E61B-4AE1-ACAF-33A8BB85041E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|x64.ActiveCfg = Debug|x64 + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|x64.Build.0 = Debug|x64 {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|Any CPU.Build.0 = Release|Any CPU + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|x64.ActiveCfg = Release|x64 + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|x64.Build.0 = Release|x64 {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|x64.ActiveCfg = Debug|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|x64.Build.0 = Debug|Any CPU {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|Any CPU.Build.0 = Release|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|x64.ActiveCfg = Release|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|x64.Build.0 = Release|Any CPU {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|x64.ActiveCfg = Debug|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|x64.Build.0 = Debug|Any CPU {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|Any CPU.Build.0 = Release|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|x64.ActiveCfg = Release|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj b/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj index 8470a1d..ba8aa43 100644 --- a/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj +++ b/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj @@ -6,7 +6,7 @@ enable true Recyclable.Collections.Compatibility.List - 0.0.1 + 0.0.2 Marek Lemańczyk N/A Recyclable.Collections.Compatibility.List @@ -18,8 +18,9 @@ LICENSE.md README.md git - Support for RecyclableList<T> & RecyclableLongList<T> added. + Collections.Benchmarks.Core package updated. Bug fixes. AnyCPU;x64 + logo.png @@ -36,6 +37,6 @@ - + diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs index 8fa9bbd..6b0e677 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs @@ -117,13 +117,12 @@ namespace Recyclable.Collections public static int FindIndex(this RecyclableList list, int startIndex, int count, Predicate match) { - int sourceItemsCount = list._count; + int sourceItemsCount = Math.Min(startIndex + count, list._count); if (sourceItemsCount == 0 || startIndex >= sourceItemsCount) { return RecyclableDefaults.ItemNotFoundIndex; } - sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount); ReadOnlySpan sourceSpan = list._memoryBlock; for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++) { diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs index 4d3fdf5..000ddb7 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs @@ -74,6 +74,7 @@ namespace Recyclable.Collections { list._capacity = RecyclableListHelpers.EnsureCapacity(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity)))); + // TODO: Count should be requiredAdditionalCapacity, we need to move as many items as many we're inserting Array.Copy(list._memoryBlock, index, list._memoryBlock, index + requiredAdditionalCapacity, list._count - index); Span memorySpan = list._memoryBlock; foreach (var item in items) @@ -94,6 +95,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); Array.Copy(items, items.GetLowerBound(0), memoryBlock, index, items.Length); list._count += items.Length; @@ -109,6 +111,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); new ReadOnlySpan(items).CopyTo(new Span(memoryBlock, index, items.Length)); list._count += items.Length; @@ -124,6 +127,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); items.CopyTo(new Span(memoryBlock, index, items.Length)); list._count += items.Length; @@ -139,6 +143,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); items.CopyTo(new Span(memoryBlock, index, items.Length)); list._count += items.Length; @@ -154,6 +159,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); items.CopyTo(memoryBlock, index); list._count += items.Count; @@ -169,6 +175,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); items.CopyTo(list._memoryBlock, index); list._count += items.Count; @@ -184,6 +191,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); items.CopyTo(memoryBlock, index); list._count += items.Count; @@ -199,6 +207,7 @@ namespace Recyclable.Collections } T[] memoryBlock = list._memoryBlock; + // TODO: Count should be items.Count, we need to move as many items as many we're inserting Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); new ReadOnlySpan(items._memoryBlock, 0, items._count).CopyTo(new(list._memoryBlock, index, items._count)); list._count += items._count; @@ -257,43 +266,64 @@ namespace Recyclable.Collections list._version++; } - public static IEnumerator InsertRange(this RecyclableList list, int index, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength) + public static void InsertRange(this RecyclableList list, int index, IEnumerable items, int growByCount = RecyclableDefaults.MinPooledArrayLength) { - int targetItemIndex = list._count; - Span memorySpan; - - int i; - var enumerator = source.GetEnumerator(); - - int capacity = list._capacity; - memorySpan = list._memoryBlock; - if (enumerator.MoveNext()) + var enumerator = items.GetEnumerator(); + if (!enumerator.MoveNext()) { - int available = capacity - targetItemIndex; - do - { - if (targetItemIndex + growByCount > capacity) - { - capacity = RecyclableListHelpers.EnsureCapacity(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); - memorySpan = list._memoryBlock; - available = capacity - targetItemIndex; - } - - for (i = 0; i < available; i++) - { - memorySpan[targetItemIndex++] = (T)enumerator.Current; - if (!enumerator.MoveNext()) - { - break; - } - } - } - while (i >= available); + return; } + int capacity = list._capacity, + added = 0, + available = capacity - list._count, + i, + movingBlockLength = list._count - index; + + T[] memoryBlock = list._memoryBlock; + if (available > 0) + { + Array.Copy(memoryBlock, index, memoryBlock, capacity - movingBlockLength, movingBlockLength); + } + + Span memorySpan = memoryBlock; + do + { + if (index + available + movingBlockLength > capacity) + { + var capacityBeforeIncrease = capacity; + capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)(capacity + growByCount))); + _ = RecyclableListHelpers.EnsureCapacity(list, memoryBlock.Length, capacity); + memorySpan = memoryBlock = list._memoryBlock; + available = capacity - index - movingBlockLength; + Array.Copy(memoryBlock, capacityBeforeIncrease - movingBlockLength, memoryBlock, capacity - movingBlockLength, movingBlockLength); + } + + for (i = 0; i < available; i++) + { + memorySpan[index++] = (T)enumerator.Current; + if (!enumerator.MoveNext()) + { + added++; + break; + } + } + + added += i; + } + while (i == available); + + if (i != available) + { + Array.Copy(memoryBlock, capacity - movingBlockLength, memoryBlock, index, movingBlockLength); + if (RecyclableList._needsClearing) + { + Array.Clear(memoryBlock, capacity - movingBlockLength, movingBlockLength); + } + } + + list._count += added; list._capacity = capacity; - list._count = targetItemIndex; - return enumerator; } public static void InsertRange(this RecyclableList list, int index, IReadOnlyList items) @@ -304,7 +334,8 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount)))); } - Span memorySpan = new(list._memoryBlock, list._count, sourceItemsCount); + Array.Copy(list._memoryBlock, index, list._memoryBlock, index + sourceItemsCount, Math.Min(list.Count - index, sourceItemsCount)); + Span memorySpan = new(list._memoryBlock, index, sourceItemsCount); for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++) { memorySpan[sourceItemIndex] = items[sourceItemIndex]; diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs index f586d7b..f40d810 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs @@ -2,6 +2,7 @@ using System.Collections.ObjectModel; using System.Numerics; +using System.Runtime.CompilerServices; using Recyclable.Collections.Pools; namespace Recyclable.Collections @@ -13,12 +14,15 @@ namespace Recyclable.Collections { public static ReadOnlyCollection AsReadOnly(this RecyclableList list) => new(list); + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static int BinarySearch(this RecyclableList list, T item) => Array.BinarySearch(list._memoryBlock, 0, list._count, item); + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static int BinarySearch(this RecyclableList list, T item, IComparer? comparer) => Array.BinarySearch(list._memoryBlock, 0, list._count, item, comparer); + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static int BinarySearch(this RecyclableList list, int index, int count, T item, IComparer? comparer) => Array.BinarySearch(list._memoryBlock, index, count, item, comparer); @@ -42,8 +46,10 @@ namespace Recyclable.Collections return result; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void CopyTo(this RecyclableList list, T[] array) => Array.Copy(list._memoryBlock, array, list._count); + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void CopyTo(this RecyclableList list, int index, T[] array, int arrayIndex, int count) => Array.Copy(list._memoryBlock, index, array, arrayIndex, count); public static void ForEach(this RecyclableList list, Action action) @@ -131,42 +137,49 @@ namespace Recyclable.Collections list._version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Reverse(this RecyclableList list) { Array.Reverse(list._memoryBlock, 0, list._count); list._version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Reverse(this RecyclableList list, int index, int count) { Array.Reverse(list._memoryBlock, index, count); list._version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Sort(this RecyclableList list) { Array.Sort(list._memoryBlock, 0, list._count); list._version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Sort(this RecyclableList list, Comparison comparison) { Array.Sort(list._memoryBlock, 0, list._count, new ComparisonToComparerAdapter(comparison)); list._version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Sort(this RecyclableList list, IComparer? comparer) { Array.Sort(list._memoryBlock, 0, list._count, comparer); list._version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Sort(this RecyclableList list, int index, int count, IComparer? comparer) { Array.Sort(list._memoryBlock, index, count, comparer); list._version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static T[] ToArray(this RecyclableList list) { T[] result = new T[list._count];