Initial version extracted from Recyclable.Collections

This commit is contained in:
mlemanczyk committed 2023-10-21 14:07:23 +02:00
1 parent 01bbd5d04f
commit 4684330d7e
22 files changed
+2403 -1

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
# License
The project & source code is free for educational & personal use. **Any kind** of commercial use requires a donation, as prescribed below.
# Donation requirements for a life-time, commercial use
* Commercial license is paid one time.
* It can be obtained by donating $10 for each developer seat, using the link provided below. If you hire 2 developers, you need to donate $20 for a life-time, commercial use of this package.
* Your license stays with you, if your developer moves somewhere else.
# Link for donations
[![Donate](buy-me-a-coffee.svg)](https://www.buymeacoffee.com/mlemanczyk)
+22 -1
View File
@@ -1 +1,22 @@
# Recyclable.Collections.Compatibility.List # Recyclable.Collections.Compatibility.List# Recyclable.Collections
`Recyclable.Collections.Compatibility.List` project is an open source add-on for [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) providing `List<T>` compatibility features to the recyclable collections framework.
## Included compatibility packs
* `RecyclableList<T>`
# Milestones
1. 👉 Implement compatibility pack
1. ✅ `RecyclableList<T>`
1. 👉 `RecyclableLongList<T>`
1. 🅿️ `RecyclableQueue<T>`
1. 🅿️ `RecyclableSortedList<T>`
1. 🅿️ `RecyclableStack<T>`
1. 🅿️ `RecyclableUnorderedList<T>`
1. 🅿️ Review performance
1. 🅿️ Optimize performance
# Characteristics of the classes
The package
## Common
* All classes will internally or externally utilize [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) to benefit from recyclable memory pools.
@@ -0,0 +1,67 @@
using Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks
{
public class BinarySearchBenchmarks<T> : CollectionsBenchmarksBase<T>
{
public void Array_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(Array.BinarySearch(list, data[i]));
DoNothing.With(Array.BinarySearch(list, data[^(i + 1)]));
}
}
public void List_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.BinarySearch(data[i]));
DoNothing.With(list.BinarySearch(data[^(i + 1)]));
}
}
public void PooledList_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.BinarySearch(data[i]));
DoNothing.With(list.BinarySearch(data[^(i + 1)]));
}
}
public void RecyclableList_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.BinarySearch(data[i]));
DoNothing.With(list.BinarySearch(data[^(i + 1)]));
}
}
// public void RecyclableLongList_BinarySearch_BestAndWorstCases()
// {
// 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)]));
// }
// }
}
}
@@ -0,0 +1,50 @@
using Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks
{
public class ConvertAllBenchmarks<T> : CollectionsBenchmarksBase<T>
{
public void Array_ConvertAll()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
DoNothing.With(Array.ConvertAll(list, static (item) => (long)item));
}
public void List_ConvertAll()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
DoNothing.With(list.ConvertAll(static (item) => (long)item));
}
public void PooledList_ConvertAll()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
using var converted = list.ConvertAll(static (item) => (long)item);
DoNothing.With(converted);
}
public void RecyclableList_ConvertAll()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
DoNothing.With(converted);
}
// public void RecyclableLongList_ConvertAll()
// {
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
// DoNothing(converted);
// }
}
}
@@ -0,0 +1,67 @@
using Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsCompatibilityListBenchmarks
{
public void Array_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(Array.Exists(list, (x) => x == data[i]));
DoNothing.With(Array.Exists(list, (x) => x == data[^(i + 1)]));
}
}
public void List_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.Exists((x) => x == data[i]));
DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
}
}
public void PooledList_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.Exists((x) => x == data[i]));
DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.Exists((x) => x == data[i]));
DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
}
}
//public void RecyclableLongList_Exists_BestAndWorstCases()
//{
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing.With(list.Exists((x) => x == data[i]));
// DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
// }
//}
}
}
@@ -0,0 +1,82 @@
using Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsCompatibilityListBenchmarks
{
public void Array_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(Array.FindAll(list, (item) => item == data[i]));
DoNothing.With(Array.FindAll(list, (item) => item == data[^(i + 1)]));
}
}
public void List_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.FindAll((item) => item == data[i]));
DoNothing.With(list.FindAll((item) => item == data[^(i + 1)]));
}
}
public void PooledList_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsPooledList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
var temp = list.FindAll((item) => item == data[i]);
DoNothing.With(temp);
temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing.With(temp);
temp.Dispose();
}
}
public void RecyclableList_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
var temp = list.FindAll((item) => item == data[i]);
DoNothing.With(temp);
temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing.With(temp);
temp.Dispose();
}
}
//public void RecyclableLongList_FindAll_BestAndWorstCases()
//{
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// var temp = list.FindAll((item) => item == data[i]);
// DoNothing.With(temp);
// temp.Dispose();
// temp = list.FindAll((item) => item == data[^(i + 1)]);
// DoNothing.With(temp);
// temp.Dispose();
// }
//}
}
}
@@ -0,0 +1,55 @@
using Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsCompatibilityListBenchmarks : CollectionsBenchmarksBase<CollectionsBenchmarksSource>
{
public void Array_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(Array.FindLastIndex(list, 0, (x) => x == data[i]));
DoNothing.With(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)]));
}
}
public void List_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
}
}
//public void RecyclableLongList_FindLastIndex_BestAndWorstCases()
//{
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing.With(list.FindLastIndex(0, (x) => x == data[i]));
// DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
// }
//}
}
}
@@ -0,0 +1,55 @@
using Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsCompatibilityListBenchmarks
{
public void Array_FindLast_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(Array.FindLast(list, (x) => x == data[i]));
DoNothing.With(Array.FindLast(list, (x) => x == data[^(i + 1)]));
}
}
public void List_FindLast_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.FindLast((x) => x == data[i]));
DoNothing.With(list.FindLast((x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_FindLast_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.FindLast((x) => x == data[i]));
DoNothing.With(list.FindLast((x) => x == data[^(i + 1)]));
}
}
//public void RecyclableLongList_FindLast_BestAndWorstCases()
//{
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing.With(list.FindLast((x) => x == data[i]));
// DoNothing.With(list.FindLast((x) => x == data[^(i + 1)]));
// }
//}
}
}
@@ -0,0 +1,55 @@
using Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsCompatibilityListBenchmarks
{
public void Array_Find_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(Array.Find(list, (x) => x == data[i]));
DoNothing.With(Array.Find(list, (x) => x == data[^(i + 1)]));
}
}
public void List_Find_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.Find((x) => x == data[i]));
DoNothing.With(list.Find((x) => x == data[^(i + 1)]));
}
}
public void RecyclableList_Find_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsRecyclableList;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing.With(list.Find((x) => x == data[i]));
DoNothing.With(list.Find((x) => x == data[^(i + 1)]));
}
}
//public void RecyclableLongList_Find_BestAndWorstCases()
//{
// var data = TestObjects;
// var list = TestObjectsAsRecyclableLongList;
// var dataCount = TestObjectCountForSlowMethods;
// for (var i = 0; i < dataCount; i++)
// {
// DoNothing.With(list.Find((x) => x == data[i]));
// DoNothing.With(list.Find((x) => x == data[^(i + 1)]));
// }
//}
}
}
@@ -0,0 +1,2 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Collections.Benchmarks.Core" Version="0.0.2" />
<PackageReference Include="Recyclable.Collections" Version="0.0.6" />
</ItemGroup>
</Project>
@@ -0,0 +1 @@
global using Xunit;
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Recyclable.Collections" Version="0.0.6" />
<PackageReference Include="Recyclable.Collections.TestData" Version="0.0.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
@@ -0,0 +1,953 @@
using FluentAssertions;
using Recyclable.Collections;
using Recyclable.Collections.TestData;
using Recyclable.Collections.Pools;
using System.Numerics;
#pragma warning disable xUnit1026, RCS1163, IDE0039, IDE0060, RCS1235
namespace Recyclable.CollectionsTests
{
public class RecyclableListCompatibilityListTests
{
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ConvertAllShouldConvertAllItems(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList().ConvertAll(item => item + 0.1);
// Act
using var actual = list.ConvertAll(item => item + 0.1);
// Validate
_ = actual.Count.Should().Be(itemsCount);
_ = actual.Should().Equal(expectedData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void CopyToShouldCopyAllItemsInTheCorrectOrderWhenFromStart(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
long[] copiedItems = new long[itemsCount];
// Act
list.CopyTo(copiedItems);
// Validate
long[] expectedData = new long[itemsCount];
testData.ToList().CopyTo(expectedData);
_ = copiedItems.Should().Equal(testData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ExistsShouldFindAllItems(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, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.Exists(item => item == expectedItem);
// Act
var actual = list.Exists(item => item == expectedItem);
// Validate
_ = actual.Should().Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ExistsShouldNotFindNonExistingItems(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, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.Exists(item => item == -expectedItem);
// Act
var actual = list.Exists(item => item == -expectedItem);
// Validate
_ = actual.Should().Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindAllIndexesShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
//* There is no equivalent of .FindAllIndexes in List<T>, so no comparison here.
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
foreach (var itemIndex in itemIndexes)
{
// Act
using var actual = list.FindAllIndexes(item => item == itemIndex + 1);
// Validate
_ = actual.Count.Should().Be(1);
if (itemsCount > 0)
{
_ = actual.Count.Should().Be(1);
_ = actual.Should().AllSatisfy(x => x.Should().Be((int)itemIndex));
}
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
public void FindAllShouldReturnCorrectIndexes(int itemsCount)
{
const int ExpectedItem = 1;
// Prepare
var testData = Enumerable.Range(0, itemsCount).Select(value => value % 2 == 0 ? ExpectedItem : -ExpectedItem);
using var list = new RecyclableList<int>(testData, itemsCount);
// Act
using var actual = list.FindAll(x => x == ExpectedItem);
// Validate
var expectedData = testData.ToList().FindAll(x => x == ExpectedItem);
_ = actual.Count.Should().Be(expectedData.Count);
_ = actual.Should().Equal(expectedData);
if (expectedData.Count > 0)
{
_ = actual.Should().AllSatisfy(x => x.Should().Be(ExpectedItem)).And.Equal(expectedData);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
public void FindAllShouldNotFindNonExistingItems(int itemsCount)
{
const int ExpectedItem = 1;
// Prepare
var testData = Enumerable.Range(0, itemsCount).Select(_ => ExpectedItem);
using var list = new RecyclableList<int>(testData, itemsCount);
// Act
using var foundItems = list.FindAll(x => x == -ExpectedItem);
// Validate
var expectedData = testData.ToList().FindAll(x => x == -ExpectedItem);
_ = foundItems.Count.Should().Be(expectedData.Count);
_ = foundItems.Should().Equal(expectedData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindIndexShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
// Act
var actual = list.FindIndex(item => item == expectedItem);
// Validate
var expected = expectedData.FindIndex(item => item == expectedItem);
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindIndexShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.FindIndex((int)itemIndex, (int)(itemsCount - itemIndex), item => item == expectedItem);
// Act
var actual = list.FindIndex((int)itemIndex, (int)(itemsCount - itemIndex), item => item == expectedItem);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.FindIndex(Math.Min(1, (int)itemIndex), item => item == expectedItem);
// Act
var actual = list.FindIndex(Math.Min(1, (int)itemIndex), item => item == expectedItem);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindLastIndexShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.FindLastIndex(x => x == expectedItem);
// Act
var actual = list.FindLastIndex(x => x == expectedItem);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindLastIndexShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
// Act & Validate
var expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount, x => x == expectedItem);
_ = list.FindLastIndex(itemsCount - 1, itemsCount, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindLastIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.FindLastIndex(itemsCount - 1, x => x == expectedItem);
// Act
var actual = list.FindLastIndex(itemsCount - 1, x => x == expectedItem);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindLastIndexShouldThrowArgumentOutOfRange(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
int expected = int.MinValue;
// Act & Validate
if (itemsCount - 1 != -1)
{
Assert.Throws<ArgumentOutOfRangeException>(() => expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = list.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected));
}
else
{
expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem);
_ = list.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected);
}
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindLastShouldReturnCorrectItem(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.FindLast(item => item == expectedItem);
// Act
var actual = list.FindLast(item => item == expectedItem);
// Validate
_ = actual.Should().Be(expectedData[(int)itemIndex]).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindShouldFindAllItems(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, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
long expectedItem = expectedData[(int)itemIndex];
var expected = expectedData.Find(item => item == expectedItem);
// Act
var actual = list.Find(item => item == expectedData[(int)itemIndex]);
// Validate
_ = actual.Should().Be(expectedData[(int)itemIndex]);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void FindShouldNotFindNonExistingItems(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, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expected = expectedData.Find(item => item == -expectedData[(int)itemIndex]);
// Act
var actual = list.Find(item => item == -expectedData[(int)itemIndex]);
// Validate
actual.Should().Be(0).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ForEachShouldGiveItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
var expected = new List<long>(itemsCount);
expectedData.ForEach(expected.Add);
// Act
var yieldedItems = new List<long>(itemsCount);
list.ForEach(yieldedItems.Add);
// Validate
_ = yieldedItems.Should().Equal(testData).And.Equal(expected);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ForEachShouldDoNothingWhenEmptyList(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
var expected = new List<long>(itemsCount);
expectedData.ForEach(expected.Add);
// Act
var yieldedItems = new List<long>(itemsCount);
list.ForEach(item => yieldedItems.Add(item));
// Validate
_ = yieldedItems.Should().BeEmpty().And.Equal(expected);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void GetRangeShouldReturnCorrectItems(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, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
int startingIndex = (int)Math.Min(1, itemIndex);
var expectedRangeItems = expectedData.GetRange(startingIndex, (int)itemIndex);
// Act
using var rangeItems = list.GetRange(startingIndex, (int)itemIndex);
// Validate
_ = rangeItems.Count.Should().Be(expectedRangeItems.Count);
_ = rangeItems.Should().Equal(expectedRangeItems);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void IndexOfShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expected = expectedData.IndexOf(expectedData[(int)itemIndex], (int)itemIndex, (int)(itemsCount - itemIndex));
// Act
var actual = list.IndexOf(expectedData[(int)itemIndex], (int)itemIndex, (int)(itemsCount - itemIndex));
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void IndexOfShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expected = expectedData.IndexOf(expectedData[(int)itemIndex], (int)itemIndex);
// Act
var actual = list.IndexOf(expectedData[(int)itemIndex], (int)itemIndex);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void LastIndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex]);
// Act
var actual = list.LastIndexOf(expectedData[(int)itemIndex]);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void LastIndexOfShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount);
// Act
var actual = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void LastIndexOfShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1);
// Act
var actual = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1);
// Validate
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void LastIndexOfShouldThrowArgumentOutOfRangeWhenCountTooBig(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
// Act & Validate
foreach (var itemIndex in itemIndexes)
{
_ = Assert.Throws<ArgumentOutOfRangeException>(() => _ = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount + 3).Should().Be((int)itemIndex));
_ = Assert.Throws<ArgumentOutOfRangeException>(() => _ = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount + 3).Should().Be((int)itemIndex));
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void InsertRangeShouldMoveItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
foreach (var itemIndex in itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
var expectedItems = testData.Reverse().ToList();
var item = expectedItems[^1];
expectedItems.InsertRange((int)itemIndex, testData);
// Act
list.InsertRange((int)itemIndex, testData);
// Validate
_ = list[(int)itemIndex].Should().Be(item);
_ = list.Count.Should().Be(itemsCount << 1);
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount << 1);
_ = list.Should().Equal(expectedItems);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void RemoveAllShouldRemoveTheCorrectItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var itemsToRemove = itemIndexes.Select(x => list[(int)x]).ToArray();
var expectedItems = testData.ToList();
var expectedResult = expectedItems.RemoveAll(itemsToRemove.Contains);
// Act
var actualResult = list.RemoveAll(itemsToRemove.Contains);
// Validate
_ = actualResult.Should().Be(expectedResult);
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
_ = list.Count.Should().Be(itemsCount - itemsToRemove.Length);
_ = list.Should().Equal(expectedItems);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void RemoveRangeShouldRemoveCorrectItems(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");
foreach (var itemIndex in itemIndexes)
{
// Prepare
int startingIndex = (int)Math.Min(1, itemIndex);
var expectedData = testData.ToList();
expectedData.RemoveRange(startingIndex, (int)itemIndex);
// Act
using var list = new RecyclableList<long>(testData, itemsCount);
list.RemoveRange(startingIndex, (int)itemIndex);
// Validate
_ = list.Count.Should().Be(expectedData.Count);
_ = list.Should().Equal(expectedData);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ReverseShouldSwapItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedData = testData.ToList();
expectedData.Reverse();
// Act
list.Reverse();
// Validate
_ = list.Should().Equal(expectedData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ReverseShouldSwapItemsInCorrectOrderWhenConstrained(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
{
// Prepare
var expectedData = testData.ToArray();
foreach (var itemIndex in itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var startingItemIndex = (int)Math.Min(1, itemIndex);
var expectedRangeItems = expectedData.ToList();
expectedRangeItems.Reverse(startingItemIndex, (int)itemIndex);
// Act
list.Reverse(startingItemIndex, (int)itemIndex);
// Validate
_ = list.Should().Equal(expectedRangeItems);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void SortShouldSortItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
var expectedData = testData.Reverse().ToList();
expectedData.Sort();
// Act
list.Sort();
// Validate
_ = list.Should().Equal(expectedData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void SortShouldSortItemsInCorrectOrderWhenWithComparer(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
IComparer<long> comparer = Comparer<long>.Default;
var expectedData = testData.Reverse().ToList();
expectedData.Sort(comparer);
// Act
list.Sort(comparer);
// Validate
_ = list.Should().Equal(expectedData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void SortShouldSortItemsInCorrectOrderWhenWithComparison(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
static int comparison(long x, long y)
{
return (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
var expectedData = testData.Reverse().ToList();
expectedData.Sort(comparison);
// Act
list.Sort(comparison);
// Validate
_ = list.Should().Equal(expectedData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void SortShouldSortItemsInCorrectOrderWhenConstrained(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
{
// Prepare
IComparer<long> comparer = Comparer<long>.Default;
var expectedData = testData.Reverse().ToArray();
foreach (var itemIndex in itemIndexes)
{
// Prepare
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
var startingIndex = (int)Math.Min(1, itemIndex);
var expectedSortedData = expectedData.ToList();
expectedSortedData.Sort(startingIndex, (int)itemIndex, comparer);
// Act
list.Sort(startingIndex, (int)itemIndex, comparer);
// Validate
_ = list.Should().Equal(expectedSortedData);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimExcessShouldDecreaseCapacity(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedCapacity = itemsCount >= RecyclableDefaults.InitialCapacity ? itemsCount : RecyclableDefaults.InitialCapacity;
// Act
list.TrimExcess();
// Validate
_ = list.Capacity.Should().Be(expectedCapacity);
_ = list.Count.Should().Be(itemsCount);
_ = list.Should().Equal(testData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimExcessShouldNotBreakAdd(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)itemsCount + 1);
expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity;
// Act
list.TrimExcess();
list.Add(-1);
// Validate
_ = list.Capacity.Should().Be(expectedCapacity);
_ = list.Count.Should().Be(itemsCount + 1);
_ = list.Should().Equal(testData.Append(-1));
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimExcessShouldNotBreakAddRange(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2(checked((uint)itemsCount << 1));
expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity;
// Act
list.TrimExcess();
list.AddRange(testData);
// Validate
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedCapacity);
_ = list.Count.Should().Be(itemsCount << 1);
_ = list.Should().Equal(testData.Concat(testData));
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimExcessShouldNotBreakClear(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
// Act
list.TrimExcess();
list.Clear();
// Validate
_ = list.Capacity.Should().Be(Math.Max(RecyclableDefaults.InitialCapacity, itemsCount));
_ = list.Count.Should().Be(0);
_ = list.Should().BeEmpty();
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimExcessShouldNotBreakInsert(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
{
// Prepare
var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)itemsCount + 1);
expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity;
var expectedData = testData.ToArray();
foreach (var itemIndex in itemIndexes)
{
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedItemsAfterInsert = expectedData.ToList();
expectedItemsAfterInsert.Insert((int)itemIndex, -3);
// Act
list.TrimExcess();
list.Insert((int)itemIndex, -3);
// Validate
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedCapacity);
_ = list.Count.Should().Be(expectedItemsAfterInsert.Count);
_ = list.Should().Equal(expectedItemsAfterInsert);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimExcessShouldNotBreakRemove(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
{
// Prepare
var expectedData = testData.ToArray();
foreach (var itemIndex in itemIndexes)
{
using var list = new RecyclableList<long>(testData, itemsCount);
var expectedItemsAfterInsert = expectedData.ToList();
expectedItemsAfterInsert.RemoveAt((int)itemIndex);
// Act
list.TrimExcess();
list.RemoveAt((int)itemIndex);
// Validate
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
_ = list.Count.Should().Be(expectedItemsAfterInsert.Count);
_ = list.Should().Equal(expectedItemsAfterInsert);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimExcessShouldNotBreakIndexOf(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
{
// Prepare
var expectedData = testData.ToArray();
foreach (var itemIndex in itemIndexes)
{
using var list = new RecyclableList<long>(testData, itemsCount);
// Act
list.TrimExcess();
var actual = list.IndexOf(expectedData[(int)itemIndex]);
// Validate
_ = actual.Should().Be((int)itemIndex);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrimmedArraysShouldNotBePooled(string testCase, IEnumerable<long> testData, int itemsCount)
{
// Prepare
using var list = new RecyclableList<long>(testData, itemsCount);
// Act
list.TrimExcess();
var trimmedMemoryBlock = list.AsArray;
list.Dispose();
// Validate
_ = list.Capacity.Should().Be(0);
_ = list.Count.Should().Be(0);
_ = list.Should().BeEmpty();
if (itemsCount >= RecyclableDefaults.MinPooledArrayLength)
{
var newMemoryBlock = RecyclableArrayPool<long>.RentShared(itemsCount);
_ = BitOperations.IsPow2(itemsCount)
? newMemoryBlock.Should().BeSameAs(trimmedMemoryBlock)
: newMemoryBlock.Should().NotBeSameAs(trimmedMemoryBlock);
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrueForAllShouldBeFalseWhenNoMatch(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
{
// Prepare
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
{
using RecyclableList<long> list = new(testData, itemsCount);
Predicate<long> condition = item => item == itemIndex;
var expected = expectedData.TrueForAll(condition);
// Act
var actual = list.TrueForAll(condition);
// Validate
_ = actual.Should().Be(expected).And.BeFalse();
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void TrueForAllShouldBeTrueWhenMatched(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
{
// Prepare
using RecyclableList<long> list = new(testData, itemsCount);
int index = 0;
Predicate<long> condition = item => item == ++index;
var expected = testData.ToList().TrueForAll(condition);
index = 0;
// Act
var actual = list.TrueForAll(condition);
// Validate
_ = actual.Should().Be(expected).And.BeTrue();
}
}
}
@@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Compatibility.List", "Recyclable.Collections.Compatibility.List\Recyclable.Collections.Compatibility.List.csproj", "{779028EF-0584-4566-A80D-B5F3D6E25DD0}"
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
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}.Release|Any CPU.ActiveCfg = Release|Any CPU
{779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|Any CPU.Build.0 = Release|Any CPU
{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}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|Any CPU.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}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {16B9D137-EEFC-487F-A158-3C6AEDFDDBD5}
EndGlobalSection
EndGlobal
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Recyclable.Collections.Compatibility.List</PackageId>
<PackageVersion>0.0.1</PackageVersion>
<Authors>Marek Lemańczyk</Authors>
<Company>N/A</Company>
<Product>Recyclable.Collections.Compatibility.List</Product>
<PackageTags>Recyclable ArrayPool Array Pool List Sortable Sort</PackageTags>
<Description>Recyclable.Collections.Compatibility.List project is an open source framework for operating dynamic lists at performance close to raw arrays. It aims at providing minimal memory footprint. It implements IList&lt;T&gt;'s interface and is targeted as direct replacements of List&lt;T&gt;, SortableList&lt;T&gt;, PriorityQueue&lt;T&gt; and similar.</Description>
<PackageProjectUrl>https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List</PackageProjectUrl>
<RepositoryUrl>https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List</RepositoryUrl>
<Copyright>Marek Lemańczyk 2023</Copyright>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<PackageReleaseNotes>Support for RecyclableList&lt;T&gt; &amp; RecyclableLongList&lt;T&gt; added.</PackageReleaseNotes>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="..\README.md" Pack="true" PackagePath="" Link="README.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Recyclable.Collections" Version="0.0.6" />
</ItemGroup>
</Project>
@@ -0,0 +1,271 @@
using System.Numerics;
namespace Recyclable.Collections
{
public static class zzzRecyclableListCompatibilityListFind
{
public static bool Exists<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return false;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return true;
}
}
return false;
}
public static T? Find<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return default;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return sourceSpan[itemIndex];
}
}
return default;
}
public static RecyclableList<T> FindAll<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return new();
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<T> result = new(sourceItemsCount >> 3);
Span<T> resultSpan = result._memoryBlock;
int capacity = result._capacity,
resultItemsCount = 0;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
if (resultItemsCount + 1 > capacity)
{
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
_ = RecyclableListHelpers<T>.EnsureCapacity(result, resultItemsCount, capacity);
resultSpan = result._memoryBlock;
}
resultSpan[resultItemsCount++] = sourceSpan[itemIndex];
}
}
result._capacity = capacity;
result._count = resultItemsCount;
return result;
}
public static RecyclableList<int> FindAllIndexes<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return new();
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<int> result = new(sourceItemsCount >> 3);
Span<int> resultSpan = result._memoryBlock;
int capacity = result._capacity,
resultItemsCount = 0;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
if (resultItemsCount + 1 > capacity)
{
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
_ = RecyclableListHelpers<int>.EnsureCapacity(result, resultItemsCount, capacity);
resultSpan = result._memoryBlock;
}
resultSpan[resultItemsCount++] = itemIndex;
}
}
result._capacity = capacity;
result._count = resultItemsCount;
return result;
}
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindIndex<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static T? FindLast<T>(this RecyclableList<T> list, Predicate<T> match)
{
if (list._count == 0)
{
return default;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return sourceSpan[itemIndex];
}
}
return default;
}
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
{
if (count == 0)
{
if (startIndex != -1)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
}
}
else if (count < 0 || startIndex - count + 1 < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Count();
}
if (startIndex < 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
int sourceItemsCount = count; // startIndex - count + 1 >= 0 ? count : startIndex + 1;
if (sourceItemsCount == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
int lastItemIndex = startIndex - sourceItemsCount;
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex > lastItemIndex; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
{
if (list._count == 0 || startIndex < 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = startIndex; itemIndex >= 0; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
public static int FindLastIndex<T>(this RecyclableList<T> list, Predicate<T> match)
{
if (list._count == 0)
{
return RecyclableDefaults.ItemNotFoundIndex;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
{
if (match(sourceSpan[itemIndex]))
{
return itemIndex;
}
}
return RecyclableDefaults.ItemNotFoundIndex;
}
}
}
@@ -0,0 +1,25 @@
namespace Recyclable.Collections
{
public static class zzzRecyclableListCompatibilityListIndexOf
{
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
? Array.IndexOf(list._memoryBlock, item, index, list._count - index)
: RecyclableDefaults.ItemNotFoundIndex;
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
? Array.IndexOf(list._memoryBlock, item, index, count)
: RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, list._count - 1)
: RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, index)
: RecyclableDefaults.ItemNotFoundIndex;
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
? Array.LastIndexOf(list._memoryBlock, item, index, count)
: RecyclableDefaults.ItemNotFoundIndex;
}
}
@@ -0,0 +1,357 @@
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Recyclable.Collections
{
public static class zzzRecyclableListCompatibilityListInsertRange
{
private static void InsertRangeEnumerated<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int growByCount)
{
var enumerator = items.GetEnumerator();
if (!enumerator.MoveNext())
{
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<T> memorySpan = memoryBlock;
do
{
if (index + available + movingBlockLength > capacity)
{
var capacityBeforeIncrease = capacity;
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)(capacity + growByCount)));
_ = RecyclableListHelpers<T>.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++] = 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<T>._needsClearing)
{
Array.Clear(memoryBlock, capacity - movingBlockLength, movingBlockLength);
}
}
list._count += added;
list._capacity = capacity;
}
private static void InsertRangeWithKnownCount<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int currentItemsCount, int requiredAdditionalCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity))));
Array.Copy(list._memoryBlock, index, list._memoryBlock, index + requiredAdditionalCapacity, list._count - index);
Span<T> memorySpan = list._memoryBlock;
foreach (var item in items)
{
memorySpan[index++] = item;
}
list._count += requiredAdditionalCapacity;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, in Array items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
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;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, in T[] items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
new ReadOnlySpan<T>(items).CopyTo(new Span<T>(memoryBlock, index, items.Length));
list._count += items.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, ReadOnlySpan<T> items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
items.CopyTo(new Span<T>(memoryBlock, index, items.Length));
list._count += items.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, Span<T> items)
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
items.CopyTo(new Span<T>(memoryBlock, index, items.Length));
list._count += items.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, List<T> items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
items.CopyTo(memoryBlock, index);
list._count += items.Count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, ICollection items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
items.CopyTo(list._memoryBlock, index);
list._count += items.Count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, ICollection<T> items)
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
items.CopyTo(memoryBlock, index);
list._count += items.Count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, RecyclableList<T> items)
{
if (list._capacity < list._count + items._count)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count))));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
new ReadOnlySpan<T>(items._memoryBlock, 0, items._count).CopyTo(new(list._memoryBlock, index, items._count));
list._count += items._count;
list._version++;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, RecyclableLongList<T> items)
{
if (items._longCount == 0)
{
return;
}
int oldCount = list._count;
long sourceItemsCount = items._longCount;
if (oldCount + sourceItemsCount > int.MaxValue)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
}
int targetCapacity = oldCount + (int)sourceItemsCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
T[] memoryBlock = list._memoryBlock;
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, oldCount - index);
Span<T> targetSpan = new(list._memoryBlock, index, list._capacity - index),
itemsSpan;
int blockIndex,
sourceBlockSize = items._blockSize,
lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1;
for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize);
itemsSpan.CopyTo(targetSpan);
targetSpan = targetSpan[sourceBlockSize..];
}
if (blockIndex == 0)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount);
itemsSpan.CopyTo(targetSpan);
}
else if (blockIndex <= items._lastBlockWithData)
{
itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex > 0 ? items._nextItemIndex : sourceBlockSize);
itemsSpan.CopyTo(targetSpan);
}
list._count = targetCapacity;
list._version++;
}
public static IEnumerator InsertRange<T>(this RecyclableList<T> list, int index, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
{
int targetItemIndex = list._count;
Span<T> memorySpan;
int i;
var enumerator = source.GetEnumerator();
int capacity = list._capacity;
memorySpan = list._memoryBlock;
if (enumerator.MoveNext())
{
int available = capacity - targetItemIndex;
do
{
if (targetItemIndex + growByCount > capacity)
{
capacity = RecyclableListHelpers<T>.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);
}
list._capacity = capacity;
list._count = targetItemIndex;
return enumerator;
}
public static void InsertRange<T>(this RecyclableList<T> list, int index, IReadOnlyList<T> items)
{
int sourceItemsCount = items.Count;
if (list._capacity < list._count + sourceItemsCount)
{
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount))));
}
Span<T> memorySpan = new(list._memoryBlock, list._count, sourceItemsCount);
for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++)
{
memorySpan[sourceItemIndex] = items[sourceItemIndex];
}
list._count += sourceItemsCount;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void InsertRange<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int growByCount = RecyclableDefaults.MinPooledArrayLength)
{
if (items is RecyclableList<T> sourceRecyclableList)
{
InsertRange(list, index, sourceRecyclableList);
}
else if (items is RecyclableLongList<T> sourceRecyclableLongList)
{
InsertRange(list, index, sourceRecyclableLongList);
}
else if (items is T[] sourceArray)
{
InsertRange(list, index, sourceArray);
}
else if (items is Array sourceArrayWithObjects)
{
InsertRange(list, index, sourceArrayWithObjects);
}
else if (items is List<T> sourceList)
{
InsertRange(list, index, sourceList);
}
else if (items is ICollection<T> sourceICollection)
{
InsertRange(list, index, sourceICollection);
}
else if (items is ICollection sourceICollectionWithObjects)
{
InsertRange(list, index, sourceICollectionWithObjects);
}
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
{
InsertRange(list, index, sourceIReadOnlyList);
}
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
{
InsertRangeWithKnownCount(list, index, items, list._count, requiredAdditionalCapacity);
}
else
{
InsertRangeEnumerated(list, index, items, growByCount);
}
}
}
}
@@ -0,0 +1,213 @@
using System.Collections.ObjectModel;
using System.Numerics;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
{
public static class zzzRecyclableListCompatibilityList
{
public static ReadOnlyCollection<T> AsReadOnly<T>(this RecyclableList<T> list) => new(list);
public static int BinarySearch<T>(this RecyclableList<T> list, T item)
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item);
public static int BinarySearch<T>(this RecyclableList<T> list, T item, IComparer<T>? comparer)
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item, comparer);
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 RecyclableList<TOutput> ConvertAll<T, TOutput>(this RecyclableList<T> list, Converter<T, TOutput> converter)
{
int itemsCount = list._count;
if (itemsCount == 0)
{
return new();
}
RecyclableList<TOutput> result = new(itemsCount);
TOutput[] resultSpan = result._memoryBlock;
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (int bufferIndex = 0; bufferIndex < itemsCount; bufferIndex++)
{
resultSpan[bufferIndex] = converter(sourceSpan[bufferIndex]);
}
result._count = itemsCount;
return result;
}
public static void CopyTo<T>(this RecyclableList<T> list, T[] array) => Array.Copy(list._memoryBlock, array, list._count);
public static void CopyTo<T>(this RecyclableList<T> list, int index, T[] array, int arrayIndex, int count) => Array.Copy(list._memoryBlock, index, array, arrayIndex, count);
public static void ForEach<T>(this RecyclableList<T> list, Action<T> action)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
action(sourceSpan[itemIndex]);
}
}
public static RecyclableList<T> GetRange<T>(this RecyclableList<T> list, int index, int count)
{
int sourceItemsCount = index + count <= list._count ? count : list._count - index;
if (sourceItemsCount <= 0)
{
return new();
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
RecyclableList<T> result = new(sourceItemsCount);
Span<T> targetSpan = result._memoryBlock;
int resultItemsCount = 0;
sourceItemsCount += index;
for (var itemIndex = index; itemIndex < sourceItemsCount; itemIndex++)
{
targetSpan[resultItemsCount++] = sourceSpan[itemIndex];
}
result._count = resultItemsCount;
return result;
}
public static int RemoveAll<T>(this RecyclableList<T> list, Predicate<T> match)
{
using var allIndexes = zzzRecyclableListCompatibilityListFind.FindAllIndexes(list, match);
int foundItemsCount = allIndexes._count;
if (foundItemsCount == 0)
{
return foundItemsCount;
}
for (int removed = 0; removed < foundItemsCount; removed++)
{
var indexToRemove = allIndexes[removed] - removed;
Array.Copy(list._memoryBlock, indexToRemove + 1, list._memoryBlock, indexToRemove, list._count - indexToRemove - removed - 1);
}
if (RecyclableList<T>._needsClearing)
{
Array.Clear(list._memoryBlock, list._count - foundItemsCount, foundItemsCount);
}
list._count -= foundItemsCount;
list._version++;
return foundItemsCount;
}
public static void RemoveRange<T>(this RecyclableList<T> list, int index, int count)
{
int oldCount = list._count;
count = count - index + 1 <= oldCount ? count : oldCount - index + 1;
if (count == 0)
{
return;
}
if (index + count < oldCount)
{
Array.Copy(list._memoryBlock, index + count, list._memoryBlock, index, oldCount - count - index);
}
if (RecyclableList<T>._needsClearing)
{
Array.Clear(list._memoryBlock, oldCount - count, count);
}
list._count -= count;
list._version++;
}
public static void Reverse<T>(this RecyclableList<T> list)
{
Array.Reverse(list._memoryBlock, 0, list._count);
list._version++;
}
public static void Reverse<T>(this RecyclableList<T> list, int index, int count)
{
Array.Reverse(list._memoryBlock, index, count);
list._version++;
}
public static void Sort<T>(this RecyclableList<T> list)
{
Array.Sort(list._memoryBlock, 0, list._count);
list._version++;
}
public static void Sort<T>(this RecyclableList<T> list, Comparison<T> comparison)
{
Array.Sort(list._memoryBlock, 0, list._count, new ComparisonToComparerAdapter<T>(comparison));
list._version++;
}
public static void Sort<T>(this RecyclableList<T> list, IComparer<T>? comparer)
{
Array.Sort(list._memoryBlock, 0, list._count, comparer);
list._version++;
}
public static void Sort<T>(this RecyclableList<T> list, int index, int count, IComparer<T>? comparer)
{
Array.Sort(list._memoryBlock, index, count, comparer);
list._version++;
}
public static T[] ToArray<T>(this RecyclableList<T> list)
{
T[] result = new T[list._count];
new ReadOnlySpan<T>(list._memoryBlock, 0, list._count).CopyTo(result);
return result;
}
public static void TrimExcess<T>(this RecyclableList<T> list)
{
var requiredCapacity = list._count >= RecyclableDefaults.InitialCapacity ? list._count : RecyclableDefaults.InitialCapacity;
T[] oldMemoryBlock = list._memoryBlock;
T[] result = requiredCapacity >= RecyclableDefaults.MinPooledArrayLength && BitOperations.IsPow2(requiredCapacity)
? RecyclableArrayPool<T>.RentShared(requiredCapacity)
: new T[requiredCapacity];
new ReadOnlySpan<T>(oldMemoryBlock, 0, list._count).CopyTo(result);
if (oldMemoryBlock.Length >= RecyclableDefaults.MinPooledArrayLength)
{
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, RecyclableList<T>._needsClearing);
}
list._memoryBlock = result;
list._capacity = requiredCapacity;
}
public static bool TrueForAll<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return false;
}
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (!match(sourceSpan[itemIndex]))
{
return false;
}
}
return true;
}
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="236" height="20" role="img" aria-label="Buy me a coffee"><title>Buy me a coffee</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="236" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="137" height="20" fill="#555"/><rect x="137" width="99" height="20" fill="#4c1"/><rect width="236" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="695" y="150" fill="#010101" transform="scale(.1)" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk">Buy me a coffee</a></text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk"/></text><text aria-hidden="true" x="705" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="890">Buy me a coffee</text><text x="695" y="142" transform="scale(.1)" fill="#fff" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk">Buy me a coffee</a></text><text aria-hidden="true" x="1890" y="142" fill="#010101" transform="scale(.1)" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk">$1 - $10</a></text></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB