diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearchValueHigherThanFirstItemBenchmarks.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearchValueHigherThanFirstItemBenchmarks.cs new file mode 100644 index 0000000..a968dfe --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearchValueHigherThanFirstItemBenchmarks.cs @@ -0,0 +1,39 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Compatibility.List.Benchmarks +{ + public class BinarySearchValueHigherThanFirstItemBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks + { + public override string MethodNameSuffix => "_BinarySearch_ValueHigherThanFirstItem"; + + public void Array_BinarySearch_ValueHigherThanFirstItem() + { + var list = TestObjects; + DoNothing.With(Array.BinarySearch(list, int.MaxValue)); + } + + public void List_BinarySearch_ValueHigherThanFirstItem() + { + var list = TestObjectsAsList; + DoNothing.With(list.BinarySearch(int.MaxValue)); + } + + public void PooledList_BinarySearch_ValueHigherThanFirstItem() + { + var list = TestObjectsAsPooledList; + DoNothing.With(list.BinarySearch(int.MaxValue)); + } + + public void RecyclableList_BinarySearch_ValueHigherThanFirstItem() + { + var list = TestObjectsAsRecyclableList; + DoNothing.With(list.BinarySearch(int.MaxValue)); + } + + public void RecyclableLongList_BinarySearch_ValueHigherThanFirstItem() + { + var list = TestObjectsAsRecyclableLongList; + DoNothing.With(list.BinarySearch(int.MaxValue)); + } + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs index 9c74f0b..2a8faf3 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs @@ -54,16 +54,16 @@ namespace Recyclable.Collections.Compatibility.List.Benchmarks } } - // public void RecyclableLongList_BinarySearch() - // { - // var data = TestObjects; - // var list = TestObjectsAsRecyclableLongList; - // var dataCount = TestObjectCountForSlowMethods; - // for (var i = 0; i < dataCount; i++) - // { - // DoNothing(list.BinarySearch(data[i])); - // DoNothing(list.BinarySearch(data[^(i + 1)])); - // } - // } + public void RecyclableLongList_BinarySearch() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableLongList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(data[i])); + DoNothing.With(list.BinarySearch(data[^(i + 1)])); + } + } } } diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCasesWhenConstrainedRange.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCasesWhenConstrainedRange.cs new file mode 100644 index 0000000..fa3a975 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCasesWhenConstrainedRange.cs @@ -0,0 +1,70 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Compatibility.List.Benchmarks +{ + public class BinarySearchBenchmarksBestAndWorstCasesWhenConstrainedRange : RecyclableCollectionsCompatibilityListBenchmarks + { + public override string MethodNameSuffix => "_BinarySearchWhenConstrainedRange"; + private readonly static Comparer _comparer = Comparer.Default; + + public void Array_BinarySearchWhenConstrainedRange() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(Array.BinarySearch(list, 0, dataCount, data[i], _comparer)); + DoNothing.With(Array.BinarySearch(list, 0, dataCount, data[^(i + 1)], _comparer)); + } + } + + public void List_BinarySearchWhenConstrainedRange() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(0, dataCount, data[i], _comparer)); + DoNothing.With(list.BinarySearch(0, dataCount, data[^(i + 1)], _comparer)); + } + } + + public void PooledList_BinarySearchWhenConstrainedRange() + { + var data = TestObjects; + var list = TestObjectsAsPooledList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(0, dataCount, data[i], _comparer)); + DoNothing.With(list.BinarySearch(0, dataCount, data[^(i + 1)], _comparer)); + } + } + + public void RecyclableList_BinarySearchWhenConstrainedRange() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(0, dataCount, data[i], _comparer)); + DoNothing.With(list.BinarySearch(0, dataCount, data[^(i + 1)], _comparer)); + } + } + + public void RecyclableLongList_BinarySearchWhenConstrainedRange() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableLongList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(0, dataCount, data[i], _comparer)); + DoNothing.With(list.BinarySearch(0, dataCount, data[^(i + 1)], _comparer)); + } + } + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_ValueLowerThanFirstItem.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_ValueLowerThanFirstItem.cs new file mode 100644 index 0000000..8f94d96 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_ValueLowerThanFirstItem.cs @@ -0,0 +1,39 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Compatibility.List.Benchmarks +{ + public class BinarySearchValueLowerThanFirstItemBenchmarks : RecyclableCollectionsCompatibilityListBenchmarks + { + public override string MethodNameSuffix => "_BinarySearch_ValueLowerThanFirstItem"; + + public void Array_BinarySearch_ValueLowerThanFirstItem() + { + var list = TestObjects; + DoNothing.With(Array.BinarySearch(list, -1)); + } + + public void List_BinarySearch_ValueLowerThanFirstItem() + { + var list = TestObjectsAsList; + DoNothing.With(list.BinarySearch(-1)); + } + + public void PooledList_BinarySearch_ValueLowerThanFirstItem() + { + var list = TestObjectsAsPooledList; + DoNothing.With(list.BinarySearch(-1)); + } + + public void RecyclableList_BinarySearch_ValueLowerThanFirstItem() + { + var list = TestObjectsAsRecyclableList; + DoNothing.With(list.BinarySearch(-1)); + } + + public void RecyclableLongList_BinarySearch_ValueLowerThanFirstItem() + { + var list = TestObjectsAsRecyclableLongList; + DoNothing.With(list.BinarySearch(-1)); + } + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs index ee58af5..2e9d0d9 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs @@ -12,13 +12,16 @@ namespace Recyclable.Collections.Compatibility.List.Benchmarks { BenchmarkRunner.Run(new[] { - typeof(BinarySearchBenchmarks), - typeof(ConvertAllBenchmarks), - typeof(ExistsBenchmarks), - typeof(FindBenchmarks), - typeof(FindAllBenchmarks), - typeof(FindLastBenchmarks), - typeof(FindLastIndexBenchmarks) + //typeof(BinarySearchBenchmarks), + typeof(BinarySearchBenchmarksBestAndWorstCasesWhenConstrainedRange), + //typeof(BinarySearchValueLowerThanFirstItemBenchmarks), + //typeof(BinarySearchValueHigherThanFirstItemBenchmarks), + //typeof(ConvertAllBenchmarks), + //typeof(ExistsBenchmarks), + //typeof(FindBenchmarks), + //typeof(FindAllBenchmarks), + //typeof(FindLastBenchmarks), + //typeof(FindLastIndexBenchmarks) }, BenchmarkConfig); } } diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/RecyclableCollectionsCompatibilityListBenchmarks.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/RecyclableCollectionsCompatibilityListBenchmarks.cs index e9616c6..cbab056 100644 --- a/Recyclable.Collections.Compatibility.List.Benchmarks/RecyclableCollectionsCompatibilityListBenchmarks.cs +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/RecyclableCollectionsCompatibilityListBenchmarks.cs @@ -10,9 +10,10 @@ namespace Recyclable.Collections.Compatibility.List.Benchmarks public override CollectionsBenchmarksSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; } [Params( - CollectionsBenchmarksSource.List, - CollectionsBenchmarksSource.PooledList, - CollectionsBenchmarksSource.RecyclableList + //CollectionsBenchmarksSource.List, + //CollectionsBenchmarksSource.PooledList, + //CollectionsBenchmarksSource.RecyclableList, + CollectionsBenchmarksSource.RecyclableLongList )] public override CollectionsBenchmarksSource DataType { get => base.DataType; set => base.DataType = value; } diff --git a/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs b/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs index c5ae40f..8fedcca 100644 --- a/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs +++ b/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs @@ -18,7 +18,7 @@ namespace Recyclable.CollectionsTests _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); // Prepare - using var list = new RecyclableList(testData, itemsCount); + using var list = new RecyclableList(testData, initialCapacity: itemsCount); var expectedData = testData.ToList(); foreach (var itemIndex in itemIndexes) @@ -34,6 +34,32 @@ namespace Recyclable.CollectionsTests } } + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldFindAllItemsWhenConstrainedRange(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, initialCapacity: itemsCount); + var comparer = Comparer.Default; + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var rangeSize = (int)Math.Min(itemsCount - itemIndex, 4); + var expectedItem = expectedData[(int)itemIndex]; + var startItemIndex = (int)(rangeSize > 2 ? Math.Max(0, itemIndex - 2) : itemIndex); + var expected = expectedData.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer); + + // Act + var actual = list.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer); + + // Validate + _ = actual.Should().Be(expected); + } + } + [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] public void BinarySearchShouldFindAllItemsWhenWithCustomComparer(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) @@ -41,7 +67,7 @@ namespace Recyclable.CollectionsTests _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); // Prepare - using var list = new RecyclableList(testData, itemsCount); + using var list = new RecyclableList(testData, initialCapacity: itemsCount); var comparer = Comparer.Default; var expectedData = testData.ToList(); @@ -58,6 +84,137 @@ namespace Recyclable.CollectionsTests } } + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldNotFindNonExistingItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableList(testData, initialCapacity: itemsCount); + list.RemoveAt((int)itemIndex); + var expectedItem = expectedData[(int)itemIndex]; + var expectedRangedData = testData.ToList(); + expectedRangedData.RemoveAt((int)itemIndex); + var expected = expectedRangedData.BinarySearch(expectedItem); + + // Act + var actual = list.BinarySearch(expectedItem); + + // Validate + _ = actual.Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldNotFindNonExistingItemsWhenConstrainedRange(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + var comparer = Comparer.Default; + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableList(testData, initialCapacity: itemsCount); + list.RemoveAt((int)itemIndex); + var expectedItem = expectedData[(int)itemIndex]; + var expectedRangedData = testData.ToList(); + expectedRangedData.RemoveAt((int)itemIndex); + // Now we have 1 element less on the lists + var rangeSize = (int)Math.Max(Math.Min(itemsCount - itemIndex - 2, 4), 0); + int startItemIndex = (int)(rangeSize > 2 ? Math.Max(0, itemIndex - 2 - 1) : Math.Max(0, itemIndex - 1)); + var expected = expectedRangedData.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer); + + // Act + var actual = list.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer); + + // Validate + _ = actual.Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldNotFindNonExistingItemsWhenOutsideRange(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, initialCapacity: itemsCount); + var comparer = Comparer.Default; + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + + // Act && Validate + var expected = expectedData.BinarySearch(0, (int)itemIndex, expectedItem, comparer); + _ = list.BinarySearch(0, (int)itemIndex, expectedItem, comparer).Should().Be(expected); + + expected = expectedData.BinarySearch((int)itemIndex + 1, itemsCount - (int)itemIndex - 1, expectedItem, comparer); + _ = list.BinarySearch((int)itemIndex + 1, itemsCount - (int)itemIndex - 1, expectedItem, comparer).Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldNotFindNonExistingItemsWhenWithCustomComparer(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + var comparer = Comparer.Default; + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableList(testData, initialCapacity: itemsCount); + list.RemoveAt((int)itemIndex); + var expectedItem = expectedData[(int)itemIndex]; + var expectedRangedData = testData.ToList(); + expectedRangedData.RemoveAt((int)itemIndex); + var expected = expectedRangedData.BinarySearch(expectedItem, comparer); + + // Act + var actual = list.BinarySearch(expectedItem, comparer); + + // Validate + _ = actual.Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldThrowArgumentOutOfRangeWhenOutsideRange(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, initialCapacity: itemsCount); + var comparer = Comparer.Default; + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + + // Act && Validate + Assert.Throws(() => _ = expectedData.BinarySearch(-1, (int)itemIndex, expectedItem, comparer)); + Assert.Throws(() => _ = list.BinarySearch(-1, (int)itemIndex, expectedItem, comparer)); + + Assert.Throws(() => _ = expectedData.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer)); + Assert.Throws(() => _ = list.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer)); + } + } + [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] public void ConvertAllShouldConvertAllItems(string testCase, IEnumerable testData, int itemsCount) diff --git a/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs b/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs index bf5e587..c2b8056 100644 --- a/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs +++ b/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs @@ -117,7 +117,6 @@ namespace Recyclable.CollectionsTests _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); // Prepare - var comparer = Comparer.Default; var expectedData = testData.ToList(); @@ -141,6 +140,81 @@ namespace Recyclable.CollectionsTests } } + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldNotFindNonExistingItemsWhenOutsideRange(string testCase, IEnumerable testData, int itemsCount, int minBlockSize, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableLongList(testData, minBlockSize, initialCapacity: itemsCount); + var comparer = Comparer.Default; + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + + // Act && Validate + var expected = expectedData.BinarySearch(0, (int)itemIndex, expectedItem, comparer); + _ = list.BinarySearch(0, (int)itemIndex, expectedItem, comparer).Should().Be(expected); + + expected = expectedData.BinarySearch((int)itemIndex + 1, itemsCount - (int)itemIndex - 1, expectedItem, comparer); + _ = list.BinarySearch((int)itemIndex + 1, itemsCount - (int)itemIndex - 1, expectedItem, comparer).Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldNotFindNonExistingItemsWhenWithCustomComparer(string testCase, IEnumerable testData, int itemsCount, int minBlockSize, in long[] itemIndexes) + { + var comparer = Comparer.Default; + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableLongList(testData, minBlockSize, initialCapacity: itemsCount); + list.RemoveAt((int)itemIndex); + var expectedItem = expectedData[(int)itemIndex]; + var expectedRangedData = testData.ToList(); + expectedRangedData.RemoveAt((int)itemIndex); + var expected = expectedRangedData.BinarySearch(expectedItem, comparer); + + // Act + var actual = list.BinarySearch(expectedItem, comparer); + + // Validate + _ = actual.Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void BinarySearchShouldThrowArgumentOutOfRangeWhenOutsideRange(string testCase, IEnumerable testData, int itemsCount, int minBlockSize, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableLongList(testData, minBlockSize, initialCapacity: itemsCount); + var comparer = Comparer.Default; + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + + // Act && Validate + Assert.Throws(() => _ = expectedData.BinarySearch(-1, (int)itemIndex, expectedItem, comparer)); + Assert.Throws(() => _ = list.BinarySearch(-1, (int)itemIndex, expectedItem, comparer)); + + Assert.Throws(() => _ = expectedData.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer)); + Assert.Throws(() => _ = list.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer)); + } + } + [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] public void ConvertAllShouldConvertAllItems(string testCase, IEnumerable testData, int minBlockSize, int itemsCount) diff --git a/Recyclable.Collections.Compatibility.List/Properties/Exceptions.Designer.cs b/Recyclable.Collections.Compatibility.List/Properties/Exceptions.Designer.cs new file mode 100644 index 0000000..c840481 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/Properties/Exceptions.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Recyclable.Collections.Compatibility.List.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Exceptions { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Exceptions() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Recyclable.Collections.Compatibility.List.Properties.Exceptions", typeof(Exceptions).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.. + /// + internal static string Argument_InvalidOffLen { + get { + return ResourceManager.GetString("Argument_InvalidOffLen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Non-negative number required. (Parameter 'index'). + /// + internal static string ArgumentOutOfRange_NeedNonNegNum { + get { + return ResourceManager.GetString("ArgumentOutOfRange_NeedNonNegNum", resourceCulture); + } + } + } +} diff --git a/Recyclable.Collections.Compatibility.List/Properties/Exceptions.resx b/Recyclable.Collections.Compatibility.List/Properties/Exceptions.resx new file mode 100644 index 0000000..039c57f --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/Properties/Exceptions.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Non-negative number required. (Parameter 'index') + + + Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. + + \ No newline at end of file diff --git a/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj b/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj index ba8aa43..dd99c6c 100644 --- a/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj +++ b/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj @@ -39,4 +39,19 @@ + + + + True + True + Exceptions.resx + + + + + + ResXFileCodeGenerator + Exceptions.Designer.cs + + diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs index f40d810..7c7d3f0 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Numerics; using System.Runtime.CompilerServices; +using Recyclable.Collections.Compatibility.List.Properties; using Recyclable.Collections.Pools; namespace Recyclable.Collections @@ -23,8 +24,9 @@ namespace Recyclable.Collections => 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); + public static int BinarySearch(this RecyclableList list, int index, int count, T item, IComparer? comparer) => list._count - index < count + ? throw new ArgumentException(Exceptions.Argument_InvalidOffLen) + : Array.BinarySearch(list._memoryBlock, index, count, item, comparer); public static RecyclableList ConvertAll(this RecyclableList list, Converter converter) { diff --git a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs index 4a39a9a..1cf780b 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs @@ -1,7 +1,5 @@ // 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 diff --git a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.InsertRange.cs b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.InsertRange.cs index 8d6cd9e..17804a6 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.InsertRange.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.InsertRange.cs @@ -1,7 +1,6 @@ // Ignore Spelling: zzz using System.Collections; -using System.Numerics; using System.Runtime.CompilerServices; namespace Recyclable.Collections diff --git a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.cs b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.cs index 0ff4b97..a46eb14 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.cs @@ -1,9 +1,9 @@ // Ignore Spelling: zzz +using Recyclable.Collections.Compatibility.List.Properties; using System.Collections.ObjectModel; -using System.Numerics; +using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; -using Recyclable.Collections.Pools; namespace Recyclable.Collections { @@ -14,96 +14,180 @@ namespace Recyclable.Collections { public static ReadOnlyCollection AsReadOnly(this RecyclableLongList list) => new(list); - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static int BinarySearch(this RecyclableLongList list, T item) { - if (list._longCount == 0) + long high = list._longCount - 1; + if (high < 0) { return RecyclableDefaults.ItemNotFoundIndex; } - int blockIndex = 0, - blockSize = list._blockSize, - foundItemIndex; var comparer = Comparer.Default; - var lastFullBlockIndex = list._lastBlockWithData; + byte blockShift = list._blockSizePow2BitShift; + int blockSizeMinus1 = list._blockSizeMinus1, + compareResult; Span memoryBlocksSpan = list._memoryBlocks; - while (blockIndex < lastFullBlockIndex) + + long low = 0; + if (high >= 0) { - foundItemIndex = new Span(memoryBlocksSpan[blockIndex], 0, blockSize).BinarySearch(item, comparer); - if (foundItemIndex >= 0) + compareResult = comparer.Compare(memoryBlocksSpan[(int)low][low], item); + if (compareResult == 0) { - return (blockIndex << list._blockSizePow2BitShift) + foundItemIndex; + return (int)low; } - else if (-foundItemIndex <= blockSize && foundItemIndex <= RecyclableDefaults.ItemNotFoundIndex) + else if (compareResult > 0) { - return foundItemIndex - (blockIndex << list._blockSizePow2BitShift); + return RecyclableDefaults.ItemNotFoundIndex; } - blockIndex++; + compareResult = comparer.Compare(memoryBlocksSpan[list._lastBlockWithData][(int)(high & blockSizeMinus1)], item); + if (compareResult == 0) + { + return checked((int)high); + } + else if (compareResult < 0) + { + return checked((int)~list._longCount); + } } - foundItemIndex = new Span(memoryBlocksSpan[blockIndex], 0, list._nextItemIndex != 0 ? list._nextItemIndex : blockSize).BinarySearch(item, comparer); - return foundItemIndex >= 0 ? (blockIndex << list._blockSizePow2BitShift) + foundItemIndex : foundItemIndex - (blockIndex << list._blockSizePow2BitShift); + while (low <= high) + { + long med = low + ((high - low) >> 1); + compareResult = comparer.Compare(memoryBlocksSpan[(int)(med >> blockShift)][(int)(med & blockSizeMinus1)], item); + if (compareResult == 0) + { + return checked((int)med); + } + + if (compareResult < 0) + { + low = med + 1; + } + else + { + high = med - 1; + } + } + + return checked((int)~low); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static int BinarySearch(this RecyclableLongList list, T item, IComparer? comparer) { - if (list._longCount == 0) + long high = list._longCount - 1; + if (high < 0) { return RecyclableDefaults.ItemNotFoundIndex; } comparer ??= Comparer.Default; - int blockIndex = 0, - blockSize = list._blockSize, - foundItemIndex; - var lastFullBlockIndex = list._lastBlockWithData; + byte blockShift = list._blockSizePow2BitShift; + int blockSizeMinus1 = list._blockSizeMinus1, + compareResult; Span memoryBlocksSpan = list._memoryBlocks; - while (blockIndex < lastFullBlockIndex) + + long low = 0; + if (high >= 0) { - foundItemIndex = new Span(memoryBlocksSpan[blockIndex], 0, blockSize).BinarySearch(item, comparer); - if (foundItemIndex >= 0) + compareResult = comparer.Compare(memoryBlocksSpan[(int)low][low], item); + if (compareResult == 0) { - return (blockIndex << list._blockSizePow2BitShift) + foundItemIndex; + return (int)low; + } + else if (compareResult > 0) + { + return RecyclableDefaults.ItemNotFoundIndex; } - blockIndex++; + compareResult = comparer.Compare(memoryBlocksSpan[list._lastBlockWithData][(int)(high & blockSizeMinus1)], item); + if (compareResult == 0) + { + return checked((int)high); + } + else if (compareResult < 0) + { + return checked((int)~list._longCount); + } } - foundItemIndex = new Span(memoryBlocksSpan[blockIndex], 0, list._nextItemIndex != 0 ? list._nextItemIndex : blockSize).BinarySearch(item, comparer); - return foundItemIndex >= 0 ? (blockIndex << list._blockSizePow2BitShift) + foundItemIndex : RecyclableDefaults.ItemNotFoundIndex; + while (low <= high) + { + long med = low + ((high - low) >> 1); + compareResult = comparer.Compare(memoryBlocksSpan[(int)(med >> blockShift)][(int)(med & blockSizeMinus1)], item); + if (compareResult == 0) + { + return checked((int)med); + } + + if (compareResult < 0) + { + low = med + 1; + } + else + { + high = med - 1; + } + } + + return checked((int)~low); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static int BinarySearch(this RecyclableLongList list, int index, int count, T item, IComparer? comparer) { + // Kept strictly for compatibility with List's behavior, including error messages. + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), Exceptions.ArgumentOutOfRange_NeedNonNegNum); + if (list._longCount - index < count) + throw new ArgumentException(Exceptions.Argument_InvalidOffLen); + + Contract.EndContractBlock(); + if (list._longCount == 0) { return RecyclableDefaults.ItemNotFoundIndex; } + long low = index; + long high = index + count - 1; + comparer ??= Comparer.Default; Span memoryBlocksSpan = list._memoryBlocks; byte blockShift = list._blockSizePow2BitShift; int blockSizeMinus1 = list._blockSizeMinus1; - long low = index, - high = index + count - 1; - int compareResult = comparer.Compare(memoryBlocksSpan[(int)(low >> blockShift)][(int)(low & blockSizeMinus1)], item); - if (compareResult == 0) + int compareResult; + if (low <= high) { - return checked((int)low); - } - else if (compareResult > 0) - { - return RecyclableDefaults.ItemNotFoundIndex; + compareResult = comparer.Compare(memoryBlocksSpan[(int)(low >> blockShift)][(int)(low & blockSizeMinus1)], item); + if (compareResult == 0) + { + return checked((int)low); + } + else if (compareResult > 0) + { + return checked((int)~low); + } + + if (low != high) + { + compareResult = comparer.Compare(memoryBlocksSpan[(int)(high >> blockShift)][(int)(high & blockSizeMinus1)], item); + if (compareResult == 0) + { + return checked((int)high); + } + else if (compareResult < 0) + { + return checked((int)~(high + 1)); + } + } } while (low <= high) { - // i might overflow if lo and hi are both large positive numbers. long med = low + ((high - low) >> 1); compareResult = comparer.Compare(memoryBlocksSpan[(int)(med >> blockShift)][(int)(med & blockSizeMinus1)], item); @@ -123,110 +207,6 @@ namespace Recyclable.Collections } return checked((int)~low); - - // ATTEMPT 2 - //int blockSizeMinus1 = list._blockSizeMinus1; - //long low = index, - // high = Math.Min(index + (count != 0 ? count : list._longCount) - 1, list._longCount - 1); - //byte blockShift = list._blockSizePow2BitShift; - - //comparer ??= Comparer.Default; - //Span memoryBlocksSpan = list._memoryBlocks; - - //while (low <= high) - //{ - // long mid = low + ((high - low) / 2); - - // // Compare the target with the middle element of the array - // int comparisonResult = comparer.Compare(item, memoryBlocksSpan[(int)mid >> blockShift][mid & blockSizeMinus1]); - // if (comparisonResult == 0) - // { - // return (int)mid; - // } - // else if (comparisonResult < 0) - // { - // low = mid + 1; - // } - // else - // { - // high = mid - 1; - // low++; - // } - //} - - //return low != 0 ? (int)-low : RecyclableDefaults.ItemNotFoundIndex ; - - - // ATTEMPT 3 - //if (list._longCount == 0 || (count == 0 && index == 0)) - //{ - // return RecyclableDefaults.ItemNotFoundIndex; - //} - - //comparer ??= Comparer.Default; - //byte blockSizePow2BitShift = list._blockSizePow2BitShift; - //int blockSize = list._blockSize, - // startItemIndex = index & list._blockSizeMinus1, - // blockIndex = index >> blockSizePow2BitShift, - // lastItemIndex = (index + count) & list._blockSizeMinus1, - // lastFullBlockIndex = ((index + count) >> blockSizePow2BitShift) - (lastItemIndex != 0 /*|| count == 1*/ ? 1 : 0); - - //Span memoryBlocksSpan = list._memoryBlocks; - //if (comparer.Compare(item, memoryBlocksSpan[blockIndex][startItemIndex]) < 0) - //{ - // return RecyclableDefaults.ItemNotFoundIndex; - //} - - //int itemIndex = - // new Span - // ( - // memoryBlocksSpan[blockIndex], - // startItemIndex, - // Math.Max(0, Math.Min(blockSize, (int)Math.Min(count, list._longCount - startItemIndex))) - // ) - // .BinarySearch(item, comparer); - - //if (itemIndex >= 0) - //{ - // return (blockIndex << blockSizePow2BitShift) + itemIndex; - //} - //else if ((itemIndex < RecyclableDefaults.ItemNotFoundIndex || blockIndex > 0) && -itemIndex <= blockSize) - //{ - // return itemIndex - (blockIndex << blockSizePow2BitShift); - //} - ////else if (blockIndex == 0) - ////{ - //// return itemIndex; - ////} - - //blockIndex++; - //while (blockIndex <= lastFullBlockIndex) - //{ - // itemIndex = new Span(memoryBlocksSpan[blockIndex], 0, blockSize).BinarySearch(item, comparer); - // if (itemIndex >= 0) - // { - // return (blockIndex << blockSizePow2BitShift) + itemIndex; - // } - // else if (itemIndex <= RecyclableDefaults.ItemNotFoundIndex) - // { - // return itemIndex - (blockIndex << blockSizePow2BitShift); - // } - - // blockIndex++; - //} - - //if (blockIndex <= list._lastBlockWithData) - //{ - // itemIndex = new Span(memoryBlocksSpan[blockIndex], 0, lastItemIndex + 1).BinarySearch(item, comparer); - // switch (itemIndex) - // { - // case >= 0: return (blockIndex << blockSizePow2BitShift) + itemIndex; - // case < RecyclableDefaults.ItemNotFoundIndex: return itemIndex - (blockIndex << blockSizePow2BitShift); - // case RecyclableDefaults.ItemNotFoundIndex: return -(blockIndex << blockSizePow2BitShift) - (blockSize > 1 ? 1 : 0); - // } - //} - - //return checked((int)-list._longCount); } public static RecyclableList ConvertAll(this RecyclableLongList list, Converter converter)