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. ✅ `Span<T>`
|
||||||
1. ✅ `T[]`
|
1. ✅ `T[]`
|
||||||
1. ✅ Release 0.0.5
|
1. ✅ Release 0.0.5
|
||||||
1. 👉 Implement `List<T>` compatibility pack in `RecyclableList<T>`
|
1. ✅ Implement `List<T>` compatibility pack in `RecyclableList<T>`
|
||||||
1. 🅿️ Overflow review
|
1. 👉 Overflow review
|
||||||
1. 🅿️ Add type casting to `long` for `<<` & `>>` operations, where required
|
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. 🅿️ Release 0.0.6
|
||||||
1. 🅿️ Implement `List<T>` compatibility pack in `RecyclableLongList<T>`
|
1. 🅿️ Implement `List<T>` compatibility pack in `RecyclableLongList<T>`
|
||||||
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>`
|
1. 🅿️ Port `RecyclableLongList<T>` optimizations to `RecyclableList<T>`
|
||||||
@@ -198,6 +198,12 @@
|
|||||||
1. 🅿️ Pools
|
1. 🅿️ Pools
|
||||||
1. 🅿️ Multi-threading
|
1. 🅿️ Multi-threading
|
||||||
1. 🅿️ Add comparison tests vs `T[]` / `List<T>` equivalents
|
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. 🅿️ Cleanup
|
||||||
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
||||||
1. 🅿️ Replace all internal properties refs with fields, where possible
|
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
|
public enum RecyclableCollectionsBenchmarkType
|
||||||
{
|
{
|
||||||
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
||||||
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, Contains_FirstItems,
|
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, BinarySearch_BestAndWorstCases,
|
||||||
Contains_LastItems, Count, Create_WithCapacity, Create, ForEach, GetItem, IndexOf_BestAndWorstCases, IndexOf_FirstItems,
|
Contains_FirstItems, Contains_LastItems, ConvertAll, Count, Create_WithCapacity, Create, Exists_BestAndWorstCases,
|
||||||
IndexOf_LastItems, LongCount, Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
|
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
|
SetItem, VersionedForEach
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,11 +33,18 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
||||||
|
RecyclableCollectionsBenchmarkType.BinarySearch_BestAndWorstCases,
|
||||||
RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
||||||
RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
||||||
|
RecyclableCollectionsBenchmarkType.ConvertAll,
|
||||||
RecyclableCollectionsBenchmarkType.Count,
|
RecyclableCollectionsBenchmarkType.Count,
|
||||||
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
||||||
RecyclableCollectionsBenchmarkType.Create,
|
RecyclableCollectionsBenchmarkType.Create,
|
||||||
|
RecyclableCollectionsBenchmarkType.Exists_BestAndWorstCases,
|
||||||
|
RecyclableCollectionsBenchmarkType.Find_BestAndWorstCases,
|
||||||
|
RecyclableCollectionsBenchmarkType.FindAll_BestAndWorstCases,
|
||||||
|
RecyclableCollectionsBenchmarkType.FindLast_BestAndWorstCases,
|
||||||
|
RecyclableCollectionsBenchmarkType.FindLastIndex_BestAndWorstCases,
|
||||||
RecyclableCollectionsBenchmarkType.ForEach,
|
RecyclableCollectionsBenchmarkType.ForEach,
|
||||||
RecyclableCollectionsBenchmarkType.GetItem,
|
RecyclableCollectionsBenchmarkType.GetItem,
|
||||||
RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
|
RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
|
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
|
||||||
// [Params(1_048_576)]
|
// [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 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(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)]
|
// [Params(10, 20, 50, 100)]
|
||||||
@@ -103,6 +103,8 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
{
|
{
|
||||||
Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******");
|
Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******");
|
||||||
|
|
||||||
|
TestObjectCountForSlowMethods = (TestObjectCount / 2) + 1;
|
||||||
|
|
||||||
BlockSize = TestObjectCount switch
|
BlockSize = TestObjectCount switch
|
||||||
{
|
{
|
||||||
0 => 1,
|
0 => 1,
|
||||||
|
|||||||
@@ -5,24 +5,10 @@ using Recyclable.Collections.Benchmarks.Core;
|
|||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
namespace Recyclable.Collections.Benchmarks.POC
|
||||||
{
|
{
|
||||||
public enum RecyclableLongListPocBenchmarkType
|
|
||||||
{
|
|
||||||
EnsureCapacity_ByPowOf2,
|
|
||||||
EnsureCapacityV1_ByPowOf2,
|
|
||||||
EnsureCapacityV3_ByPowOf2,
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public partial class RecyclableLongListPocBenchmarks : PocBenchmarkBase<RecyclableLongListPocBenchmarkType>
|
public partial class RecyclableLongListPocBenchmarks<T> : PocBenchmarkBase<T>
|
||||||
{
|
{
|
||||||
[Params(RecyclableLongListPocBenchmarkType.EnsureCapacity_ByPowOf2)]
|
private long[]? _testArray;
|
||||||
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;
|
|
||||||
public long[] TestObjects => _testArray ?? throw new NullReferenceException("Something is wrong and test objects are null");
|
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");
|
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");
|
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 _ensureCapacityNewFunc;
|
||||||
private static readonly MethodInfo _ensureCapacityV1Func;
|
|
||||||
private static readonly MethodInfo _ensureCapacityV3Func;
|
|
||||||
|
|
||||||
static RecyclableLongListPocBenchmarks()
|
static RecyclableLongListPocBenchmarks()
|
||||||
{
|
{
|
||||||
_ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
|
_ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
|
||||||
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongList<long>)}");
|
?? 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()
|
public override void Setup()
|
||||||
@@ -67,7 +31,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
_testArray = DataGenerator.EnumerateTestObjects(TestObjectCount);
|
_testArray = DataGenerator.EnumerateTestObjects(TestObjectCount);
|
||||||
|
|
||||||
base.Setup();
|
base.Setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Cleanup()
|
public override void Cleanup()
|
||||||
{
|
{
|
||||||
@@ -79,18 +43,5 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
|
|
||||||
base.Cleanup();
|
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];
|
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 T[] RentShared(int blockSize) => _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent();
|
||||||
public static void ReturnShared(in T[] memoryBlock, bool needsClearing)
|
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)
|
public static ref OneSizeArrayPool<T> Shared(int blockSize)
|
||||||
=> ref _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)];
|
=> ref _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)];
|
||||||
|
|||||||
@@ -1,48 +1,44 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
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)
|
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();
|
var enumerator = items.GetEnumerator();
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
int capacity = list._capacity;
|
|
||||||
memorySpan = new(list._memoryBlock);
|
|
||||||
if (enumerator.MoveNext())
|
|
||||||
{
|
{
|
||||||
int available = capacity - targetItemIndex;
|
return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
break;
|
||||||
if (!enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (i >= available);
|
|
||||||
}
|
}
|
||||||
|
while (i >= available);
|
||||||
|
|
||||||
list._capacity = capacity;
|
list._capacity = capacity;
|
||||||
list._count = targetItemIndex;
|
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)
|
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);
|
Span<T> memorySpan = new(list._memoryBlock);
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
@@ -66,10 +62,10 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (list._capacity < list._count + items.Length)
|
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._count += items.Length;
|
||||||
list._version++;
|
list._version++;
|
||||||
}
|
}
|
||||||
@@ -79,7 +75,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (list._capacity < list._count + items.Length)
|
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));
|
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)
|
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));
|
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)
|
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));
|
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)
|
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);
|
items.CopyTo(list._memoryBlock, list._count);
|
||||||
@@ -131,7 +127,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (list._capacity < list._count + items.Count)
|
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);
|
items.CopyTo(list._memoryBlock, list._count);
|
||||||
@@ -144,7 +140,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (list._capacity < list._count + items.Count)
|
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);
|
items.CopyTo(list._memoryBlock, list._count);
|
||||||
@@ -157,7 +153,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (list._capacity < list._count + items._count)
|
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));
|
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;
|
int targetCapacity = oldCount + (int)sourceItemsCount;
|
||||||
if (list._capacity < targetCapacity)
|
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.
|
// TODO: Avoid unnecessary range operator - pass as arguments instead.
|
||||||
@@ -233,7 +229,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (targetItemIndex + growByCount > capacity)
|
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);
|
memorySpan = new(list._memoryBlock);
|
||||||
available = capacity - targetItemIndex;
|
available = capacity - targetItemIndex;
|
||||||
}
|
}
|
||||||
@@ -260,7 +256,7 @@ namespace Recyclable.Collections
|
|||||||
int sourceItemsCount = items.Count;
|
int sourceItemsCount = items.Count;
|
||||||
if (list._capacity < list._count + sourceItemsCount)
|
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);
|
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;
|
private static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
||||||
|
|
||||||
[Obsolete("This method MAY be removed in the near future.")]
|
public static int CalculateIndex(int index, int lowerBound) => index >= 0 ? index + lowerBound : RecyclableDefaults.ItemNotFoundIndex;
|
||||||
[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;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void ResizeAndCopy(RecyclableList<T> list)
|
public static void EnsureCapacity(RecyclableList<T> list)
|
||||||
{
|
{
|
||||||
// TODO: Measure performance
|
// TODO: Measure performance
|
||||||
// T[] newMemoryBlock = RecyclableListHelpers<T>._arrayPool.Rent(_capacity <<= 1);
|
// T[] newMemoryBlock = RecyclableListHelpers<T>._arrayPool.Rent(_capacity <<= 1);
|
||||||
@@ -129,14 +35,38 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[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;
|
T[] oldMemoryBlock = sourceList._memoryBlock;
|
||||||
sourceList._memoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength
|
sourceList._memoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength
|
||||||
? RecyclableArrayPool<T>.RentShared(newSize)
|
? RecyclableArrayPool<T>.RentShared(newSize)
|
||||||
: new T[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
|
// & WAS SLOWER AS ARRAY
|
||||||
new Span<T>(oldMemoryBlock, 0, oldSize).CopyTo(sourceList._memoryBlock);
|
new Span<T>(oldMemoryBlock, 0, oldSize).CopyTo(sourceList._memoryBlock);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Recyclable.Collections
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
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);
|
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)
|
public RecyclableList(in Array source)
|
||||||
{
|
{
|
||||||
_capacity = RecyclableDefaults.InitialCapacity;
|
_capacity = RecyclableDefaults.InitialCapacity;
|
||||||
@@ -168,14 +184,30 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (_capacity != value)
|
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++;
|
_version++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int _count;
|
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;
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
@@ -184,7 +216,14 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (_count >= _capacity)
|
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;
|
_memoryBlock[_count++] = item;
|
||||||
@@ -217,7 +256,9 @@ namespace Recyclable.Collections
|
|||||||
public void CopyTo(T[] array, int index) => Array.Copy(_memoryBlock, 0, array, index, _count);
|
public void CopyTo(T[] array, int index) => Array.Copy(_memoryBlock, 0, array, index, _count);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Insert(int index, T item)
|
public void Insert(int index, T item)
|
||||||
@@ -225,11 +266,13 @@ namespace Recyclable.Collections
|
|||||||
int oldCount = _count;
|
int oldCount = _count;
|
||||||
if (_capacity < oldCount + 1)
|
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)
|
if (index < oldCount)
|
||||||
{
|
{
|
||||||
|
// TODO: Compare performance
|
||||||
|
// Array.Copy(_memoryBlock, index, _memoryBlock, index + 1, oldCount);
|
||||||
new Span<T>(_memoryBlock, index, oldCount -= index)
|
new Span<T>(_memoryBlock, index, oldCount -= index)
|
||||||
.CopyTo(new Span<T>(_memoryBlock, index + 1, oldCount));
|
.CopyTo(new Span<T>(_memoryBlock, index + 1, oldCount));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,109 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int ToArrayIndex(long index, int blockSize) => (int)(index / blockSize);
|
public static int ToArrayIndex(long index, int blockSize) => (int)(index / blockSize);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static long ToItemIndex(long index, int blockSize) => index % blockSize;
|
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)
|
private void SetItem(long index, T value)
|
||||||
{
|
{
|
||||||
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
||||||
int arrayIndex = RecyclableQueueHelpers.ToArrayIndex(absoluteIndex, BlockSize);
|
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
|
||||||
long itemIndex = RecyclableQueueHelpers.ToItemIndex(absoluteIndex, BlockSize);
|
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
|
||||||
Memory[arrayIndex][itemIndex] = value;
|
Memory[arrayIndex][itemIndex] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +108,8 @@ namespace Recyclable.Collections
|
|||||||
private T GetItem(long index)
|
private T GetItem(long index)
|
||||||
{
|
{
|
||||||
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
||||||
int arrayIndex = RecyclableQueueHelpers.ToArrayIndex(absoluteIndex, BlockSize);
|
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
|
||||||
long itemIndex = RecyclableQueueHelpers.ToItemIndex(absoluteIndex, BlockSize);
|
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
|
||||||
return Memory[arrayIndex][itemIndex];
|
return Memory[arrayIndex][itemIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,13 +166,13 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
public bool Contains(T item) => RecyclableListHelpers<T>.Contains(Memory, item);
|
public bool Contains(T item) => RecyclableQueueHelpers<T>.Contains(Memory, item);
|
||||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableListHelpers<T>.CopyTo(Memory, RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
public void CopyTo(T[] array, int arrayIndex) => RecyclableQueueHelpers<T>.CopyTo(Memory, RemovedCount, BlockSize, (int)(LongCount % BlockSize), array, arrayIndex);
|
||||||
public IEnumerable<T> Enumerate() => RecyclableListHelpers<T>.Enumerate(Memory, BlockSize, LongCount);
|
public IEnumerable<T> Enumerate() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount);
|
||||||
public IEnumerator<T> GetEnumerator() => RecyclableListHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
public IEnumerator<T> GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableListHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||||
public int IndexOf(T item) => (int)GetRelativeIndex(RecyclableListHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
public int IndexOf(T item) => (int)GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
||||||
public long LongIndexOf(T item) => GetRelativeIndex(RecyclableListHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
public long LongIndexOf(T item) => GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
public void Insert(int index, T item) => throw new NotSupportedException();
|
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.
|
/// This is a helper method to throw <see cref="ArgumentOutOfRangeException"/> with the given message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Always</exception>
|
/// <exception cref="ArgumentOutOfRangeException">Always</exception>
|
||||||
public static void ThrowArgumentOutOfRangeException(string argumentName, string message)
|
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.");
|
||||||
Contract.EnsuresOnThrow<ArgumentOutOfRangeException>(false);
|
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");
|
||||||
throw new ArgumentOutOfRangeException(argumentName, message);
|
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");
|
||||||
}
|
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,6 @@
|
|||||||
using MoreLinq;
|
using MoreLinq;
|
||||||
using Open.Numeric.Primes;
|
using Open.Numeric.Primes;
|
||||||
using Recyclable.Collections;
|
using Recyclable.Collections;
|
||||||
using System.Collections;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Recyclable.CollectionsTests
|
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
|
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[]
|
public static readonly IEnumerable<int> BlockSizeVariants = new int[]
|
||||||
{
|
{
|
||||||
1, 2, 4, 16, RecyclableDefaults.MinPooledArrayLength - 5, (int)BitOperations.RoundUpToPowerOf2(RecyclableDefaults.MinPooledArrayLength)
|
1, 2, 4, 16, RecyclableDefaults.MinPooledArrayLength - 5, (int)BitOperations.RoundUpToPowerOf2(RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
|||||||
Reference in new issue
Block a user