List<T> compatibility methods implemented. Unit tests extended. README.md updated.
This commit is contained in:
1 parent
e6e7b4995a
commit
262590ed6e
26 files changed
+2721
-312
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user