Initial version extracted from Recyclable.Collections
This commit is contained in:
1 parent
01bbd5d04f
commit
4684330d7e
22 files changed
+2403
-1
No files matched your search
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# License
|
||||||
|
The project & source code is free for educational & personal use. **Any kind** of commercial use requires a donation, as prescribed below.
|
||||||
|
|
||||||
|
# Donation requirements for a life-time, commercial use
|
||||||
|
* Commercial license is paid one time.
|
||||||
|
* It can be obtained by donating $10 for each developer seat, using the link provided below. If you hire 2 developers, you need to donate $20 for a life-time, commercial use of this package.
|
||||||
|
* Your license stays with you, if your developer moves somewhere else.
|
||||||
|
|
||||||
|
# Link for donations
|
||||||
|
[](https://www.buymeacoffee.com/mlemanczyk)
|
||||||
@@ -1 +1,22 @@
|
|||||||
# Recyclable.Collections.Compatibility.List
|
# Recyclable.Collections.Compatibility.List# Recyclable.Collections
|
||||||
|
`Recyclable.Collections.Compatibility.List` project is an open source add-on for [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) providing `List<T>` compatibility features to the recyclable collections framework.
|
||||||
|
|
||||||
|
## Included compatibility packs
|
||||||
|
* `RecyclableList<T>`
|
||||||
|
|
||||||
|
# Milestones
|
||||||
|
1. 👉 Implement compatibility pack
|
||||||
|
1. ✅ `RecyclableList<T>`
|
||||||
|
1. 👉 `RecyclableLongList<T>`
|
||||||
|
1. 🅿️ `RecyclableQueue<T>`
|
||||||
|
1. 🅿️ `RecyclableSortedList<T>`
|
||||||
|
1. 🅿️ `RecyclableStack<T>`
|
||||||
|
1. 🅿️ `RecyclableUnorderedList<T>`
|
||||||
|
1. 🅿️ Review performance
|
||||||
|
1. 🅿️ Optimize performance
|
||||||
|
|
||||||
|
# Characteristics of the classes
|
||||||
|
The package
|
||||||
|
|
||||||
|
## Common
|
||||||
|
* All classes will internally or externally utilize [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) to benefit from recyclable memory pools.
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
using Collections.Benchmarks.Core;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public class BinarySearchBenchmarks<T> : CollectionsBenchmarksBase<T>
|
||||||
|
{
|
||||||
|
public void Array_BinarySearch_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjects;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(Array.BinarySearch(list, data[i]));
|
||||||
|
DoNothing.With(Array.BinarySearch(list, data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_BinarySearch_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.BinarySearch(data[i]));
|
||||||
|
DoNothing.With(list.BinarySearch(data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PooledList_BinarySearch_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsPooledList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.BinarySearch(data[i]));
|
||||||
|
DoNothing.With(list.BinarySearch(data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_BinarySearch_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsRecyclableList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.BinarySearch(data[i]));
|
||||||
|
DoNothing.With(list.BinarySearch(data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public void RecyclableLongList_BinarySearch_BestAndWorstCases()
|
||||||
|
// {
|
||||||
|
// var data = TestObjects;
|
||||||
|
// var list = TestObjectsAsRecyclableLongList;
|
||||||
|
// var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
// for (var i = 0; i < dataCount; i++)
|
||||||
|
// {
|
||||||
|
// DoNothing(list.BinarySearch(data[i]));
|
||||||
|
// DoNothing(list.BinarySearch(data[^(i + 1)]));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using Collections.Benchmarks.Core;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public class ConvertAllBenchmarks<T> : CollectionsBenchmarksBase<T>
|
||||||
|
{
|
||||||
|
public void Array_ConvertAll()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjects;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
DoNothing.With(Array.ConvertAll(list, static (item) => (long)item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_ConvertAll()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
DoNothing.With(list.ConvertAll(static (item) => (long)item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PooledList_ConvertAll()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsPooledList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
using var converted = list.ConvertAll(static (item) => (long)item);
|
||||||
|
DoNothing.With(converted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_ConvertAll()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsRecyclableList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
|
||||||
|
DoNothing.With(converted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public void RecyclableLongList_ConvertAll()
|
||||||
|
// {
|
||||||
|
// var data = TestObjects;
|
||||||
|
// var list = TestObjectsAsRecyclableLongList;
|
||||||
|
// var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
// using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
|
||||||
|
// DoNothing(converted);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using Collections.Benchmarks.Core;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public partial class RecyclableCollectionsCompatibilityListBenchmarks
|
||||||
|
{
|
||||||
|
public void Array_Exists_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjects;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(Array.Exists(list, (x) => x == data[i]));
|
||||||
|
DoNothing.With(Array.Exists(list, (x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_Exists_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.Exists((x) => x == data[i]));
|
||||||
|
DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PooledList_Exists_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsPooledList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.Exists((x) => x == data[i]));
|
||||||
|
DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_Exists_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsRecyclableList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.Exists((x) => x == data[i]));
|
||||||
|
DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void RecyclableLongList_Exists_BestAndWorstCases()
|
||||||
|
//{
|
||||||
|
// var data = TestObjects;
|
||||||
|
// var list = TestObjectsAsRecyclableLongList;
|
||||||
|
// var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
// for (var i = 0; i < dataCount; i++)
|
||||||
|
// {
|
||||||
|
// DoNothing.With(list.Exists((x) => x == data[i]));
|
||||||
|
// DoNothing.With(list.Exists((x) => x == data[^(i + 1)]));
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
using Collections.Benchmarks.Core;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public partial class RecyclableCollectionsCompatibilityListBenchmarks
|
||||||
|
{
|
||||||
|
public void Array_FindAll_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjects;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(Array.FindAll(list, (item) => item == data[i]));
|
||||||
|
DoNothing.With(Array.FindAll(list, (item) => item == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_FindAll_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.FindAll((item) => item == data[i]));
|
||||||
|
DoNothing.With(list.FindAll((item) => item == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PooledList_FindAll_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsPooledList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
var temp = list.FindAll((item) => item == data[i]);
|
||||||
|
DoNothing.With(temp);
|
||||||
|
temp.Dispose();
|
||||||
|
|
||||||
|
temp = list.FindAll((item) => item == data[^(i + 1)]);
|
||||||
|
DoNothing.With(temp);
|
||||||
|
temp.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_FindAll_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsRecyclableList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
var temp = list.FindAll((item) => item == data[i]);
|
||||||
|
DoNothing.With(temp);
|
||||||
|
temp.Dispose();
|
||||||
|
|
||||||
|
temp = list.FindAll((item) => item == data[^(i + 1)]);
|
||||||
|
DoNothing.With(temp);
|
||||||
|
temp.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void RecyclableLongList_FindAll_BestAndWorstCases()
|
||||||
|
//{
|
||||||
|
// var data = TestObjects;
|
||||||
|
// var list = TestObjectsAsRecyclableLongList;
|
||||||
|
// var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
// for (var i = 0; i < dataCount; i++)
|
||||||
|
// {
|
||||||
|
// var temp = list.FindAll((item) => item == data[i]);
|
||||||
|
// DoNothing.With(temp);
|
||||||
|
// temp.Dispose();
|
||||||
|
|
||||||
|
// temp = list.FindAll((item) => item == data[^(i + 1)]);
|
||||||
|
// DoNothing.With(temp);
|
||||||
|
// temp.Dispose();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
using Collections.Benchmarks.Core;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public partial class RecyclableCollectionsCompatibilityListBenchmarks : CollectionsBenchmarksBase<CollectionsBenchmarksSource>
|
||||||
|
{
|
||||||
|
public void Array_FindLastIndex_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjects;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(Array.FindLastIndex(list, 0, (x) => x == data[i]));
|
||||||
|
DoNothing.With(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_FindLastIndex_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.FindLastIndex(0, (x) => x == data[i]));
|
||||||
|
DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_FindLastIndex_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsRecyclableList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.FindLastIndex(0, (x) => x == data[i]));
|
||||||
|
DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void RecyclableLongList_FindLastIndex_BestAndWorstCases()
|
||||||
|
//{
|
||||||
|
// var data = TestObjects;
|
||||||
|
// var list = TestObjectsAsRecyclableLongList;
|
||||||
|
// var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
// for (var i = 0; i < dataCount; i++)
|
||||||
|
// {
|
||||||
|
// DoNothing.With(list.FindLastIndex(0, (x) => x == data[i]));
|
||||||
|
// DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Collections.Benchmarks.Core;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public partial class RecyclableCollectionsCompatibilityListBenchmarks
|
||||||
|
{
|
||||||
|
public void Array_FindLast_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjects;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(Array.FindLast(list, (x) => x == data[i]));
|
||||||
|
DoNothing.With(Array.FindLast(list, (x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_FindLast_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.FindLast((x) => x == data[i]));
|
||||||
|
DoNothing.With(list.FindLast((x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_FindLast_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsRecyclableList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.FindLast((x) => x == data[i]));
|
||||||
|
DoNothing.With(list.FindLast((x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void RecyclableLongList_FindLast_BestAndWorstCases()
|
||||||
|
//{
|
||||||
|
// var data = TestObjects;
|
||||||
|
// var list = TestObjectsAsRecyclableLongList;
|
||||||
|
// var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
// for (var i = 0; i < dataCount; i++)
|
||||||
|
// {
|
||||||
|
// DoNothing.With(list.FindLast((x) => x == data[i]));
|
||||||
|
// DoNothing.With(list.FindLast((x) => x == data[^(i + 1)]));
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Collections.Benchmarks.Core;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public partial class RecyclableCollectionsCompatibilityListBenchmarks
|
||||||
|
{
|
||||||
|
public void Array_Find_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjects;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(Array.Find(list, (x) => x == data[i]));
|
||||||
|
DoNothing.With(Array.Find(list, (x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_Find_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.Find((x) => x == data[i]));
|
||||||
|
DoNothing.With(list.Find((x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_Find_BestAndWorstCases()
|
||||||
|
{
|
||||||
|
var data = TestObjects;
|
||||||
|
var list = TestObjectsAsRecyclableList;
|
||||||
|
var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
for (var i = 0; i < dataCount; i++)
|
||||||
|
{
|
||||||
|
DoNothing.With(list.Find((x) => x == data[i]));
|
||||||
|
DoNothing.With(list.Find((x) => x == data[^(i + 1)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void RecyclableLongList_Find_BestAndWorstCases()
|
||||||
|
//{
|
||||||
|
// var data = TestObjects;
|
||||||
|
// var list = TestObjectsAsRecyclableLongList;
|
||||||
|
// var dataCount = TestObjectCountForSlowMethods;
|
||||||
|
// for (var i = 0; i < dataCount; i++)
|
||||||
|
// {
|
||||||
|
// DoNothing.With(list.Find((x) => x == data[i]));
|
||||||
|
// DoNothing.With(list.Find((x) => x == data[^(i + 1)]));
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// See https://aka.ms/new-console-template for more information
|
||||||
|
Console.WriteLine("Hello, World!");
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Collections.Benchmarks.Core" Version="0.0.2" />
|
||||||
|
<PackageReference Include="Recyclable.Collections" Version="0.0.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
global using Xunit;
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||||
|
<PackageReference Include="Recyclable.Collections" Version="0.0.6" />
|
||||||
|
<PackageReference Include="Recyclable.Collections.TestData" Version="0.0.2" />
|
||||||
|
<PackageReference Include="xunit" Version="2.5.3" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
+953
@@ -0,0 +1,953 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using Recyclable.Collections;
|
||||||
|
using Recyclable.Collections.TestData;
|
||||||
|
using Recyclable.Collections.Pools;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
#pragma warning disable xUnit1026, RCS1163, IDE0039, IDE0060, RCS1235
|
||||||
|
|
||||||
|
namespace Recyclable.CollectionsTests
|
||||||
|
{
|
||||||
|
public class RecyclableListCompatibilityListTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void ConvertAllShouldConvertAllItems(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList().ConvertAll(item => item + 0.1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
using var actual = list.ConvertAll(item => item + 0.1);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Count.Should().Be(itemsCount);
|
||||||
|
_ = actual.Should().Equal(expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void CopyToShouldCopyAllItemsInTheCorrectOrderWhenFromStart(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
long[] copiedItems = new long[itemsCount];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.CopyTo(copiedItems);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
long[] expectedData = new long[itemsCount];
|
||||||
|
testData.ToList().CopyTo(expectedData);
|
||||||
|
|
||||||
|
_ = copiedItems.Should().Equal(testData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void ExistsShouldFindAllItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.Exists(item => item == expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.Exists(item => item == expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void ExistsShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.Exists(item => item == -expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.Exists(item => item == -expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindAllIndexesShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
//* There is no equivalent of .FindAllIndexes in List<T>, so no comparison here.
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
using var actual = list.FindAllIndexes(item => item == itemIndex + 1);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Count.Should().Be(1);
|
||||||
|
if (itemsCount > 0)
|
||||||
|
{
|
||||||
|
_ = actual.Count.Should().Be(1);
|
||||||
|
_ = actual.Should().AllSatisfy(x => x.Should().Be((int)itemIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindAllShouldReturnCorrectIndexes(int itemsCount)
|
||||||
|
{
|
||||||
|
const int ExpectedItem = 1;
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
var testData = Enumerable.Range(0, itemsCount).Select(value => value % 2 == 0 ? ExpectedItem : -ExpectedItem);
|
||||||
|
using var list = new RecyclableList<int>(testData, itemsCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
using var actual = list.FindAll(x => x == ExpectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
var expectedData = testData.ToList().FindAll(x => x == ExpectedItem);
|
||||||
|
|
||||||
|
_ = actual.Count.Should().Be(expectedData.Count);
|
||||||
|
_ = actual.Should().Equal(expectedData);
|
||||||
|
if (expectedData.Count > 0)
|
||||||
|
{
|
||||||
|
_ = actual.Should().AllSatisfy(x => x.Should().Be(ExpectedItem)).And.Equal(expectedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindAllShouldNotFindNonExistingItems(int itemsCount)
|
||||||
|
{
|
||||||
|
const int ExpectedItem = 1;
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
var testData = Enumerable.Range(0, itemsCount).Select(_ => ExpectedItem);
|
||||||
|
using var list = new RecyclableList<int>(testData, itemsCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
using var foundItems = list.FindAll(x => x == -ExpectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
var expectedData = testData.ToList().FindAll(x => x == -ExpectedItem);
|
||||||
|
|
||||||
|
_ = foundItems.Count.Should().Be(expectedData.Count);
|
||||||
|
_ = foundItems.Should().Equal(expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindIndexShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.FindIndex(item => item == expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
var expected = expectedData.FindIndex(item => item == expectedItem);
|
||||||
|
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindIndexShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.FindIndex((int)itemIndex, (int)(itemsCount - itemIndex), item => item == expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.FindIndex((int)itemIndex, (int)(itemsCount - itemIndex), item => item == expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.FindIndex(Math.Min(1, (int)itemIndex), item => item == expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.FindIndex(Math.Min(1, (int)itemIndex), item => item == expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindLastIndexShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.FindLastIndex(x => x == expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.FindLastIndex(x => x == expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindLastIndexShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
|
||||||
|
// Act & Validate
|
||||||
|
var expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount, x => x == expectedItem);
|
||||||
|
_ = list.FindLastIndex(itemsCount - 1, itemsCount, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindLastIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.FindLastIndex(itemsCount - 1, x => x == expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.FindLastIndex(itemsCount - 1, x => x == expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindLastIndexShouldThrowArgumentOutOfRange(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
int expected = int.MinValue;
|
||||||
|
|
||||||
|
// Act & Validate
|
||||||
|
if (itemsCount - 1 != -1)
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem));
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = list.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem);
|
||||||
|
_ = list.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindLastShouldReturnCorrectItem(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.FindLast(item => item == expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.FindLast(item => item == expectedItem);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be(expectedData[(int)itemIndex]).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindShouldFindAllItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
long expectedItem = expectedData[(int)itemIndex];
|
||||||
|
var expected = expectedData.Find(item => item == expectedItem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.Find(item => item == expectedData[(int)itemIndex]);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be(expectedData[(int)itemIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void FindShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expected = expectedData.Find(item => item == -expectedData[(int)itemIndex]);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.Find(item => item == -expectedData[(int)itemIndex]);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
actual.Should().Be(0).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void ForEachShouldGiveItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
var expected = new List<long>(itemsCount);
|
||||||
|
expectedData.ForEach(expected.Add);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var yieldedItems = new List<long>(itemsCount);
|
||||||
|
list.ForEach(yieldedItems.Add);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = yieldedItems.Should().Equal(testData).And.Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void ForEachShouldDoNothingWhenEmptyList(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
var expected = new List<long>(itemsCount);
|
||||||
|
expectedData.ForEach(expected.Add);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var yieldedItems = new List<long>(itemsCount);
|
||||||
|
list.ForEach(item => yieldedItems.Add(item));
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = yieldedItems.Should().BeEmpty().And.Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void GetRangeShouldReturnCorrectItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||||
|
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
int startingIndex = (int)Math.Min(1, itemIndex);
|
||||||
|
var expectedRangeItems = expectedData.GetRange(startingIndex, (int)itemIndex);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
using var rangeItems = list.GetRange(startingIndex, (int)itemIndex);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = rangeItems.Count.Should().Be(expectedRangeItems.Count);
|
||||||
|
_ = rangeItems.Should().Equal(expectedRangeItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void IndexOfShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expected = expectedData.IndexOf(expectedData[(int)itemIndex], (int)itemIndex, (int)(itemsCount - itemIndex));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.IndexOf(expectedData[(int)itemIndex], (int)itemIndex, (int)(itemsCount - itemIndex));
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void IndexOfShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expected = expectedData.IndexOf(expectedData[(int)itemIndex], (int)itemIndex);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.IndexOf(expectedData[(int)itemIndex], (int)itemIndex);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void LastIndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex]);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.LastIndexOf(expectedData[(int)itemIndex]);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void LastIndexOfShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void LastIndexOfShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex).And.Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void LastIndexOfShouldThrowArgumentOutOfRangeWhenCountTooBig(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
// Act & Validate
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
_ = Assert.Throws<ArgumentOutOfRangeException>(() => _ = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount + 3).Should().Be((int)itemIndex));
|
||||||
|
_ = Assert.Throws<ArgumentOutOfRangeException>(() => _ = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount + 3).Should().Be((int)itemIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void InsertRangeShouldMoveItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
|
||||||
|
var expectedItems = testData.Reverse().ToList();
|
||||||
|
var item = expectedItems[^1];
|
||||||
|
expectedItems.InsertRange((int)itemIndex, testData);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.InsertRange((int)itemIndex, testData);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list[(int)itemIndex].Should().Be(item);
|
||||||
|
_ = list.Count.Should().Be(itemsCount << 1);
|
||||||
|
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount << 1);
|
||||||
|
_ = list.Should().Equal(expectedItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void RemoveAllShouldRemoveTheCorrectItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var itemsToRemove = itemIndexes.Select(x => list[(int)x]).ToArray();
|
||||||
|
var expectedItems = testData.ToList();
|
||||||
|
var expectedResult = expectedItems.RemoveAll(itemsToRemove.Contains);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actualResult = list.RemoveAll(itemsToRemove.Contains);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actualResult.Should().Be(expectedResult);
|
||||||
|
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||||
|
_ = list.Count.Should().Be(itemsCount - itemsToRemove.Length);
|
||||||
|
_ = list.Should().Equal(expectedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void RemoveRangeShouldRemoveCorrectItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||||
|
{
|
||||||
|
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
int startingIndex = (int)Math.Min(1, itemIndex);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
expectedData.RemoveRange(startingIndex, (int)itemIndex);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
list.RemoveRange(startingIndex, (int)itemIndex);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Count.Should().Be(expectedData.Count);
|
||||||
|
_ = list.Should().Equal(expectedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void ReverseShouldSwapItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
expectedData.Reverse();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Reverse();
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Should().Equal(expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void ReverseShouldSwapItemsInCorrectOrderWhenConstrained(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
var expectedData = testData.ToArray();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var startingItemIndex = (int)Math.Min(1, itemIndex);
|
||||||
|
var expectedRangeItems = expectedData.ToList();
|
||||||
|
expectedRangeItems.Reverse(startingItemIndex, (int)itemIndex);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Reverse(startingItemIndex, (int)itemIndex);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Should().Equal(expectedRangeItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void SortShouldSortItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
|
||||||
|
var expectedData = testData.Reverse().ToList();
|
||||||
|
expectedData.Sort();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Sort();
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Should().Equal(expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void SortShouldSortItemsInCorrectOrderWhenWithComparer(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
|
||||||
|
IComparer<long> comparer = Comparer<long>.Default;
|
||||||
|
var expectedData = testData.Reverse().ToList();
|
||||||
|
expectedData.Sort(comparer);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Sort(comparer);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Should().Equal(expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void SortShouldSortItemsInCorrectOrderWhenWithComparison(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
|
||||||
|
|
||||||
|
static int comparison(long x, long y)
|
||||||
|
{
|
||||||
|
return (x - y) switch
|
||||||
|
{
|
||||||
|
> 0 => 1,
|
||||||
|
< 0 => -1,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedData = testData.Reverse().ToList();
|
||||||
|
expectedData.Sort(comparison);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Sort(comparison);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Should().Equal(expectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void SortShouldSortItemsInCorrectOrderWhenConstrained(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
IComparer<long> comparer = Comparer<long>.Default;
|
||||||
|
var expectedData = testData.Reverse().ToArray();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData.Reverse(), itemsCount);
|
||||||
|
var startingIndex = (int)Math.Min(1, itemIndex);
|
||||||
|
|
||||||
|
var expectedSortedData = expectedData.ToList();
|
||||||
|
expectedSortedData.Sort(startingIndex, (int)itemIndex, comparer);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Sort(startingIndex, (int)itemIndex, comparer);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Should().Equal(expectedSortedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimExcessShouldDecreaseCapacity(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedCapacity = itemsCount >= RecyclableDefaults.InitialCapacity ? itemsCount : RecyclableDefaults.InitialCapacity;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().Be(expectedCapacity);
|
||||||
|
_ = list.Count.Should().Be(itemsCount);
|
||||||
|
_ = list.Should().Equal(testData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimExcessShouldNotBreakAdd(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)itemsCount + 1);
|
||||||
|
expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
list.Add(-1);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().Be(expectedCapacity);
|
||||||
|
_ = list.Count.Should().Be(itemsCount + 1);
|
||||||
|
_ = list.Should().Equal(testData.Append(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimExcessShouldNotBreakAddRange(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2(checked((uint)itemsCount << 1));
|
||||||
|
expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
list.AddRange(testData);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedCapacity);
|
||||||
|
_ = list.Count.Should().Be(itemsCount << 1);
|
||||||
|
_ = list.Should().Equal(testData.Concat(testData));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimExcessShouldNotBreakClear(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().Be(Math.Max(RecyclableDefaults.InitialCapacity, itemsCount));
|
||||||
|
_ = list.Count.Should().Be(0);
|
||||||
|
_ = list.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimExcessShouldNotBreakInsert(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)itemsCount + 1);
|
||||||
|
expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity;
|
||||||
|
var expectedData = testData.ToArray();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedItemsAfterInsert = expectedData.ToList();
|
||||||
|
expectedItemsAfterInsert.Insert((int)itemIndex, -3);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
list.Insert((int)itemIndex, -3);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedCapacity);
|
||||||
|
_ = list.Count.Should().Be(expectedItemsAfterInsert.Count);
|
||||||
|
_ = list.Should().Equal(expectedItemsAfterInsert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimExcessShouldNotBreakRemove(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
var expectedData = testData.ToArray();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
var expectedItemsAfterInsert = expectedData.ToList();
|
||||||
|
expectedItemsAfterInsert.RemoveAt((int)itemIndex);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
list.RemoveAt((int)itemIndex);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||||
|
_ = list.Count.Should().Be(expectedItemsAfterInsert.Count);
|
||||||
|
_ = list.Should().Equal(expectedItemsAfterInsert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimExcessShouldNotBreakIndexOf(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
var expectedData = testData.ToArray();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
var actual = list.IndexOf(expectedData[(int)itemIndex]);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be((int)itemIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrimmedArraysShouldNotBePooled(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.TrimExcess();
|
||||||
|
var trimmedMemoryBlock = list.AsArray;
|
||||||
|
|
||||||
|
list.Dispose();
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().Be(0);
|
||||||
|
_ = list.Count.Should().Be(0);
|
||||||
|
_ = list.Should().BeEmpty();
|
||||||
|
|
||||||
|
if (itemsCount >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
{
|
||||||
|
var newMemoryBlock = RecyclableArrayPool<long>.RentShared(itemsCount);
|
||||||
|
_ = BitOperations.IsPow2(itemsCount)
|
||||||
|
? newMemoryBlock.Should().BeSameAs(trimmedMemoryBlock)
|
||||||
|
: newMemoryBlock.Should().NotBeSameAs(trimmedMemoryBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrueForAllShouldBeFalseWhenNoMatch(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
var expectedData = testData.ToList();
|
||||||
|
|
||||||
|
foreach (var itemIndex in itemIndexes)
|
||||||
|
{
|
||||||
|
using RecyclableList<long> list = new(testData, itemsCount);
|
||||||
|
Predicate<long> condition = item => item == itemIndex;
|
||||||
|
var expected = expectedData.TrueForAll(condition);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.TrueForAll(condition);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be(expected).And.BeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||||
|
public void TrueForAllShouldBeTrueWhenMatched(string testCase, IEnumerable<long> testData, int itemsCount, long[] itemIndexes)
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
using RecyclableList<long> list = new(testData, itemsCount);
|
||||||
|
int index = 0;
|
||||||
|
Predicate<long> condition = item => item == ++index;
|
||||||
|
var expected = testData.ToList().TrueForAll(condition);
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = list.TrueForAll(condition);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = actual.Should().Be(expected).And.BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.7.34031.279
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Compatibility.List", "Recyclable.Collections.Compatibility.List\Recyclable.Collections.Compatibility.List.csproj", "{779028EF-0584-4566-A80D-B5F3D6E25DD0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Compatibility.List.Tests", "Recyclable.Collections.Compatibility.List.Tests\Recyclable.Collections.Compatibility.List.Tests.csproj", "{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Recyclable.Collections.Compatibility.List.Benchmarks", "Recyclable.Collections.Compatibility.List.Benchmarks\Recyclable.Collections.Compatibility.List.Benchmarks.csproj", "{A1414948-E61B-4AE1-ACAF-33A8BB85041E}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {16B9D137-EEFC-487F-A158-3C6AEDFDDBD5}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<PackageId>Recyclable.Collections.Compatibility.List</PackageId>
|
||||||
|
<PackageVersion>0.0.1</PackageVersion>
|
||||||
|
<Authors>Marek Lemańczyk</Authors>
|
||||||
|
<Company>N/A</Company>
|
||||||
|
<Product>Recyclable.Collections.Compatibility.List</Product>
|
||||||
|
<PackageTags>Recyclable ArrayPool Array Pool List Sortable Sort</PackageTags>
|
||||||
|
<Description>Recyclable.Collections.Compatibility.List project is an open source framework for operating dynamic lists at performance close to raw arrays. It aims at providing minimal memory footprint. It implements IList<T>'s interface and is targeted as direct replacements of List<T>, SortableList<T>, PriorityQueue<T> and similar.</Description>
|
||||||
|
<PackageProjectUrl>https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List</RepositoryUrl>
|
||||||
|
<Copyright>Marek Lemańczyk 2023</Copyright>
|
||||||
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageReleaseNotes>Support for RecyclableList<T> & RecyclableLongList<T> added.</PackageReleaseNotes>
|
||||||
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Include="..\README.md" Pack="true" PackagePath="" Link="README.md">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Recyclable.Collections" Version="0.0.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
public static class zzzRecyclableListCompatibilityListFind
|
||||||
|
{
|
||||||
|
public static bool Exists<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? Find<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return sourceSpan[itemIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RecyclableList<T> FindAll<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
RecyclableList<T> result = new(sourceItemsCount >> 3);
|
||||||
|
Span<T> resultSpan = result._memoryBlock;
|
||||||
|
int capacity = result._capacity,
|
||||||
|
resultItemsCount = 0;
|
||||||
|
|
||||||
|
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
if (resultItemsCount + 1 > capacity)
|
||||||
|
{
|
||||||
|
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
|
||||||
|
_ = RecyclableListHelpers<T>.EnsureCapacity(result, resultItemsCount, capacity);
|
||||||
|
resultSpan = result._memoryBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultSpan[resultItemsCount++] = sourceSpan[itemIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result._capacity = capacity;
|
||||||
|
result._count = resultItemsCount;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public static RecyclableList<int> FindAllIndexes<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
RecyclableList<int> result = new(sourceItemsCount >> 3);
|
||||||
|
Span<int> resultSpan = result._memoryBlock;
|
||||||
|
int capacity = result._capacity,
|
||||||
|
resultItemsCount = 0;
|
||||||
|
|
||||||
|
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
if (resultItemsCount + 1 > capacity)
|
||||||
|
{
|
||||||
|
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1));
|
||||||
|
_ = RecyclableListHelpers<int>.EnsureCapacity(result, resultItemsCount, capacity);
|
||||||
|
resultSpan = result._memoryBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultSpan[resultItemsCount++] = itemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result._capacity = capacity;
|
||||||
|
result._count = resultItemsCount;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return itemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FindIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0 || startIndex >= sourceItemsCount)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount);
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return itemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FindIndex<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return itemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? FindLast<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
if (list._count == 0)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return sourceSpan[itemIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, int count, Predicate<T> match)
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
if (startIndex != -1)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (count < 0 || startIndex - count + 1 < 0)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException_Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startIndex < 0)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sourceItemsCount = count; // startIndex - count + 1 >= 0 ? count : startIndex + 1;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastItemIndex = startIndex - sourceItemsCount;
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = startIndex; itemIndex > lastItemIndex; itemIndex--)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return itemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FindLastIndex<T>(this RecyclableList<T> list, int startIndex, Predicate<T> match)
|
||||||
|
{
|
||||||
|
if (list._count == 0 || startIndex < 0)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = startIndex; itemIndex >= 0; itemIndex--)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return itemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FindLastIndex<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
if (list._count == 0)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--)
|
||||||
|
{
|
||||||
|
if (match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return itemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
public static class zzzRecyclableListCompatibilityListIndexOf
|
||||||
|
{
|
||||||
|
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
|
||||||
|
? Array.IndexOf(list._memoryBlock, item, index, list._count - index)
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
|
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
|
||||||
|
? Array.IndexOf(list._memoryBlock, item, index, count)
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
|
public static int LastIndexOf<T>(this RecyclableList<T> list, T item) => list._count != 0
|
||||||
|
? Array.LastIndexOf(list._memoryBlock, item, list._count - 1)
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
|
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
|
||||||
|
? Array.LastIndexOf(list._memoryBlock, item, index)
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
|
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
|
||||||
|
? Array.LastIndexOf(list._memoryBlock, item, index, count)
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
+357
@@ -0,0 +1,357 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
public static class zzzRecyclableListCompatibilityListInsertRange
|
||||||
|
{
|
||||||
|
private static void InsertRangeEnumerated<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int growByCount)
|
||||||
|
{
|
||||||
|
var enumerator = items.GetEnumerator();
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int capacity = list._capacity,
|
||||||
|
added = 0,
|
||||||
|
available = capacity - list._count,
|
||||||
|
i,
|
||||||
|
movingBlockLength = list._count - index;
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
if (available > 0)
|
||||||
|
{
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, capacity - movingBlockLength, movingBlockLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<T> memorySpan = memoryBlock;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (index + available + movingBlockLength > capacity)
|
||||||
|
{
|
||||||
|
var capacityBeforeIncrease = capacity;
|
||||||
|
capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)(capacity + growByCount)));
|
||||||
|
_ = RecyclableListHelpers<T>.EnsureCapacity(list, memoryBlock.Length, capacity);
|
||||||
|
memorySpan = memoryBlock = list._memoryBlock;
|
||||||
|
available = capacity - index - movingBlockLength;
|
||||||
|
Array.Copy(memoryBlock, capacityBeforeIncrease - movingBlockLength, memoryBlock, capacity - movingBlockLength, movingBlockLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < available; i++)
|
||||||
|
{
|
||||||
|
memorySpan[index++] = enumerator.Current;
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
added++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
added += i;
|
||||||
|
}
|
||||||
|
while (i == available);
|
||||||
|
|
||||||
|
if (i != available)
|
||||||
|
{
|
||||||
|
Array.Copy(memoryBlock, capacity - movingBlockLength, memoryBlock, index, movingBlockLength);
|
||||||
|
if (RecyclableList<T>._needsClearing)
|
||||||
|
{
|
||||||
|
Array.Clear(memoryBlock, capacity - movingBlockLength, movingBlockLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list._count += added;
|
||||||
|
list._capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InsertRangeWithKnownCount<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int currentItemsCount, int requiredAdditionalCapacity)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity))));
|
||||||
|
|
||||||
|
Array.Copy(list._memoryBlock, index, list._memoryBlock, index + requiredAdditionalCapacity, list._count - index);
|
||||||
|
Span<T> memorySpan = list._memoryBlock;
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
memorySpan[index++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
list._count += requiredAdditionalCapacity;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, in Array items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items.Length)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
|
||||||
|
Array.Copy(items, items.GetLowerBound(0), memoryBlock, index, items.Length);
|
||||||
|
list._count += items.Length;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, in T[] items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items.Length)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
|
||||||
|
new ReadOnlySpan<T>(items).CopyTo(new Span<T>(memoryBlock, index, items.Length));
|
||||||
|
list._count += items.Length;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, ReadOnlySpan<T> items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items.Length)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
|
||||||
|
items.CopyTo(new Span<T>(memoryBlock, index, items.Length));
|
||||||
|
list._count += items.Length;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, Span<T> items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items.Length)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index);
|
||||||
|
items.CopyTo(new Span<T>(memoryBlock, index, items.Length));
|
||||||
|
list._count += items.Length;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, List<T> items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items.Count)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
|
||||||
|
items.CopyTo(memoryBlock, index);
|
||||||
|
list._count += items.Count;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, ICollection items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items.Count)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
|
||||||
|
items.CopyTo(list._memoryBlock, index);
|
||||||
|
list._count += items.Count;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, ICollection<T> items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items.Count)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
|
||||||
|
items.CopyTo(memoryBlock, index);
|
||||||
|
list._count += items.Count;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, RecyclableList<T> items)
|
||||||
|
{
|
||||||
|
if (list._capacity < list._count + items._count)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count))));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index);
|
||||||
|
new ReadOnlySpan<T>(items._memoryBlock, 0, items._count).CopyTo(new(list._memoryBlock, index, items._count));
|
||||||
|
list._count += items._count;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, RecyclableLongList<T> items)
|
||||||
|
{
|
||||||
|
if (items._longCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldCount = list._count;
|
||||||
|
long sourceItemsCount = items._longCount;
|
||||||
|
if (oldCount + sourceItemsCount > int.MaxValue)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
int targetCapacity = oldCount + (int)sourceItemsCount;
|
||||||
|
if (list._capacity < targetCapacity)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] memoryBlock = list._memoryBlock;
|
||||||
|
Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, oldCount - index);
|
||||||
|
Span<T> targetSpan = new(list._memoryBlock, index, list._capacity - index),
|
||||||
|
itemsSpan;
|
||||||
|
|
||||||
|
int blockIndex,
|
||||||
|
sourceBlockSize = items._blockSize,
|
||||||
|
lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1;
|
||||||
|
|
||||||
|
for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++)
|
||||||
|
{
|
||||||
|
itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize);
|
||||||
|
itemsSpan.CopyTo(targetSpan);
|
||||||
|
targetSpan = targetSpan[sourceBlockSize..];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockIndex == 0)
|
||||||
|
{
|
||||||
|
itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount);
|
||||||
|
itemsSpan.CopyTo(targetSpan);
|
||||||
|
}
|
||||||
|
else if (blockIndex <= items._lastBlockWithData)
|
||||||
|
{
|
||||||
|
itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex > 0 ? items._nextItemIndex : sourceBlockSize);
|
||||||
|
itemsSpan.CopyTo(targetSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
list._count = targetCapacity;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator InsertRange<T>(this RecyclableList<T> list, int index, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
{
|
||||||
|
int targetItemIndex = list._count;
|
||||||
|
Span<T> memorySpan;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
var enumerator = source.GetEnumerator();
|
||||||
|
|
||||||
|
int capacity = list._capacity;
|
||||||
|
memorySpan = list._memoryBlock;
|
||||||
|
if (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
int available = capacity - targetItemIndex;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (targetItemIndex + growByCount > capacity)
|
||||||
|
{
|
||||||
|
capacity = RecyclableListHelpers<T>.EnsureCapacity(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount))));
|
||||||
|
memorySpan = list._memoryBlock;
|
||||||
|
available = capacity - targetItemIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < available; i++)
|
||||||
|
{
|
||||||
|
memorySpan[targetItemIndex++] = (T)enumerator.Current;
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (i >= available);
|
||||||
|
}
|
||||||
|
|
||||||
|
list._capacity = capacity;
|
||||||
|
list._count = targetItemIndex;
|
||||||
|
return enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, IReadOnlyList<T> items)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = items.Count;
|
||||||
|
if (list._capacity < list._count + sourceItemsCount)
|
||||||
|
{
|
||||||
|
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<T> memorySpan = new(list._memoryBlock, list._count, sourceItemsCount);
|
||||||
|
for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++)
|
||||||
|
{
|
||||||
|
memorySpan[sourceItemIndex] = items[sourceItemIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
list._count += sourceItemsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void InsertRange<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
{
|
||||||
|
if (items is RecyclableList<T> sourceRecyclableList)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceRecyclableList);
|
||||||
|
}
|
||||||
|
else if (items is RecyclableLongList<T> sourceRecyclableLongList)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceRecyclableLongList);
|
||||||
|
}
|
||||||
|
else if (items is T[] sourceArray)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceArray);
|
||||||
|
}
|
||||||
|
else if (items is Array sourceArrayWithObjects)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceArrayWithObjects);
|
||||||
|
}
|
||||||
|
else if (items is List<T> sourceList)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceList);
|
||||||
|
}
|
||||||
|
else if (items is ICollection<T> sourceICollection)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceICollection);
|
||||||
|
}
|
||||||
|
else if (items is ICollection sourceICollectionWithObjects)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceICollectionWithObjects);
|
||||||
|
}
|
||||||
|
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
|
||||||
|
{
|
||||||
|
InsertRange(list, index, sourceIReadOnlyList);
|
||||||
|
}
|
||||||
|
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
|
||||||
|
{
|
||||||
|
InsertRangeWithKnownCount(list, index, items, list._count, requiredAdditionalCapacity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InsertRangeEnumerated(list, index, items, growByCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Numerics;
|
||||||
|
using Recyclable.Collections.Pools;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
|
||||||
|
public static class zzzRecyclableListCompatibilityList
|
||||||
|
{
|
||||||
|
public static ReadOnlyCollection<T> AsReadOnly<T>(this RecyclableList<T> list) => new(list);
|
||||||
|
|
||||||
|
public static int BinarySearch<T>(this RecyclableList<T> list, T item)
|
||||||
|
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item);
|
||||||
|
|
||||||
|
public static int BinarySearch<T>(this RecyclableList<T> list, T item, IComparer<T>? comparer)
|
||||||
|
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item, comparer);
|
||||||
|
|
||||||
|
public static int BinarySearch<T>(this RecyclableList<T> list, int index, int count, T item, IComparer<T>? comparer)
|
||||||
|
=> Array.BinarySearch(list._memoryBlock, index, count, item, comparer);
|
||||||
|
|
||||||
|
public static RecyclableList<TOutput> ConvertAll<T, TOutput>(this RecyclableList<T> list, Converter<T, TOutput> converter)
|
||||||
|
{
|
||||||
|
int itemsCount = list._count;
|
||||||
|
if (itemsCount == 0)
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
RecyclableList<TOutput> result = new(itemsCount);
|
||||||
|
TOutput[] resultSpan = result._memoryBlock;
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (int bufferIndex = 0; bufferIndex < itemsCount; bufferIndex++)
|
||||||
|
{
|
||||||
|
resultSpan[bufferIndex] = converter(sourceSpan[bufferIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result._count = itemsCount;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CopyTo<T>(this RecyclableList<T> list, T[] array) => Array.Copy(list._memoryBlock, array, list._count);
|
||||||
|
|
||||||
|
public static void CopyTo<T>(this RecyclableList<T> list, int index, T[] array, int arrayIndex, int count) => Array.Copy(list._memoryBlock, index, array, arrayIndex, count);
|
||||||
|
|
||||||
|
public static void ForEach<T>(this RecyclableList<T> list, Action<T> action)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
action(sourceSpan[itemIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RecyclableList<T> GetRange<T>(this RecyclableList<T> list, int index, int count)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = index + count <= list._count ? count : list._count - index;
|
||||||
|
if (sourceItemsCount <= 0)
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
RecyclableList<T> result = new(sourceItemsCount);
|
||||||
|
Span<T> targetSpan = result._memoryBlock;
|
||||||
|
int resultItemsCount = 0;
|
||||||
|
sourceItemsCount += index;
|
||||||
|
for (var itemIndex = index; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
targetSpan[resultItemsCount++] = sourceSpan[itemIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
result._count = resultItemsCount;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int RemoveAll<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
using var allIndexes = zzzRecyclableListCompatibilityListFind.FindAllIndexes(list, match);
|
||||||
|
int foundItemsCount = allIndexes._count;
|
||||||
|
if (foundItemsCount == 0)
|
||||||
|
{
|
||||||
|
return foundItemsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int removed = 0; removed < foundItemsCount; removed++)
|
||||||
|
{
|
||||||
|
var indexToRemove = allIndexes[removed] - removed;
|
||||||
|
Array.Copy(list._memoryBlock, indexToRemove + 1, list._memoryBlock, indexToRemove, list._count - indexToRemove - removed - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RecyclableList<T>._needsClearing)
|
||||||
|
{
|
||||||
|
Array.Clear(list._memoryBlock, list._count - foundItemsCount, foundItemsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
list._count -= foundItemsCount;
|
||||||
|
list._version++;
|
||||||
|
return foundItemsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveRange<T>(this RecyclableList<T> list, int index, int count)
|
||||||
|
{
|
||||||
|
int oldCount = list._count;
|
||||||
|
count = count - index + 1 <= oldCount ? count : oldCount - index + 1;
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index + count < oldCount)
|
||||||
|
{
|
||||||
|
Array.Copy(list._memoryBlock, index + count, list._memoryBlock, index, oldCount - count - index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RecyclableList<T>._needsClearing)
|
||||||
|
{
|
||||||
|
Array.Clear(list._memoryBlock, oldCount - count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
list._count -= count;
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Reverse<T>(this RecyclableList<T> list)
|
||||||
|
{
|
||||||
|
Array.Reverse(list._memoryBlock, 0, list._count);
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Reverse<T>(this RecyclableList<T> list, int index, int count)
|
||||||
|
{
|
||||||
|
Array.Reverse(list._memoryBlock, index, count);
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sort<T>(this RecyclableList<T> list)
|
||||||
|
{
|
||||||
|
Array.Sort(list._memoryBlock, 0, list._count);
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sort<T>(this RecyclableList<T> list, Comparison<T> comparison)
|
||||||
|
{
|
||||||
|
Array.Sort(list._memoryBlock, 0, list._count, new ComparisonToComparerAdapter<T>(comparison));
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sort<T>(this RecyclableList<T> list, IComparer<T>? comparer)
|
||||||
|
{
|
||||||
|
Array.Sort(list._memoryBlock, 0, list._count, comparer);
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sort<T>(this RecyclableList<T> list, int index, int count, IComparer<T>? comparer)
|
||||||
|
{
|
||||||
|
Array.Sort(list._memoryBlock, index, count, comparer);
|
||||||
|
list._version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T[] ToArray<T>(this RecyclableList<T> list)
|
||||||
|
{
|
||||||
|
T[] result = new T[list._count];
|
||||||
|
new ReadOnlySpan<T>(list._memoryBlock, 0, list._count).CopyTo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TrimExcess<T>(this RecyclableList<T> list)
|
||||||
|
{
|
||||||
|
var requiredCapacity = list._count >= RecyclableDefaults.InitialCapacity ? list._count : RecyclableDefaults.InitialCapacity;
|
||||||
|
|
||||||
|
T[] oldMemoryBlock = list._memoryBlock;
|
||||||
|
T[] result = requiredCapacity >= RecyclableDefaults.MinPooledArrayLength && BitOperations.IsPow2(requiredCapacity)
|
||||||
|
? RecyclableArrayPool<T>.RentShared(requiredCapacity)
|
||||||
|
: new T[requiredCapacity];
|
||||||
|
|
||||||
|
new ReadOnlySpan<T>(oldMemoryBlock, 0, list._count).CopyTo(result);
|
||||||
|
|
||||||
|
if (oldMemoryBlock.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
{
|
||||||
|
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, RecyclableList<T>._needsClearing);
|
||||||
|
}
|
||||||
|
|
||||||
|
list._memoryBlock = result;
|
||||||
|
list._capacity = requiredCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TrueForAll<T>(this RecyclableList<T> list, Predicate<T> match)
|
||||||
|
{
|
||||||
|
int sourceItemsCount = list._count;
|
||||||
|
if (sourceItemsCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
||||||
|
for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++)
|
||||||
|
{
|
||||||
|
if (!match(sourceSpan[itemIndex]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="236" height="20" role="img" aria-label="Buy me a coffee"><title>Buy me a coffee</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="236" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="137" height="20" fill="#555"/><rect x="137" width="99" height="20" fill="#4c1"/><rect width="236" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="695" y="150" fill="#010101" transform="scale(.1)" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk">Buy me a coffee</a></text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk"/></text><text aria-hidden="true" x="705" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="890">Buy me a coffee</text><text x="695" y="142" transform="scale(.1)" fill="#fff" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk">Buy me a coffee</a></text><text aria-hidden="true" x="1890" y="142" fill="#010101" transform="scale(.1)" textLength="auto"><a href="https://www.buymeacoffee.com/mlemanczyk">$1 - $10</a></text></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
Reference in new issue
Block a user