List<T> compatibility methods implemented. Unit tests extended. README.md updated.

This commit is contained in:
mlemanczyk committed 2023-10-07 13:35:48 +02:00
1 parent e6e7b4995a
commit 262590ed6e
26 files changed
+2721 -312

No files matched your search

+9 -3
View File
@@ -138,10 +138,10 @@
1. ✅ `Span<T>`
1. ✅ `T[]`
1. ✅ Release 0.0.5
1. 👉 Implement `List<T>` compatibility pack in `RecyclableList<T>`
1. 🅿️ Overflow review
1. ✅ Implement `List<T>` compatibility pack in `RecyclableList<T>`
1. 👉 Overflow review
1. 🅿️ Add type casting to `long` for `<<` & `>>` operations, where required
1. 🅿️ Make type castings `checked`
1. 🅿️ Make type castings `checked`, where required
1. 🅿️ Release 0.0.6
1. 🅿️ Implement `List<T>` compatibility pack in `RecyclableLongList<T>`
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>`
@@ -198,6 +198,12 @@
1. 🅿️ Pools
1. 🅿️ Multi-threading
1. 🅿️ Add comparison tests vs `T[]` / `List<T>` equivalents
1. 🅿️ Add test cases for indexes beyond items count
1. 🅿️ Cover border cases
1. 🅿️ Non-existing items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()`
1. 🅿️ Duplicated items in `.Contains...()`, `.Exists...()`, `.Find...()`, `.IndexOf...()`, `.Last...()`
1. 🅿️ Append test data in reversed order in `.AddRange()`, `.InsertRange()` and similar tests
1. 🅿️ Switch tests to use Source / Target data variants in `.AddRange()`, `.InsertRange()` and similar tests
1. 🅿️ Cleanup
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
1. 🅿️ Replace all internal properties refs with fields, where possible
@@ -0,0 +1,65 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_BinarySearch_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.BinarySearch(list, data[i]));
DoNothing(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(list.BinarySearch(data[i]));
DoNothing(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(list.BinarySearch(data[i]));
DoNothing(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(list.BinarySearch(data[i]));
DoNothing(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,48 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_ConvertAll()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
DoNothing(Array.ConvertAll(list, static (item) => (long) item));
}
public void List_ConvertAll()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
DoNothing(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(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(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,65 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Exists_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.Exists(list, (x) => x == data[i]));
DoNothing(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(list.Exists((x) => x == data[i]));
DoNothing(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(list.Exists((x) => x == data[i]));
DoNothing(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(list.Exists((x) => x == data[i]));
DoNothing(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(list.Exists((x) => x == data[i]));
// DoNothing(list.Exists((x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,80 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_FindAll_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.FindAll(list, (item) => item == data[i]));
DoNothing(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(list.FindAll((item) => item == data[i]));
DoNothing(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(temp);
temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing(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(temp);
temp.Dispose();
temp = list.FindAll((item) => item == data[^(i + 1)]);
DoNothing(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(temp);
// temp.Dispose();
// temp = list.FindAll((item) => item == data[^(i + 1)]);
// DoNothing(temp);
// temp.Dispose();
// }
// }
}
}
@@ -0,0 +1,63 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
for (var i = 0; i < dataCount; i++)
{
// Console.WriteLine($"data.Length={data.Length}");
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[i]));
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)]));
}
}
public void List_FindLastIndex_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjectsAsList;
var dataCount = TestObjectCountForSlowMethods;
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
for (var i = 0; i < dataCount; i++)
{
// Console.WriteLine($"data.Length={data.Length}");
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing(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(list.FindLastIndex(0, (x) => x == data[i]));
DoNothing(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(list.FindLastIndex(0, (x) => x == data[i]));
// DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,53 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_FindLast_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.FindLast(list, (x) => x == data[i]));
DoNothing(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(list.FindLast((x) => x == data[i]));
DoNothing(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(list.FindLast((x) => x == data[i]));
DoNothing(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(list.FindLast((x) => x == data[i]));
// DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -0,0 +1,53 @@
namespace Recyclable.Collections.Benchmarks
{
public partial class RecyclableCollectionsBenchmarks
{
public void Array_Find_BestAndWorstCases()
{
var data = TestObjects;
var list = TestObjects;
var dataCount = TestObjectCountForSlowMethods;
for (var i = 0; i < dataCount; i++)
{
DoNothing(Array.Find(list, (x) => x == data[i]));
DoNothing(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(list.Find((x) => x == data[i]));
DoNothing(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(list.Find((x) => x == data[i]));
DoNothing(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(list.Find((x) => x == data[i]));
// DoNothing(list.Find((x) => x == data[^(i + 1)]));
// }
// }
}
}
@@ -6,9 +6,11 @@ namespace Recyclable.Collections.Benchmarks
public enum RecyclableCollectionsBenchmarkType
{
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, Contains_FirstItems,
Contains_LastItems, Count, Create_WithCapacity, Create, ForEach, GetItem, IndexOf_BestAndWorstCases, IndexOf_FirstItems,
IndexOf_LastItems, LongCount, Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, BinarySearch_BestAndWorstCases,
Contains_FirstItems, Contains_LastItems, ConvertAll, Count, Create_WithCapacity, Create, Exists_BestAndWorstCases,
Find_BestAndWorstCases, FindAll_BestAndWorstCases, FindLast_BestAndWorstCases, FindLastIndex_BestAndWorstCases,
ForEach, GetItem, IndexOf_BestAndWorstCases,IndexOf_FirstItems, IndexOf_LastItems, LongCount,
Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
SetItem, VersionedForEach
}
@@ -31,11 +33,18 @@ namespace Recyclable.Collections.Benchmarks
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
RecyclableCollectionsBenchmarkType.BinarySearch_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.Contains_FirstItems,
RecyclableCollectionsBenchmarkType.Contains_LastItems,
RecyclableCollectionsBenchmarkType.ConvertAll,
RecyclableCollectionsBenchmarkType.Count,
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
RecyclableCollectionsBenchmarkType.Create,
RecyclableCollectionsBenchmarkType.Exists_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.Find_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindAll_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindLast_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.FindLastIndex_BestAndWorstCases,
RecyclableCollectionsBenchmarkType.ForEach,
RecyclableCollectionsBenchmarkType.GetItem,
RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
@@ -60,7 +60,7 @@ namespace Recyclable.Collections.Benchmarks.POC
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
// [Params(1_048_576)]
public virtual int TestObjectCount { get; set; } = 0; //8_192;//131_072; //1; //33_554_432; //524_288; //131_072;
public virtual int TestObjectCountForSlowMethods => (TestObjectCount / 2) + 1;// Math.Min(TestObjectCount, 131_072);
public virtual int TestObjectCountForSlowMethods { get; private set; }
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)]
// [Params(10, 20, 50, 100)]
@@ -103,6 +103,8 @@ namespace Recyclable.Collections.Benchmarks.POC
{
Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******");
TestObjectCountForSlowMethods = (TestObjectCount / 2) + 1;
BlockSize = TestObjectCount switch
{
0 => 1,
@@ -5,24 +5,10 @@ using Recyclable.Collections.Benchmarks.Core;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum RecyclableLongListPocBenchmarkType
{
EnsureCapacity_ByPowOf2,
EnsureCapacityV1_ByPowOf2,
EnsureCapacityV3_ByPowOf2,
}
[MemoryDiagnoser]
public partial class RecyclableLongListPocBenchmarks : PocBenchmarkBase<RecyclableLongListPocBenchmarkType>
public partial class RecyclableLongListPocBenchmarks<T> : PocBenchmarkBase<T>
{
[Params(RecyclableLongListPocBenchmarkType.EnsureCapacity_ByPowOf2)]
public override RecyclableLongListPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
// [Params(BenchmarkType.Array, BenchmarkType.List, BenchmarkType.RecyclableList, BenchmarkType.RecyclableLongList)]
[Params(RecyclableLongListPocBenchmarkType.EnsureCapacityV1_ByPowOf2)]
public override RecyclableLongListPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
private long[]? _testArray;
private long[]? _testArray;
public long[] TestObjects => _testArray ?? throw new NullReferenceException("Something is wrong and test objects are null");
public RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testRecyclableLongList ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongList<long>)} is null");
@@ -31,34 +17,12 @@ namespace Recyclable.Collections.Benchmarks.POC
internal PooledList<long> TestPooledList => _testPooledList ?? throw new NullReferenceException($"Something is wrong and {nameof(PooledList<long>)} is null");
private static readonly MethodInfo _ensureCapacityNewFunc;
private static readonly MethodInfo _ensureCapacityV1Func;
private static readonly MethodInfo _ensureCapacityV3Func;
static RecyclableLongListPocBenchmarks()
{
_ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongList<long>)}");
_ensureCapacityV1Func = typeof(RecyclableLongListV1<long>).GetMethod("EnsureCapacity", BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongListV1<long>)}");
_ensureCapacityV3Func = typeof(RecyclableLongListV3<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongListV3<long>)}");
}
public static void Run()
{
var benchmark = new RecyclableLongListPocBenchmarks();
benchmark.Setup();
// benchmark.RecyclableLongList_EnsureCapacity_ByPowOf2();
// benchmark.RecyclableLongList_EnsureCapacityV1_ByPowOf2();
// benchmark.RecyclableLongList_EnsureCapacityV2_ByPowOf2();
// benchmark.RecyclableLongList_EnsureCapacityV3_ByPowOf2();
// benchmark.RecyclableLongList_EnsureCapacity_ByBlockSize();
// benchmark.RecyclableLongList_EnsureCapacityV1_ByBlockSize();
// benchmark.RecyclableLongList_EnsureCapacityV2_ByBlockSize();
// benchmark.RecyclableLongList_EnsureCapacityV3_ByBlockSize();
benchmark.Cleanup();
}
public override void Setup()
@@ -67,7 +31,7 @@ namespace Recyclable.Collections.Benchmarks.POC
_testArray = DataGenerator.EnumerateTestObjects(TestObjectCount);
base.Setup();
}
}
public override void Cleanup()
{
@@ -79,18 +43,5 @@ namespace Recyclable.Collections.Benchmarks.POC
base.Cleanup();
}
protected override Action? GetTestMethod(RecyclableLongListPocBenchmarkType benchmarkType) => benchmarkType switch
{
RecyclableLongListPocBenchmarkType.EnsureCapacity_ByPowOf2 => () => RecyclableLongList_EnsureCapacity_ByPowOf2(),
RecyclableLongListPocBenchmarkType.EnsureCapacityV1_ByPowOf2 => () => RecyclableLongList_EnsureCapacityV1_ByPowOf2(),
RecyclableLongListPocBenchmarkType.EnsureCapacityV3_ByPowOf2 => () => RecyclableLongList_EnsureCapacityV3_ByPowOf2(),
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
protected override void PrepareData<T>(T benchmarkType)
{
base.PrepareData(benchmarkType);
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Recyclable.Collections
{
public static class CompareResult
{
public const int XLessThanY = -1;
public const int XEqualY = 0;
public const int XGreaterThenY = 1;
}
}
@@ -0,0 +1,32 @@
namespace Recyclable.Collections
{
public readonly struct ComparisonToComparerAdapter<T> : IComparer<T>
{
private readonly Comparison<T> _comparison;
public ComparisonToComparerAdapter(Comparison<T> comparison)
{
_comparison = comparison;
}
public int Compare(T? x, T? y)
{
if (x == null && y == null)
{
return CompareResult.XEqualY;
}
else if (x != null && y == null)
{
return CompareResult.XGreaterThenY;
}
else if (y != null && x == null)
{
return CompareResult.XLessThanY;
}
else
{
return _comparison(x!, y!);
}
}
}
}
@@ -7,7 +7,12 @@ namespace Recyclable.Collections.Pools
internal static readonly OneSizeArrayPool<T>[] _pools = new OneSizeArrayPool<T>[32];
public static T[] RentShared(int blockSize) => _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent();
public static void ReturnShared(in T[] memoryBlock, bool needsClearing)
=> _pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing);
{
if (BitOperations.IsPow2(memoryBlock.Length))
{
_pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing);
}
}
public static ref OneSizeArrayPool<T> Shared(int blockSize)
=> ref _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)];
@@ -1,48 +1,44 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Recyclable.Collections
{
public static class zRecyclableListAddRange
public static class zRecyclableListAddRange
{
private static void AddRangeEnumerated<T>(this RecyclableList<T> list, IEnumerable<T> items, int growByCount)
{
int targetItemIndex = list._count;
Span<T> memorySpan;
int i;
var enumerator = items.GetEnumerator();
int capacity = list._capacity;
memorySpan = new(list._memoryBlock);
if (enumerator.MoveNext())
if (!enumerator.MoveNext())
{
int available = capacity - targetItemIndex;
do
{
if (targetItemIndex + growByCount > capacity)
{
capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
memorySpan = new(list._memoryBlock);
available = capacity - targetItemIndex;
}
return;
}
for (i = 0; i < available; i++)
int capacity = list._capacity,
targetItemIndex = list._count,
available = capacity - targetItemIndex,
i;
Span<T> memorySpan = new(list._memoryBlock);
do
{
if (targetItemIndex + growByCount > capacity)
{
capacity = RecyclableListHelpers<T>.EnsureCapacity(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
memorySpan = new(list._memoryBlock);
available = capacity - targetItemIndex;
}
for (i = 0; i < available; i++)
{
memorySpan[targetItemIndex++] = enumerator.Current;
if (!enumerator.MoveNext())
{
memorySpan[targetItemIndex++] = enumerator.Current;
if (!enumerator.MoveNext())
{
break;
}
break;
}
}
while (i >= available);
}
while (i >= available);
list._capacity = capacity;
list._count = targetItemIndex;
@@ -50,7 +46,7 @@ namespace Recyclable.Collections
private static void AddRangeWithKnownCount<T>(this RecyclableList<T> list, IEnumerable<T> items, int currentItemsCount, int requiredAdditionalCapacity)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity))));
Span<T> memorySpan = new(list._memoryBlock);
foreach (var item in items)
@@ -66,10 +62,10 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
Array.Copy(items, items.GetLowerBound(0), list._memoryBlock, list._count, items.LongLength);
Array.Copy(items, items.GetLowerBound(0), list._memoryBlock, list._count, items.Length);
list._count += items.Length;
list._version++;
}
@@ -79,7 +75,7 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
new Span<T>(items).CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
@@ -92,7 +88,7 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
@@ -105,7 +101,7 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items.Length)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
}
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
@@ -118,7 +114,7 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
items.CopyTo(list._memoryBlock, list._count);
@@ -131,7 +127,7 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
items.CopyTo(list._memoryBlock, list._count);
@@ -144,7 +140,7 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items.Count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
}
items.CopyTo(list._memoryBlock, list._count);
@@ -157,7 +153,7 @@ namespace Recyclable.Collections
{
if (list._capacity < list._count + items._count)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count))));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count))));
}
new Span<T>(items._memoryBlock, 0, items._count).CopyTo(new Span<T>(list._memoryBlock, list._count, items._count));
@@ -183,7 +179,7 @@ namespace Recyclable.Collections
int targetCapacity = oldCount + (int)sourceItemsCount;
if (list._capacity < targetCapacity)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
}
// TODO: Avoid unnecessary range operator - pass as arguments instead.
@@ -233,7 +229,7 @@ namespace Recyclable.Collections
{
if (targetItemIndex + growByCount > capacity)
{
capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
capacity = RecyclableListHelpers<T>.EnsureCapacity(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
memorySpan = new(list._memoryBlock);
available = capacity - targetItemIndex;
}
@@ -260,7 +256,7 @@ namespace Recyclable.Collections
int sourceItemsCount = items.Count;
if (list._capacity < list._count + sourceItemsCount)
{
list._capacity = RecyclableListHelpers<T>.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(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);
@@ -0,0 +1,271 @@
using System.Numerics;
namespace Recyclable.Collections
{
public static class zRecyclableListCompatibilityListFind
{
public static bool Exists<T>(this RecyclableList<T> list, Predicate<T> match)
{
int sourceItemsCount = list._count;
if (sourceItemsCount == 0)
{
return false;
}
ReadOnlySpan<T> sourceSpan = new(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 = new(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 = new(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 = new(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 = new(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 = new(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 = new(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 = new(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 = new(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 = new(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 = new(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 zRecyclableListCompatibilityListIndexOf
{
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 zRecyclableListCompatibilityListInsertRange
{
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 = new(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 = new(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 = new(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 Span<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 Span<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 = new(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 = new(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 zRecyclableListCompatibilityList
{
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 = new(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 = new(list._memoryBlock);
RecyclableList<T> result = new(sourceItemsCount);
Span<T> resultSpan = result._memoryBlock;
int resultItemsCount = 0;
sourceItemsCount += index;
for (var itemIndex = index; itemIndex < sourceItemsCount; itemIndex++)
{
resultSpan[resultItemsCount++] = sourceSpan[itemIndex];
}
result._count = resultItemsCount;
return result;
}
public static int RemoveAll<T>(this RecyclableList<T> list, Predicate<T> match)
{
using var allIndexes = list.FindAllIndexes(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 Span<T>(list._memoryBlock, 0, list._count).CopyTo(new Span<T>(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 Span<T>(oldMemoryBlock, 0, list._count).CopyTo(new(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 = new(list._memoryBlock);
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
{
if (!match(sourceSpan[itemIndex]))
{
return false;
}
}
return true;
}
}
}
@@ -7,104 +7,10 @@ namespace Recyclable.Collections
{
private static readonly bool NeedsClearing = !typeof(T).IsValueType;
[Obsolete("This method MAY be removed in the near future.")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains(RecyclableList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
[Obsolete("This method MAY be removed in the near future.")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyTo(RecyclableList<T[]> arrays, long startingIndex, int blockSize, int lastBlockSize, T[] destinationArray, int destinationArrayIndex)
{
Span<T> arrayMemory = destinationArray.AsSpan();
arrayMemory = arrayMemory[destinationArrayIndex..];
// TODO: Convert "/" & "%" to bit-shifting
int startingArrayIndex = (int)(startingIndex / blockSize);
startingIndex %= blockSize;
if (arrays._count > 0)
{
ReadOnlySpan<T> sourceMemory = arrays[0].AsSpan();
var maxToCopy = (int)Math.Min(blockSize - startingIndex, arrayMemory.Length);
if (arrays._count == 1)
{
maxToCopy = Math.Min(maxToCopy, lastBlockSize);
}
sourceMemory = sourceMemory[(int)startingIndex..maxToCopy];
sourceMemory.CopyTo(arrayMemory);
arrayMemory = arrayMemory[maxToCopy..];
}
for (var arrayIdx = startingArrayIndex + 1; arrayIdx < arrays._count - 1 && arrayMemory.Length > 0; arrayIdx++)
{
ReadOnlySpan<T> sourceMemory = arrays[arrayIdx].AsSpan();
var maxToCopy = Math.Min(blockSize, arrayMemory.Length);
sourceMemory = sourceMemory[..maxToCopy];
sourceMemory.CopyTo(arrayMemory);
arrayMemory = arrayMemory[maxToCopy..];
}
if (arrays._count > 1 && arrayMemory.Length > 0)
{
ReadOnlySpan<T> sourceMemory = arrays[^1].AsSpan();
var maxToCopy = Math.Min(lastBlockSize, arrayMemory.Length);
sourceMemory = sourceMemory[..maxToCopy];
sourceMemory.CopyTo(arrayMemory);
}
}
[Obsolete("This method WILL be removed in the near future. Please use the existing enumerators instead of it.")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEnumerable<T> Enumerate(RecyclableList<T[]> arrays, int chunkSize, long totalCount)
{
long currentCount = 0;
for (var arrayIdx = 0; (arrayIdx < arrays._count) && (currentCount < totalCount); arrayIdx++)
{
var array = arrays[arrayIdx];
currentCount += Math.Min(chunkSize, totalCount);
switch (currentCount < totalCount)
{
case true:
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
{
yield return array[valueIdx];
}
break;
case false:
var partialCount = (int)(totalCount % chunkSize);
int maxCount = partialCount > 0 ? partialCount : chunkSize;
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
{
yield return array[valueIdx];
}
break;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long LongIndexOf(RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
{
for (var arrayIdx = 0; arrayIdx < arrays._count; arrayIdx++)
{
var array = arrays[arrayIdx];
Span<T> arrayMemory = array.AsSpan();
for (int memoryIdx = 0; memoryIdx < arrayMemory.Length; memoryIdx++)
{
if (comparer.Equals(arrayMemory[memoryIdx], item))
{
return (arrayIdx * (long)blockSize) + memoryIdx;
}
}
}
return -1;
}
public static int CalculateIndex(int index, int lowerBound) => index >= 0 ? index + lowerBound : RecyclableDefaults.ItemNotFoundIndex;
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void ResizeAndCopy(RecyclableList<T> list)
public static void EnsureCapacity(RecyclableList<T> list)
{
// TODO: Measure performance
// T[] newMemoryBlock = RecyclableListHelpers<T>._arrayPool.Rent(_capacity <<= 1);
@@ -129,14 +35,38 @@ namespace Recyclable.Collections
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static int ResizeAndCopy(RecyclableList<T> sourceList, int oldSize, int newSize)
public static int EnsureCapacity(RecyclableList<T> sourceList, int newSize)
{
T[] oldMemoryBlock = sourceList._memoryBlock;
sourceList._memoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(newSize)
: new T[newSize];
// & WAS SLOWER WITHOUT
// & WAS SLOWER AS ARRAY
new Span<T>(oldMemoryBlock, 0, sourceList._capacity).CopyTo(sourceList._memoryBlock);
if (oldMemoryBlock.Length >= RecyclableDefaults.MinPooledArrayLength)
{
// TODO: Measure gain vs relying on arrayPool to clear
//if (NeedsClearing)
//{
// Array.Clear(source);
//}
// If anything, it has been already cleared above, so we don't need to repeat it.
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, NeedsClearing);
}
return newSize;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static int EnsureCapacity(RecyclableList<T> sourceList, int oldSize, int newSize)
{
T[] oldMemoryBlock = sourceList._memoryBlock;
sourceList._memoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(newSize)
: new T[newSize];
// & WAS SLOWER AS ARRAY
new Span<T>(oldMemoryBlock, 0, oldSize).CopyTo(sourceList._memoryBlock);
+49 -6
View File
@@ -8,7 +8,7 @@ namespace Recyclable.Collections
[Serializable]
public sealed partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IDisposable
{
private static readonly bool _needsClearing = !typeof(T).IsValueType;
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
public static explicit operator ReadOnlySpan<T>(RecyclableList<T> source) => new(source._memoryBlock, 0, source._count);
@@ -41,6 +41,22 @@ namespace Recyclable.Collections
}
}
public RecyclableList(int initialCapacity, int lowerBound)
{
if (initialCapacity >= RecyclableDefaults.InitialCapacity)
{
_memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
? RecyclableArrayPool<T>.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity)))
: new T[initialCapacity];
_capacity = _memoryBlock.Length;
}
else
{
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
_capacity = RecyclableDefaults.InitialCapacity;
}
}
public RecyclableList(in Array source)
{
_capacity = RecyclableDefaults.InitialCapacity;
@@ -168,14 +184,30 @@ namespace Recyclable.Collections
{
if (_capacity != value)
{
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)value)));
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)value)));
_version++;
}
}
}
internal int _count;
public int Count => _count;
public int Count
{
get => _count;
set
{
if (_count != value)
{
if (value > _capacity)
{
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, _capacity, checked((int)BitOperations.RoundUpToPowerOf2((uint)value)));
}
_count = value;
_version++;
}
}
}
public bool IsReadOnly => false;
@@ -184,7 +216,14 @@ namespace Recyclable.Collections
{
if (_count >= _capacity)
{
RecyclableListHelpers<T>.ResizeAndCopy(this);
if (BitOperations.IsPow2(_capacity))
{
RecyclableListHelpers<T>.EnsureCapacity(this);
}
else
{
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, (int)BitOperations.RoundUpToPowerOf2((uint)_capacity));
}
}
_memoryBlock[_count++] = item;
@@ -217,7 +256,9 @@ namespace Recyclable.Collections
public void CopyTo(T[] array, int index) => Array.Copy(_memoryBlock, 0, array, index, _count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(T itemToFind) => _count != 0 ? Array.IndexOf(_memoryBlock, itemToFind, 0, _count) : RecyclableDefaults.ItemNotFoundIndex;
public int IndexOf(T itemToFind) => _count != 0
? Array.IndexOf(_memoryBlock, itemToFind, 0, _count)
: RecyclableDefaults.ItemNotFoundIndex;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Insert(int index, T item)
@@ -225,11 +266,13 @@ namespace Recyclable.Collections
int oldCount = _count;
if (_capacity < oldCount + 1)
{
_capacity = RecyclableListHelpers<T>.ResizeAndCopy(this, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)oldCount + 1)));
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)oldCount + 1)));
}
if (index < oldCount)
{
// TODO: Compare performance
// Array.Copy(_memoryBlock, index, _memoryBlock, index + 1, oldCount);
new Span<T>(_memoryBlock, index, oldCount -= index)
.CopyTo(new Span<T>(_memoryBlock, index + 1, oldCount));
}
@@ -2,12 +2,109 @@ using System.Runtime.CompilerServices;
namespace Recyclable.Collections
{
internal static class RecyclableQueueHelpers
internal static class RecyclableQueueHelpers<T>
{
[Obsolete("This method MAY be removed in the near future.")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool Contains(RecyclableList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToArrayIndex(long index, int blockSize) => (int)(index / blockSize);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long ToItemIndex(long index, int blockSize) => index % blockSize;
[Obsolete("This method MAY be removed in the near future.")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void CopyTo(RecyclableList<T[]> arrays, long startingIndex, int blockSize, int lastBlockSize, T[] destinationArray, int destinationArrayIndex)
{
Span<T> arrayMemory = destinationArray.AsSpan();
arrayMemory = arrayMemory[destinationArrayIndex..];
// TODO: Convert "/" & "%" to bit-shifting
int startingArrayIndex = (int)(startingIndex / blockSize);
startingIndex %= blockSize;
if (arrays._count > 0)
{
ReadOnlySpan<T> sourceMemory = arrays[0].AsSpan();
var maxToCopy = (int)Math.Min(blockSize - startingIndex, arrayMemory.Length);
if (arrays._count == 1)
{
maxToCopy = Math.Min(maxToCopy, lastBlockSize);
}
sourceMemory = sourceMemory[(int)startingIndex..maxToCopy];
sourceMemory.CopyTo(arrayMemory);
arrayMemory = arrayMemory[maxToCopy..];
}
for (var arrayIdx = startingArrayIndex + 1; arrayIdx < arrays._count - 1 && arrayMemory.Length > 0; arrayIdx++)
{
ReadOnlySpan<T> sourceMemory = arrays[arrayIdx].AsSpan();
var maxToCopy = Math.Min(blockSize, arrayMemory.Length);
sourceMemory = sourceMemory[..maxToCopy];
sourceMemory.CopyTo(arrayMemory);
arrayMemory = arrayMemory[maxToCopy..];
}
if (arrays._count > 1 && arrayMemory.Length > 0)
{
ReadOnlySpan<T> sourceMemory = arrays[^1].AsSpan();
var maxToCopy = Math.Min(lastBlockSize, arrayMemory.Length);
sourceMemory = sourceMemory[..maxToCopy];
sourceMemory.CopyTo(arrayMemory);
}
}
[Obsolete("This method WILL be removed in the near future. Please use the existing enumerators instead of it.")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static IEnumerable<T> Enumerate(RecyclableList<T[]> arrays, int chunkSize, long totalCount)
{
long currentCount = 0;
for (var arrayIdx = 0; (arrayIdx < arrays._count) && (currentCount < totalCount); arrayIdx++)
{
var array = arrays[arrayIdx];
currentCount += Math.Min(chunkSize, totalCount);
switch (currentCount < totalCount)
{
case true:
for (var valueIdx = 0; valueIdx < chunkSize; valueIdx++)
{
yield return array[valueIdx];
}
break;
case false:
var partialCount = (int)(totalCount % chunkSize);
int maxCount = partialCount > 0 ? partialCount : chunkSize;
for (var valueIdx = 0; valueIdx < maxCount; valueIdx++)
{
yield return array[valueIdx];
}
break;
}
}
}
[Obsolete]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long LongIndexOf(RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
{
for (var arrayIdx = 0; arrayIdx < arrays._count; arrayIdx++)
{
var array = arrays[arrayIdx];
Span<T> arrayMemory = array.AsSpan();
for (int memoryIdx = 0; memoryIdx < arrayMemory.Length; memoryIdx++)
{
if (comparer.Equals(arrayMemory[memoryIdx], item))
{
return (arrayIdx * (long)blockSize) + memoryIdx;
}
}
}
return -1;
}
}
}
+11 -11
View File
@@ -99,8 +99,8 @@ namespace Recyclable.Collections
private void SetItem(long index, T value)
{
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
int arrayIndex = RecyclableQueueHelpers.ToArrayIndex(absoluteIndex, BlockSize);
long itemIndex = RecyclableQueueHelpers.ToItemIndex(absoluteIndex, BlockSize);
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
Memory[arrayIndex][itemIndex] = value;
}
@@ -108,8 +108,8 @@ namespace Recyclable.Collections
private T GetItem(long index)
{
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
int arrayIndex = RecyclableQueueHelpers.ToArrayIndex(absoluteIndex, BlockSize);
long itemIndex = RecyclableQueueHelpers.ToItemIndex(absoluteIndex, BlockSize);
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
return Memory[arrayIndex][itemIndex];
}
@@ -166,13 +166,13 @@ namespace Recyclable.Collections
}
#pragma warning disable CS0618
public bool Contains(T item) => RecyclableListHelpers<T>.Contains(Memory, item);
public void CopyTo(T[] array, int arrayIndex) => RecyclableListHelpers<T>.CopyTo(Memory, RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
public IEnumerable<T> Enumerate() => RecyclableListHelpers<T>.Enumerate(Memory, BlockSize, LongCount);
public IEnumerator<T> GetEnumerator() => RecyclableListHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => RecyclableListHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
public int IndexOf(T item) => (int)GetRelativeIndex(RecyclableListHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
public long LongIndexOf(T item) => GetRelativeIndex(RecyclableListHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
public bool Contains(T item) => RecyclableQueueHelpers<T>.Contains(Memory, item);
public void CopyTo(T[] array, int arrayIndex) => RecyclableQueueHelpers<T>.CopyTo(Memory, RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
public IEnumerable<T> Enumerate() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount);
public IEnumerator<T> GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
public int IndexOf(T item) => (int)GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
public long LongIndexOf(T item) => GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
#pragma warning restore CS0618
public void Insert(int index, T item) => throw new NotSupportedException();
+4 -10
View File
@@ -8,15 +8,9 @@ namespace Recyclable.Collections
/// This is a helper method to throw <see cref="ArgumentOutOfRangeException"/> with the given message.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Always</exception>
public static void ThrowArgumentOutOfRangeException(string argumentName, string message)
{
Contract.EnsuresOnThrow<ArgumentOutOfRangeException>(false);
throw new ArgumentOutOfRangeException(argumentName, message);
}
public static void ThrowWrongReturnedArraySizeException(int returnedLength, int blockSize)
{
throw new InvalidOperationException($"The array size {returnedLength} doesn't match the size {blockSize} of the pool buffer and cannot be returned to this pool.");
}
public static void ThrowArgumentOutOfRangeException(string argumentName, string message) => throw new ArgumentOutOfRangeException(argumentName, message);
public static void ThrowWrongReturnedArraySizeException(int returnedLength, int blockSize) => throw new InvalidOperationException($"The array size {returnedLength} doesn't match the size {blockSize} of the pool buffer and cannot be returned to this pool.");
public static void ThrowArgumentOutOfRangeException_Index() => throw new ArgumentOutOfRangeException("index", "Parameter `index` has invalid value. It must be within the no. of elements in the collection");
public static void ThrowArgumentOutOfRangeException_Count() => throw new ArgumentOutOfRangeException("index", "Parameter `count` has invalid value. Combined with index, it must be within the no. of elements in the collection");
}
}
File diff suppressed because it is too large. Load diff
@@ -1,7 +1,6 @@
using MoreLinq;
using Open.Numeric.Primes;
using Recyclable.Collections;
using System.Collections;
using System.Numerics;
namespace Recyclable.CollectionsTests
@@ -34,6 +33,8 @@ namespace Recyclable.CollectionsTests
0, 1, 2, 3, 7, 10, 16, RecyclableDefaults.MinPooledArrayLength - 5, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 5, RecyclableDefaults.MinPooledArrayLength * 100
};
public static readonly IEnumerable<object[]> ItemsCountTestCases = ItemsCountVariants.Select(x => new object[] { (int)x });
public static readonly IEnumerable<int> BlockSizeVariants = new int[]
{
1, 2, 4, 16, RecyclableDefaults.MinPooledArrayLength - 5, (int)BitOperations.RoundUpToPowerOf2(RecyclableDefaults.MinPooledArrayLength)