RecyclableLongList.BinarySearch implemented. Various performance improvements & fixes in RecyclableList.BinarySearch. Benchmarks & Tests extended.

This commit is contained in:
mlemanczyk committed 2023-11-01 20:27:59 +01:00
1 parent 393685f061
commit 1f7603fe08
15 files changed
+756 -172

No files matched your search

@@ -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));
}
}
}
@@ -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)]));
}
}
}
}
@@ -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<int> _comparer = Comparer<int>.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));
}
}
}
}
@@ -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));
}
}
}
@@ -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);
}
}
@@ -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; }
@@ -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<long>(testData, itemsCount);
using var list = new RecyclableList<long>(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<long> 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<long>(testData, initialCapacity: itemsCount);
var comparer = Comparer<long>.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<long> 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<long>(testData, itemsCount);
using var list = new RecyclableList<long>(testData, initialCapacity: itemsCount);
var comparer = Comparer<long>.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<long> 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<long>(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<long> 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<long>.Default;
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
using var list = new RecyclableList<long>(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<long> 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<long>(testData, initialCapacity: itemsCount);
var comparer = Comparer<long>.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<long> testData, int itemsCount, in long[] itemIndexes)
{
var comparer = Comparer<long>.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<long>(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<long> 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<long>(testData, initialCapacity: itemsCount);
var comparer = Comparer<long>.Default;
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
// Act && Validate
Assert.Throws<ArgumentOutOfRangeException>(() => _ = expectedData.BinarySearch(-1, (int)itemIndex, expectedItem, comparer));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = list.BinarySearch(-1, (int)itemIndex, expectedItem, comparer));
Assert.Throws<ArgumentException>(() => _ = expectedData.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer));
Assert.Throws<ArgumentException>(() => _ = list.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer));
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ConvertAllShouldConvertAllItems(string testCase, IEnumerable<long> testData, int itemsCount)
@@ -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<long>.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<long> 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<long>(testData, minBlockSize, initialCapacity: itemsCount);
var comparer = Comparer<long>.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<long> testData, int itemsCount, int minBlockSize, in long[] itemIndexes)
{
var comparer = Comparer<long>.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<long>(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<long> 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<long>(testData, minBlockSize, initialCapacity: itemsCount);
var comparer = Comparer<long>.Default;
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
// Act && Validate
Assert.Throws<ArgumentOutOfRangeException>(() => _ = expectedData.BinarySearch(-1, (int)itemIndex, expectedItem, comparer));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = list.BinarySearch(-1, (int)itemIndex, expectedItem, comparer));
Assert.Throws<ArgumentException>(() => _ = expectedData.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer));
Assert.Throws<ArgumentException>(() => _ = list.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer));
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ConvertAllShouldConvertAllItems(string testCase, IEnumerable<long> testData, int minBlockSize, int itemsCount)
@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Recyclable.Collections.Compatibility.List.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 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..
/// </summary>
internal static string Argument_InvalidOffLen {
get {
return ResourceManager.GetString("Argument_InvalidOffLen", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Non-negative number required. (Parameter &apos;index&apos;).
/// </summary>
internal static string ArgumentOutOfRange_NeedNonNegNum {
get {
return ResourceManager.GetString("ArgumentOutOfRange_NeedNonNegNum", resourceCulture);
}
}
}
}
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
<value>Non-negative number required. (Parameter 'index')</value>
</data>
<data name="Argument_InvalidOffLen" xml:space="preserve">
<value>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.</value>
</data>
</root>
@@ -39,4 +39,19 @@
<ItemGroup>
<PackageReference Include="Recyclable.Collections" Version="0.0.6.1" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Exceptions.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Exceptions.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Exceptions.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Exceptions.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>
@@ -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<T>(this RecyclableList<T> list, int index, int count, T item, IComparer<T>? comparer)
=> Array.BinarySearch(list._memoryBlock, index, count, item, comparer);
public static int BinarySearch<T>(this RecyclableList<T> list, int index, int count, T item, IComparer<T>? comparer) => list._count - index < count
? throw new ArgumentException(Exceptions.Argument_InvalidOffLen)
: Array.BinarySearch(list._memoryBlock, index, count, item, comparer);
public static RecyclableList<TOutput> ConvertAll<T, TOutput>(this RecyclableList<T> list, Converter<T, TOutput> converter)
{
@@ -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
@@ -1,7 +1,6 @@
// Ignore Spelling: zzz
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Recyclable.Collections
@@ -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<T> AsReadOnly<T>(this RecyclableLongList<T> list) => new(list);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int BinarySearch<T>(this RecyclableLongList<T> 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<T>.Default;
var lastFullBlockIndex = list._lastBlockWithData;
byte blockShift = list._blockSizePow2BitShift;
int blockSizeMinus1 = list._blockSizeMinus1,
compareResult;
Span<T[]> memoryBlocksSpan = list._memoryBlocks;
while (blockIndex < lastFullBlockIndex)
long low = 0;
if (high >= 0)
{
foundItemIndex = new Span<T>(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<T>(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<T>(this RecyclableLongList<T> list, T item, IComparer<T>? comparer)
{
if (list._longCount == 0)
long high = list._longCount - 1;
if (high < 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
comparer ??= Comparer<T>.Default;
int blockIndex = 0,
blockSize = list._blockSize,
foundItemIndex;
var lastFullBlockIndex = list._lastBlockWithData;
byte blockShift = list._blockSizePow2BitShift;
int blockSizeMinus1 = list._blockSizeMinus1,
compareResult;
Span<T[]> memoryBlocksSpan = list._memoryBlocks;
while (blockIndex < lastFullBlockIndex)
long low = 0;
if (high >= 0)
{
foundItemIndex = new Span<T>(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<T>(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<T>(this RecyclableLongList<T> list, int index, int count, T item, IComparer<T>? 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<T>.Default;
Span<T[]> 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<T>.Default;
//Span<T[]> 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<T>.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<T[]> memoryBlocksSpan = list._memoryBlocks;
//if (comparer.Compare(item, memoryBlocksSpan[blockIndex][startItemIndex]) < 0)
//{
// return RecyclableDefaults.ItemNotFoundIndex;
//}
//int itemIndex =
// new Span<T>
// (
// 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<T>(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<T>(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<TOutput> ConvertAll<T, TOutput>(this RecyclableLongList<T> list, Converter<T, TOutput> converter)