Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00b147304 | ||
|
|
5d27fb599a | ||
|
|
0cecbe9645 | ||
|
|
97e60e381b | ||
|
|
6788c7a18c | ||
|
|
56c14910e0 | ||
|
|
8a4364e0e9 | ||
|
|
90ae27ce5d | ||
|
|
15a282de9f | ||
|
|
19775255bd | ||
|
|
c0e6f33f74 | ||
|
|
560e5e95c9 | ||
|
|
203b13268f | ||
|
|
ec69b1c406 |
No files matched your search
@@ -1,28 +0,0 @@
|
|||||||
# This workflow will build a .NET project
|
|
||||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
|
|
||||||
|
|
||||||
name: .NET
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v3
|
|
||||||
with:
|
|
||||||
dotnet-version: 8.0.x
|
|
||||||
- name: Restore dependencies
|
|
||||||
run: dotnet restore
|
|
||||||
- name: Build
|
|
||||||
run: dotnet build --no-restore
|
|
||||||
- name: Test
|
|
||||||
run: dotnet test --framework net8.0 --verbosity normal
|
|
||||||
Vendored
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"preLaunchTask": "build",
|
"preLaunchTask": "build",
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
"program": "${workspaceFolder}/Recyclable.Collections.Benchmarks/bin/Debug/net8.0/Recyclable.Collections.Benchmarks.dll",
|
"program": "${workspaceFolder}/Recyclable.Collections.Benchmarks/bin/Debug/net6.0/Recyclable.Collections.Benchmarks.dll",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}/Recyclable.Collections.Benchmarks",
|
"cwd": "${workspaceFolder}/Recyclable.Collections.Benchmarks",
|
||||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
# AGENTS
|
|
||||||
|
|
||||||
This repository hosts various libraries distributed as NuGet packages. Keep these links up to date so contributors and tools can easily locate them.
|
|
||||||
|
|
||||||
Only the .NET SDK **8.0** is available in the execution environment. Use this version when building or running tests.
|
|
||||||
|
|
||||||
## Implementation Guidelines
|
|
||||||
1. All `*.csproj` files should enable nullable reference types with `<Nullable>enable</Nullable>`.
|
|
||||||
1. Implementations should be optimized for maximum performance even if that introduces some code duplication.
|
|
||||||
1. Consider applying attributes such as `MethodImplOptions.AggressiveInlining` or faster alternatives to common operations when appropriate. Another would be modulo "%" or division "/" operations, which could be often replaced with faster alternatives, possibly under specific conditions.
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
1. When running `dotnet test`, do **not** pass `--no-build` because that leads to missing assemblies, test failures without logs and other issues.
|
|
||||||
1. When running `dotnet test` specify the `--framework` argument matching the SDK available in the environment (currently `net8.0`). If you omit this parameter, the tests will fail running with no logs etc.
|
|
||||||
1. The tests run for a very long time, because there are 61_000+ unit tests for all the collections. Set timeout when running them to 15 mis.
|
|
||||||
1. All tests must use **xUnit** together with **FluentAssertions**.
|
|
||||||
|
|
||||||
## Source Code Formatting Guidelines
|
|
||||||
1. Indent code using four spaces or a single tab per level.
|
|
||||||
1. Place one blank line after each closing curly brace of a code block, except when several closing braces appear sequentially.
|
|
||||||
1. Always use braces for `if` and `else` bodies.
|
|
||||||
|
|
||||||
## NuGet packages
|
|
||||||
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
|
|
||||||
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
|
|
||||||
- [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
|
|
||||||
- [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
|
|
||||||
- [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
|
|
||||||
- [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData)
|
|
||||||
+1
-1
@@ -3,7 +3,7 @@ The project & source code is free for educational & personal use. **Any
|
|||||||
|
|
||||||
# Donation requirements for a life-time, commercial use
|
# Donation requirements for a life-time, commercial use
|
||||||
* Commercial license is paid one time.
|
* Commercial license is paid one time.
|
||||||
* It can be obtained by donaiting $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.
|
* 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.
|
* Your license stays with you, if your developer moves somewhere else.
|
||||||
|
|
||||||
# Link for donations
|
# Link for donations
|
||||||
|
|||||||
@@ -5,14 +5,6 @@
|
|||||||
* `RecyclableList<T>`
|
* `RecyclableList<T>`
|
||||||
* `RecyclableLongList<T>`
|
* `RecyclableLongList<T>`
|
||||||
|
|
||||||
## NuGet packages
|
|
||||||
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
|
|
||||||
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
|
|
||||||
- [Recyclable.Collections.Concurrent](https://github.com/mlemanczyk/Recyclable.Collections.Concurrent)
|
|
||||||
- [Recyclable.Collections.Linq](https://github.com/mlemanczyk/Recyclable.Collections.Linq)
|
|
||||||
- [Recyclable.Collections.Searching](https://github.com/mlemanczyk/Recyclable.Collections.Searching)
|
|
||||||
- [Recyclable.Collections.TestData](https://github.com/mlemanczyk/Recyclable.Collections.TestData)
|
|
||||||
|
|
||||||
# Milestones
|
# Milestones
|
||||||
1. ✅ Create basic classes
|
1. ✅ Create basic classes
|
||||||
1. ✅ `RecyclableList<T>`
|
1. ✅ `RecyclableList<T>`
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Add()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
var list = new long[dataCount];
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list[i] = data[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Add()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
var list = new List<long>();
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Add()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new PooledList<long>(ClearMode.Auto);
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Add()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new RecyclableList<long>();
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Add()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new RecyclableLongList<long>();
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_AddRangeWhenSourceIsArray()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = new long[TestObjectCount];
|
|
||||||
data.CopyTo(list, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_AddRangeWhenSourceIsArray()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = new List<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_AddRangeWhenSourceIsArray()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new PooledList<long>(ClearMode.Auto);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_AddRangeWhenSourceIsArray()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableList<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_AddRangeWhenSourceIsArray()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_AddRangeWhenSourceIsIEnumerable()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsIEnumerable;
|
|
||||||
var list = new long[TestObjectCount];
|
|
||||||
int itemIndex = 0;
|
|
||||||
|
|
||||||
foreach (var item in data)
|
|
||||||
{
|
|
||||||
list[itemIndex++] = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_AddRangeWhenSourceIsIEnumerable()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsIEnumerable;
|
|
||||||
var list = new List<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_AddRangeWhenSourceIsIEnumerable()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsIEnumerable;
|
|
||||||
using var list = new PooledList<long>(ClearMode.Auto);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_AddRangeWhenSourceIsIEnumerable()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsIEnumerable;
|
|
||||||
using var list = new RecyclableList<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_AddRangeWhenSourceIsIEnumerable()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsIEnumerable;
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_AddRangeWhenSourceIsIList()
|
|
||||||
{
|
|
||||||
var data = (IList<long>)TestObjectsAsRecyclableLongList;
|
|
||||||
var list = new long[TestObjectCount];
|
|
||||||
data.CopyTo(list, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_AddRangeWhenSourceIsIList()
|
|
||||||
{
|
|
||||||
var data = (IList<long>)TestObjectsAsRecyclableLongList;
|
|
||||||
var list = new List<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_AddRangeWhenSourceIsIList()
|
|
||||||
{
|
|
||||||
var data = (IList<long>)TestObjectsAsRecyclableLongList;
|
|
||||||
using var list = new PooledList<long>(ClearMode.Auto);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_AddRangeWhenSourceIsIList()
|
|
||||||
{
|
|
||||||
var data = (IList<long>)TestObjectsAsRecyclableLongList;
|
|
||||||
using var list = new RecyclableList<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_AddRangeWhenSourceIsIList()
|
|
||||||
{
|
|
||||||
var data = (IList<long>)TestObjectsAsRecyclableLongList;
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_AddRangeWhenSourceIsList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
var list = new long[TestObjectCount];
|
|
||||||
data.CopyTo(list, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_AddRangeWhenSourceIsList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
var list = new List<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_AddRangeWhenSourceIsList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
using var list = new PooledList<long>(ClearMode.Auto);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_AddRangeWhenSourceIsList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
using var list = new RecyclableList<long>();
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_AddRangeWhenSourceIsList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void PooledList_AddRangeWhenSourceIsPooledList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsPooledList;
|
|
||||||
using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_AddRangeWhenSourceIsRecyclableList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableList;
|
|
||||||
using var list = new RecyclableList<long>(TestObjectCount);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_AddRangeWhenSourceIsRecyclableLongList()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableList;
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_AddRange_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
var list = new long[dataCount];
|
|
||||||
data.CopyTo(list, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_AddRange_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
var list = new List<long>(dataCount);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_AddRange_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_AddRange_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new RecyclableList<long>(dataCount);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_AddRange_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount);
|
|
||||||
list.AddRange(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void List_Add_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
var list = new List<long>(dataCount);
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Add_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new PooledList<long>(capacity: dataCount, ClearMode.Auto);
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Add_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new RecyclableList<long>(dataCount);
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Add_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: dataCount);
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_BinarySearch_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.BinarySearch(list, data[i]));
|
|
||||||
DoNothing(Array.BinarySearch(list, data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_BinarySearch_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.BinarySearch(data[i]));
|
|
||||||
DoNothing(list.BinarySearch(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_BinarySearch_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.BinarySearch(data[i]));
|
|
||||||
DoNothing(list.BinarySearch(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_BinarySearch_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.BinarySearch(data[i]));
|
|
||||||
DoNothing(list.BinarySearch(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void RecyclableLongList_BinarySearch_BestAndWorstCases()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var list = TestObjectsAsRecyclableLongList;
|
|
||||||
// var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.BinarySearch(data[i]));
|
|
||||||
// DoNothing(list.BinarySearch(data[^(i + 1)]));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Contains_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.IndexOf(list, data[i]) >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Contains_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Contains_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Contains_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Contains_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Contains_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Contains_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Contains_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Contains_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Contains_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Contains(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_ConvertAll()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
DoNothing(Array.ConvertAll(list, static (item) => (long) item));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_ConvertAll()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
DoNothing(list.ConvertAll(static (item) => (long)item));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_ConvertAll()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
using var converted = list.ConvertAll(static (item) => (long)item);
|
|
||||||
DoNothing(converted);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_ConvertAll()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
|
|
||||||
DoNothing(converted);
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void RecyclableLongList_ConvertAll()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var list = TestObjectsAsRecyclableLongList;
|
|
||||||
// var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// using RecyclableList<long> converted = list.ConvertAll(static (item) => (long)item);
|
|
||||||
// DoNothing(converted);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Count()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
DoNothing(data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Count()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
DoNothing(data.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Count()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsPooledList;
|
|
||||||
DoNothing(data.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Count()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableList;
|
|
||||||
DoNothing(data.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Count()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
DoNothing(data.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
#pragma warning disable CA1822, CA1825
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Create()
|
|
||||||
{
|
|
||||||
var list = new long[0];
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Create()
|
|
||||||
{
|
|
||||||
var list = new List<long>();
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Create()
|
|
||||||
{
|
|
||||||
using var list = new PooledList<long>(ClearMode.Auto);
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Create()
|
|
||||||
{
|
|
||||||
using var list = new RecyclableList<long>();
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Create()
|
|
||||||
{
|
|
||||||
using var list = new RecyclableLongList<long>();
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Create_WithCapacity()
|
|
||||||
{
|
|
||||||
var data = new long[TestObjectCount];
|
|
||||||
DoNothing(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Create_WithCapacity()
|
|
||||||
{
|
|
||||||
var list = new List<long>(TestObjectCount);
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Create_WithCapacity()
|
|
||||||
{
|
|
||||||
using var list = new PooledList<long>(capacity: TestObjectCount, ClearMode.Auto);
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Create_WithCapacity()
|
|
||||||
{
|
|
||||||
using var list = new RecyclableList<long>(TestObjectCount);
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Create_WithCapacity()
|
|
||||||
{
|
|
||||||
using var list = new RecyclableLongList<long>(minBlockSize: BlockSize, initialCapacity: TestObjectCount);
|
|
||||||
DoNothing(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Exists_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.Exists(list, (x) => x == data[i]));
|
|
||||||
DoNothing(Array.Exists(list, (x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Exists_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Exists((x) => x == data[i]));
|
|
||||||
DoNothing(list.Exists((x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Exists_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Exists((x) => x == data[i]));
|
|
||||||
DoNothing(list.Exists((x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Exists_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Exists((x) => x == data[i]));
|
|
||||||
DoNothing(list.Exists((x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void RecyclableLongList_Exists_BestAndWorstCases()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var list = TestObjectsAsRecyclableLongList;
|
|
||||||
// var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.Exists((x) => x == data[i]));
|
|
||||||
// DoNothing(list.Exists((x) => x == data[^(i + 1)]));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_FindAll_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.FindAll(list, (item) => item == data[i]));
|
|
||||||
DoNothing(Array.FindAll(list, (item) => item == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_FindAll_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.FindAll((item) => item == data[i]));
|
|
||||||
DoNothing(list.FindAll((item) => item == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_FindAll_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
var temp = list.FindAll((item) => item == data[i]);
|
|
||||||
DoNothing(temp);
|
|
||||||
temp.Dispose();
|
|
||||||
|
|
||||||
temp = list.FindAll((item) => item == data[^(i + 1)]);
|
|
||||||
DoNothing(temp);
|
|
||||||
temp.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_FindAll_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
var temp = list.FindAll((item) => item == data[i]);
|
|
||||||
DoNothing(temp);
|
|
||||||
temp.Dispose();
|
|
||||||
|
|
||||||
temp = list.FindAll((item) => item == data[^(i + 1)]);
|
|
||||||
DoNothing(temp);
|
|
||||||
temp.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void RecyclableLongList_FindAll_BestAndWorstCases()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var list = TestObjectsAsRecyclableLongList;
|
|
||||||
// var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// var temp = list.FindAll((item) => item == data[i]);
|
|
||||||
// DoNothing(temp);
|
|
||||||
// temp.Dispose();
|
|
||||||
|
|
||||||
// temp = list.FindAll((item) => item == data[^(i + 1)]);
|
|
||||||
// DoNothing(temp);
|
|
||||||
// temp.Dispose();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_FindLastIndex_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
// Console.WriteLine($"data.Length={data.Length}");
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
|
|
||||||
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[i]));
|
|
||||||
DoNothing(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_FindLastIndex_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
// Console.WriteLine($"data.Length={data.Length}");
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCount)}={TestObjectCount}");
|
|
||||||
// Console.WriteLine($"{nameof(TestObjectCountForSlowMethods)}={TestObjectCountForSlowMethods}");
|
|
||||||
DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
|
|
||||||
DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_FindLastIndex_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
|
|
||||||
DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void RecyclableLongList_FindLastIndex_BestAndWorstCases()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var list = TestObjectsAsRecyclableLongList;
|
|
||||||
// var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.FindLastIndex(0, (x) => x == data[i]));
|
|
||||||
// DoNothing(list.FindLastIndex(0, (x) => x == data[^(i + 1)]));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_FindLast_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.FindLast(list, (x) => x == data[i]));
|
|
||||||
DoNothing(Array.FindLast(list, (x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_FindLast_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.FindLast((x) => x == data[i]));
|
|
||||||
DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_FindLast_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.FindLast((x) => x == data[i]));
|
|
||||||
DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void RecyclableLongList_FindLast_BestAndWorstCases()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var list = TestObjectsAsRecyclableLongList;
|
|
||||||
// var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.FindLast((x) => x == data[i]));
|
|
||||||
// DoNothing(list.FindLast((x) => x == data[^(i + 1)]));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_Find_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.Find(list, (x) => x == data[i]));
|
|
||||||
DoNothing(Array.Find(list, (x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_Find_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Find((x) => x == data[i]));
|
|
||||||
DoNothing(list.Find((x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Find_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Find((x) => x == data[i]));
|
|
||||||
DoNothing(list.Find((x) => x == data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void RecyclableLongList_Find_BestAndWorstCases()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var list = TestObjectsAsRecyclableLongList;
|
|
||||||
// var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.Find((x) => x == data[i]));
|
|
||||||
// DoNothing(list.Find((x) => x == data[^(i + 1)]));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_ForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjects)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Array_VersionedForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjects)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_ForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjectsAsList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_VersionedForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjectsAsList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_ForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjectsAsPooledList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_VersionedForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjectsAsPooledList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_ForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjectsAsRecyclableList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_VersionedForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in (IRecyclableVersionedList<long>)TestObjectsAsRecyclableList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_ForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in TestObjectsAsRecyclableLongList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_VersionedForEach()
|
|
||||||
{
|
|
||||||
foreach (var item in (IRecyclableVersionedLongList<long>)TestObjectsAsRecyclableLongList)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_GetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_GetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_GetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_GetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_GetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_GetItem_AsSpan()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
int blockSize = data.BlockSize;
|
|
||||||
int nextItemBlockIndex = data.NextItemBlockIndex;
|
|
||||||
|
|
||||||
var memoryBlocks = new Span<long[]>(data.AsArray, 0, data.LastBlockWithData + 1);
|
|
||||||
for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++)
|
|
||||||
{
|
|
||||||
var memoryBlock = new Span<long>(memoryBlocks[blockIndex], 0, blockSize);
|
|
||||||
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
|
|
||||||
{
|
|
||||||
DoNothing(memoryBlock[itemIndex]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextItemBlockIndex == data.LastBlockWithData)
|
|
||||||
{
|
|
||||||
var memoryBlock = new Span<long>(memoryBlocks[nextItemBlockIndex], 0, data.NextItemIndex);
|
|
||||||
for (var itemIndex = 0; itemIndex < memoryBlock.Length; itemIndex++)
|
|
||||||
{
|
|
||||||
DoNothing(memoryBlock[itemIndex]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_GetItem_AsArray()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
int blockSize = data.BlockSize;
|
|
||||||
int nextItemBlockIndex = data.NextItemBlockIndex;
|
|
||||||
|
|
||||||
var memoryBlocks = data.AsArray;
|
|
||||||
for (var blockIndex = 0; blockIndex < nextItemBlockIndex; blockIndex++)
|
|
||||||
{
|
|
||||||
var memoryBlock = memoryBlocks[blockIndex];
|
|
||||||
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
|
|
||||||
{
|
|
||||||
DoNothing(memoryBlock[itemIndex]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextItemBlockIndex == data.LastBlockWithData)
|
|
||||||
{
|
|
||||||
var memoryBlock = memoryBlocks[nextItemBlockIndex];
|
|
||||||
blockSize = data.NextItemIndex;
|
|
||||||
for (var itemIndex = 0; itemIndex < blockSize; itemIndex++)
|
|
||||||
{
|
|
||||||
DoNothing(memoryBlock[itemIndex]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_GetItem_AsForEach()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
foreach (var item in data)
|
|
||||||
{
|
|
||||||
DoNothing(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_GetItem_AsWhile()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
long dataCount = TestObjectCount;
|
|
||||||
if (dataCount == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int blockSize = data.BlockSize;
|
|
||||||
int nextItemBlockIndex = data.NextItemBlockIndex;
|
|
||||||
int itemIndex = 0;
|
|
||||||
int blockIndex = 0;
|
|
||||||
var memoryBlocks = data.AsArray;
|
|
||||||
var memoryBlock = memoryBlocks[0];
|
|
||||||
while (blockIndex < nextItemBlockIndex)
|
|
||||||
{
|
|
||||||
DoNothing(memoryBlock[itemIndex]);
|
|
||||||
if (itemIndex + 1 < blockSize)
|
|
||||||
{
|
|
||||||
itemIndex++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
blockIndex++;
|
|
||||||
memoryBlock = memoryBlocks[blockIndex];
|
|
||||||
itemIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextItemBlockIndex == data.LastBlockWithData)
|
|
||||||
{
|
|
||||||
memoryBlock = memoryBlocks[nextItemBlockIndex];
|
|
||||||
blockSize = data.NextItemIndex;
|
|
||||||
while (itemIndex < blockSize)
|
|
||||||
{
|
|
||||||
DoNothing(memoryBlock[itemIndex++]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_IndexOf_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.IndexOf(list, data[i]));
|
|
||||||
DoNothing(Array.IndexOf(list, data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_IndexOf_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_IndexOf_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_IndexOf_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_IndexOf_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_LongIndexOf_BestAndWorstCases()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
DoNothing(list.LongIndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_IndexOf_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.IndexOf(list, data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_IndexOf_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_IndexOf_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_IndexOf_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_IndexOf_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_LongIndexOf_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.LongIndexOf(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_IndexOf_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjects;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(Array.IndexOf(list, data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_IndexOf_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_IndexOf_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_IndexOf_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_IndexOf_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.IndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_LongIndexOf_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCountForSlowMethods;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.LongIndexOf(data[^(i + 1)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_LongCount()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
DoNothing(data.LongLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_LongCount()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
DoNothing(data.LongCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public enum RecyclableCollectionsBenchmarkType
|
|
||||||
{
|
|
||||||
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
|
||||||
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, BinarySearch_BestAndWorstCases,
|
|
||||||
Contains_FirstItems, Contains_LastItems, ConvertAll, Count, Create_WithCapacity, Create, Exists_BestAndWorstCases,
|
|
||||||
Find_BestAndWorstCases, FindAll_BestAndWorstCases, FindLast_BestAndWorstCases, FindLastIndex_BestAndWorstCases,
|
|
||||||
ForEach, GetItem, IndexOf_BestAndWorstCases,IndexOf_FirstItems, IndexOf_LastItems, LongCount,
|
|
||||||
Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
|
|
||||||
SetItem, VersionedForEach
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class RecyclableCollectionsBenchmarks : RecyclableBenchmarkBase<RecyclableCollectionsBenchmarkSource>
|
|
||||||
{
|
|
||||||
public RecyclableCollectionsBenchmarks()
|
|
||||||
{
|
|
||||||
TestObjectCount = 1;
|
|
||||||
//BaseDataType = RecyclableCollectionsBenchmarkSource.List;
|
|
||||||
DataType = RecyclableCollectionsBenchmarkSource.RecyclableList;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Params
|
|
||||||
(
|
|
||||||
RecyclableCollectionsBenchmarkType.Add_WithCapacity,
|
|
||||||
RecyclableCollectionsBenchmarkType.Add,
|
|
||||||
RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
|
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
|
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
|
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
|
||||||
RecyclableCollectionsBenchmarkType.BinarySearch_BestAndWorstCases,
|
|
||||||
RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.ConvertAll,
|
|
||||||
RecyclableCollectionsBenchmarkType.Count,
|
|
||||||
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
|
||||||
RecyclableCollectionsBenchmarkType.Create,
|
|
||||||
RecyclableCollectionsBenchmarkType.Exists_BestAndWorstCases,
|
|
||||||
RecyclableCollectionsBenchmarkType.Find_BestAndWorstCases,
|
|
||||||
RecyclableCollectionsBenchmarkType.FindAll_BestAndWorstCases,
|
|
||||||
RecyclableCollectionsBenchmarkType.FindLast_BestAndWorstCases,
|
|
||||||
RecyclableCollectionsBenchmarkType.FindLastIndex_BestAndWorstCases,
|
|
||||||
RecyclableCollectionsBenchmarkType.ForEach,
|
|
||||||
RecyclableCollectionsBenchmarkType.GetItem,
|
|
||||||
RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
|
|
||||||
RecyclableCollectionsBenchmarkType.IndexOf_FirstItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.IndexOf_LastItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.LongCount,
|
|
||||||
RecyclableCollectionsBenchmarkType.Remove_FirstItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.Remove_LastItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.RemoveAt_FirstItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.RemoveAt_LastItems,
|
|
||||||
RecyclableCollectionsBenchmarkType.SetItem,
|
|
||||||
RecyclableCollectionsBenchmarkType.VersionedForEach
|
|
||||||
)]
|
|
||||||
|
|
||||||
public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases;
|
|
||||||
|
|
||||||
[Params
|
|
||||||
(
|
|
||||||
RecyclableCollectionsBenchmarkSource.List
|
|
||||||
)]
|
|
||||||
public override RecyclableCollectionsBenchmarkSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
|
||||||
|
|
||||||
[Params
|
|
||||||
(
|
|
||||||
RecyclableCollectionsBenchmarkSource.Array,
|
|
||||||
RecyclableCollectionsBenchmarkSource.List,
|
|
||||||
RecyclableCollectionsBenchmarkSource.PooledList,
|
|
||||||
RecyclableCollectionsBenchmarkSource.RecyclableList,
|
|
||||||
RecyclableCollectionsBenchmarkSource.RecyclableLongList
|
|
||||||
)]
|
|
||||||
public override RecyclableCollectionsBenchmarkSource DataType { get => base.DataType; set => base.DataType = value; }
|
|
||||||
|
|
||||||
protected override Action? GetTestMethod(RecyclableCollectionsBenchmarkSource benchmarkType)
|
|
||||||
=> TestCase switch
|
|
||||||
{
|
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType => (Action?)GetType().GetMethod($"{benchmarkType}_AddRangeWhenSourceIs{benchmarkType}", BindingFlags.Instance | BindingFlags.Public)?.CreateDelegate(typeof(Action), this),
|
|
||||||
_ => (Action?) GetType().GetMethod($"{benchmarkType}_{TestCase}", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)?.CreateDelegate(typeof(Action), this) ?? throw new InvalidOperationException($"Method {benchmarkType}_{TestCase} not found in class {GetType().FullName}")
|
|
||||||
};
|
|
||||||
|
|
||||||
public override void Setup()
|
|
||||||
{
|
|
||||||
base.Setup();
|
|
||||||
switch(TestCase)
|
|
||||||
{
|
|
||||||
case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList:
|
|
||||||
PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableLongList);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType:
|
|
||||||
PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableList);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void List_RemoveAt_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = new List<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.RemoveAt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_RemoveAt_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new PooledList<long>(data, ClearMode.Auto);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.RemoveAt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_RemoveAt_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableList<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.RemoveAt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_RemoveAt_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
list.RemoveAt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void List_RemoveAt_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = new List<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
list.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_RemoveAt_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new PooledList<long>(data, ClearMode.Auto);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
list.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_RemoveAt_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableList<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
list.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_RemoveAt_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
list.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void List_Remove_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = new List<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Remove_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new PooledList<long>(data, ClearMode.Auto);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Remove_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableList<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Remove_FirstItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity: base.TestObjectCount);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using Collections.Pooled;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void List_Remove_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var list = new List<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_Remove_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new PooledList<long>(data, ClearMode.Auto);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_Remove_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableList<long>(data);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_Remove_LastItems()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
using var list = new RecyclableLongList<long>(data, minBlockSize: BlockSize, initialCapacity:base.TestObjectCount);
|
|
||||||
var dataCount = TestObjectCount / 10 > 0 ? TestObjectCount / 10 : TestObjectCount;
|
|
||||||
for (var i = data.Length - 1; i > data.Length - dataCount - 1; i--)
|
|
||||||
{
|
|
||||||
DoNothing(list.Remove(data[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public partial class RecyclableCollectionsBenchmarks
|
|
||||||
{
|
|
||||||
public void Array_SetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
long index = 0;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
data[index] = TestObjects[index++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void List_SetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
data[i] = TestObjects[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PooledList_SetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsPooledList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
data[i] = TestObjects[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableList_SetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
data[i] = TestObjects[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecyclableLongList_SetItem()
|
|
||||||
{
|
|
||||||
var data = TestObjectsAsRecyclableLongList;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (var i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
data[i] = TestObjects[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Benchmarks.Core
|
|
||||||
{
|
|
||||||
public static class DataGenerator
|
|
||||||
{
|
|
||||||
public static long[] EnumerateTestObjects(long testObjectCount)
|
|
||||||
{
|
|
||||||
var result = new long[testObjectCount];
|
|
||||||
Console.WriteLine("******* TEST DATA ARRAY CREATED *******");
|
|
||||||
|
|
||||||
Span<long> resultSpan = result;
|
|
||||||
for (int i = 0; i < testObjectCount; i++)
|
|
||||||
{
|
|
||||||
resultSpan[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public enum ArraySizeLimitPocBenchmarkType
|
|
||||||
{
|
|
||||||
ArraySizeLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ArraySizeLimitPocBenchmarks : PocBenchmarkBase<ArraySizeLimitPocBenchmarkType>
|
|
||||||
{
|
|
||||||
public static void Run()
|
|
||||||
{
|
|
||||||
var benchmark = new ArraySizeLimitPocBenchmarks();
|
|
||||||
benchmark.Setup();
|
|
||||||
FindArraySizeLimit();
|
|
||||||
benchmark.Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Params(2_147_483_605)]
|
|
||||||
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = 1)]
|
|
||||||
public void Max_Allowed_Size_2_147_483_605()
|
|
||||||
{
|
|
||||||
long[] arr = ArrayPool<long>.Shared.Rent(TestObjectCount);
|
|
||||||
DoNothing(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = 1)]
|
|
||||||
public static void FindArraySizeLimit()
|
|
||||||
{
|
|
||||||
var pool = ArrayPool<long>.Shared;
|
|
||||||
var bufferSize = int.MaxValue / 2;
|
|
||||||
var oldValue = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Console.Write($"Allocating {bufferSize}...");
|
|
||||||
var arr = pool.Rent(bufferSize);
|
|
||||||
Console.WriteLine("success");
|
|
||||||
pool.Return(arr);
|
|
||||||
arr = null;
|
|
||||||
|
|
||||||
var nextValue = bufferSize - oldValue;
|
|
||||||
oldValue = bufferSize;
|
|
||||||
bufferSize = checked(bufferSize + nextValue);
|
|
||||||
|
|
||||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
Console.WriteLine("error");
|
|
||||||
var nextValue = (bufferSize - oldValue) >> 1;
|
|
||||||
if (nextValue == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bufferSize = oldValue + nextValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("All done");
|
|
||||||
throw new Exception($"Everything was successful. You can create {oldValue} byte big arrays. Exception is logged to prevent more benchmarks.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public enum BoolOrComparePocBenchmarkType
|
|
||||||
{
|
|
||||||
Bool,
|
|
||||||
Compare,
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class BoolOrComparePocBenchmarks : PocBenchmarkBase<BoolOrComparePocBenchmarkType>
|
|
||||||
{
|
|
||||||
[Params(BoolOrComparePocBenchmarkType.Bool, BoolOrComparePocBenchmarkType.Compare)]
|
|
||||||
public override BoolOrComparePocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
|
|
||||||
|
|
||||||
[Params(8)]
|
|
||||||
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
|
|
||||||
|
|
||||||
[Params(true)]
|
|
||||||
public bool TestBool = true;
|
|
||||||
|
|
||||||
public static void Run()
|
|
||||||
{
|
|
||||||
var benchmark = new BoolOrComparePocBenchmarks();
|
|
||||||
benchmark.Setup();
|
|
||||||
benchmark.Bool();
|
|
||||||
benchmark.Compare();
|
|
||||||
benchmark.Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Bool()
|
|
||||||
{
|
|
||||||
if (TestBool)
|
|
||||||
{
|
|
||||||
DoNothing(TestBool);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DoNothing(TestBool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Compare()
|
|
||||||
{
|
|
||||||
if (TestObjectCount >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
DoNothing(TestBool);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DoNothing(TestBool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Action? GetTestMethod(BoolOrComparePocBenchmarkType benchmarkType) => benchmarkType switch
|
|
||||||
{
|
|
||||||
BoolOrComparePocBenchmarkType.Bool => Bool,
|
|
||||||
BoolOrComparePocBenchmarkType.Compare => Compare,
|
|
||||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,346 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public enum DelegateVsComparerPocBenchmarkType
|
|
||||||
{
|
|
||||||
Comparer,
|
|
||||||
Delegate,
|
|
||||||
Func,
|
|
||||||
ListOfBoxedStructs,
|
|
||||||
ListOfClasses,
|
|
||||||
ListOfComparers,
|
|
||||||
ListOfDelegates,
|
|
||||||
ListOfFuncs,
|
|
||||||
ListOfLocalFuncs,
|
|
||||||
ListOfStructs,
|
|
||||||
ListOfUnboxedReadOnlyStructs,
|
|
||||||
ListOfUnboxedStructs,
|
|
||||||
StaticFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class DelegateVsComparerPocBenchmarks : PocBenchmarkBase<DelegateVsComparerPocBenchmarkType>
|
|
||||||
{
|
|
||||||
[Params
|
|
||||||
(
|
|
||||||
DelegateVsComparerPocBenchmarkType.Delegate,
|
|
||||||
DelegateVsComparerPocBenchmarkType.Func,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfClasses,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfComparers,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfDelegates,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfFuncs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.StaticFunc
|
|
||||||
)]
|
|
||||||
public override DelegateVsComparerPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
|
|
||||||
|
|
||||||
[Params(DelegateVsComparerPocBenchmarkType.Comparer)]
|
|
||||||
public override DelegateVsComparerPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
|
||||||
|
|
||||||
#region Dummy classes
|
|
||||||
|
|
||||||
private delegate T Compare<T>(T x, T y);
|
|
||||||
|
|
||||||
public readonly struct MyComparerReadOnlyStruct<T> : IComparer<T>
|
|
||||||
{
|
|
||||||
public int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct MyComparerStruct<T> : IComparer<T>
|
|
||||||
{
|
|
||||||
public readonly int Compare(T? x, T? y) => Comparer<T>.Default.Compare(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MyComparer : IComparer<long>
|
|
||||||
{
|
|
||||||
public int Compare(long x, long y) => (x - y) switch
|
|
||||||
{
|
|
||||||
> 0 => 1,
|
|
||||||
< 0 => -1,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public static void Run()
|
|
||||||
{
|
|
||||||
var benchmark = new DelegateVsComparerPocBenchmarks();
|
|
||||||
benchmark.Setup();
|
|
||||||
benchmark.Comparer();
|
|
||||||
benchmark.Delegate();
|
|
||||||
benchmark.Func();
|
|
||||||
benchmark.ListOfBoxedStructs();
|
|
||||||
benchmark.ListOfClasses();
|
|
||||||
benchmark.ListOfComparers();
|
|
||||||
benchmark.ListOfDelegates();
|
|
||||||
benchmark.ListOfFuncs();
|
|
||||||
benchmark.ListOfLocalFuncs();
|
|
||||||
benchmark.ListOfStructs();
|
|
||||||
benchmark.ListOfUnboxedReadOnlyStructs();
|
|
||||||
benchmark.ListOfUnboxedStructs();
|
|
||||||
benchmark.StaticFunc();
|
|
||||||
benchmark.Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Comparer<long> _comparer = Comparer<long>.Default;
|
|
||||||
private IEnumerable<long>? _testObjects;
|
|
||||||
private IEnumerable<Func<long, long, int>>? _testListOfFunctions;
|
|
||||||
private IEnumerable<IComparer<long>>? _testListOfComparers;
|
|
||||||
private IEnumerable<Compare<long>>? _testListOfDelegates;
|
|
||||||
private IEnumerable<Func<long, long, int>>? _testListOfLocalFunctions;
|
|
||||||
private IEnumerable<MyComparer>? _testListOfClasses;
|
|
||||||
private IEnumerable<MyComparerStruct<long>>? _testListOfStructs;
|
|
||||||
private IEnumerable<IComparer<long>>? _testListOfBoxedStructs;
|
|
||||||
private IEnumerable<TComparer> TestListOfUnboxedStructs<TComparer, TValue>() where TComparer : IComparer<TValue>, new()
|
|
||||||
{
|
|
||||||
foreach (var _ in _testObjects!)
|
|
||||||
{
|
|
||||||
yield return new TComparer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<Func<long, long, int>> GetLocalFunctions(IEnumerable<long> testObjects)
|
|
||||||
{
|
|
||||||
static int compareFunc(long x, long y)
|
|
||||||
{
|
|
||||||
return (x - y) switch
|
|
||||||
{
|
|
||||||
> 0 => 1,
|
|
||||||
< 0 => -1,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var _ in testObjects)
|
|
||||||
{
|
|
||||||
yield return compareFunc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TestWithComparer(IComparer<long> comparer)
|
|
||||||
{
|
|
||||||
var testData = _testObjects!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
|
|
||||||
{
|
|
||||||
_ = comparer.Compare(testData[itemIdx], testData[itemIdx + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TestWithDelegate(Compare<long> compareFunc)
|
|
||||||
{
|
|
||||||
var testData = _testObjects!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
|
|
||||||
{
|
|
||||||
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TestWithFunc(Func<long, long, int> compareFunc)
|
|
||||||
{
|
|
||||||
var testData = _testObjects!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
|
|
||||||
{
|
|
||||||
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Comparer()
|
|
||||||
{
|
|
||||||
TestWithComparer(_comparer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Func()
|
|
||||||
{
|
|
||||||
TestWithFunc((x, y) => (x - y) switch
|
|
||||||
{
|
|
||||||
> 0 => 1,
|
|
||||||
< 0 => -1,
|
|
||||||
_ => 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StaticFunc()
|
|
||||||
{
|
|
||||||
TestWithFunc(static (x, y) => (x - y) switch
|
|
||||||
{
|
|
||||||
> 0 => 1,
|
|
||||||
< 0 => -1,
|
|
||||||
_ => 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Delegate()
|
|
||||||
{
|
|
||||||
TestWithDelegate((x, y) => (x - y) switch
|
|
||||||
{
|
|
||||||
> 0 => 1,
|
|
||||||
< 0 => -1,
|
|
||||||
_ => 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfFuncs()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = _testListOfFunctions!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var compareFunc = testData[itemIdx];
|
|
||||||
_ = compareFunc(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfLocalFuncs()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = _testListOfLocalFunctions!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var compareFunc = testData[itemIdx];
|
|
||||||
_ = compareFunc(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfComparers()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = _testListOfComparers!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var comparer = testData[itemIdx];
|
|
||||||
_ = comparer.Compare(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfClasses()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = _testListOfClasses!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var comparer = testData[itemIdx];
|
|
||||||
_ = comparer.Compare(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfStructs()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = _testListOfStructs!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var comparer = testData[itemIdx];
|
|
||||||
_ = comparer.Compare(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfBoxedStructs()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = _testListOfBoxedStructs!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var comparer = testData[itemIdx];
|
|
||||||
_ = comparer.Compare(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfUnboxedStructs()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = TestListOfUnboxedStructs<MyComparerStruct<long>, long>().ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var comparer = testData[itemIdx];
|
|
||||||
_ = comparer.Compare(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfUnboxedReadOnlyStructs()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = TestListOfUnboxedStructs<MyComparerReadOnlyStruct<long>, long>().ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var comparer = testData[itemIdx];
|
|
||||||
_ = comparer.Compare(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ListOfDelegates()
|
|
||||||
{
|
|
||||||
const long x = 2, y = 5;
|
|
||||||
var testData = _testListOfDelegates!.ToArray();
|
|
||||||
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
|
|
||||||
{
|
|
||||||
var compareDelegate = testData[itemIdx];
|
|
||||||
_ = compareDelegate(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Action? GetTestMethod(DelegateVsComparerPocBenchmarkType benchmarkType) => benchmarkType switch
|
|
||||||
{
|
|
||||||
DelegateVsComparerPocBenchmarkType.Comparer => Comparer,
|
|
||||||
DelegateVsComparerPocBenchmarkType.Delegate => Delegate,
|
|
||||||
DelegateVsComparerPocBenchmarkType.Func => Func,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs => ListOfBoxedStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfClasses => ListOfClasses,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfComparers => ListOfComparers,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfDelegates => ListOfDelegates,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfFuncs => ListOfFuncs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs => ListOfLocalFuncs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfStructs => ListOfStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfUnboxedReadOnlyStructs => ListOfUnboxedReadOnlyStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.ListOfUnboxedStructs => ListOfUnboxedStructs,
|
|
||||||
DelegateVsComparerPocBenchmarkType.StaticFunc => StaticFunc,
|
|
||||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
|
||||||
};
|
|
||||||
|
|
||||||
public override void Setup()
|
|
||||||
{
|
|
||||||
Console.WriteLine("******* SETTING UP COMMON TEST DATA *******");
|
|
||||||
_testObjects = Enumerable.Range(1, TestObjectCount).Select(x => (long)x);
|
|
||||||
base.Setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Cleanup()
|
|
||||||
{
|
|
||||||
_testObjects = null;
|
|
||||||
base.Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PrepareData<T>(T benchmarkType)
|
|
||||||
{
|
|
||||||
switch (benchmarkType)
|
|
||||||
{
|
|
||||||
case DelegateVsComparerPocBenchmarkType.ListOfBoxedStructs:
|
|
||||||
_testListOfBoxedStructs = _testObjects!.Select(_ => (IComparer<long>)new MyComparerStruct<long>());
|
|
||||||
break;
|
|
||||||
case DelegateVsComparerPocBenchmarkType.ListOfClasses:
|
|
||||||
_testListOfClasses = _testObjects!.Select(_ => new MyComparer());
|
|
||||||
break;
|
|
||||||
case DelegateVsComparerPocBenchmarkType.ListOfComparers:
|
|
||||||
_testListOfComparers = _testObjects!.Select(_ => (IComparer<long>)new MyComparer());
|
|
||||||
break;
|
|
||||||
case DelegateVsComparerPocBenchmarkType.ListOfDelegates:
|
|
||||||
_testListOfDelegates = _testObjects!.Select(_ => new Compare<long>((x, y) => new MyComparer().Compare(x, y)));
|
|
||||||
break;
|
|
||||||
case DelegateVsComparerPocBenchmarkType.ListOfFuncs:
|
|
||||||
_testListOfFunctions = _testObjects!.Select<long, Func<long, long, int>>(static _ => new MyComparer().Compare);
|
|
||||||
break;
|
|
||||||
case DelegateVsComparerPocBenchmarkType.ListOfLocalFuncs:
|
|
||||||
_testListOfLocalFunctions = GetLocalFunctions(_testObjects!);
|
|
||||||
break;
|
|
||||||
case DelegateVsComparerPocBenchmarkType.ListOfStructs:
|
|
||||||
_testListOfStructs = _testObjects!.Select(_ => new MyComparerStruct<long>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Recyclable.CollectionsTests;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
|
|
||||||
public class EqualVsContainsInConsecutiveOrderBenchmarks : IDisposable
|
|
||||||
{
|
|
||||||
private const int TestItemsCount = 10000;
|
|
||||||
|
|
||||||
private readonly IEnumerable<long> _list1 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToRecyclableList();
|
|
||||||
private readonly IEnumerable<long> _list2 = RecyclableLongListTestData.CreateTestData(TestItemsCount).ToArray();
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void Equal()
|
|
||||||
{
|
|
||||||
_ = _list1.Should().Equal(_list2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void ContainsInConsecutiveOrder()
|
|
||||||
{
|
|
||||||
_ = _list1.Should().ContainInConsecutiveOrder(_list2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_list1 is IDisposable disposable1)
|
|
||||||
{
|
|
||||||
disposable1.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_list2 is IDisposable disposable2)
|
|
||||||
{
|
|
||||||
disposable2.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-57
@@ -1,57 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
#pragma warning disable CA1822
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public class GreaterOperatorVsDoesntEqualOperatorBenchmarks
|
|
||||||
{
|
|
||||||
private static readonly long num = RecyclableDefaults.MinItemsCountForParallelization;
|
|
||||||
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
Console.WriteLine($"*** We're comparing if {num} is < 0 ***");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cleanup() { }
|
|
||||||
|
|
||||||
#pragma warning disable RCS1163, IDE0060
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private static void DoNothing<T>(T item)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning restore RCS1163
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void CompareWithGreater()
|
|
||||||
{
|
|
||||||
DoNothing(num > 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void CompareWithGreaterEqual()
|
|
||||||
{
|
|
||||||
DoNothing(num >= 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void CompareWithDoesntEqual()
|
|
||||||
{
|
|
||||||
DoNothing(num != 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void CompareWithLess()
|
|
||||||
{
|
|
||||||
DoNothing(num < 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void CompareWithEqual()
|
|
||||||
{
|
|
||||||
DoNothing(num == 0 ? 0 : 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
#pragma warning disable CA1822
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public class LessOperatorVsAndOperatorBenchmarks
|
|
||||||
{
|
|
||||||
private const int bitShift = 1 << 31;
|
|
||||||
private static readonly Random _rnd = new(1);
|
|
||||||
private static int num = -Math.Abs(_rnd.Next());
|
|
||||||
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
num = -1;
|
|
||||||
Console.WriteLine($"*** We're comparing if {num} is < 0 ***");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cleanup() { }
|
|
||||||
|
|
||||||
#pragma warning disable RCS1163, IDE0060
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private static void DoNothing<T>(T item)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning restore RCS1163
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void CompareWithLess()
|
|
||||||
{
|
|
||||||
DoNothing(num < 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void CompareWithAnd()
|
|
||||||
{
|
|
||||||
DoNothing((num & bitShift) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void CompareWithEqual()
|
|
||||||
{
|
|
||||||
DoNothing(num == -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void CompareToZero()
|
|
||||||
{
|
|
||||||
DoNothing(num == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,376 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.Intrinsics;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
|
|
||||||
#pragma warning disable xUnit1026, RCS1163, IDE0060
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class ModuloPocBenchmarks
|
|
||||||
{
|
|
||||||
public static void Run()
|
|
||||||
{
|
|
||||||
var benchmark = new ModuloPocBenchmarks();
|
|
||||||
benchmark.Setup();
|
|
||||||
benchmark.AddWithFor();
|
|
||||||
benchmark.AddWithForOptimized();
|
|
||||||
benchmark.DividerAndModulusWithOperator();
|
|
||||||
benchmark.MathDivRem();
|
|
||||||
benchmark.MixedApproachWithForLoopOptimized();
|
|
||||||
benchmark.MixedApproachWithSubtractWithStackalloc();
|
|
||||||
benchmark.MixedApproachWithSubtractWithWhile();
|
|
||||||
benchmark.MixedApproachWithSubtractWithWhileAsVector();
|
|
||||||
benchmark.MixedApproachWithSubtractWithWhileWithSse2();
|
|
||||||
benchmark.SubtractWithWhile();
|
|
||||||
benchmark.SubtractWithWhileWithSse2();
|
|
||||||
benchmark.XDivYMulX();
|
|
||||||
benchmark.XDivYMulXWithLocalVar();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long _cutoff;
|
|
||||||
private Vector128<long> _increment;
|
|
||||||
private long _lastCaseIndex;
|
|
||||||
|
|
||||||
[GlobalSetup]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
_cutoff = checked((long)BlockSize << 4);
|
|
||||||
_increment = Vector128.Create(-BlockSize, 1L);
|
|
||||||
_lastCaseIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DoNothing<T>(T result, T remainder, [CallerMemberName] string? callerName = null)
|
|
||||||
{
|
|
||||||
//Console.WriteLine($"{callerName}: result = {result}, remainder = {remainder}");
|
|
||||||
}
|
|
||||||
|
|
||||||
//[Params(123, 10, 2, int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 10, int.MaxValue / 15, int.MaxValue / 20)]
|
|
||||||
[Params(int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 8, int.MaxValue / 10, int.MaxValue / 100)]
|
|
||||||
//[Params(int.MaxValue / 10, int.MaxValue / 20, int.MaxValue / 100)]
|
|
||||||
public int BlockSize { get; set; } = int.MaxValue;
|
|
||||||
|
|
||||||
[Params(int.MaxValue, int.MaxValue / 1000, int.MaxValue / 10000)]
|
|
||||||
//[Params(int.MaxValue)]
|
|
||||||
//[Params(int.MaxValue * 100L)]
|
|
||||||
public long TestNumber { get; set; } = int.MaxValue;
|
|
||||||
|
|
||||||
private void CalculateDividerAndModulusWithOperator(long i)
|
|
||||||
{
|
|
||||||
DoNothing(i / BlockSize, i % BlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithMathDivRem(long i)
|
|
||||||
{
|
|
||||||
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithAddWithFor(long i)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
int remainder = 0;
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
for (long iteration = 0; iteration < i; iteration++)
|
|
||||||
{
|
|
||||||
if (remainder > blockSize - 1)
|
|
||||||
{
|
|
||||||
remainder = 0;
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(result, remainder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithAddWithForOptimized(long i)
|
|
||||||
{
|
|
||||||
long total = 0;
|
|
||||||
int result = 0;
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
while (total + blockSize <= i)
|
|
||||||
{
|
|
||||||
total += blockSize;
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(result, i - total);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithXDivYMulX(long i)
|
|
||||||
{
|
|
||||||
DoNothing(i / BlockSize, i - (i / BlockSize * BlockSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithXDivYMulXWithLocalVar(long i)
|
|
||||||
{
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
int result = (int)(i / blockSize);
|
|
||||||
DoNothing(result, i - (result * blockSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithSubtractWithWhile(long i)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
long remainder = i;
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
while (remainder >= blockSize)
|
|
||||||
{
|
|
||||||
remainder -= blockSize;
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(result, remainder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithSubtractWithWhileWithSse2(long i)
|
|
||||||
{
|
|
||||||
var state = Vector128.Create(i, 0);
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
var inc = _increment;
|
|
||||||
while (state.GetElement(0) >= blockSize)
|
|
||||||
{
|
|
||||||
state = Sse2.Add(state, inc);
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(state.GetElement(1), state.GetElement(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithMixedApproachWithSubtractWithWhile(long i)
|
|
||||||
{
|
|
||||||
long remainder;
|
|
||||||
if (i >= _cutoff)
|
|
||||||
{
|
|
||||||
DoNothing(Math.DivRem(i, BlockSize, out remainder), remainder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
remainder = i;
|
|
||||||
while (remainder >= blockSize)
|
|
||||||
{
|
|
||||||
remainder -= blockSize;
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(result, remainder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithMixedApproachWithSubtractWithWhileAsVector(long i)
|
|
||||||
{
|
|
||||||
if (i >= _cutoff)
|
|
||||||
{
|
|
||||||
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var state = new Vector<long>(new[] { 0L, i, 0, 0 });
|
|
||||||
var increment = new Vector<long>(new[] { 1L, -BlockSize, 0, 0 });
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
while (state[1] >= blockSize)
|
|
||||||
{
|
|
||||||
state += increment;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(state[0], state[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithMixedApproachWithSubtractWithWhileWithSse2(long i)
|
|
||||||
{
|
|
||||||
if (i >= _cutoff)
|
|
||||||
{
|
|
||||||
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var increment = _increment;
|
|
||||||
var state = Vector128.Create(i, 0);
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
while (state.GetElement(0) >= blockSize)
|
|
||||||
{
|
|
||||||
state = Sse2.Add(state, increment);
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(state.GetElement(1), state.GetElement(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithMixedApproachWithSubtractWithStackalloc(long i)
|
|
||||||
{
|
|
||||||
if (i >= _cutoff)
|
|
||||||
{
|
|
||||||
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Span<long> state = stackalloc long[2] { i, 0 };// Vector128.Create(BlockSize, 0);
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
while (state[0] >= blockSize)
|
|
||||||
{
|
|
||||||
state[0] -= blockSize;
|
|
||||||
state[1]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(state[1], state[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateWithMixedApproachWithForLoopOptimized(long i)
|
|
||||||
{
|
|
||||||
long total;
|
|
||||||
if (i >= _cutoff)
|
|
||||||
{
|
|
||||||
DoNothing(Math.DivRem(i, BlockSize, out total), total);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
total = 0;
|
|
||||||
int result = 0;
|
|
||||||
int blockSize = BlockSize;
|
|
||||||
while (total + blockSize <= i)
|
|
||||||
{
|
|
||||||
total += blockSize;
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DoNothing(result, i - total);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void DividerAndModulusWithOperator()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateDividerAndModulusWithOperator(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void MathDivRem()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithMathDivRem(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void AddWithFor()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithAddWithFor(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void AddWithForOptimized()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithAddWithForOptimized(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void XDivYMulX()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithXDivYMulX(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void XDivYMulXWithLocalVar()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithXDivYMulXWithLocalVar(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void SubtractWithWhile()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithSubtractWithWhile(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void SubtractWithWhileWithSse2()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithSubtractWithWhileWithSse2(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void MixedApproachWithSubtractWithWhile()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithMixedApproachWithSubtractWithWhile(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void MixedApproachWithSubtractWithWhileAsVector()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithMixedApproachWithSubtractWithWhileAsVector(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void MixedApproachWithSubtractWithWhileWithSse2()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithMixedApproachWithSubtractWithWhileWithSse2(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void MixedApproachWithSubtractWithStackalloc()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithMixedApproachWithSubtractWithStackalloc(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void MixedApproachWithForLoopOptimized()
|
|
||||||
{
|
|
||||||
long testNumber = TestNumber;
|
|
||||||
for (long i = testNumber; i >= _lastCaseIndex; i--)
|
|
||||||
{
|
|
||||||
CalculateWithMixedApproachWithForLoopOptimized(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public abstract class PocBenchmarkBase<TBenchmarkType>
|
|
||||||
{
|
|
||||||
public virtual TBenchmarkType? DataType { get; set; }
|
|
||||||
public virtual TBenchmarkType? BaseDataType { get; set; }
|
|
||||||
|
|
||||||
private Action? _testMethod;
|
|
||||||
protected Action TestMethod
|
|
||||||
{
|
|
||||||
get => _testMethod ?? throw new OperationCanceledException("Skip - No actual defined");
|
|
||||||
set => _testMethod = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Action? _baselineMethod;
|
|
||||||
protected Action BaselineMethod
|
|
||||||
{
|
|
||||||
get => _baselineMethod ?? throw new OperationCanceledException("Skip - No baseline defined");
|
|
||||||
set => _baselineMethod = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void Baseline() => BaselineMethod.Invoke();
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Actual() => TestMethod.Invoke();
|
|
||||||
|
|
||||||
// 0 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524_288
|
|
||||||
// 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912
|
|
||||||
// 1073741824 2147483648
|
|
||||||
|
|
||||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
|
|
||||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
|
|
||||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 100_000_000)]
|
|
||||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 850_000 - 1, 1_000_000, RecyclableDefaults.MaxPooledBlockSize)]
|
|
||||||
// [Params(1_000_000, 10_000_000, 100_000_000, 1_000_000_000)]
|
|
||||||
// [Params(1, 10, 64, 128, 192, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144)]
|
|
||||||
// [Params(524_288, 786_432, 1_048_576)]
|
|
||||||
// [Params(262_144, 393_216, 524_288)]
|
|
||||||
// [Params(850_000_000)]
|
|
||||||
// [Params(100_000_000)]
|
|
||||||
// [Params(RecyclableDefaults.MinItemsCountForParallelization)]
|
|
||||||
// [Params(127)]
|
|
||||||
// [Params(16)]
|
|
||||||
// [Params(10, 15, 128)]
|
|
||||||
// [Params(1)]
|
|
||||||
// [Params(RecyclableDefaults.MaxPooledBlockSize)]
|
|
||||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 10_000_000, 33_554_432, 50_000_000, 100_000_000)]
|
|
||||||
// *** [Params(RecyclableDefaults.MinPooledArrayLength - 3, RecyclableDefaults.MinPooledArrayLength - 2, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 2, RecyclableDefaults.MinPooledArrayLength + 3)]
|
|
||||||
[Params(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 70, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)]
|
|
||||||
// *** [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 70, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)]
|
|
||||||
// [Params(256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, RecyclableDefaults.MinItemsCountForParallelization)]
|
|
||||||
// [Params(RecyclableDefaults.MinItemsCountForParallelization, 1_048_576, 2_097_152, 4_194_304, 8_388_608, 16_777_216, 33_554_432, RecyclableDefaults.MaxPooledBlockSize)]
|
|
||||||
// *** [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, 40, 50, 60, 64, 70, 80, 90, 100, 127, 128)]
|
|
||||||
// [Params(1, 10, 1024, 10240, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinItemsCountForParallelization)]
|
|
||||||
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
|
|
||||||
// [Params(1_048_576)]
|
|
||||||
public virtual int TestObjectCount { get; set; } = 0; //8_192;//131_072; //1; //33_554_432; //524_288; //131_072;
|
|
||||||
public virtual int TestObjectCountForSlowMethods { get; private set; }
|
|
||||||
|
|
||||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)]
|
|
||||||
// [Params(10, 20, 50, 100)]
|
|
||||||
// [Params(1, 10)]
|
|
||||||
public int Divider = 10;
|
|
||||||
//public int BlockSize = 1_048_576;//int.MaxValue - 1;
|
|
||||||
public int BlockSize { get; private set; }
|
|
||||||
|
|
||||||
public byte BlockSizePow2BitShift => checked((byte)(31 - BitOperations.LeadingZeroCount((uint)BlockSize)));
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
protected static void DoNothing<T>(in T item)
|
|
||||||
{
|
|
||||||
_ = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Exception CreateUnknownBenchmarkTypeException<T>(T benchmarkType)
|
|
||||||
{
|
|
||||||
return new InvalidOperationException($"******* UNKNOWN BENCHMARK TYPE {{{benchmarkType}}} *******");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Exception CreateMethodNotFoundException(in string methodName, in string? className)
|
|
||||||
{
|
|
||||||
return new MethodAccessException($"******* METHOD {{{methodName}}} NOT FOUND IN CLASS {{{className}}} *******");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void PrepareData<T>(T benchmarkType) { }
|
|
||||||
protected virtual Action? GetTestMethod(TBenchmarkType? benchmarkType) => null;
|
|
||||||
|
|
||||||
[GlobalCleanup]
|
|
||||||
public virtual void Cleanup()
|
|
||||||
{
|
|
||||||
_baselineMethod = null;
|
|
||||||
_testMethod = null;
|
|
||||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[GlobalSetup]
|
|
||||||
public virtual void Setup()
|
|
||||||
{
|
|
||||||
Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******");
|
|
||||||
|
|
||||||
TestObjectCountForSlowMethods = (TestObjectCount / 2) + 1;
|
|
||||||
|
|
||||||
BlockSize = TestObjectCount switch
|
|
||||||
{
|
|
||||||
0 => 1,
|
|
||||||
> 0 and <= 10_240 => TestObjectCount,
|
|
||||||
_ => TestObjectCount / Divider > 0 ? TestObjectCount / Divider : TestObjectCount
|
|
||||||
};
|
|
||||||
|
|
||||||
if (BlockSize > 0)
|
|
||||||
{
|
|
||||||
BlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)BlockSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"******* BaseDataType = {BaseDataType}; default = {default(TBenchmarkType)} *******");
|
|
||||||
|
|
||||||
if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, default))
|
|
||||||
{
|
|
||||||
Console.WriteLine("******* SETTING UP BASELINE DATA *******");
|
|
||||||
PrepareData(BaseDataType);
|
|
||||||
_baselineMethod = GetTestMethod(BaseDataType);
|
|
||||||
}
|
|
||||||
|
|
||||||
//~ We don't want to prepare data if baseline data is the same is benchmark data.
|
|
||||||
//~ One would override another - waste of time & resources.
|
|
||||||
if (!EqualityComparer<TBenchmarkType>.Default.Equals(BaseDataType, DataType))
|
|
||||||
{
|
|
||||||
Console.WriteLine("******* SETTING UP TEST CASE DATA *******");
|
|
||||||
//~ If BaseDataType == null, then we'll come here only if DataType != null.
|
|
||||||
PrepareData(DataType!);
|
|
||||||
_testMethod = GetTestMethod(DataType);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("******* DATA PREPARED *******");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using Collections.Pooled;
|
|
||||||
using Recyclable.Collections.Benchmarks.Core;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public partial class RecyclableLongListPocBenchmarks<T> : PocBenchmarkBase<T>
|
|
||||||
{
|
|
||||||
private long[]? _testArray;
|
|
||||||
public long[] TestObjects => _testArray ?? throw new NullReferenceException("Something is wrong and test objects are null");
|
|
||||||
|
|
||||||
public RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testRecyclableLongList ?? throw new NullReferenceException($"Something is wrong and {nameof(RecyclableLongList<long>)} is null");
|
|
||||||
private RecyclableLongList<long>? _testRecyclableLongList;
|
|
||||||
private PooledList<long>? _testPooledList;
|
|
||||||
internal PooledList<long> TestPooledList => _testPooledList ?? throw new NullReferenceException($"Something is wrong and {nameof(PooledList<long>)} is null");
|
|
||||||
|
|
||||||
private static readonly MethodInfo _ensureCapacityNewFunc;
|
|
||||||
|
|
||||||
static RecyclableLongListPocBenchmarks()
|
|
||||||
{
|
|
||||||
_ensureCapacityNewFunc = typeof(RecyclableLongList<long>).GetMethod("EnsureCapacity", BindingFlags.Static | BindingFlags.NonPublic)
|
|
||||||
?? throw new NullReferenceException($"Method EnsureCapacity not found in class {nameof(RecyclableLongList<long>)}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Setup()
|
|
||||||
{
|
|
||||||
Console.WriteLine("******* SETTING UP EXPECTED RESULTS DATA *******");
|
|
||||||
_testArray = DataGenerator.EnumerateTestObjects(TestObjectCount);
|
|
||||||
|
|
||||||
base.Setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Cleanup()
|
|
||||||
{
|
|
||||||
_testArray = null;
|
|
||||||
_testRecyclableLongList?.Dispose();
|
|
||||||
_testRecyclableLongList = null;
|
|
||||||
_testPooledList?.Dispose();
|
|
||||||
_testPooledList = null;
|
|
||||||
|
|
||||||
base.Cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
BenchmarkDotNet=v0.13.3, OS=ubuntu 20.04
|
|
||||||
Intel Xeon Platinum 8272CL CPU 2.60GHz, 1 CPU, 4 logical and 2 physical cores
|
|
||||||
.NET SDK=7.0.203
|
|
||||||
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
|
|
||||||
DefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
|
|
||||||
|
|
||||||
|
|
||||||
| Method | Mean | Error | StdDev | Ratio | RatioSD |
|
|
||||||
|------------------------- |--------:|---------:|---------:|------:|--------:|
|
|
||||||
| RecyclableLongList_IndexOfV1 | 2.274 s | 0.0447 s | 0.0894 s | 1.00 | 0.04 |
|
|
||||||
| RecyclableLongList_IndexOfV2 | 2.302 s | 0.0456 s | 0.0750 s | 1.00 | 0.00 |
|
|
||||||
| RecyclableLongList_IndexOf | 2.234 s | 0.0446 s | 0.0681 s | 0.97 | 0.05 |
|
|
||||||
|
|
||||||
// * Hints *
|
|
||||||
Outliers
|
|
||||||
RecyclableLongListPocBenchmarks.RecyclableLongList_IndexOfV2: Default -> 2 outliers were removed (2.54 s, 2.56 s)
|
|
||||||
RecyclableLongListPocBenchmarks.RecyclableLongList_IndexOf: Default -> 1 outlier was detected (2.06 s)
|
|
||||||
|
|
||||||
// * Legends *
|
|
||||||
Mean : Arithmetic mean of all measurements
|
|
||||||
Error : Half of 99.9% confidence interval
|
|
||||||
StdDev : Standard deviation of all measurements
|
|
||||||
Ratio : Mean of the ratio distribution ([Current]/[Baseline])
|
|
||||||
RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline])
|
|
||||||
1 s : 1 Second (1 sec)
|
|
||||||
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
#pragma warning disable RCS1085
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public class RefVsInstanceMemberPocBenchmarks
|
|
||||||
{
|
|
||||||
public static void Run()
|
|
||||||
{
|
|
||||||
var benchmark = new RefVsInstanceMemberPocBenchmarks();
|
|
||||||
benchmark.ArrayInstanceMember2();
|
|
||||||
benchmark.ArrayInstanceMember3();
|
|
||||||
benchmark.ArrayInstanceMember4();
|
|
||||||
benchmark.ArrayInstanceMember5();
|
|
||||||
benchmark.ArrayInstanceMember6();
|
|
||||||
benchmark.ArrayInstanceMemberLocalVar5();
|
|
||||||
benchmark.ArrayInstanceMemberLocalVar6();
|
|
||||||
benchmark.IntInstanceMember2();
|
|
||||||
benchmark.IntInstanceMember3();
|
|
||||||
benchmark.IntInstanceMember4();
|
|
||||||
benchmark.IntInstanceMember5();
|
|
||||||
benchmark.RefToArray2();
|
|
||||||
benchmark.RefToArray3();
|
|
||||||
benchmark.RefToArray4();
|
|
||||||
benchmark.RefToArray5();
|
|
||||||
benchmark.RefToArray6();
|
|
||||||
benchmark.RefToInt2();
|
|
||||||
benchmark.RefToInt3();
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly TestCase _testCase = new();
|
|
||||||
private static void DoNothing(int count)
|
|
||||||
{
|
|
||||||
_ = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestCase
|
|
||||||
{
|
|
||||||
private int[] _items = new int[123];
|
|
||||||
private int _count = 123;
|
|
||||||
private static int Add(int count)
|
|
||||||
{
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get => _count;
|
|
||||||
set => _count = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref int GetCountAsRefCount2()
|
|
||||||
{
|
|
||||||
ref int count = ref _count;
|
|
||||||
count = 234;
|
|
||||||
return ref count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref int GetCountAsRefCount3()
|
|
||||||
{
|
|
||||||
ref int count = ref _count;
|
|
||||||
count = 234;
|
|
||||||
_ = TestCase.Add(count);
|
|
||||||
return ref count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref int GetCountAsRefCount4()
|
|
||||||
{
|
|
||||||
ref int count = ref _count;
|
|
||||||
count = 234;
|
|
||||||
_ = TestCase.Add(count);
|
|
||||||
_ = TestCase.Add(count);
|
|
||||||
return ref count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref int GetCountAsRefCount5()
|
|
||||||
{
|
|
||||||
ref int count = ref _count;
|
|
||||||
count = 234;
|
|
||||||
_ = TestCase.Add(count);
|
|
||||||
_ = TestCase.Add(count);
|
|
||||||
_ = TestCase.Add(count);
|
|
||||||
return ref count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCountAsInstanceMember2()
|
|
||||||
{
|
|
||||||
_count = 234;
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCountAsInstanceMember3()
|
|
||||||
{
|
|
||||||
_count = 234;
|
|
||||||
_ = TestCase.Add(_count);
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCountAsInstanceMember4()
|
|
||||||
{
|
|
||||||
_count = 234;
|
|
||||||
_ = TestCase.Add(_count);
|
|
||||||
_ = TestCase.Add(_count);
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCountAsInstanceMember5()
|
|
||||||
{
|
|
||||||
_count = 234;
|
|
||||||
_ = TestCase.Add(_count);
|
|
||||||
_ = TestCase.Add(_count);
|
|
||||||
_ = TestCase.Add(_count);
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsArray2()
|
|
||||||
{
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
return _items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsArray3()
|
|
||||||
{
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
return _items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsArray4()
|
|
||||||
{
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
return _items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsArray5()
|
|
||||||
{
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
return _items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsArray6()
|
|
||||||
{
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
_ = TestCase.Add(_items.Length);
|
|
||||||
return _items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsArrayLocalVar5()
|
|
||||||
{
|
|
||||||
var items = _items;
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
return items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsArrayLocalVar6()
|
|
||||||
{
|
|
||||||
var items = _items;
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
return items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsRefArray2()
|
|
||||||
{
|
|
||||||
ref int[] items = ref _items;
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
return items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsRefArray3()
|
|
||||||
{
|
|
||||||
ref int[] items = ref _items;
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
return items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsRefArray4()
|
|
||||||
{
|
|
||||||
ref int[] items = ref _items;
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
return items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsRefArray5()
|
|
||||||
{
|
|
||||||
ref int[] items = ref _items;
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
return items.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetLengthAsRefArray6()
|
|
||||||
{
|
|
||||||
ref int[] items = ref _items;
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
_ = TestCase.Add(items.Length);
|
|
||||||
return items.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToInt2()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsRefCount2();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToInt3()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsRefCount3();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToInt4()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsRefCount4();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToInt5()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsRefCount5();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void IntInstanceMember2()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsInstanceMember2();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void IntInstanceMember3()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsInstanceMember3();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void IntInstanceMember4()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsInstanceMember4();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void IntInstanceMember5()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetCountAsInstanceMember5();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToArray2()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsRefArray2();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToArray3()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsRefArray3();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToArray4()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsRefArray4();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToArray5()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsRefArray5();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void RefToArray6()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsRefArray6();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void ArrayInstanceMember2()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsArray2();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void ArrayInstanceMember3()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsArray3();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void ArrayInstanceMember4()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsArray4();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void ArrayInstanceMember5()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsArray5();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void ArrayInstanceMember6()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsArray6();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void ArrayInstanceMemberLocalVar5()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsArrayLocalVar5();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
|
||||||
public void ArrayInstanceMemberLocalVar6()
|
|
||||||
{
|
|
||||||
var count = _testCase.GetLengthAsArrayLocalVar6();
|
|
||||||
DoNothing(count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public enum RoundBenchmarkType
|
|
||||||
{
|
|
||||||
MathFloor,
|
|
||||||
MathRound,
|
|
||||||
WithTypecasting,
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RoundBenchmarks : PocBenchmarkBase<RoundBenchmarkType>
|
|
||||||
{
|
|
||||||
[Params(RoundBenchmarkType.MathFloor)]
|
|
||||||
public override RoundBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
|
||||||
|
|
||||||
[Params(RoundBenchmarkType.MathRound, RoundBenchmarkType.WithTypecasting)]
|
|
||||||
public override RoundBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
|
|
||||||
|
|
||||||
private const double OptimalParallelSearchStep = 0.329;
|
|
||||||
|
|
||||||
public void RoundWithMathFloor()
|
|
||||||
{
|
|
||||||
DoNothing((long)Math.Floor(TestObjectCount * OptimalParallelSearchStep));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RoundWithMathRound()
|
|
||||||
{
|
|
||||||
DoNothing((long)Math.Round(TestObjectCount * OptimalParallelSearchStep, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RoundWithTypeCasting()
|
|
||||||
{
|
|
||||||
DoNothing((long)(TestObjectCount * OptimalParallelSearchStep));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Action GetTestMethod(RoundBenchmarkType benchmarkType) => benchmarkType switch
|
|
||||||
{
|
|
||||||
RoundBenchmarkType.MathFloor => RoundWithMathFloor,
|
|
||||||
RoundBenchmarkType.MathRound => RoundWithMathRound,
|
|
||||||
RoundBenchmarkType.WithTypecasting => RoundWithTypeCasting,
|
|
||||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
static class RoundUpToPowerOf2Alternatives
|
|
||||||
{
|
|
||||||
private static readonly int[] PowersOfTwo =
|
|
||||||
{
|
|
||||||
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
|
|
||||||
2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576,
|
|
||||||
2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824
|
|
||||||
};
|
|
||||||
|
|
||||||
public static int BinarySearch(int value)
|
|
||||||
{
|
|
||||||
if (value <= 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Input must be a positive integer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the smallest power of 2 greater than or equal to value using a lookup table
|
|
||||||
int index = Array.BinarySearch(PowersOfTwo, value);
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
// If value is not a power of 2, use the next power of 2 from the table
|
|
||||||
index = ~index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PowersOfTwo[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int RightBitShifting(int value)
|
|
||||||
{
|
|
||||||
// Decrease value by 1 to handle the case where the input is already a power of 2
|
|
||||||
value--;
|
|
||||||
|
|
||||||
value |= value >> 1;
|
|
||||||
value |= value >> 2;
|
|
||||||
value |= value >> 4;
|
|
||||||
value |= value >> 8;
|
|
||||||
value |= value >> 16;
|
|
||||||
|
|
||||||
return value + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int LeftBitShifting(int value)
|
|
||||||
{
|
|
||||||
if (value == 0)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure value is a power of 2 or the next power of 2
|
|
||||||
int result = 1;
|
|
||||||
while (result < value)
|
|
||||||
{
|
|
||||||
result = checked(result << 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RoundUpToPowerOf2BenchmarkType
|
|
||||||
{
|
|
||||||
Undefined,
|
|
||||||
BitOperations,
|
|
||||||
RightBitShifting,
|
|
||||||
LeftBitShifting,
|
|
||||||
BinarySearch,
|
|
||||||
IsPowOf2,
|
|
||||||
IsPowOf2_GreaterThanFirst,
|
|
||||||
IsPowOf2_WithIf,
|
|
||||||
IsPowOf2_GreaterEqual,
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RoundUpToPowerOf2vsOtherBenchmarks : PocBenchmarkBase<RoundUpToPowerOf2BenchmarkType>
|
|
||||||
{
|
|
||||||
// [Params(-850_000, 850_000)]
|
|
||||||
[Params(-int.MaxValue, int.MaxValue)]
|
|
||||||
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
|
|
||||||
|
|
||||||
[Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2)]
|
|
||||||
public override RoundUpToPowerOf2BenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
|
||||||
|
|
||||||
[Params(RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst, RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf, RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual)]
|
|
||||||
// [Params(RoundUpToPowerOf2BenchmarkType.RightBitShifting, RoundUpToPowerOf2BenchmarkType.LeftBitShifting, RoundUpToPowerOf2BenchmarkType.BinarySearch)]
|
|
||||||
public override RoundUpToPowerOf2BenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
|
|
||||||
|
|
||||||
private void BitOperations()
|
|
||||||
{
|
|
||||||
DoNothing(System.Numerics.BitOperations.RoundUpToPowerOf2((uint)base.TestObjectCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RightBitShifting()
|
|
||||||
{
|
|
||||||
DoNothing(RoundUpToPowerOf2Alternatives.RightBitShifting(TestObjectCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LeftBitShifting()
|
|
||||||
{
|
|
||||||
DoNothing(RoundUpToPowerOf2Alternatives.LeftBitShifting(TestObjectCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BinarySearch()
|
|
||||||
{
|
|
||||||
DoNothing(RoundUpToPowerOf2Alternatives.BinarySearch(TestObjectCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void IsPowOf2()
|
|
||||||
{
|
|
||||||
DoNothing((TestObjectCount & (TestObjectCount - 1)) == 0 && TestObjectCount > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void IsPowOf2_GreaterThanFirst()
|
|
||||||
{
|
|
||||||
DoNothing(TestObjectCount > 0 && (TestObjectCount & (TestObjectCount - 1)) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void IsPowOf2_WithIf()
|
|
||||||
{
|
|
||||||
DoNothing(TestObjectCount > 0 ? (TestObjectCount & (TestObjectCount - 1)) == 0 : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void IsPowOf2_GreaterEqual()
|
|
||||||
{
|
|
||||||
DoNothing(TestObjectCount >= 0 && TestObjectCount != 0 && (TestObjectCount & (TestObjectCount - 1)) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Action? GetTestMethod(RoundUpToPowerOf2BenchmarkType benchmarkType) => benchmarkType switch
|
|
||||||
{
|
|
||||||
RoundUpToPowerOf2BenchmarkType.BitOperations => BitOperations,
|
|
||||||
RoundUpToPowerOf2BenchmarkType.RightBitShifting => RightBitShifting,
|
|
||||||
RoundUpToPowerOf2BenchmarkType.LeftBitShifting => LeftBitShifting,
|
|
||||||
RoundUpToPowerOf2BenchmarkType.BinarySearch => BinarySearch,
|
|
||||||
RoundUpToPowerOf2BenchmarkType.IsPowOf2 => IsPowOf2,
|
|
||||||
RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterThanFirst => IsPowOf2_GreaterThanFirst,
|
|
||||||
RoundUpToPowerOf2BenchmarkType.IsPowOf2_WithIf => IsPowOf2_WithIf,
|
|
||||||
RoundUpToPowerOf2BenchmarkType.IsPowOf2_GreaterEqual => IsPowOf2_GreaterEqual,
|
|
||||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override void PrepareData<T>(T benchmarkType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public enum SpanConstructorVsTypecastingPocBenchmarkType
|
|
||||||
{
|
|
||||||
Unknown,
|
|
||||||
Constructor,
|
|
||||||
TypeCasting,
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class SpanConstructorVsTypecastingPocBenchmarks : PocBenchmarkBase<SpanConstructorVsTypecastingPocBenchmarkType>
|
|
||||||
{
|
|
||||||
private int[]? TestData { get; set; }
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
|
||||||
private void DoNothing(ReadOnlySpan<int> span) { }
|
|
||||||
|
|
||||||
[Params(SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting)]
|
|
||||||
public override SpanConstructorVsTypecastingPocBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
|
||||||
|
|
||||||
[Params(SpanConstructorVsTypecastingPocBenchmarkType.Constructor)]
|
|
||||||
public override SpanConstructorVsTypecastingPocBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
|
|
||||||
|
|
||||||
[Params(8)]
|
|
||||||
public override int TestObjectCount { get => base.TestObjectCount; set => base.TestObjectCount = value; }
|
|
||||||
|
|
||||||
public static void Run()
|
|
||||||
{
|
|
||||||
var benchmark = new SpanConstructorVsTypecastingPocBenchmarks();
|
|
||||||
benchmark.Setup();
|
|
||||||
benchmark.Constructor();
|
|
||||||
benchmark.TypeCasting();
|
|
||||||
benchmark.Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Constructor()
|
|
||||||
{
|
|
||||||
ReadOnlySpan<int> span = new(TestData);
|
|
||||||
DoNothing(span);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TypeCasting()
|
|
||||||
{
|
|
||||||
ReadOnlySpan<int> span = TestData;
|
|
||||||
DoNothing(span);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Setup()
|
|
||||||
{
|
|
||||||
TestData = new int[TestObjectCount];
|
|
||||||
base.Setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Action? GetTestMethod(SpanConstructorVsTypecastingPocBenchmarkType benchmarkType) => benchmarkType switch
|
|
||||||
{
|
|
||||||
SpanConstructorVsTypecastingPocBenchmarkType.Constructor => Constructor,
|
|
||||||
SpanConstructorVsTypecastingPocBenchmarkType.TypeCasting => TypeCasting,
|
|
||||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
public enum SpanVsArrayBenchmarkType
|
|
||||||
{
|
|
||||||
ArrayGetWithVar,
|
|
||||||
ArrayGetWithField,
|
|
||||||
SpanGet,
|
|
||||||
ArraySetWithVar,
|
|
||||||
ArraySetWithField,
|
|
||||||
SpanSet
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SpanVsArrayBenchmarks : RecyclableBenchmarkBase<SpanVsArrayBenchmarkType>
|
|
||||||
{
|
|
||||||
// [Params(SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet)]
|
|
||||||
// [Params(SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet)]
|
|
||||||
[Params
|
|
||||||
(
|
|
||||||
SpanVsArrayBenchmarkType.ArrayGetWithVar, SpanVsArrayBenchmarkType.SpanGet,
|
|
||||||
SpanVsArrayBenchmarkType.ArraySetWithField, SpanVsArrayBenchmarkType.ArraySetWithVar, SpanVsArrayBenchmarkType.SpanSet
|
|
||||||
)]
|
|
||||||
public override SpanVsArrayBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
|
|
||||||
|
|
||||||
[Params(SpanVsArrayBenchmarkType.ArrayGetWithField)]
|
|
||||||
// [Params(SpanVsArrayBenchmarkType.ArraySetWithField)]
|
|
||||||
public override SpanVsArrayBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
|
||||||
|
|
||||||
protected override Action GetTestMethod(SpanVsArrayBenchmarkType benchmarkType) => benchmarkType switch
|
|
||||||
{
|
|
||||||
SpanVsArrayBenchmarkType.ArrayGetWithVar => ArrayGetWithVar,
|
|
||||||
SpanVsArrayBenchmarkType.ArrayGetWithField => ArrayGetWithField,
|
|
||||||
SpanVsArrayBenchmarkType.SpanGet => SpanGet,
|
|
||||||
SpanVsArrayBenchmarkType.ArraySetWithVar => ArraySetWithVar,
|
|
||||||
SpanVsArrayBenchmarkType.ArraySetWithField => ArraySetWithField,
|
|
||||||
SpanVsArrayBenchmarkType.SpanSet => SpanSet,
|
|
||||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
|
||||||
};
|
|
||||||
|
|
||||||
public void ArrayGetWithVar()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (long i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ArrayGetWithField()
|
|
||||||
{
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (long i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(_testObjects![i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SpanGet()
|
|
||||||
{
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
Span<long> data = new(_testObjects, 0, dataCount);
|
|
||||||
for (int i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
DoNothing(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ArraySetWithVar()
|
|
||||||
{
|
|
||||||
var data = TestObjects;
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (long i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
data[i] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ArraySetWithField()
|
|
||||||
{
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
for (long i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
_testObjects![i] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SpanSet()
|
|
||||||
{
|
|
||||||
var dataCount = TestObjectCount;
|
|
||||||
Span<long> data = new(_testObjects, 0, dataCount);
|
|
||||||
for (int i = 0; i < dataCount; i++)
|
|
||||||
{
|
|
||||||
data[i] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading.Tasks.Sources;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using Microsoft.Extensions.ObjectPool;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
|
||||||
{
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class TaskRunVsTaskFactoryStartNewBenchmarks
|
|
||||||
{
|
|
||||||
public int NumberOfTasks = 100;
|
|
||||||
private static readonly TaskFactory TaskPool = new();
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void OwnTaskFactory()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < NumberOfTasks; i++)
|
|
||||||
{
|
|
||||||
_ = TaskPool.StartNew(() => {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskRun()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < NumberOfTasks; i++)
|
|
||||||
{
|
|
||||||
_ = Task.Run(() => { });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
|
||||||
public void TaskFactoryStartNew()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < NumberOfTasks; i++)
|
|
||||||
{
|
|
||||||
_ = Task.Factory.StartNew(() => { });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskConstructor()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < NumberOfTasks; i++)
|
|
||||||
{
|
|
||||||
new Task(() => { }).Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskConstructorWithKnownScheduler()
|
|
||||||
{
|
|
||||||
TaskScheduler scheduler = TaskScheduler.Current;
|
|
||||||
for (var i = 0; i < NumberOfTasks; i++)
|
|
||||||
{
|
|
||||||
new Task(() => { }).Start(scheduler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using BenchmarkDotNet.Configs;
|
|
||||||
using BenchmarkDotNet.Running;
|
|
||||||
using Recyclable.Collections.Benchmarks.POC;
|
|
||||||
|
|
||||||
#pragma warning disable RCS1036, RCS1213, IDE0051
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public static class Program
|
|
||||||
{
|
|
||||||
static IConfig BenchmarkConfig { get; } = ManualConfig.Create(DefaultConfig.Instance)
|
|
||||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary);
|
|
||||||
|
|
||||||
static void RunAssemblyBenchmarks()
|
|
||||||
{
|
|
||||||
_ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" });
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RunRecyclableCollectionsBenchmarks()
|
|
||||||
{
|
|
||||||
_ = BenchmarkRunner.Run<RecyclableCollectionsBenchmarks>(BenchmarkConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RunSelectedBenchmarks()
|
|
||||||
{
|
|
||||||
// ****************
|
|
||||||
// *** Template ***
|
|
||||||
// ****************
|
|
||||||
var benchmark = new RecyclableCollectionsBenchmarks();
|
|
||||||
benchmark.Setup();
|
|
||||||
|
|
||||||
foreach (var _ in Enumerable.Range(1, 1000))
|
|
||||||
{
|
|
||||||
// benchmark.RecyclableList_Create_WithCapacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark.Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RunPocBenchmarks()
|
|
||||||
{
|
|
||||||
_ = BenchmarkRunner.Run<BoolOrComparePocBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<DelegateVsComparerPocBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<ModuloPocBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<TaskRunVsTaskFactoryStartNewBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<RefVsInstanceMemberPocBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<RoundBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<SpanVsArrayBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<LessOperatorVsAndOperatorBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<EqualVsContainsInConsecutiveOrderBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<RoundUpToPowerOf2vsOtherBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<GreaterOperatorVsDoesntEqualOperatorBenchmarks>(BenchmarkConfig);
|
|
||||||
_ = BenchmarkRunner.Run<SpanConstructorVsTypecastingPocBenchmarks>(BenchmarkConfig);
|
|
||||||
// ArraySizeLimitPocBenchmarks>(BenchmarkConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Main()
|
|
||||||
{
|
|
||||||
RunRecyclableCollectionsBenchmarks();
|
|
||||||
// RunPocBenchmarks();
|
|
||||||
// RunSelectedBenchmarks();
|
|
||||||
// RunAssemblyBenchmarks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"profiles": {
|
|
||||||
"WSL": {
|
|
||||||
"commandName": "WSL2",
|
|
||||||
"distributionName": ""
|
|
||||||
},
|
|
||||||
".Net": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"nativeDebugging": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
|
||||||
<StartupObject>Recyclable.Collections.Benchmarks.Program</StartupObject>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
|
|
||||||
<PackageReference Include="Collections.Pooled" Version="1.0.82" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj" />
|
|
||||||
<ProjectReference Include="..\Recyclable.Collections\Recyclable.Collections.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,340 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using Collections.Pooled;
|
|
||||||
using Recyclable.Collections.Benchmarks.Core;
|
|
||||||
using Recyclable.Collections.Benchmarks.POC;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks
|
|
||||||
{
|
|
||||||
public enum RecyclableCollectionsBenchmarkSource
|
|
||||||
{
|
|
||||||
Unknown,
|
|
||||||
Array,
|
|
||||||
List,
|
|
||||||
PooledList,
|
|
||||||
RecyclableList,
|
|
||||||
RecyclableLongList,
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemoryDiagnoser]
|
|
||||||
public class RecyclableBenchmarkBase<TBenchmarkType> : PocBenchmarkBase<TBenchmarkType>
|
|
||||||
{
|
|
||||||
protected long[]? _testObjects;
|
|
||||||
protected long[] TestObjects => _testObjects ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
|
||||||
|
|
||||||
protected List<long>? _testObjectsAsList;
|
|
||||||
protected List<long> TestObjectsAsList => _testObjectsAsList ?? throw new NullReferenceException("Something went wrong and the field is not initialized");
|
|
||||||
|
|
||||||
protected PooledList<long>? _testObjectsAsPooledList;
|
|
||||||
public PooledList<long> TestObjectsAsPooledList => _testObjectsAsPooledList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
|
||||||
protected RecyclableList<long>? _testObjectsAsRecyclableList;
|
|
||||||
protected RecyclableList<long> TestObjectsAsRecyclableList => _testObjectsAsRecyclableList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
|
||||||
protected RecyclableLongList<long>? _testObjectsAsRecyclableLongList;
|
|
||||||
protected RecyclableLongList<long> TestObjectsAsRecyclableLongList => _testObjectsAsRecyclableLongList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
|
||||||
protected IEnumerable<long> TestObjectsAsIEnumerable
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
for (long i = 0; i < TestObjectCount; i++)
|
|
||||||
{
|
|
||||||
yield return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Setup()
|
|
||||||
{
|
|
||||||
PrepareData(RecyclableCollectionsBenchmarkSource.Array);
|
|
||||||
base.Setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PrepareData<T>(T benchmarkType)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"******* PREPARING DATA FOR {benchmarkType} *******");
|
|
||||||
switch (benchmarkType)
|
|
||||||
{
|
|
||||||
case RecyclableCollectionsBenchmarkSource.Unknown:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecyclableCollectionsBenchmarkSource.Array:
|
|
||||||
_testObjects ??= DataGenerator.EnumerateTestObjects(TestObjectCount);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecyclableCollectionsBenchmarkSource.List:
|
|
||||||
_testObjectsAsList ??= TestObjects.ToList();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecyclableCollectionsBenchmarkSource.PooledList:
|
|
||||||
_testObjectsAsPooledList ??= new PooledList<long>(TestObjects, ClearMode.Auto);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecyclableCollectionsBenchmarkSource.RecyclableList:
|
|
||||||
_testObjectsAsRecyclableList ??= new(TestObjects, initialCapacity: TestObjectCount);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecyclableCollectionsBenchmarkSource.RecyclableLongList:
|
|
||||||
_testObjectsAsRecyclableLongList ??= new(TestObjects, BlockSize, initialCapacity: base.TestObjectCount);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw CreateUnknownBenchmarkTypeException(benchmarkType);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.PrepareData(benchmarkType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Cleanup()
|
|
||||||
{
|
|
||||||
Console.WriteLine("******* GLOBAL CLEANUP *******");
|
|
||||||
// Enable this if item type is changed to reference type
|
|
||||||
//if (_testObjects != null)
|
|
||||||
//{
|
|
||||||
// Array.Fill(_testObjects, 0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
_testObjects = default;
|
|
||||||
_testObjectsAsList = default;
|
|
||||||
_testObjectsAsPooledList?.Dispose();
|
|
||||||
_testObjectsAsPooledList = default;
|
|
||||||
_testObjectsAsRecyclableList?.Dispose();
|
|
||||||
_testObjectsAsRecyclableList = default;
|
|
||||||
_testObjectsAsRecyclableLongList?.Dispose();
|
|
||||||
_testObjectsAsRecyclableLongList = default;
|
|
||||||
base.Cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//[MemoryDiagnoser]
|
|
||||||
//public class ListVsLinkedListBenchmarks
|
|
||||||
//{
|
|
||||||
// [Benchmark]
|
|
||||||
// public void RecyclableList()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// using var list = new RecyclableList<object>(initialCapacity: dataCount);
|
|
||||||
// for (long i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Add(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for (long i = 0; i < list.LongCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// [Benchmark(Baseline = true)]
|
|
||||||
// public void RecyclableLongList()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// long dataCount = TestObjectCount;
|
|
||||||
// using var list = new RecyclableLongList<object>(BlockSize, totalItemsCount: dataCount);
|
|
||||||
// for (long i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Add(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for (long i = 0; i < list.LongCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void List()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new List<object>(capacity: dataCount);
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Add(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for (var i = 0; i < list.Count; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void LinkedList()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new LinkedList<object>();
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// _ = list.AddLast(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// foreach (var node in list)
|
|
||||||
// {
|
|
||||||
// DoNothing(node);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void RecyclableQueue()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new RecyclableQueue<object>(BlockSize);
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Enqueue(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// while (list.LongCount > 0)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.Dequeue());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void Queue()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new Queue<object>(capacity: dataCount);
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Enqueue(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// while (list.Count > 0)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.Dequeue());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void PriorityQueue()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new PriorityQueue<object, long>(initialCapacity: dataCount);
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Enqueue(data[i], i);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// while (list.Count > 0)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.Dequeue());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void RecyclableSortedList_InUpdateMode()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new RecyclableSortedList<long, object>(BlockSize);
|
|
||||||
// list.BeginUpdate();
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Add(i, data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.EndUpdate();
|
|
||||||
|
|
||||||
// for (var i = 0; i < list.LongCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void RecyclableSortedList()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new RecyclableSortedList<long, object>(BlockSize);
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Add(i, data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for (var i = 0; i < list.LongCount; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void SortedList()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new SortedList<long, object>();
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Add(i, data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for (var i = 0; i < list.Count; i++)
|
|
||||||
// {
|
|
||||||
// DoNothing(list[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void RecyclableStack()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new RecyclableStack<object>(BlockSize);
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Push(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// while (list.LongCount > 0)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.Pop());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //[Benchmark]
|
|
||||||
// public void Stack()
|
|
||||||
// {
|
|
||||||
// var data = TestObjects;
|
|
||||||
// var dataCount = TestObjectCount;
|
|
||||||
// var list = new Stack<object>();
|
|
||||||
// for (var i = 0; i < dataCount; i++)
|
|
||||||
// {
|
|
||||||
// list.Push(data[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// while (list.Count > 0)
|
|
||||||
// {
|
|
||||||
// DoNothing(list.Pop());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// list.Clear();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections", "R
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.CollectionsTests", "Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj", "{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.CollectionsTests", "Recyclable.CollectionsTests\Recyclable.CollectionsTests.csproj", "{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Benchmarks", "Recyclable.Collections.Benchmarks\Recyclable.Collections.Benchmarks.csproj", "{CDC0CF78-FC11-43E3-9059-3261DA3639D1}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -33,14 +31,6 @@ Global
|
|||||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.ActiveCfg = Release|x64
|
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.ActiveCfg = Release|x64
|
||||||
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.Build.0 = Release|x64
|
{2B7223FA-2D5B-40BF-9BD9-9D6B05DC18E2}.Release|x64.Build.0 = Release|x64
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{CDC0CF78-FC11-43E3-9059-3261DA3639D1}.Release|x64.Build.0 = Release|x64
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
return CompareResult.XEqualY;
|
return CompareResult.XEqualY;
|
||||||
}
|
}
|
||||||
|
#pragma warning disable IDE0046 // This is intentional due to better performance - IL contained unnecessary local variable.
|
||||||
else if (x != null && y == null)
|
else if (x != null && y == null)
|
||||||
{
|
{
|
||||||
return CompareResult.XGreaterThenY;
|
return CompareResult.XGreaterThenY;
|
||||||
@@ -27,6 +28,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
return _comparison(x!, y!);
|
return _comparison(x!, y!);
|
||||||
}
|
}
|
||||||
|
#pragma warning restore IDE0046
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
internal static class ExceptionExtensions
|
internal static class ExceptionExtensions
|
||||||
{
|
{
|
||||||
public static void CaptureAndRethrow(this Exception ex)
|
public static void CaptureAndReThrow(this Exception ex)
|
||||||
{
|
{
|
||||||
ExceptionDispatchInfo.Capture(ex).Throw();
|
ExceptionDispatchInfo.Capture(ex).Throw();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
// Ignore Spelling: Linq
|
// Ignore Spelling: Linq
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
// The source code was taken from
|
// The source code was taken from
|
||||||
// https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
|
// https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
|
||||||
|
|||||||
@@ -31,5 +31,14 @@ namespace Recyclable.Collections
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long Min(long number1, long number2, long number3) =>
|
||||||
|
number1 <= number2
|
||||||
|
? number1 <= number3
|
||||||
|
? number1
|
||||||
|
: number3
|
||||||
|
: number2 <= number3
|
||||||
|
? number2
|
||||||
|
: number3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,12 +13,12 @@ namespace Recyclable.Collections
|
|||||||
_memory = memory;
|
_memory = memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable RCS1085
|
#pragma warning disable IDE0032 // This is intentional due to better performance
|
||||||
private T[] _memory;
|
private T[] _memory;
|
||||||
public T[] Memory { get => _memory; set => _memory = value; }
|
public T[] Memory { get => _memory; set => _memory = value; }
|
||||||
|
|
||||||
private MemoryBucket<T>? _nextBucket;
|
private MemoryBucket<T>? _nextBucket;
|
||||||
|
#pragma warning restore IDE0032 // Use auto property
|
||||||
public MemoryBucket<T>? NextBucket { get => _nextBucket; set => _nextBucket = value; }
|
public MemoryBucket<T>? NextBucket { get => _nextBucket; set => _nextBucket = value; }
|
||||||
#pragma warning restore RCS1085
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Pools
|
|
||||||
{
|
|
||||||
public sealed class BiDirectionalRecyclableArrayPoolChunk<T>
|
|
||||||
{
|
|
||||||
private static readonly T[] _empty = Array.Empty<T>();
|
|
||||||
|
|
||||||
internal static T[] Empty => _empty;
|
|
||||||
|
|
||||||
internal T[] Value;
|
|
||||||
internal int Top;
|
|
||||||
internal int Bottom;
|
|
||||||
internal BiDirectionalRecyclableArrayPoolChunk<T>? Previous;
|
|
||||||
internal BiDirectionalRecyclableArrayPoolChunk<T>? Next;
|
|
||||||
|
|
||||||
public BiDirectionalRecyclableArrayPoolChunk()
|
|
||||||
{
|
|
||||||
Value = _empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using Microsoft.Extensions.ObjectPool;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Pools
|
|
||||||
{
|
|
||||||
public static class BiDirectionalRecyclableArrayPoolChunkPool<T>
|
|
||||||
{
|
|
||||||
private static readonly ObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>> _pool =
|
|
||||||
new DefaultObjectPool<BiDirectionalRecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<BiDirectionalRecyclableArrayPoolChunk<T>>());
|
|
||||||
|
|
||||||
public static BiDirectionalRecyclableArrayPoolChunk<T> Rent() => _pool.Get();
|
|
||||||
|
|
||||||
public static void Return(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
|
|
||||||
{
|
|
||||||
chunk.Top = 0;
|
|
||||||
chunk.Bottom = 0;
|
|
||||||
chunk.Previous = null;
|
|
||||||
chunk.Next = null;
|
|
||||||
_pool.Return(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Dispose(BiDirectionalRecyclableArrayPoolChunk<T> chunk, bool needsClearing)
|
|
||||||
{
|
|
||||||
var buffer = chunk.Value;
|
|
||||||
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk.Value = BiDirectionalRecyclableArrayPoolChunk<T>.Empty;
|
|
||||||
chunk.Top = 0;
|
|
||||||
chunk.Bottom = 0;
|
|
||||||
chunk.Previous = null;
|
|
||||||
chunk.Next = null;
|
|
||||||
_pool.Return(chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,26 +7,20 @@ namespace Recyclable.Collections.Pools
|
|||||||
Low
|
Low
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class MemoryUtils
|
internal static class MemoryUtils
|
||||||
{
|
{
|
||||||
public static MemoryPressure GetMemoryPressure()
|
private const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high"
|
||||||
{
|
private const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium"
|
||||||
const double HighPressureThreshold = .90; // Percent of GC memory pressure threshold we consider "high"
|
|
||||||
const double MediumPressureThreshold = .70; // Percent of GC memory pressure threshold we consider "medium"
|
|
||||||
|
|
||||||
GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo();
|
public static MemoryPressure GetMemoryPressure()
|
||||||
|
{
|
||||||
|
GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo();
|
||||||
|
|
||||||
if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold)
|
return memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold
|
||||||
{
|
? MemoryPressure.High
|
||||||
return MemoryPressure.High;
|
: memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold
|
||||||
}
|
? MemoryPressure.Medium
|
||||||
|
: MemoryPressure.Low;
|
||||||
if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold)
|
}
|
||||||
{
|
}
|
||||||
return MemoryPressure.Medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MemoryPressure.Low;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace Recyclable.Collections.Pools
|
|
||||||
{
|
|
||||||
public sealed class RecyclableArrayPoolChunk<T>
|
|
||||||
{
|
|
||||||
private static readonly T[] _empty = Array.Empty<T>();
|
|
||||||
|
|
||||||
internal static T[] Empty => _empty;
|
|
||||||
|
|
||||||
internal T[] Value;
|
|
||||||
internal int Index;
|
|
||||||
internal RecyclableArrayPoolChunk<T>? Previous;
|
|
||||||
internal RecyclableArrayPoolChunk<T>? Next;
|
|
||||||
|
|
||||||
public RecyclableArrayPoolChunk()
|
|
||||||
{
|
|
||||||
Value = _empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
using Microsoft.Extensions.ObjectPool;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Pools
|
|
||||||
{
|
|
||||||
public static class RecyclableArrayPoolChunkPool<T>
|
|
||||||
{
|
|
||||||
private static readonly ObjectPool<RecyclableArrayPoolChunk<T>> _pool =
|
|
||||||
new DefaultObjectPool<RecyclableArrayPoolChunk<T>>(new DefaultPooledObjectPolicy<RecyclableArrayPoolChunk<T>>());
|
|
||||||
|
|
||||||
public static RecyclableArrayPoolChunk<T> Rent() => _pool.Get();
|
|
||||||
|
|
||||||
public static void Return(RecyclableArrayPoolChunk<T> chunk)
|
|
||||||
{
|
|
||||||
chunk.Index = 0;
|
|
||||||
chunk.Previous = null;
|
|
||||||
chunk.Next = null;
|
|
||||||
_pool.Return(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Dispose(RecyclableArrayPoolChunk<T> chunk, bool needsClearing)
|
|
||||||
{
|
|
||||||
var buffer = chunk.Value;
|
|
||||||
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
|
|
||||||
}
|
|
||||||
chunk.Value = RecyclableArrayPoolChunk<T>.Empty;
|
|
||||||
chunk.Index = 0;
|
|
||||||
chunk.Previous = null;
|
|
||||||
chunk.Next = null;
|
|
||||||
_pool.Return(chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<DefineConstants>$(DefineConstants);WITH_VERSIONING</DefineConstants>
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<PackageId>Recyclable.Collections</PackageId>
|
<PackageId>Recyclable.Collections</PackageId>
|
||||||
<PackageVersion>0.0.6</PackageVersion>
|
<PackageVersion>0.0.6.2</PackageVersion>
|
||||||
<Authors>Marek Lemańczyk</Authors>
|
<Authors>Marek Lemańczyk</Authors>
|
||||||
<Company>N/A</Company>
|
<Company>N/A</Company>
|
||||||
<Product>Recyclable.Collections</Product>
|
<Product>Recyclable.Collections</Product>
|
||||||
@@ -18,11 +17,15 @@
|
|||||||
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PackageReleaseNotes>List<T> compatibility pack added for RecyclableList<T>. Various performance improvements and fixes.</PackageReleaseNotes>
|
<PackageReleaseNotes>Various bug fixes and improvements, primarily for .IndexOf().</PackageReleaseNotes>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
|
<PackageIcon>logo.png</PackageIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="..\logo.png" Pack="true" PackagePath="" Link="logo.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
|
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
@@ -31,12 +34,7 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
|
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
|
||||||
<PackageReference Include="morelinq" Version="3.4.2" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\VersionedCollectionsGenerator\VersionedCollectionsGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,406 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
internal sealed class RecyclableDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
|
|
||||||
|
|
||||||
internal readonly int _blockSize;
|
|
||||||
internal readonly int _blockSizeMinus1;
|
|
||||||
internal readonly byte _blockShift;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
internal Entry[][] _entries;
|
|
||||||
#nullable restore
|
|
||||||
internal int[] _buckets;
|
|
||||||
internal int _count;
|
|
||||||
internal bool _disposed;
|
|
||||||
|
|
||||||
internal struct Entry
|
|
||||||
{
|
|
||||||
public int HashCode;
|
|
||||||
public int Next;
|
|
||||||
public TKey Key;
|
|
||||||
public TValue Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecyclableDictionary(int blockSize = RecyclableDefaults.BlockSize)
|
|
||||||
{
|
|
||||||
if (!BitOperations.IsPow2((uint)blockSize))
|
|
||||||
{
|
|
||||||
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
_blockSize = blockSize;
|
|
||||||
_blockSizeMinus1 = blockSize - 1;
|
|
||||||
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
|
|
||||||
|
|
||||||
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry[]>.RentShared(4)
|
|
||||||
: new Entry[4][];
|
|
||||||
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry>.RentShared(blockSize)
|
|
||||||
: new Entry[blockSize];
|
|
||||||
_buckets = new int[4];
|
|
||||||
Array.Fill(_buckets, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _count;
|
|
||||||
|
|
||||||
public TValue this[TKey key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var index = FindIndex(key, out _);
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
return GetEntry(index).Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
|
|
||||||
return default!;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
var index = FindIndex(key, out _);
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(index);
|
|
||||||
entry.Value = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Add(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Add(TKey key, TValue value)
|
|
||||||
{
|
|
||||||
var hash = key.GetHashCode() & int.MaxValue;
|
|
||||||
if (_count >= (_buckets.Length * 3) / 4)
|
|
||||||
{
|
|
||||||
ResizeBuckets(_buckets.Length * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsureCapacity(_count + 1);
|
|
||||||
|
|
||||||
int bucket = hash & (_buckets.Length - 1);
|
|
||||||
int index = _buckets[bucket];
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
ref var check = ref GetEntry(index);
|
|
||||||
if (check.HashCode == hash && EqualityComparer<TKey>.Default.Equals(check.Key, key))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("An element with the same key already exists.", nameof(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
index = check.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newIndex = _count++;
|
|
||||||
ref var entry = ref GetEntry(newIndex);
|
|
||||||
entry.HashCode = hash;
|
|
||||||
entry.Key = key;
|
|
||||||
entry.Value = value;
|
|
||||||
entry.Next = _buckets[bucket];
|
|
||||||
_buckets[bucket] = newIndex;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool ContainsKey(TKey key) => FindIndex(key, out _) >= 0;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryGetValue(TKey key, out TValue value)
|
|
||||||
{
|
|
||||||
var index = FindIndex(key, out _);
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
value = GetEntry(index).Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = default!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Remove(TKey key)
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash = key.GetHashCode() & int.MaxValue;
|
|
||||||
int bucket = hash & (_buckets.Length - 1);
|
|
||||||
int prev = -1;
|
|
||||||
int index = _buckets[bucket];
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(index);
|
|
||||||
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
|
|
||||||
{
|
|
||||||
if (prev < 0)
|
|
||||||
{
|
|
||||||
_buckets[bucket] = entry.Next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GetEntry(prev).Next = entry.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lastIndex = _count - 1;
|
|
||||||
if (index != lastIndex)
|
|
||||||
{
|
|
||||||
MoveEntry(lastIndex, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearEntry(lastIndex);
|
|
||||||
_count--;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = index;
|
|
||||||
index = entry.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
Array.Fill(_buckets, -1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int blockIndex = 0;
|
|
||||||
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
|
|
||||||
_entries[blockIndex++] = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
|
|
||||||
{
|
|
||||||
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
|
|
||||||
_entries[blockIndex++] = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
|
|
||||||
{
|
|
||||||
_entries[blockIndex++] = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_count = 0;
|
|
||||||
Array.Fill(_buckets, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TKey GetKey(int index) => GetEntry(index).Key;
|
|
||||||
|
|
||||||
public TValue GetValue(int index) => GetEntry(index).Value;
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
|
|
||||||
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_entries = Array.Empty<Entry[]>();
|
|
||||||
_buckets = Array.Empty<int>();
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ref Entry GetEntry(int index)
|
|
||||||
{
|
|
||||||
return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearEntry(int index)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(index);
|
|
||||||
entry.HashCode = 0;
|
|
||||||
entry.Next = -1;
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
entry.Key = default!;
|
|
||||||
entry.Value = default!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveEntry(int sourceIndex, int targetIndex)
|
|
||||||
{
|
|
||||||
ref var source = ref GetEntry(sourceIndex);
|
|
||||||
ref var target = ref GetEntry(targetIndex);
|
|
||||||
target = source;
|
|
||||||
|
|
||||||
int bucket = source.HashCode & (_buckets.Length - 1);
|
|
||||||
int cur = _buckets[bucket];
|
|
||||||
int prev = -1;
|
|
||||||
while (cur != sourceIndex)
|
|
||||||
{
|
|
||||||
prev = cur;
|
|
||||||
cur = GetEntry(cur).Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev < 0)
|
|
||||||
{
|
|
||||||
_buckets[bucket] = targetIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GetEntry(prev).Next = targetIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureCapacity(int min)
|
|
||||||
{
|
|
||||||
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
|
|
||||||
if (_entries.Length < requiredBlocks)
|
|
||||||
{
|
|
||||||
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
|
|
||||||
: new Entry[requiredBlocks][];
|
|
||||||
|
|
||||||
Array.Copy(_entries, newBlocks, _entries.Length);
|
|
||||||
|
|
||||||
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_entries = newBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < requiredBlocks; i++)
|
|
||||||
{
|
|
||||||
if (_entries[i] == null)
|
|
||||||
{
|
|
||||||
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry>.RentShared(_blockSize)
|
|
||||||
: new Entry[_blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResizeBuckets(int newSize)
|
|
||||||
{
|
|
||||||
var newBuckets = new int[newSize];
|
|
||||||
Array.Fill(newBuckets, -1);
|
|
||||||
for (int i = 0; i < _count; i++)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(i);
|
|
||||||
int bucket = entry.HashCode & (newSize - 1);
|
|
||||||
entry.Next = newBuckets[bucket];
|
|
||||||
newBuckets[bucket] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buckets = newBuckets;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int FindIndex(TKey key, out int hash)
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
hash = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = key.GetHashCode() & int.MaxValue;
|
|
||||||
int bucket = hash & (_buckets.Length - 1);
|
|
||||||
int index = _buckets[bucket];
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(index);
|
|
||||||
if (entry.HashCode == hash && EqualityComparer<TKey>.Default.Equals(entry.Key, key))
|
|
||||||
{
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = entry.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
|
|
||||||
{
|
|
||||||
private readonly RecyclableDictionary<TKey, TValue> _dictionary;
|
|
||||||
private int _index;
|
|
||||||
private KeyValuePair<TKey, TValue> _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableDictionary<TKey, TValue> dictionary)
|
|
||||||
{
|
|
||||||
_dictionary = dictionary;
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyValuePair<TKey, TValue> Current => _current;
|
|
||||||
|
|
||||||
object IEnumerator.Current => _current;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_index >= _dictionary._count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var entry = _dictionary.GetEntry(_index++);
|
|
||||||
_current = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,362 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
internal sealed class RecyclableHashSet<T> : IEnumerable<T>, IDisposable
|
|
||||||
where T : notnull
|
|
||||||
{
|
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
|
||||||
|
|
||||||
internal readonly int _blockSize;
|
|
||||||
internal readonly int _blockSizeMinus1;
|
|
||||||
internal readonly byte _blockShift;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
internal Entry[][] _entries;
|
|
||||||
#nullable restore
|
|
||||||
internal int[] _buckets;
|
|
||||||
internal int _count;
|
|
||||||
internal bool _disposed;
|
|
||||||
|
|
||||||
internal struct Entry
|
|
||||||
{
|
|
||||||
public int HashCode;
|
|
||||||
public int Next;
|
|
||||||
public T Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecyclableHashSet(int blockSize = RecyclableDefaults.BlockSize)
|
|
||||||
{
|
|
||||||
if (!BitOperations.IsPow2((uint)blockSize))
|
|
||||||
{
|
|
||||||
blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
_blockSize = blockSize;
|
|
||||||
_blockSizeMinus1 = blockSize - 1;
|
|
||||||
_blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
|
|
||||||
|
|
||||||
_entries = 4 >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry[]>.RentShared(4)
|
|
||||||
: new Entry[4][];
|
|
||||||
_entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry>.RentShared(blockSize)
|
|
||||||
: new Entry[blockSize];
|
|
||||||
_buckets = new int[4];
|
|
||||||
Array.Fill(_buckets, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _count;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Add(T item)
|
|
||||||
{
|
|
||||||
var hash = item.GetHashCode() & int.MaxValue;
|
|
||||||
if (_count >= (_buckets.Length * 3) / 4)
|
|
||||||
{
|
|
||||||
ResizeBuckets(_buckets.Length * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsureCapacity(_count + 1);
|
|
||||||
|
|
||||||
int bucket = hash & (_buckets.Length - 1);
|
|
||||||
int index = _buckets[bucket];
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
ref var check = ref GetEntry(index);
|
|
||||||
if (check.HashCode == hash && EqualityComparer<T>.Default.Equals(check.Value, item))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = check.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newIndex = _count++;
|
|
||||||
ref var entry = ref GetEntry(newIndex);
|
|
||||||
entry.HashCode = hash;
|
|
||||||
entry.Value = item;
|
|
||||||
entry.Next = _buckets[bucket];
|
|
||||||
_buckets[bucket] = newIndex;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Contains(T item) => FindIndex(item, out _) >= 0;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Remove(T item)
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash = item.GetHashCode() & int.MaxValue;
|
|
||||||
int bucket = hash & (_buckets.Length - 1);
|
|
||||||
int prev = -1;
|
|
||||||
int index = _buckets[bucket];
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(index);
|
|
||||||
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
|
|
||||||
{
|
|
||||||
if (prev < 0)
|
|
||||||
{
|
|
||||||
_buckets[bucket] = entry.Next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GetEntry(prev).Next = entry.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lastIndex = _count - 1;
|
|
||||||
if (index != lastIndex)
|
|
||||||
{
|
|
||||||
MoveEntry(lastIndex, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearEntry(lastIndex);
|
|
||||||
_count--;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = index;
|
|
||||||
index = entry.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
Array.Fill(_buckets, -1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int blockIndex = 0;
|
|
||||||
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<Entry>.ReturnShared(_entries[blockIndex]!, _needsClearing);
|
|
||||||
_entries[blockIndex++] = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
|
|
||||||
{
|
|
||||||
Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length);
|
|
||||||
_entries[blockIndex++] = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (blockIndex < _entries.Length && _entries[blockIndex] != null)
|
|
||||||
{
|
|
||||||
_entries[blockIndex++] = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_count = 0;
|
|
||||||
Array.Fill(_buckets, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_entries = Array.Empty<Entry[]>();
|
|
||||||
_buckets = Array.Empty<int>();
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ref Entry GetEntry(int index)
|
|
||||||
{
|
|
||||||
return ref _entries[index >> _blockShift][index & _blockSizeMinus1];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearEntry(int index)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(index);
|
|
||||||
entry.HashCode = 0;
|
|
||||||
entry.Next = -1;
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
entry.Value = default!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveEntry(int sourceIndex, int targetIndex)
|
|
||||||
{
|
|
||||||
ref var source = ref GetEntry(sourceIndex);
|
|
||||||
ref var target = ref GetEntry(targetIndex);
|
|
||||||
target = source;
|
|
||||||
|
|
||||||
int bucket = source.HashCode & (_buckets.Length - 1);
|
|
||||||
int cur = _buckets[bucket];
|
|
||||||
int prev = -1;
|
|
||||||
while (cur != sourceIndex)
|
|
||||||
{
|
|
||||||
prev = cur;
|
|
||||||
cur = GetEntry(cur).Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev < 0)
|
|
||||||
{
|
|
||||||
_buckets[bucket] = targetIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GetEntry(prev).Next = targetIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureCapacity(int min)
|
|
||||||
{
|
|
||||||
int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift;
|
|
||||||
if (_entries.Length < requiredBlocks)
|
|
||||||
{
|
|
||||||
var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry[]>.RentShared(requiredBlocks)
|
|
||||||
: new Entry[requiredBlocks][];
|
|
||||||
|
|
||||||
Array.Copy(_entries, newBlocks, _entries.Length);
|
|
||||||
|
|
||||||
if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<Entry[]>.ReturnShared(_entries, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_entries = newBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < requiredBlocks; i++)
|
|
||||||
{
|
|
||||||
if (_entries[i] == null)
|
|
||||||
{
|
|
||||||
_entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<Entry>.RentShared(_blockSize)
|
|
||||||
: new Entry[_blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResizeBuckets(int newSize)
|
|
||||||
{
|
|
||||||
int[] newBuckets = newSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<int>.RentShared(newSize)
|
|
||||||
: new int[newSize];
|
|
||||||
Array.Fill(newBuckets, -1);
|
|
||||||
for (int i = 0; i < _count; i++)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(i);
|
|
||||||
int bucket = entry.HashCode & (newSize - 1);
|
|
||||||
entry.Next = newBuckets[bucket];
|
|
||||||
newBuckets[bucket] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<int>.ReturnShared(_buckets, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_buckets = newBuckets;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int FindIndex(T item, out int hash)
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
hash = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = item.GetHashCode() & int.MaxValue;
|
|
||||||
int bucket = hash & (_buckets.Length - 1);
|
|
||||||
int index = _buckets[bucket];
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
ref var entry = ref GetEntry(index);
|
|
||||||
if (entry.HashCode == hash && EqualityComparer<T>.Default.Equals(entry.Value, item))
|
|
||||||
{
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = entry.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private readonly RecyclableHashSet<T> _set;
|
|
||||||
private int _index;
|
|
||||||
private T? _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableHashSet<T> set)
|
|
||||||
{
|
|
||||||
_set = set;
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => _current!;
|
|
||||||
|
|
||||||
object IEnumerator.Current => _current!;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_index >= _set._count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = _set.GetEntry(_index++).Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,323 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
internal sealed class RecyclableLinkedList<T> : IEnumerable<T>, IDisposable
|
|
||||||
{
|
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous, bool forHead)
|
|
||||||
{
|
|
||||||
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
|
|
||||||
if (chunk.Value.Length < size)
|
|
||||||
{
|
|
||||||
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<T>.RentShared(size)
|
|
||||||
: new T[size];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forHead)
|
|
||||||
{
|
|
||||||
chunk.Top = size;
|
|
||||||
chunk.Bottom = size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chunk.Top = 0;
|
|
||||||
chunk.Bottom = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk.Previous = previous;
|
|
||||||
chunk.Next = null;
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
|
|
||||||
{
|
|
||||||
BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
|
|
||||||
{
|
|
||||||
BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal BiDirectionalRecyclableArrayPoolChunk<T> _head;
|
|
||||||
internal BiDirectionalRecyclableArrayPoolChunk<T> _tail;
|
|
||||||
internal bool _disposed;
|
|
||||||
internal long _capacity;
|
|
||||||
internal long _count;
|
|
||||||
|
|
||||||
public RecyclableLinkedList(int initialCapacity = RecyclableDefaults.InitialCapacity)
|
|
||||||
{
|
|
||||||
if (initialCapacity < 1)
|
|
||||||
{
|
|
||||||
initialCapacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BitOperations.IsPow2((uint)initialCapacity))
|
|
||||||
{
|
|
||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
_head = RentChunk(initialCapacity, null, false);
|
|
||||||
_tail = _head;
|
|
||||||
_capacity = initialCapacity;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecyclableLinkedList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
|
|
||||||
{
|
|
||||||
foreach (var item in source)
|
|
||||||
{
|
|
||||||
AddLast(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => checked((int)_count);
|
|
||||||
public long LongCount => _count;
|
|
||||||
|
|
||||||
public void AddLast(T item)
|
|
||||||
{
|
|
||||||
if (_tail.Top == _tail.Value.Length)
|
|
||||||
{
|
|
||||||
GrowTail();
|
|
||||||
}
|
|
||||||
|
|
||||||
_tail.Value[_tail.Top++] = item;
|
|
||||||
_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddFirst(T item)
|
|
||||||
{
|
|
||||||
if (_head.Bottom == 0)
|
|
||||||
{
|
|
||||||
GrowHead();
|
|
||||||
}
|
|
||||||
|
|
||||||
_head.Value[--_head.Bottom] = item;
|
|
||||||
_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T RemoveFirst()
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
_count--;
|
|
||||||
var value = _head.Value[_head.Bottom];
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_head.Value[_head.Bottom] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
_head.Bottom++;
|
|
||||||
if (_head.Bottom == _head.Top && _head.Next != null)
|
|
||||||
{
|
|
||||||
ReleaseHeadChunk();
|
|
||||||
}
|
|
||||||
else if (_head.Bottom == _head.Top)
|
|
||||||
{
|
|
||||||
_head.Bottom = _head.Top;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T RemoveLast()
|
|
||||||
{
|
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
_count--;
|
|
||||||
_tail.Top--;
|
|
||||||
var value = _tail.Value[_tail.Top];
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_tail.Value[_tail.Top] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_tail.Top == _tail.Bottom && _tail.Previous != null)
|
|
||||||
{
|
|
||||||
ReleaseTailChunk();
|
|
||||||
}
|
|
||||||
else if (_tail.Top == _tail.Bottom)
|
|
||||||
{
|
|
||||||
_tail.Top = _tail.Bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
while (_head.Next != null)
|
|
||||||
{
|
|
||||||
ReleaseHeadChunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (_tail.Previous != null)
|
|
||||||
{
|
|
||||||
ReleaseTailChunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_needsClearing && _head.Top > _head.Bottom)
|
|
||||||
{
|
|
||||||
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
_capacity = _head.Value.Length;
|
|
||||||
_head.Bottom = _head.Top;
|
|
||||||
_tail = _head;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
private void GrowTail()
|
|
||||||
{
|
|
||||||
int newSize;
|
|
||||||
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
|
|
||||||
{
|
|
||||||
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var doubled = _capacity << 1;
|
|
||||||
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newChunk = RentChunk(newSize, _tail, false);
|
|
||||||
_tail.Next = newChunk;
|
|
||||||
_tail = newChunk;
|
|
||||||
_capacity += newSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GrowHead()
|
|
||||||
{
|
|
||||||
int newSize;
|
|
||||||
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
|
|
||||||
{
|
|
||||||
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var doubled = _capacity << 1;
|
|
||||||
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newChunk = RentChunk(newSize, null, true);
|
|
||||||
newChunk.Next = _head;
|
|
||||||
_head.Previous = newChunk;
|
|
||||||
_head = newChunk;
|
|
||||||
_capacity += newSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReleaseHeadChunk()
|
|
||||||
{
|
|
||||||
var toReturn = _head;
|
|
||||||
_head = toReturn.Next!;
|
|
||||||
_head.Previous = null;
|
|
||||||
_capacity -= toReturn.Value.Length;
|
|
||||||
ReturnChunk(toReturn);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReleaseTailChunk()
|
|
||||||
{
|
|
||||||
var toReturn = _tail;
|
|
||||||
_tail = toReturn.Previous!;
|
|
||||||
_tail.Next = null;
|
|
||||||
_capacity -= toReturn.Value.Length;
|
|
||||||
ReturnChunk(toReturn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chunk = _head;
|
|
||||||
while (chunk != null)
|
|
||||||
{
|
|
||||||
var next = chunk.Next;
|
|
||||||
DisposeChunk(chunk);
|
|
||||||
chunk = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start;
|
|
||||||
private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk;
|
|
||||||
private int _index;
|
|
||||||
private T? _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableLinkedList<T> list)
|
|
||||||
{
|
|
||||||
_start = list._head;
|
|
||||||
_chunk = list._head;
|
|
||||||
_index = _chunk != null ? _chunk.Bottom : 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => _current!;
|
|
||||||
object IEnumerator.Current => _current!;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_chunk == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_index >= _chunk.Top)
|
|
||||||
{
|
|
||||||
_chunk = _chunk.Next;
|
|
||||||
if (_chunk == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_index = _chunk.Bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = _chunk.Value[_index++];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_chunk = _start;
|
|
||||||
_index = _chunk != null ? _chunk.Bottom : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_chunk = null;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,82 +1,15 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
public static class zRecyclableListAddRange
|
#pragma warning disable IDE1006 // This is intentional naming for the class to show up at the end of IntelliSense list.
|
||||||
{
|
public static class zRecyclableListAddRange
|
||||||
|
#pragma warning restore IDE1006 // This is intentional naming for the class to show up at the end of IntelliSense list.
|
||||||
private static class AddRangeHelper<T>
|
{
|
||||||
{
|
private static void AddRangeEnumerated<T>(this RecyclableList<T> list, IEnumerable<T> items, int growByCount)
|
||||||
private static readonly Action<RecyclableList<T>, object>? _dictionaryAdder;
|
{
|
||||||
private static readonly Action<RecyclableList<T>, object>? _sortedListAdder;
|
|
||||||
private static readonly Type? _dictionaryType;
|
|
||||||
private static readonly Type? _sortedListType;
|
|
||||||
|
|
||||||
static AddRangeHelper()
|
|
||||||
{
|
|
||||||
Type elementType = typeof(T);
|
|
||||||
if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
|
|
||||||
{
|
|
||||||
Type[] args = elementType.GetGenericArguments();
|
|
||||||
_dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args);
|
|
||||||
MethodInfo? method = typeof(zRecyclableListAddRange).GetMethod(
|
|
||||||
nameof(AddRange),
|
|
||||||
BindingFlags.NonPublic | BindingFlags.Static,
|
|
||||||
binder: null,
|
|
||||||
types: new[] { typeof(RecyclableList<>).MakeGenericType(elementType), _dictionaryType },
|
|
||||||
modifiers: null);
|
|
||||||
if (method != null)
|
|
||||||
{
|
|
||||||
var listParam = Expression.Parameter(typeof(RecyclableList<T>));
|
|
||||||
var objParam = Expression.Parameter(typeof(object));
|
|
||||||
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _dictionaryType));
|
|
||||||
_dictionaryAdder = Expression.Lambda<Action<RecyclableList<T>, object>>(call, listParam, objParam).Compile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>))
|
|
||||||
{
|
|
||||||
Type[] args = elementType.GetGenericArguments();
|
|
||||||
_sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args);
|
|
||||||
MethodInfo? method = typeof(zRecyclableListAddRange).GetMethod(
|
|
||||||
nameof(AddRange),
|
|
||||||
BindingFlags.NonPublic | BindingFlags.Static,
|
|
||||||
binder: null,
|
|
||||||
types: new[] { typeof(RecyclableList<>).MakeGenericType(elementType), _sortedListType },
|
|
||||||
modifiers: null);
|
|
||||||
if (method != null)
|
|
||||||
{
|
|
||||||
var listParam = Expression.Parameter(typeof(RecyclableList<T>));
|
|
||||||
var objParam = Expression.Parameter(typeof(object));
|
|
||||||
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _sortedListType));
|
|
||||||
_sortedListAdder = Expression.Lambda<Action<RecyclableList<T>, object>>(call, listParam, objParam).Compile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool TryAddRange(RecyclableList<T> list, IEnumerable<T> items)
|
|
||||||
{
|
|
||||||
if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items))
|
|
||||||
{
|
|
||||||
_dictionaryAdder(list, items);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items))
|
|
||||||
{
|
|
||||||
_sortedListAdder(list, items);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddRangeEnumerated<T>(this RecyclableList<T> list, IEnumerable<T> items, int growByCount)
|
|
||||||
{
|
|
||||||
var enumerator = items.GetEnumerator();
|
var enumerator = items.GetEnumerator();
|
||||||
if (!enumerator.MoveNext())
|
if (!enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
@@ -126,9 +59,8 @@ namespace Recyclable.Collections
|
|||||||
list._count = currentItemsCount;
|
list._count = currentItemsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
[VersionedCollectionsGenerator.RewriteForVersioned]
|
public static void AddRange<T>(this RecyclableList<T> list, in Array items)
|
||||||
public static void AddRange<T>(this RecyclableList<T> list, in Array items)
|
|
||||||
{
|
{
|
||||||
if (list._capacity < list._count + items.Length)
|
if (list._capacity < list._count + items.Length)
|
||||||
{
|
{
|
||||||
@@ -137,9 +69,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
Array.Copy(items, items.GetLowerBound(0), list._memoryBlock, list._count, items.Length);
|
Array.Copy(items, items.GetLowerBound(0), list._memoryBlock, list._count, items.Length);
|
||||||
list._count += items.Length;
|
list._count += items.Length;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -152,9 +82,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
new ReadOnlySpan<T>(items).CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
|
new ReadOnlySpan<T>(items).CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
|
||||||
list._count += items.Length;
|
list._count += items.Length;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -167,9 +95,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
|
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
|
||||||
list._count += items.Length;
|
list._count += items.Length;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -182,9 +108,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
|
items.CopyTo(new Span<T>(list._memoryBlock, list._count, items.Length));
|
||||||
list._count += items.Length;
|
list._count += items.Length;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -197,9 +121,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
items.CopyTo(list._memoryBlock, list._count);
|
items.CopyTo(list._memoryBlock, list._count);
|
||||||
list._count += items.Count;
|
list._count += items.Count;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -212,9 +134,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
items.CopyTo(list._memoryBlock, list._count);
|
items.CopyTo(list._memoryBlock, list._count);
|
||||||
list._count += items.Count;
|
list._count += items.Count;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -227,9 +147,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
items.CopyTo(list._memoryBlock, list._count);
|
items.CopyTo(list._memoryBlock, list._count);
|
||||||
list._count += items.Count;
|
list._count += items.Count;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -242,18 +160,16 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
new ReadOnlySpan<T>(items._memoryBlock, 0, items._count).CopyTo(new Span<T>(list._memoryBlock, list._count, items._count));
|
new ReadOnlySpan<T>(items._memoryBlock, 0, items._count).CopyTo(new Span<T>(list._memoryBlock, list._count, items._count));
|
||||||
list._count += items._count;
|
list._count += items._count;
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void AddRange<T>(this RecyclableList<T> list, RecyclableLongList<T> items)
|
public static void AddRange<T>(this RecyclableList<T> list, RecyclableLongList<T> items)
|
||||||
{
|
{
|
||||||
if (items._longCount == 0)
|
if (items._longCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int oldCount = list._count;
|
int oldCount = list._count;
|
||||||
long sourceItemsCount = items._longCount;
|
long sourceItemsCount = items._longCount;
|
||||||
@@ -294,281 +210,9 @@ namespace Recyclable.Collections
|
|||||||
itemsSpan.CopyTo(targetSpan);
|
itemsSpan.CopyTo(targetSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
list._count = targetCapacity;
|
list._count = targetCapacity;
|
||||||
#if WITH_VERSIONING
|
list._version++;
|
||||||
list._version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableHashSet<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = items._count,
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
|
|
||||||
var entries = items._entries;
|
|
||||||
int shift = items._blockShift,
|
|
||||||
mask = items._blockSizeMinus1;
|
|
||||||
|
|
||||||
for (int i = 0; i < sourceCount; i++)
|
|
||||||
{
|
|
||||||
targetSpan[i] = entries[i >> shift][i & mask].Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableStack<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items._count > int.MaxValue - list._count)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = checked((int)items._count),
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
|
|
||||||
int index = 0;
|
|
||||||
var chunk = items._current;
|
|
||||||
while (chunk != null)
|
|
||||||
{
|
|
||||||
var array = chunk.Value;
|
|
||||||
for (int i = chunk.Index - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
targetSpan[index++] = array[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = chunk.Previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableSortedSet<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = items._count,
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
new ReadOnlySpan<T>(items._items, 0, sourceCount).CopyTo(new Span<T>(list._memoryBlock, oldCount, sourceCount));
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableLinkedList<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items._count > int.MaxValue - list._count)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = checked((int)items._count),
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
|
|
||||||
int index = 0;
|
|
||||||
var chunk = items._head;
|
|
||||||
while (chunk != null)
|
|
||||||
{
|
|
||||||
for (int i = chunk.Bottom; i < chunk.Top; i++)
|
|
||||||
{
|
|
||||||
targetSpan[index++] = chunk.Value[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = chunk.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableList<T> list, RecyclablePriorityQueue<T> items)
|
|
||||||
{
|
|
||||||
if (items._size == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = items._size,
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
new ReadOnlySpan<T>(items._heap, 0, sourceCount).CopyTo(new Span<T>(list._memoryBlock, oldCount, sourceCount));
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableList<T> list, RecyclableQueue<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items._count > int.MaxValue - list._count)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = checked((int)items._count),
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<T>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<T> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
|
|
||||||
int index = 0;
|
|
||||||
var chunk = items._head;
|
|
||||||
while (chunk != null)
|
|
||||||
{
|
|
||||||
for (int i = chunk.Bottom; i < chunk.Top; i++)
|
|
||||||
{
|
|
||||||
targetSpan[index++] = chunk.Value[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = chunk.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<TKey, TValue>(this RecyclableList<KeyValuePair<TKey, TValue>> list, RecyclableDictionary<TKey, TValue> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = items._count,
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<KeyValuePair<TKey, TValue>>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<KeyValuePair<TKey, TValue>> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
|
|
||||||
var entries = items._entries;
|
|
||||||
int shift = items._blockShift,
|
|
||||||
mask = items._blockSizeMinus1;
|
|
||||||
|
|
||||||
for (int i = 0; i < sourceCount; i++)
|
|
||||||
{
|
|
||||||
ref var entry = ref entries[i >> shift][i & mask];
|
|
||||||
targetSpan[i] = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<TKey, TValue>(this RecyclableList<(TKey Key, TValue Value)> list, RecyclableSortedList<TKey, TValue> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int oldCount = list._count,
|
|
||||||
sourceCount = items._count,
|
|
||||||
targetCapacity = oldCount + sourceCount;
|
|
||||||
|
|
||||||
if (list._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
list._capacity = RecyclableListHelpers<(TKey Key, TValue Value)>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<(TKey Key, TValue Value)> targetSpan = new(list._memoryBlock, oldCount, sourceCount);
|
|
||||||
for (int i = 0; i < sourceCount; i++)
|
|
||||||
{
|
|
||||||
targetSpan[i] = (items._keys[i], items._values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
list._count = targetCapacity;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerator AddRange<T>(this RecyclableList<T> list, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
public static IEnumerator AddRange<T>(this RecyclableList<T> list, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
@@ -649,58 +293,27 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
AddRange(list, sourceList);
|
AddRange(list, sourceList);
|
||||||
}
|
}
|
||||||
else if (items is ICollection<T> sourceICollection)
|
else if (items is ICollection<T> sourceICollection)
|
||||||
{
|
{
|
||||||
AddRange(list, sourceICollection);
|
AddRange(list, sourceICollection);
|
||||||
}
|
}
|
||||||
else if (items is ICollection sourceICollectionWithObjects)
|
else if (items is ICollection sourceICollectionWithObjects)
|
||||||
{
|
{
|
||||||
AddRange(list, sourceICollectionWithObjects);
|
AddRange(list, sourceICollectionWithObjects);
|
||||||
}
|
}
|
||||||
else if (items is RecyclableHashSet<T> sourceHashSet)
|
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
|
||||||
{
|
{
|
||||||
AddRange(list, sourceHashSet);
|
AddRange(list, sourceIReadOnlyList);
|
||||||
}
|
}
|
||||||
else if (items is RecyclableStack<T> sourceStack)
|
|
||||||
{
|
|
||||||
AddRange(list, sourceStack);
|
|
||||||
}
|
|
||||||
else if (items is RecyclableSortedSet<T> sourceSortedSet)
|
|
||||||
{
|
|
||||||
AddRange(list, sourceSortedSet);
|
|
||||||
}
|
|
||||||
else if (items is RecyclableLinkedList<T> sourceLinkedList)
|
|
||||||
{
|
|
||||||
AddRange(list, sourceLinkedList);
|
|
||||||
}
|
|
||||||
else if (items is RecyclablePriorityQueue<T> sourcePriorityQueue)
|
|
||||||
{
|
|
||||||
AddRange(list, sourcePriorityQueue);
|
|
||||||
}
|
|
||||||
else if (items is RecyclableQueue<T> sourceQueue)
|
|
||||||
{
|
|
||||||
AddRange(list, sourceQueue);
|
|
||||||
}
|
|
||||||
else if (AddRangeHelper<T>.TryAddRange(list, items))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (items is IReadOnlyList<T> sourceIReadOnlyList)
|
|
||||||
{
|
|
||||||
AddRange(list, sourceIReadOnlyList);
|
|
||||||
}
|
|
||||||
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
|
else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0)
|
||||||
{
|
{
|
||||||
AddRangeWithKnownCount(list, items, list._count, requiredAdditionalCapacity);
|
AddRangeWithKnownCount(list, items, list._count, requiredAdditionalCapacity);
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddRangeEnumerated(list, items, growByCount);
|
AddRangeEnumerated(list, items, growByCount);
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
list._version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,272 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
|
|
||||||
public static class zRecyclableListCompatibilityListFind
|
|
||||||
{
|
|
||||||
public static bool Exists<T>(this RecyclableList<T> list, Predicate<T> match)
|
|
||||||
{
|
|
||||||
int sourceItemsCount = list._count;
|
|
||||||
if (sourceItemsCount == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadOnlySpan<T> sourceSpan = 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
|
|
||||||
public static class zRecyclableListCompatibilityListIndexOf
|
|
||||||
{
|
|
||||||
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
|
|
||||||
? Array.IndexOf(list._memoryBlock, item, index, list._count - index)
|
|
||||||
: RecyclableDefaults.ItemNotFoundIndex;
|
|
||||||
|
|
||||||
public static int IndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
|
|
||||||
? Array.IndexOf(list._memoryBlock, item, index, count)
|
|
||||||
: RecyclableDefaults.ItemNotFoundIndex;
|
|
||||||
|
|
||||||
public static int LastIndexOf<T>(this RecyclableList<T> list, T item) => list._count != 0
|
|
||||||
? Array.LastIndexOf(list._memoryBlock, item, list._count - 1)
|
|
||||||
: RecyclableDefaults.ItemNotFoundIndex;
|
|
||||||
|
|
||||||
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index) => list._count != 0
|
|
||||||
? Array.LastIndexOf(list._memoryBlock, item, index)
|
|
||||||
: RecyclableDefaults.ItemNotFoundIndex;
|
|
||||||
|
|
||||||
public static int LastIndexOf<T>(this RecyclableList<T> list, T item, int index, int count) => list._count != 0
|
|
||||||
? Array.LastIndexOf(list._memoryBlock, item, index, count)
|
|
||||||
: RecyclableDefaults.ItemNotFoundIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,378 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
|
|
||||||
public static class zRecyclableListCompatibilityListInsertRange
|
|
||||||
{
|
|
||||||
private static void InsertRangeEnumerated<T>(this RecyclableList<T> list, int index, IEnumerable<T> items, int growByCount)
|
|
||||||
{
|
|
||||||
var enumerator = items.GetEnumerator();
|
|
||||||
if (!enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int capacity = list._capacity,
|
|
||||||
added = 0,
|
|
||||||
available = capacity - list._count,
|
|
||||||
i,
|
|
||||||
movingBlockLength = list._count - index;
|
|
||||||
|
|
||||||
T[] memoryBlock = list._memoryBlock;
|
|
||||||
if (available > 0)
|
|
||||||
{
|
|
||||||
Array.Copy(memoryBlock, index, memoryBlock, capacity - movingBlockLength, movingBlockLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<T> memorySpan = 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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Numerics;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
|
|
||||||
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
|
|
||||||
public static class zRecyclableListCompatibilityList
|
|
||||||
{
|
|
||||||
public static ReadOnlyCollection<T> AsReadOnly<T>(this RecyclableList<T> list) => new(list);
|
|
||||||
|
|
||||||
public static int BinarySearch<T>(this RecyclableList<T> list, T item)
|
|
||||||
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item);
|
|
||||||
|
|
||||||
public static int BinarySearch<T>(this RecyclableList<T> list, T item, IComparer<T>? comparer)
|
|
||||||
=> Array.BinarySearch(list._memoryBlock, 0, list._count, item, comparer);
|
|
||||||
|
|
||||||
public static int BinarySearch<T>(this RecyclableList<T> list, int index, int count, T item, IComparer<T>? comparer)
|
|
||||||
=> Array.BinarySearch(list._memoryBlock, index, count, item, comparer);
|
|
||||||
|
|
||||||
public static RecyclableList<TOutput> ConvertAll<T, TOutput>(this RecyclableList<T> list, Converter<T, TOutput> converter)
|
|
||||||
{
|
|
||||||
int itemsCount = list._count;
|
|
||||||
if (itemsCount == 0)
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
|
|
||||||
RecyclableList<TOutput> result = new(itemsCount);
|
|
||||||
TOutput[] resultSpan = result._memoryBlock;
|
|
||||||
ReadOnlySpan<T> sourceSpan = list._memoryBlock;
|
|
||||||
for (int bufferIndex = 0; bufferIndex < itemsCount; bufferIndex++)
|
|
||||||
{
|
|
||||||
resultSpan[bufferIndex] = converter(sourceSpan[bufferIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
result._count = itemsCount;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CopyTo<T>(this RecyclableList<T> list, T[] array) => Array.Copy(list._memoryBlock, array, list._count);
|
|
||||||
|
|
||||||
public static void CopyTo<T>(this RecyclableList<T> list, int index, T[] array, int arrayIndex, int count) => Array.Copy(list._memoryBlock, index, array, arrayIndex, count);
|
|
||||||
|
|
||||||
public static void ForEach<T>(this RecyclableList<T> list, Action<T> action)
|
|
||||||
{
|
|
||||||
int sourceItemsCount = list._count;
|
|
||||||
if (sourceItemsCount == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadOnlySpan<T> sourceSpan = 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 = list.FindAllIndexes(match);
|
|
||||||
int foundItemsCount = allIndexes._count;
|
|
||||||
if (foundItemsCount == 0)
|
|
||||||
{
|
|
||||||
return foundItemsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int removed = 0; removed < foundItemsCount; removed++)
|
|
||||||
{
|
|
||||||
var indexToRemove = allIndexes[removed] - removed;
|
|
||||||
Array.Copy(list._memoryBlock, indexToRemove + 1, list._memoryBlock, indexToRemove, list._count - indexToRemove - removed - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RecyclableList<T>._needsClearing)
|
|
||||||
{
|
|
||||||
Array.Clear(list._memoryBlock, list._count - foundItemsCount, foundItemsCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
list._count -= foundItemsCount;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
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;
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Reverse<T>(this RecyclableList<T> list)
|
|
||||||
{
|
|
||||||
Array.Reverse(list._memoryBlock, 0, list._count);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Reverse<T>(this RecyclableList<T> list, int index, int count)
|
|
||||||
{
|
|
||||||
Array.Reverse(list._memoryBlock, index, count);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sort<T>(this RecyclableList<T> list)
|
|
||||||
{
|
|
||||||
Array.Sort(list._memoryBlock, 0, list._count);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sort<T>(this RecyclableList<T> list, Comparison<T> comparison)
|
|
||||||
{
|
|
||||||
Array.Sort(list._memoryBlock, 0, list._count, new ComparisonToComparerAdapter<T>(comparison));
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sort<T>(this RecyclableList<T> list, IComparer<T>? comparer)
|
|
||||||
{
|
|
||||||
Array.Sort(list._memoryBlock, 0, list._count, comparer);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sort<T>(this RecyclableList<T> list, int index, int count, IComparer<T>? comparer)
|
|
||||||
{
|
|
||||||
Array.Sort(list._memoryBlock, index, count, comparer);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,9 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
public partial class RecyclableList<T>
|
public partial class RecyclableList<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public struct Enumerator : IEnumerator<T>
|
||||||
// TODO: confirm if enumerator should implement a versioned interface for generated types
|
{
|
||||||
public struct Enumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private static readonly bool _isReferenceType = !typeof(T).IsValueType;
|
private static readonly bool _isReferenceType = !typeof(T).IsValueType;
|
||||||
|
|
||||||
private int _currentItemIndex;
|
private int _currentItemIndex;
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
[VersionedCollectionsGenerator.GenerateVersioned]
|
public static class RecyclableListExtensions
|
||||||
public static class RecyclableListExtensions
|
{
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static RecyclableList<T> ToRecyclableList<T>(this Array values) => new(values);
|
public static RecyclableList<T> ToRecyclableList<T>(this Array values) => new(values);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
internal static class RecyclableListHelpers<T>
|
internal static class RecyclableListHelpers<T>
|
||||||
{
|
{
|
||||||
private static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
private static readonly bool _needsClearing = !typeof(T).IsValueType;
|
||||||
|
|
||||||
public static int CalculateIndex(int index, int lowerBound) => index >= 0 ? index + lowerBound : RecyclableDefaults.ItemNotFoundIndex;
|
public static int CalculateIndex(int index, int lowerBound) => index >= 0 ? index + lowerBound : RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ namespace Recyclable.Collections
|
|||||||
// Array.Clear(source);
|
// Array.Clear(source);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, NeedsClearing);
|
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, _needsClearing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ namespace Recyclable.Collections
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
// If anything, it has been already cleared above, so we don't need to repeat it.
|
// If anything, it has been already cleared above, so we don't need to repeat it.
|
||||||
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, NeedsClearing);
|
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, _needsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSize;
|
return newSize;
|
||||||
@@ -80,7 +80,7 @@ namespace Recyclable.Collections
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
// If anything, it has been already cleared above, so we don't need to repeat it.
|
// If anything, it has been already cleared above, so we don't need to repeat it.
|
||||||
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, NeedsClearing);
|
RecyclableArrayPool<T>.ReturnShared(oldMemoryBlock, _needsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSize;
|
return newSize;
|
||||||
|
|||||||
@@ -6,14 +6,11 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
public partial class RecyclableList<T> : IRecyclableVersionedList<T>
|
public partial class RecyclableList<T> : IRecyclableVersionedList<T>
|
||||||
{
|
{
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
VersionedEnumerator IRecyclableVersionedList<T>.GetEnumerator() => new(this);
|
||||||
VersionedEnumerator IRecyclableVersionedList<T>.GetEnumerator() => new(this);
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public struct VersionedEnumerator : IEnumerator<T>
|
||||||
// TODO: confirm if enumerator should implement a versioned interface for generated types
|
{
|
||||||
public struct VersionedEnumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private int _currentItemIndex;
|
private int _currentItemIndex;
|
||||||
private readonly ulong _enumeratorVersion;
|
private readonly ulong _enumeratorVersion;
|
||||||
private readonly RecyclableList<T> _list;
|
private readonly RecyclableList<T> _list;
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ using Recyclable.Collections.Pools;
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
[VersionedCollectionsGenerator.GenerateVersioned]
|
public sealed partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
||||||
public sealed partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
|
||||||
{
|
{
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
||||||
|
|
||||||
@@ -18,8 +17,7 @@ namespace Recyclable.Collections
|
|||||||
#nullable restore
|
#nullable restore
|
||||||
|
|
||||||
internal ulong _version;
|
internal ulong _version;
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public ulong Version => _version;
|
||||||
public ulong Version => _version;
|
|
||||||
|
|
||||||
public RecyclableList()
|
public RecyclableList()
|
||||||
{
|
{
|
||||||
@@ -43,22 +41,6 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecyclableList(int initialCapacity, int lowerBound)
|
|
||||||
{
|
|
||||||
if (initialCapacity >= RecyclableDefaults.InitialCapacity)
|
|
||||||
{
|
|
||||||
_memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<T>.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity)))
|
|
||||||
: new T[initialCapacity];
|
|
||||||
_capacity = _memoryBlock.Length;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_memoryBlock = new T[RecyclableDefaults.InitialCapacity];
|
|
||||||
_capacity = RecyclableDefaults.InitialCapacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecyclableList(in Array source)
|
public RecyclableList(in Array source)
|
||||||
{
|
{
|
||||||
_capacity = RecyclableDefaults.InitialCapacity;
|
_capacity = RecyclableDefaults.InitialCapacity;
|
||||||
@@ -174,9 +156,7 @@ namespace Recyclable.Collections
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
_memoryBlock[index] = value;
|
_memoryBlock[index] = value;
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,19 +169,17 @@ namespace Recyclable.Collections
|
|||||||
if (_capacity != value)
|
if (_capacity != value)
|
||||||
{
|
{
|
||||||
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)value)));
|
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)value)));
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int _count;
|
internal int _count;
|
||||||
public int Count
|
public int Count
|
||||||
{
|
{
|
||||||
get => _count;
|
get => _count;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_count != value)
|
if (_count != value)
|
||||||
{
|
{
|
||||||
if (value > _capacity)
|
if (value > _capacity)
|
||||||
@@ -210,30 +188,12 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_count = value;
|
_count = value;
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public bool IsReadOnly => false;
|
||||||
/// Ensures that the capacity of this list is at least the specified value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="capacity">The minimum capacity to ensure.</param>
|
|
||||||
/// <returns>The new capacity of the list.</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public int EnsureCapacity(int capacity)
|
|
||||||
{
|
|
||||||
if (capacity > _capacity)
|
|
||||||
{
|
|
||||||
_capacity = RecyclableListHelpers<T>.EnsureCapacity(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)capacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsReadOnly => false;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(T item)
|
public void Add(T item)
|
||||||
@@ -251,9 +211,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_memoryBlock[_count++] = item;
|
_memoryBlock[_count++] = item;
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T[] AsArray => _memoryBlock;
|
public T[] AsArray => _memoryBlock;
|
||||||
@@ -272,9 +230,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_count = 0;
|
_count = 0;
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -288,6 +244,16 @@ namespace Recyclable.Collections
|
|||||||
? Array.IndexOf(_memoryBlock, itemToFind, 0, _count)
|
? Array.IndexOf(_memoryBlock, itemToFind, 0, _count)
|
||||||
: RecyclableDefaults.ItemNotFoundIndex;
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int IndexOf(T item, int index) => _count != 0
|
||||||
|
? Array.IndexOf(_memoryBlock, item, index, _count - index)
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int IndexOf(T item, int index, int count) => _count != 0
|
||||||
|
? Array.IndexOf(_memoryBlock, item, index, count)
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Insert(int index, T item)
|
public void Insert(int index, T item)
|
||||||
{
|
{
|
||||||
@@ -307,9 +273,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
_memoryBlock[index] = item;
|
_memoryBlock[index] = item;
|
||||||
_count++;
|
_count++;
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -329,9 +293,7 @@ namespace Recyclable.Collections
|
|||||||
_memoryBlock[_count] = default;
|
_memoryBlock[_count] = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -358,31 +320,21 @@ namespace Recyclable.Collections
|
|||||||
_memoryBlock[_count] = default;
|
_memoryBlock[_count] = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public Enumerator GetEnumerator() => new(this);
|
||||||
// TODO: review if returned enumerator type should change for versioned list
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
||||||
// TODO: review if returned enumerator type should change for versioned list
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
||||||
// TODO: review if returned enumerator type should change for versioned list
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
#if WITH_VERSIONING
|
|
||||||
_version++;
|
_version++;
|
||||||
#endif
|
|
||||||
if (_count != 0)
|
if (_count != 0)
|
||||||
{
|
{
|
||||||
if (_needsClearing)
|
if (_needsClearing)
|
||||||
|
|||||||
@@ -1,83 +1,15 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
using Recyclable.Collections.Pools;
|
using Recyclable.Collections.Pools;
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
public static class zRecyclableLongListAddRange
|
#pragma warning disable IDE1006 // This is intentional naming for the class to show up at the end of IntelliSense list.
|
||||||
{
|
public static class zRecyclableLongListAddRange
|
||||||
|
#pragma warning restore IDE1006
|
||||||
private static class AddRangeHelper<T>
|
{
|
||||||
{
|
private static void AddRangeEnumerated<T>(this RecyclableLongList<T> targetList, IEnumerable<T> items)
|
||||||
private static readonly Action<RecyclableLongList<T>, object>? _dictionaryAdder;
|
{
|
||||||
private static readonly Action<RecyclableLongList<T>, object>? _sortedListAdder;
|
|
||||||
private static readonly Type? _dictionaryType;
|
|
||||||
private static readonly Type? _sortedListType;
|
|
||||||
|
|
||||||
static AddRangeHelper()
|
|
||||||
{
|
|
||||||
Type elementType = typeof(T);
|
|
||||||
if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
|
|
||||||
{
|
|
||||||
Type[] args = elementType.GetGenericArguments();
|
|
||||||
_dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args);
|
|
||||||
MethodInfo? method = typeof(zRecyclableLongListAddRange).GetMethod(
|
|
||||||
nameof(AddRange),
|
|
||||||
BindingFlags.NonPublic | BindingFlags.Static,
|
|
||||||
binder: null,
|
|
||||||
types: new[] { typeof(RecyclableLongList<>).MakeGenericType(elementType), _dictionaryType },
|
|
||||||
modifiers: null);
|
|
||||||
if (method != null)
|
|
||||||
{
|
|
||||||
var listParam = Expression.Parameter(typeof(RecyclableLongList<T>));
|
|
||||||
var objParam = Expression.Parameter(typeof(object));
|
|
||||||
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _dictionaryType));
|
|
||||||
_dictionaryAdder = Expression.Lambda<Action<RecyclableLongList<T>, object>>(call, listParam, objParam).Compile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>))
|
|
||||||
{
|
|
||||||
Type[] args = elementType.GetGenericArguments();
|
|
||||||
_sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args);
|
|
||||||
MethodInfo? method = typeof(zRecyclableLongListAddRange).GetMethod(
|
|
||||||
nameof(AddRange),
|
|
||||||
BindingFlags.NonPublic | BindingFlags.Static,
|
|
||||||
binder: null,
|
|
||||||
types: new[] { typeof(RecyclableLongList<>).MakeGenericType(elementType), _sortedListType },
|
|
||||||
modifiers: null);
|
|
||||||
if (method != null)
|
|
||||||
{
|
|
||||||
var listParam = Expression.Parameter(typeof(RecyclableLongList<T>));
|
|
||||||
var objParam = Expression.Parameter(typeof(object));
|
|
||||||
var call = Expression.Call(method, listParam, Expression.Convert(objParam, _sortedListType));
|
|
||||||
_sortedListAdder = Expression.Lambda<Action<RecyclableLongList<T>, object>>(call, listParam, objParam).Compile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool TryAddRange(RecyclableLongList<T> list, IEnumerable<T> items)
|
|
||||||
{
|
|
||||||
if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items))
|
|
||||||
{
|
|
||||||
_dictionaryAdder(list, items);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items))
|
|
||||||
{
|
|
||||||
_sortedListAdder(list, items);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddRangeEnumerated<T>(this RecyclableLongList<T> targetList, IEnumerable<T> items)
|
|
||||||
{
|
|
||||||
using IEnumerator<T> enumerator = items.GetEnumerator();
|
using IEnumerator<T> enumerator = items.GetEnumerator();
|
||||||
if (!enumerator.MoveNext())
|
if (!enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
@@ -100,7 +32,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (copied + blockSize > capacity)
|
if (copied + blockSize > capacity)
|
||||||
{
|
{
|
||||||
capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
|
capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
|
||||||
memoryBlocksSpan = targetList._memoryBlocks;
|
memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
available = capacity - copied;
|
available = capacity - copied;
|
||||||
}
|
}
|
||||||
@@ -113,7 +45,7 @@ namespace Recyclable.Collections
|
|||||||
if (!enumerator.MoveNext())
|
if (!enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
targetList._lastBlockWithData = targetBlockIdx;
|
targetList._lastBlockWithData = targetBlockIdx;
|
||||||
targetList._longCount += copied + i - targetList._longCount;
|
targetList._longCount = copied + i;
|
||||||
if (targetItemIdx < blockSize)
|
if (targetItemIdx < blockSize)
|
||||||
{
|
{
|
||||||
targetList._nextItemBlockIndex = targetBlockIdx;
|
targetList._nextItemBlockIndex = targetBlockIdx;
|
||||||
@@ -126,9 +58,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetList._capacity = capacity;
|
targetList._capacity = capacity;
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +93,7 @@ targetList._version++;
|
|||||||
|
|
||||||
if (targetList._capacity < requiredCapacity)
|
if (targetList._capacity < requiredCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
@@ -188,13 +118,10 @@ targetList._version++;
|
|||||||
targetList._nextItemBlockIndex = targetBlockIdx;
|
targetList._nextItemBlockIndex = targetBlockIdx;
|
||||||
targetList._nextItemIndex = targetItemIdx;
|
targetList._nextItemIndex = targetItemIdx;
|
||||||
targetList._lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1);
|
targetList._lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1);
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[VersionedCollectionsGenerator.RewriteForVersioned]
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, in Array items)
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, in Array items)
|
|
||||||
{
|
{
|
||||||
if (items.LongLength == 0)
|
if (items.LongLength == 0)
|
||||||
{
|
{
|
||||||
@@ -210,7 +137,7 @@ targetList._version++;
|
|||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
@@ -245,9 +172,7 @@ targetList._version++;
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
targetList._longCount = targetCapacity;
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection items)
|
||||||
@@ -261,7 +186,7 @@ targetList._version++;
|
|||||||
long targetCapacity = targetList._longCount + items.Count;
|
long targetCapacity = targetList._longCount + items.Count;
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're better off to temporarily copy it to a fixed array,
|
// We're better off to temporarily copy it to a fixed array,
|
||||||
@@ -312,9 +237,7 @@ targetList._version++;
|
|||||||
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
|
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection<T> items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, ICollection<T> items)
|
||||||
@@ -329,7 +252,7 @@ targetList._version++;
|
|||||||
long targetCapacity = targetList._longCount + items.Count;
|
long targetCapacity = targetList._longCount + items.Count;
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're better off to temporarily copy it to a fixed array,
|
// We're better off to temporarily copy it to a fixed array,
|
||||||
@@ -382,9 +305,7 @@ targetList._version++;
|
|||||||
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
|
RecyclableArrayPool<T>.ReturnShared(itemsBuffer, RecyclableLongList<T>._needsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, IEnumerable source)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, IEnumerable source)
|
||||||
@@ -411,7 +332,7 @@ targetList._version++;
|
|||||||
{
|
{
|
||||||
if (copied + blockSize > capacity)
|
if (copied + blockSize > capacity)
|
||||||
{
|
{
|
||||||
capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
|
capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize))));
|
||||||
memoryBlocksSpan = targetList._memoryBlocks;
|
memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
available = capacity - copied;
|
available = capacity - copied;
|
||||||
}
|
}
|
||||||
@@ -424,7 +345,7 @@ targetList._version++;
|
|||||||
if (!enumerator.MoveNext())
|
if (!enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
targetList._lastBlockWithData = targetBlockIdx;
|
targetList._lastBlockWithData = targetBlockIdx;
|
||||||
targetList._longCount += copied + i - targetList._longCount;
|
targetList._longCount = copied + i;
|
||||||
if (targetItemIdx < blockSize)
|
if (targetItemIdx < blockSize)
|
||||||
{
|
{
|
||||||
targetList._nextItemBlockIndex = targetBlockIdx;
|
targetList._nextItemBlockIndex = targetBlockIdx;
|
||||||
@@ -486,56 +407,27 @@ targetList._version++;
|
|||||||
targetList.AddRange(sourceICollection);
|
targetList.AddRange(sourceICollection);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case ICollection sourceICollectionWithObjects:
|
case ICollection sourceICollectionWithObjects:
|
||||||
targetList.AddRange(sourceICollectionWithObjects);
|
targetList.AddRange(sourceICollectionWithObjects);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case IReadOnlyList<T> sourceIReadOnlyList:
|
case IReadOnlyList<T> sourceIReadOnlyList:
|
||||||
targetList.AddRange(sourceIReadOnlyList);
|
targetList.AddRange(sourceIReadOnlyList);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case RecyclableHashSet<T> sourceHashSet:
|
default:
|
||||||
targetList.AddRange(sourceHashSet);
|
if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||||
return;
|
{
|
||||||
|
targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetList.AddRangeEnumerated(items);
|
||||||
|
}
|
||||||
|
|
||||||
case RecyclableStack<T> sourceStack:
|
return;
|
||||||
targetList.AddRange(sourceStack);
|
}
|
||||||
return;
|
}
|
||||||
|
|
||||||
case RecyclableSortedSet<T> sourceSortedSet:
|
|
||||||
targetList.AddRange(sourceSortedSet);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case RecyclableLinkedList<T> sourceLinkedList:
|
|
||||||
targetList.AddRange(sourceLinkedList);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case RecyclablePriorityQueue<T> sourcePriorityQueue:
|
|
||||||
targetList.AddRange(sourcePriorityQueue);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case RecyclableQueue<T> sourceQueue:
|
|
||||||
targetList.AddRange(sourceQueue);
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (AddRangeHelper<T>.TryAddRange(targetList, items))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
|
||||||
{
|
|
||||||
targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
targetList.AddRangeEnumerated(items);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, IReadOnlyList<T> items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, IReadOnlyList<T> items)
|
||||||
{
|
{
|
||||||
@@ -553,7 +445,7 @@ targetList._version++;
|
|||||||
|
|
||||||
if (targetList._capacity < requiredCapacity)
|
if (targetList._capacity < requiredCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
@@ -596,7 +488,7 @@ targetList._version++;
|
|||||||
long targetCapacity = targetList._longCount + items.Count;
|
long targetCapacity = targetList._longCount + items.Count;
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
@@ -627,9 +519,7 @@ targetList._version++;
|
|||||||
targetList._nextItemBlockIndex = targetBlockIndex;
|
targetList._nextItemBlockIndex = targetBlockIndex;
|
||||||
targetList._longCount = targetCapacity;
|
targetList._longCount = targetCapacity;
|
||||||
targetList._lastBlockWithData = targetBlockIndex - (targetList._nextItemIndex > 0 ? 0 : 1);
|
targetList._lastBlockWithData = targetBlockIndex - (targetList._nextItemIndex > 0 ? 0 : 1);
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, ReadOnlySpan<T> items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, ReadOnlySpan<T> items)
|
||||||
@@ -644,7 +534,7 @@ targetList._version++;
|
|||||||
long targetCapacity = targetList._longCount + items.Length;
|
long targetCapacity = targetList._longCount + items.Length;
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
@@ -684,9 +574,7 @@ targetList._version++;
|
|||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
targetList._longCount = targetCapacity;
|
||||||
targetList._lastBlockWithData = targetBlockIndex;
|
targetList._lastBlockWithData = targetBlockIndex;
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableList<T> items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableList<T> items)
|
||||||
@@ -701,7 +589,7 @@ targetList._version++;
|
|||||||
long targetCapacity = targetList._longCount + items._count;
|
long targetCapacity = targetList._longCount + items._count;
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T> itemsSpan = new(items._memoryBlock, 0, items._count);
|
ReadOnlySpan<T> itemsSpan = new(items._memoryBlock, 0, items._count);
|
||||||
@@ -743,9 +631,7 @@ targetList._version++;
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
targetList._longCount = targetCapacity;
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableLongList<T> items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableLongList<T> items)
|
||||||
@@ -765,7 +651,7 @@ targetList._version++;
|
|||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T[]> sourceMemoryBlocksSpan = items._memoryBlocks;
|
ReadOnlySpan<T[]> sourceMemoryBlocksSpan = items._memoryBlocks;
|
||||||
@@ -818,9 +704,7 @@ targetList._version++;
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
targetList._longCount = targetCapacity;
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, Span<T> items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, Span<T> items)
|
||||||
@@ -835,7 +719,7 @@ targetList._version++;
|
|||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
ReadOnlySpan<T[]> memoryBlocksSpan = targetList._memoryBlocks;
|
||||||
@@ -875,24 +759,22 @@ targetList._version++;
|
|||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
targetList._longCount = targetCapacity;
|
||||||
targetList._lastBlockWithData = targetBlockIndex;
|
targetList._lastBlockWithData = targetBlockIndex;
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRange<T>(this RecyclableLongList<T> targetList, in T[] items)
|
public static void AddRange<T>(this RecyclableLongList<T> targetList, in T[] items)
|
||||||
{
|
{
|
||||||
if (items.LongLength == 0)
|
if (items.LongLength == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int targetBlockIndex = targetList._nextItemBlockIndex;
|
int targetBlockIndex = targetList._nextItemBlockIndex;
|
||||||
|
|
||||||
long targetCapacity = targetList._longCount + items.LongLength;
|
long targetCapacity = targetList._longCount + items.LongLength;
|
||||||
if (targetList._capacity < targetCapacity)
|
if (targetList._capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(ref targetList._memoryBlocks, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<T> itemsSpan = items;
|
ReadOnlySpan<T> itemsSpan = items;
|
||||||
@@ -932,362 +814,8 @@ targetList._version++;
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
targetList._longCount = targetCapacity;
|
||||||
targetList._lastBlockWithData = targetBlockIndex;
|
targetList._lastBlockWithData = targetBlockIndex;
|
||||||
#if WITH_VERSIONING
|
targetList._version++;
|
||||||
targetList._version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableHashSet<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._count,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
var entries = items._entries;
|
|
||||||
int shift = items._blockShift,
|
|
||||||
mask = items._blockSizeMinus1;
|
|
||||||
|
|
||||||
for (long i = 0; i < sourceCount; i++)
|
|
||||||
{
|
|
||||||
blocks[blockIndex][itemIndex++] = entries[i >> shift][(int)(i & mask)].Value;
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableStack<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._count,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
var chunk = items._current;
|
|
||||||
while (chunk != null)
|
|
||||||
{
|
|
||||||
var array = chunk.Value;
|
|
||||||
for (int i = chunk.Index - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
blocks[blockIndex][itemIndex++] = array[i];
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = chunk.Previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableSortedSet<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._count,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
while (idx < items._count)
|
|
||||||
{
|
|
||||||
blocks[blockIndex][itemIndex++] = items._items[idx++];
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableLinkedList<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._count,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
var chunk = items._head;
|
|
||||||
while (chunk != null)
|
|
||||||
{
|
|
||||||
for (int i = chunk.Bottom; i < chunk.Top; i++)
|
|
||||||
{
|
|
||||||
blocks[blockIndex][itemIndex++] = chunk.Value[i];
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = chunk.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclablePriorityQueue<T> items)
|
|
||||||
{
|
|
||||||
if (items._size == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._size,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
while (idx < items._size)
|
|
||||||
{
|
|
||||||
blocks[blockIndex][itemIndex++] = items._heap[idx++];
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<T>(this RecyclableLongList<T> targetList, RecyclableQueue<T> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._count,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<T>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
var chunkQ = items._head;
|
|
||||||
while (chunkQ != null)
|
|
||||||
{
|
|
||||||
for (int i = chunkQ.Bottom; i < chunkQ.Top; i++)
|
|
||||||
{
|
|
||||||
blocks[blockIndex][itemIndex++] = chunkQ.Value[i];
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkQ = chunkQ.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<TKey, TValue>(this RecyclableLongList<KeyValuePair<TKey, TValue>> targetList, RecyclableDictionary<TKey, TValue> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._count,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<KeyValuePair<TKey, TValue>>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
var entries = items._entries;
|
|
||||||
int shift = items._blockShift,
|
|
||||||
mask = items._blockSizeMinus1;
|
|
||||||
|
|
||||||
for (long i = 0; i < sourceCount; i++)
|
|
||||||
{
|
|
||||||
ref var entry = ref entries[i >> shift][(int)(i & mask)];
|
|
||||||
blocks[blockIndex][itemIndex++] = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
|
||||||
internal static void AddRange<TKey, TValue>(this RecyclableLongList<(TKey Key, TValue Value)> targetList, RecyclableSortedList<TKey, TValue> items)
|
|
||||||
{
|
|
||||||
if (items._count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sourceCount = items._count,
|
|
||||||
targetCapacity = targetList._longCount + sourceCount;
|
|
||||||
|
|
||||||
if (targetList._capacity < targetCapacity)
|
|
||||||
{
|
|
||||||
targetList._capacity = RecyclableLongList<(TKey Key, TValue Value)>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks = targetList._memoryBlocks;
|
|
||||||
int blockIndex = targetList._nextItemBlockIndex,
|
|
||||||
itemIndex = targetList._nextItemIndex,
|
|
||||||
blockSize = targetList._blockSize;
|
|
||||||
|
|
||||||
int idx2 = 0;
|
|
||||||
while (idx2 < items._count)
|
|
||||||
{
|
|
||||||
blocks[blockIndex][itemIndex++] = (items._keys[idx2], items._values[idx2]);
|
|
||||||
idx2++;
|
|
||||||
if (itemIndex == blockSize)
|
|
||||||
{
|
|
||||||
itemIndex = 0;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetList._longCount = targetCapacity;
|
|
||||||
targetList._nextItemBlockIndex = blockIndex;
|
|
||||||
targetList._nextItemIndex = itemIndex;
|
|
||||||
targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
targetList._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Recyclable.Collections.Linq;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
[Obsolete("It'll be moved to Recyclable.Collections.Compatibility.List NuGet package in the next release.")]
|
|
||||||
public static class zRecyclableLongListCompatibilityListSorted
|
|
||||||
{
|
|
||||||
public static void Sort<T>(this RecyclableLongList<T> list)
|
|
||||||
{
|
|
||||||
QuickSortExtensions<T>.QuickSort(list);
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sort<T>(this RecyclableLongList<T> list, Comparison<T> comparison)
|
|
||||||
{
|
|
||||||
QuickSortExtensions<T>.QuickSort(list, new ComparisonToComparerAdapter<T>(comparison), new SystemRandomNumberGenerator());
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sort<T>(this RecyclableLongList<T> list, IComparer<T>? comparer)
|
|
||||||
{
|
|
||||||
QuickSortExtensions<T>.QuickSort(list, comparer ?? Comparer<T>.Default, new SystemRandomNumberGenerator());
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Sort<T>(this RecyclableLongList<T> list, long index, long count, IComparer<T>? comparer)
|
|
||||||
{
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QuickSortExtensions<T>.QuickSort(list, (int)index, (int)(index + count - 1), comparer ?? Comparer<T>.Default, new SystemRandomNumberGenerator());
|
|
||||||
#if WITH_VERSIONING
|
|
||||||
list._version++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,8 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
public partial class RecyclableLongList<T>
|
public partial class RecyclableLongList<T>
|
||||||
{
|
{
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public struct Enumerator : IEnumerator<T>
|
||||||
// TODO: confirm if enumerator should implement a versioned interface for generated types
|
{
|
||||||
public struct Enumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
private static readonly T _default = default;
|
private static readonly T _default = default;
|
||||||
#nullable restore
|
#nullable restore
|
||||||
@@ -18,7 +16,9 @@ namespace Recyclable.Collections
|
|||||||
private readonly RecyclableLongList<T> _list;
|
private readonly RecyclableLongList<T> _list;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
#pragma warning disable IDE0032 // This is intentional due to better performance
|
||||||
private T _current;
|
private T _current;
|
||||||
|
#pragma warning restore IDE0032
|
||||||
#nullable restore
|
#nullable restore
|
||||||
|
|
||||||
[Pure]
|
[Pure]
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
[VersionedCollectionsGenerator.GenerateVersioned]
|
public static class RecyclableLongListExtensions
|
||||||
public static class RecyclableLongListExtensions
|
{
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static RecyclableLongList<T> ToRecyclableLongList<T>(this Array values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
public static RecyclableLongList<T> ToRecyclableLongList<T>(this Array values, int minBlockSize = RecyclableDefaults.BlockSize) => new(values, minBlockSize);
|
||||||
|
|
||||||
|
|||||||
@@ -142,9 +142,9 @@ namespace Recyclable.Collections
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>The maximum no. of items <paramref name="list"/> can store.</returns>
|
/// <returns>The maximum no. of items <paramref name="list"/> can store.</returns>
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static long Resize(RecyclableLongList<T> list, int minBlockSize, byte minBlockSizePow2Shift, long newCapacity)
|
public static long Resize(ref T[][] memoryBlocks, int minBlockSize, byte minBlockSizePow2Shift, long newCapacity)
|
||||||
{
|
{
|
||||||
int sourceBlockCount = list._memoryBlocks?.Length ?? 0;
|
int sourceBlockCount = memoryBlocks?.Length ?? 0;
|
||||||
int requiredBlockCount = checked((int)(newCapacity >> minBlockSizePow2Shift) + ((newCapacity & (minBlockSize - 1)) != 0 ? 1 : 0));
|
int requiredBlockCount = checked((int)(newCapacity >> minBlockSizePow2Shift) + ((newCapacity & (minBlockSize - 1)) != 0 ? 1 : 0));
|
||||||
int blockIndex;
|
int blockIndex;
|
||||||
|
|
||||||
@@ -157,19 +157,19 @@ namespace Recyclable.Collections
|
|||||||
// Copy arrays from the old memory block for all arrays
|
// Copy arrays from the old memory block for all arrays
|
||||||
if (sourceBlockCount > 0)
|
if (sourceBlockCount > 0)
|
||||||
{
|
{
|
||||||
Array.Copy(list._memoryBlocks!, newMemoryBlocks, sourceBlockCount);
|
Array.Copy(memoryBlocks!, newMemoryBlocks, sourceBlockCount);
|
||||||
// We can now return the old memory block for all arrays itself
|
// We can now return the old memory block for all arrays itself
|
||||||
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
|
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
RecyclableArrayPool<T[]>.ReturnShared(list._memoryBlocks!, true);
|
RecyclableArrayPool<T[]>.ReturnShared(memoryBlocks!, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list._memoryBlocks = newMemoryBlocks;
|
memoryBlocks = newMemoryBlocks;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newMemoryBlocks = list._memoryBlocks!;
|
newMemoryBlocks = memoryBlocks!;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate arrays for any new blocks
|
// Allocate arrays for any new blocks
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ namespace Recyclable.Collections
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static long IndexOfParallel(RecyclableLongList<T> list, T item)
|
public static long IndexOfParallel(RecyclableLongList<T> list, T item)
|
||||||
{
|
{
|
||||||
// & WAS SLOWER
|
// & WAS SLOWER
|
||||||
//long itemsCount = Array.IndexOf(list._memoryBlocks[0], item, 0, (int)Math.Min(list._blockSize, list._longCount));
|
//long itemsCount = Array.IndexOf(list._memoryBlocks[0], item, 0, (int)Math.Min(list._blockSize, list._longCount));
|
||||||
@@ -284,7 +284,184 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
context.Exception.CaptureAndRethrow();
|
context.Exception.CaptureAndReThrow();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
context.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndexLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static long IndexOfParallel(RecyclableLongList<T> list, T item, int firstBlockIndex)
|
||||||
|
{
|
||||||
|
IndexOfSynchronizationContext context = IndexOfSynchronizationContextPool.GetWithOneParticipant();
|
||||||
|
|
||||||
|
int blockIndex = firstBlockIndex,
|
||||||
|
lastBlockWithData = list._lastBlockWithData,
|
||||||
|
lastFullBlockWithData = lastBlockWithData - BlocksForEachTask;
|
||||||
|
|
||||||
|
while (blockIndex < lastFullBlockWithData)
|
||||||
|
{
|
||||||
|
// At this point itemIndex is limited to blockSize range - int.
|
||||||
|
|
||||||
|
// & WAS SLOWER
|
||||||
|
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||||
|
// Task.Factory.StartNew(() =>
|
||||||
|
context.AddParticipant();
|
||||||
|
var startingBlock = blockIndex;
|
||||||
|
_ = Task.Run(() => IndexOfInConsecutiveBlocks(context, list, startingBlock, item));
|
||||||
|
|
||||||
|
// & WAS SLOWER
|
||||||
|
// ScheduleIndexOfTask(context, list, blockIndex, item);
|
||||||
|
if (context._isItemFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockIndex += BlocksForEachTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context._isItemFound)
|
||||||
|
{
|
||||||
|
while (blockIndex < lastBlockWithData)
|
||||||
|
{
|
||||||
|
// & WAS SLOWER
|
||||||
|
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||||
|
// Task.Factory.StartNew(() =>
|
||||||
|
var startingBlock = blockIndex;
|
||||||
|
context.AddParticipant();
|
||||||
|
_ = Task.Run(() => IndexOfIn1Block(context, list, startingBlock, item));
|
||||||
|
|
||||||
|
// & WAS SLOWER
|
||||||
|
// ScheduleIndexOfTaskSingle(context, list, blockIndex, item);
|
||||||
|
if (context._isItemFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context._isItemFound)
|
||||||
|
{
|
||||||
|
int itemIndex = list._nextItemIndex != 0
|
||||||
|
? Array.IndexOf(list._memoryBlocks[lastBlockWithData], item, 0, list._nextItemIndex)
|
||||||
|
: Array.IndexOf(list._memoryBlocks[lastBlockWithData], item, 0, list._blockSize);
|
||||||
|
|
||||||
|
if (!context._isItemFound && itemIndex >= 0)
|
||||||
|
{
|
||||||
|
ExchangeFoundItemIndexIfLower(context, lastBlockWithData, ((long)lastBlockWithData << list._blockSizePow2BitShift) + itemIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.SignalAndWait();
|
||||||
|
if (context.Exception == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return context.FoundItemIndex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
context.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
context.Exception.CaptureAndReThrow();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
context.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndexLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static long IndexOfParallel(RecyclableLongList<T> list, T item, int firstBlockIndex, long count)
|
||||||
|
{
|
||||||
|
IndexOfSynchronizationContext context = IndexOfSynchronizationContextPool.GetWithOneParticipant();
|
||||||
|
|
||||||
|
int blockIndex = firstBlockIndex,
|
||||||
|
lastBlockWithData = (int)Math.Min((count >> list._blockSizePow2BitShift) + firstBlockIndex + ((count & list._blockSizeMinus1) > 0 ? 1 : 0) - 1, list._lastBlockWithData),
|
||||||
|
lastFullBlockWithData = lastBlockWithData - BlocksForEachTask;
|
||||||
|
|
||||||
|
while (blockIndex < lastFullBlockWithData)
|
||||||
|
{
|
||||||
|
// At this point itemIndex is limited to blockSize range - int.
|
||||||
|
|
||||||
|
// & WAS SLOWER
|
||||||
|
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||||
|
// Task.Factory.StartNew(() =>
|
||||||
|
context.AddParticipant();
|
||||||
|
var startingBlock = blockIndex;
|
||||||
|
_ = Task.Run(() => IndexOfInConsecutiveBlocks(context, list, startingBlock, item));
|
||||||
|
|
||||||
|
// & WAS SLOWER
|
||||||
|
// ScheduleIndexOfTask(context, list, blockIndex, item);
|
||||||
|
if (context._isItemFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockIndex += BlocksForEachTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context._isItemFound)
|
||||||
|
{
|
||||||
|
count -= (blockIndex - firstBlockIndex) << list._blockSizePow2BitShift;
|
||||||
|
while (blockIndex < lastBlockWithData)
|
||||||
|
{
|
||||||
|
// & WAS SLOWER
|
||||||
|
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||||
|
// Task.Factory.StartNew(() =>
|
||||||
|
var startingBlock = blockIndex;
|
||||||
|
context.AddParticipant();
|
||||||
|
_ = Task.Run(() => IndexOfIn1Block(context, list, startingBlock, item));
|
||||||
|
|
||||||
|
// & WAS SLOWER
|
||||||
|
// ScheduleIndexOfTaskSingle(context, list, blockIndex, item);
|
||||||
|
if (context._isItemFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockIndex++;
|
||||||
|
count -= list._blockSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context._isItemFound && count > 0)
|
||||||
|
{
|
||||||
|
int itemIndex = Array.IndexOf(list._memoryBlocks[lastBlockWithData], item, 0, (int)count);
|
||||||
|
if (!context._isItemFound && itemIndex >= 0)
|
||||||
|
{
|
||||||
|
ExchangeFoundItemIndexIfLower(context, lastBlockWithData, ((long)lastBlockWithData << list._blockSizePow2BitShift) + itemIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.SignalAndWait();
|
||||||
|
if (context.Exception == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return context.FoundItemIndex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
context.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
context.Exception.CaptureAndReThrow();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -344,6 +521,119 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
return itemIndex >= 0 ? itemIndex + ((long)list._lastBlockWithData << list._blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
|
return itemIndex >= 0 ? itemIndex + ((long)list._lastBlockWithData << list._blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static long IndexOfSequential(RecyclableLongList<T> list, in T item, int firstBlockIndex, long count)
|
||||||
|
{
|
||||||
|
int blockIndex,
|
||||||
|
blockSize = list._blockSize,
|
||||||
|
itemIndex,
|
||||||
|
lastBlockWithData = (int)Math.Min((count >> list._blockSizePow2BitShift) + firstBlockIndex + ((count & list._blockSizeMinus1) > 0 ? 1 : 0) - 1, list._lastBlockWithData);
|
||||||
|
|
||||||
|
byte blockSizePow2BitShift = list._blockSizePow2BitShift;
|
||||||
|
|
||||||
|
ReadOnlySpan<T[]> memoryBlocksSpan = new(list._memoryBlocks, 0, lastBlockWithData + 1);
|
||||||
|
for (blockIndex = firstBlockIndex; blockIndex < lastBlockWithData && count > blockSize; blockIndex += 4)
|
||||||
|
{
|
||||||
|
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)blockIndex << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
count -= blockSize;
|
||||||
|
if (blockIndex + 1 < lastBlockWithData && count > blockSize)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 1], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)(blockIndex + 1) << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
count -= blockSize;
|
||||||
|
if (blockIndex + 2 < lastBlockWithData && count > blockSize)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 2], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)(blockIndex + 2) << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
count -= blockSize;
|
||||||
|
if (blockIndex + 3 < lastBlockWithData && count > blockSize)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 3], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)(blockIndex + 3) << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
count -= blockSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[lastBlockWithData], item, 0, (int)count);
|
||||||
|
|
||||||
|
return itemIndex >= 0 ? itemIndex + ((long)lastBlockWithData << blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static long IndexOfSequential(RecyclableLongList<T> list, in T item, int firstBlockIndex)
|
||||||
|
{
|
||||||
|
int blockIndex,
|
||||||
|
blockSize = list._blockSize,
|
||||||
|
itemIndex,
|
||||||
|
lastBlockWithData = list._lastBlockWithData;
|
||||||
|
|
||||||
|
byte blockSizePow2BitShift = list._blockSizePow2BitShift;
|
||||||
|
|
||||||
|
ReadOnlySpan<T[]> memoryBlocksSpan = new(list._memoryBlocks, 0, lastBlockWithData + 1);
|
||||||
|
for (blockIndex = firstBlockIndex; blockIndex < lastBlockWithData; blockIndex += 4)
|
||||||
|
{
|
||||||
|
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)blockIndex << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockIndex + 1 < lastBlockWithData)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 1], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)(blockIndex + 1) << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockIndex + 2 < lastBlockWithData)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 2], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)(blockIndex + 2) << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockIndex + 3 < lastBlockWithData)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex + 3], item, 0, blockSize);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return itemIndex + ((long)(blockIndex + 3) << blockSizePow2BitShift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemIndex = list._nextItemIndex != 0
|
||||||
|
? Array.IndexOf(memoryBlocksSpan[lastBlockWithData], item, 0, list._nextItemIndex)
|
||||||
|
: Array.IndexOf(memoryBlocksSpan[lastBlockWithData], item, 0, list._blockSize);
|
||||||
|
|
||||||
|
return itemIndex >= 0 ? itemIndex + ((long)lastBlockWithData << blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -5,15 +5,11 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
public partial class RecyclableLongList<T> : IRecyclableVersionedLongList<T>
|
public partial class RecyclableLongList<T> : IRecyclableVersionedLongList<T>
|
||||||
{
|
{
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public VersionedEnumerator GetVersionedEnumerator() => new(this);
|
||||||
public VersionedEnumerator GetVersionedEnumerator() => new(this);
|
VersionedEnumerator IRecyclableVersionedLongList<T>.GetEnumerator() => new(this);
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
|
||||||
VersionedEnumerator IRecyclableVersionedLongList<T>.GetEnumerator() => new(this);
|
|
||||||
|
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public struct VersionedEnumerator : IEnumerator<T>
|
||||||
// TODO: confirm if enumerator should implement a versioned interface for generated types
|
{
|
||||||
public struct VersionedEnumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
private static readonly T _default = default;
|
private static readonly T _default = default;
|
||||||
#nullable restore
|
#nullable restore
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Recyclable.Collections.Pools;
|
using Recyclable.Collections.Pools;
|
||||||
@@ -9,8 +10,7 @@ using Recyclable.Collections.Pools;
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
[VersionedCollectionsGenerator.GenerateVersioned]
|
public partial class RecyclableLongList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
||||||
public partial class RecyclableLongList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
|
||||||
{
|
{
|
||||||
private static readonly bool _defaultIsNull = default(T) == null;
|
private static readonly bool _defaultIsNull = default(T) == null;
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
||||||
@@ -26,7 +26,7 @@ namespace Recyclable.Collections
|
|||||||
#nullable restore
|
#nullable restore
|
||||||
internal int _nextItemBlockIndex;
|
internal int _nextItemBlockIndex;
|
||||||
internal int _nextItemIndex;
|
internal int _nextItemIndex;
|
||||||
internal ulong _version;
|
internal ulong _version;
|
||||||
|
|
||||||
public int BlockSize => _blockSize;
|
public int BlockSize => _blockSize;
|
||||||
public int BlockSizeMinus1 => _blockSizeMinus1;
|
public int BlockSizeMinus1 => _blockSizeMinus1;
|
||||||
@@ -36,19 +36,15 @@ namespace Recyclable.Collections
|
|||||||
get => _capacity;
|
get => _capacity;
|
||||||
private set
|
private set
|
||||||
{
|
{
|
||||||
_capacity = value;
|
_capacity = value;
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
if (_capacity != value)
|
||||||
#endif
|
{
|
||||||
if (_capacity != value)
|
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)value)));
|
||||||
{
|
_version++;
|
||||||
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)value)));
|
}
|
||||||
#if WITH_VERSIONING
|
}
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => checked((int)_longCount);
|
public int Count => checked((int)_longCount);
|
||||||
public bool IsReadOnly => false;
|
public bool IsReadOnly => false;
|
||||||
@@ -56,8 +52,7 @@ namespace Recyclable.Collections
|
|||||||
public long LongCount => _longCount;
|
public long LongCount => _longCount;
|
||||||
public int NextItemBlockIndex => _nextItemBlockIndex;
|
public int NextItemBlockIndex => _nextItemBlockIndex;
|
||||||
public int NextItemIndex => _nextItemIndex;
|
public int NextItemIndex => _nextItemIndex;
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public ulong Version => _version;
|
||||||
public ulong Version => _version;
|
|
||||||
|
|
||||||
public RecyclableLongList(int minBlockSize = RecyclableDefaults.BlockSize, long initialCapacity = RecyclableDefaults.InitialCapacity)
|
public RecyclableLongList(int minBlockSize = RecyclableDefaults.BlockSize, long initialCapacity = RecyclableDefaults.InitialCapacity)
|
||||||
{
|
{
|
||||||
@@ -67,7 +62,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
}
|
}
|
||||||
@@ -80,7 +75,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -95,7 +90,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -110,7 +105,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -125,7 +120,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -140,7 +135,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -155,7 +150,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -170,7 +165,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -185,7 +180,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -200,7 +195,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -215,7 +210,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -230,7 +225,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -245,7 +240,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
_blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize));
|
||||||
_capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity)));
|
||||||
_blockSize = minBlockSize;
|
_blockSize = minBlockSize;
|
||||||
_blockSizeMinus1 = minBlockSize - 1;
|
_blockSizeMinus1 = minBlockSize - 1;
|
||||||
|
|
||||||
@@ -257,24 +252,20 @@ namespace Recyclable.Collections
|
|||||||
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
new Span<T>(_memoryBlocks[(int)(index >> _blockSizePow2BitShift)])[(int)(index & _blockSizeMinus1)] = value;
|
new Span<T>(_memoryBlocks[(int)(index >> _blockSizePow2BitShift)])[(int)(index & _blockSizeMinus1)] = value;
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T this[int index]
|
public T this[int index]
|
||||||
{
|
{
|
||||||
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = value;
|
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = value;
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T[][] AsArray => _memoryBlocks;
|
public T[][] AsArray => _memoryBlocks;
|
||||||
|
|
||||||
@@ -283,7 +274,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (_capacity < _longCount + 1)
|
if (_capacity < _longCount + 1)
|
||||||
{
|
{
|
||||||
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(_longCount + 1))));
|
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(_longCount + 1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex++] = item;
|
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex++] = item;
|
||||||
@@ -302,11 +293,9 @@ namespace Recyclable.Collections
|
|||||||
_lastBlockWithData++;
|
_lastBlockWithData++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_longCount++;
|
_longCount++;
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
@@ -338,11 +327,9 @@ namespace Recyclable.Collections
|
|||||||
_nextItemBlockIndex = 0;
|
_nextItemBlockIndex = 0;
|
||||||
_nextItemIndex = 0;
|
_nextItemIndex = 0;
|
||||||
_lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
_lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
_longCount = 0;
|
_longCount = 0;
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool Contains(T item)
|
public bool Contains(T item)
|
||||||
@@ -372,15 +359,9 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
public void CopyTo(T[] array, int arrayIndex) => Helpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
public void CopyTo(T[] array, int arrayIndex) => Helpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||||
|
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
public Enumerator GetEnumerator() => new(this);
|
||||||
// TODO: review if returned enumerator type should change for versioned list
|
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
||||||
public Enumerator GetEnumerator() => new(this);
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
|
||||||
// TODO: review if returned enumerator type should change for versioned list
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
[VersionedCollectionsGenerator.CloneForVersioned]
|
|
||||||
// TODO: review if returned enumerator type should change for versioned list
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public int IndexOf(T item)
|
public int IndexOf(T item)
|
||||||
@@ -394,7 +375,7 @@ namespace Recyclable.Collections
|
|||||||
return RecyclableDefaults.ItemNotFoundIndex;
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
int itemIndex = Array.IndexOf(_memoryBlocks[0], item, 0, _longCount < _blockSize ? _nextItemIndex : _blockSize);
|
int itemIndex = Array.IndexOf(_memoryBlocks[0], item, 0, _blockSize);
|
||||||
if (itemIndex >= 0)
|
if (itemIndex >= 0)
|
||||||
{
|
{
|
||||||
return itemIndex;
|
return itemIndex;
|
||||||
@@ -417,6 +398,104 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int IndexOf(T item, int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= _longCount)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
|
||||||
|
}
|
||||||
|
|
||||||
|
Contract.Requires(index >= 0);
|
||||||
|
Contract.Requires(index < _longCount);
|
||||||
|
Contract.EndContractBlock();
|
||||||
|
|
||||||
|
if (_lastBlockWithData == 0)
|
||||||
|
{
|
||||||
|
return Array.IndexOf(_memoryBlocks[0], item, index, (_longCount < _blockSize ? _nextItemIndex : _blockSize) - index);
|
||||||
|
}
|
||||||
|
else if (_longCount == 0)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int firstBlockIndex = index >> _blockSizePow2BitShift;
|
||||||
|
int firstItemIndex = index & _blockSizeMinus1;
|
||||||
|
int itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex], item, firstItemIndex, (firstBlockIndex == _lastBlockWithData && _nextItemIndex > 0 ? _nextItemIndex : _blockSize) - firstItemIndex);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return (firstBlockIndex << _blockSizePow2BitShift) + itemIndex;
|
||||||
|
}
|
||||||
|
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization && firstBlockIndex + 1 <= _lastBlockWithData)
|
||||||
|
{
|
||||||
|
return (int)RecyclableLongList<T>.IndexOfHelpers.IndexOfSequential(this, item, firstBlockIndex + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (firstBlockIndex + 1 <= _lastBlockWithData)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex + 1], item, 0, (int)Math.Min(firstBlockIndex + 1 == _lastBlockWithData ? _nextItemIndex : _blockSize, _longCount - _blockSize));
|
||||||
|
return itemIndex >= 0 ? ((firstBlockIndex + 1) << _blockSizePow2BitShift) + itemIndex : checked((int)RecyclableLongList<T>.IndexOfHelpers.IndexOfParallel(this, item, firstBlockIndex + 2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(T item, int index, int count)
|
||||||
|
{
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count + index > _longCount)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException_Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
Contract.Requires(index >= 0);
|
||||||
|
Contract.Requires(count + index <= _longCount);
|
||||||
|
Contract.EndContractBlock();
|
||||||
|
|
||||||
|
if (_lastBlockWithData == 0)
|
||||||
|
{
|
||||||
|
return Array.IndexOf(_memoryBlocks[0], item, index, count);
|
||||||
|
}
|
||||||
|
else if (_longCount == 0 || count == 0)
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockSize = _blockSize,
|
||||||
|
firstBlockIndex = index >> _blockSizePow2BitShift,
|
||||||
|
firstItemIndex = index & _blockSizeMinus1;
|
||||||
|
|
||||||
|
int firstBlockItemsCount = Math.Min(blockSize - firstItemIndex, count);
|
||||||
|
int itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex], item, firstItemIndex, firstBlockItemsCount);
|
||||||
|
if (itemIndex >= 0)
|
||||||
|
{
|
||||||
|
return (firstBlockIndex << _blockSizePow2BitShift) + itemIndex;
|
||||||
|
}
|
||||||
|
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization && firstBlockIndex + 1 <= _lastBlockWithData)
|
||||||
|
{
|
||||||
|
return (int)RecyclableLongList<T>.IndexOfHelpers.IndexOfSequential(this, item, firstBlockIndex + 1, count - firstBlockItemsCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (firstBlockIndex + 1 <= _lastBlockWithData)
|
||||||
|
{
|
||||||
|
itemIndex = Array.IndexOf(_memoryBlocks[firstBlockIndex + 1], item, 0, (int)MathUtils.Min(blockSize, _longCount - blockSize, count - firstBlockItemsCount));
|
||||||
|
return itemIndex >= 0 ? ((firstBlockIndex + 1) << _blockSizePow2BitShift) + itemIndex : checked((int)RecyclableLongList<T>.IndexOfHelpers.IndexOfParallel(this, item, firstBlockIndex + 2, count - firstBlockItemsCount - blockSize));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Insert(int index, T item)
|
public void Insert(int index, T item)
|
||||||
{
|
{
|
||||||
if (index > _longCount || index < 0)
|
if (index > _longCount || index < 0)
|
||||||
@@ -426,7 +505,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
if (_capacity < ++_longCount)
|
if (_capacity < ++_longCount)
|
||||||
{
|
{
|
||||||
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index + 1 < _longCount)
|
if (index + 1 < _longCount)
|
||||||
@@ -453,10 +532,8 @@ namespace Recyclable.Collections
|
|||||||
_lastBlockWithData++;
|
_lastBlockWithData++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Insert(long index, T item)
|
public void Insert(long index, T item)
|
||||||
{
|
{
|
||||||
@@ -467,7 +544,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
if (_capacity < ++_longCount)
|
if (_capacity < ++_longCount)
|
||||||
{
|
{
|
||||||
_capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
|
_capacity = Helpers.Resize(ref _memoryBlocks, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)_longCount)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index + 1 < _longCount)
|
if (index + 1 < _longCount)
|
||||||
@@ -494,10 +571,8 @@ namespace Recyclable.Collections
|
|||||||
_lastBlockWithData++;
|
_lastBlockWithData++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public long LongIndexOf(T item)
|
public long LongIndexOf(T item)
|
||||||
@@ -567,11 +642,9 @@ namespace Recyclable.Collections
|
|||||||
#nullable restore
|
#nullable restore
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
return true;
|
||||||
#endif
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -611,10 +684,8 @@ namespace Recyclable.Collections
|
|||||||
#nullable restore
|
#nullable restore
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void RemoveAt(long index)
|
public void RemoveAt(long index)
|
||||||
@@ -651,10 +722,8 @@ namespace Recyclable.Collections
|
|||||||
#nullable restore
|
#nullable restore
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveBlock(int index)
|
public void RemoveBlock(int index)
|
||||||
{
|
{
|
||||||
@@ -674,21 +743,17 @@ namespace Recyclable.Collections
|
|||||||
_nextItemBlockIndex--;
|
_nextItemBlockIndex--;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastBlockWithData--;
|
_lastBlockWithData--;
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
#if WITH_VERSIONING
|
_version++;
|
||||||
_version++;
|
if (_capacity > 0)
|
||||||
#endif
|
{
|
||||||
if (_capacity > 0)
|
Clear();
|
||||||
{
|
if (_memoryBlocks.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
Clear();
|
|
||||||
if (_memoryBlocks.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
{
|
||||||
// If anything, it has been already cleared by .Clear(), Remove() or RemoveAt() methods, as the list was modified / disposed.
|
// If anything, it has been already cleared by .Clear(), Remove() or RemoveAt() methods, as the list was modified / disposed.
|
||||||
RecyclableArrayPool<T[]>.ReturnShared(_memoryBlocks, false);
|
RecyclableArrayPool<T[]>.ReturnShared(_memoryBlocks, false);
|
||||||
|
|||||||
@@ -1,244 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Numerics;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
internal sealed class RecyclablePriorityQueue<T> : IEnumerable<T>, IDisposable
|
|
||||||
{
|
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
|
||||||
|
|
||||||
internal readonly IComparer<T> _comparer;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
internal T[] _heap;
|
|
||||||
#nullable restore
|
|
||||||
internal int _size;
|
|
||||||
internal bool _disposed;
|
|
||||||
|
|
||||||
public RecyclablePriorityQueue(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<T>? comparer = null)
|
|
||||||
{
|
|
||||||
if (initialCapacity < 1)
|
|
||||||
{
|
|
||||||
initialCapacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BitOperations.IsPow2((uint)initialCapacity))
|
|
||||||
{
|
|
||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
_heap = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<T>.RentShared(initialCapacity)
|
|
||||||
: new T[initialCapacity];
|
|
||||||
_size = 0;
|
|
||||||
_comparer = comparer ?? Comparer<T>.Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _size;
|
|
||||||
public long LongCount => _size;
|
|
||||||
|
|
||||||
public void Enqueue(T item)
|
|
||||||
{
|
|
||||||
if (_size == _heap.Length)
|
|
||||||
{
|
|
||||||
Grow();
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = _size++;
|
|
||||||
_heap[index] = item;
|
|
||||||
MoveUp(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Dequeue()
|
|
||||||
{
|
|
||||||
if (_size == 0)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_size), "Queue is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
var root = _heap[0];
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_heap[0] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
_size--;
|
|
||||||
if (_size > 0)
|
|
||||||
{
|
|
||||||
_heap[0] = _heap[_size];
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_heap[_size] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
MoveDown(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryDequeue(out T? item)
|
|
||||||
{
|
|
||||||
if (_size > 0)
|
|
||||||
{
|
|
||||||
item = Dequeue();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Peek()
|
|
||||||
{
|
|
||||||
if (_size == 0)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_size), "Queue is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _heap[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
Array.Clear(_heap, 0, _size);
|
|
||||||
}
|
|
||||||
|
|
||||||
_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
private void Grow()
|
|
||||||
{
|
|
||||||
int newSize;
|
|
||||||
if (_heap.Length >= RecyclableDefaults.MaxPooledBlockSize)
|
|
||||||
{
|
|
||||||
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var doubled = _heap.Length << 1;
|
|
||||||
newSize = (int)Math.Min(doubled, RecyclableDefaults.MaxPooledBlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newHeap = newSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<T>.RentShared(newSize)
|
|
||||||
: new T[newSize];
|
|
||||||
Array.Copy(_heap, newHeap, _size);
|
|
||||||
|
|
||||||
if (_heap.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(_heap, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_heap = newHeap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveUp(int index)
|
|
||||||
{
|
|
||||||
var item = _heap[index];
|
|
||||||
while (index > 0)
|
|
||||||
{
|
|
||||||
int parent = (index - 1) >> 1;
|
|
||||||
if (_comparer.Compare(item, _heap[parent]) >= 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_heap[index] = _heap[parent];
|
|
||||||
index = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
_heap[index] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveDown(int index)
|
|
||||||
{
|
|
||||||
var item = _heap[index];
|
|
||||||
int half = _size >> 1;
|
|
||||||
while (index < half)
|
|
||||||
{
|
|
||||||
int child = (index << 1) + 1;
|
|
||||||
int right = child + 1;
|
|
||||||
if (right < _size && _comparer.Compare(_heap[right], _heap[child]) < 0)
|
|
||||||
{
|
|
||||||
child = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_comparer.Compare(_heap[child], item) >= 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_heap[index] = _heap[child];
|
|
||||||
index = child;
|
|
||||||
}
|
|
||||||
|
|
||||||
_heap[index] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_heap.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(_heap, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_heap = Array.Empty<T>();
|
|
||||||
_size = 0;
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private readonly RecyclablePriorityQueue<T> _queue;
|
|
||||||
private int _index;
|
|
||||||
private T? _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclablePriorityQueue<T> queue)
|
|
||||||
{
|
|
||||||
_queue = queue;
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => _current!;
|
|
||||||
object IEnumerator.Current => _current!;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_index >= _queue._size)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = _queue._heap[_index++];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,257 +1,209 @@
|
|||||||
|
using System.Buffers;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
internal sealed class RecyclableQueue<T> : IEnumerable<T>, IDisposable
|
internal class RecyclableQueue<T> : IRecyclableOwner<T>, IList<T>, IDisposable
|
||||||
{
|
{
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
private static readonly ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
|
||||||
|
private static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private bool _disposedValue;
|
||||||
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous)
|
private RecyclableList<T[]> Memory { get; }
|
||||||
{
|
|
||||||
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
|
|
||||||
if (chunk.Value.Length < size)
|
|
||||||
{
|
|
||||||
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
? RecyclableArrayPool<T>.RentShared(size)
|
private static T Dequeue(RecyclableQueue<T> queue)
|
||||||
: new T[size];
|
{
|
||||||
}
|
if (queue.LongCount <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(queue), "There are no items in the queue");
|
||||||
|
}
|
||||||
|
|
||||||
chunk.Top = 0;
|
var toRemove = queue[0];
|
||||||
chunk.Bottom = 0;
|
queue.RemovedCount++;
|
||||||
chunk.Previous = previous;
|
queue.LongCount--;
|
||||||
chunk.Next = null;
|
if (queue.Capacity - queue.LongCount == queue.BlockSize)
|
||||||
return chunk;
|
{
|
||||||
}
|
queue.RemoveBlock(0);
|
||||||
|
queue.RemovedCount -= queue.BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
return toRemove;
|
||||||
private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
|
}
|
||||||
{
|
|
||||||
BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
|
private static bool TryDequeue(RecyclableQueue<T> queue, out T? item)
|
||||||
{
|
{
|
||||||
BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
|
if (queue.LongCount > 0)
|
||||||
}
|
{
|
||||||
|
item = queue.Dequeue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
internal BiDirectionalRecyclableArrayPoolChunk<T> _head;
|
item = default;
|
||||||
internal BiDirectionalRecyclableArrayPoolChunk<T> _tail;
|
return false;
|
||||||
internal bool _disposed;
|
}
|
||||||
internal long _capacity;
|
|
||||||
internal long _count;
|
|
||||||
|
|
||||||
public RecyclableQueue(int initialCapacity = RecyclableDefaults.InitialCapacity)
|
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
||||||
{
|
|
||||||
if (initialCapacity < 1)
|
|
||||||
{
|
|
||||||
initialCapacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BitOperations.IsPow2((uint)initialCapacity))
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
protected static long GetAbsoluteIndex(long index, long removedCount) => index + removedCount;
|
||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
_head = RentChunk(initialCapacity, null);
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
_tail = _head;
|
protected long GetRelativeIndex(long index) => index - RemovedCount;
|
||||||
_capacity = initialCapacity;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecyclableQueue(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
|
protected long RemovedCount { get; set; }
|
||||||
{
|
|
||||||
foreach (var item in source)
|
|
||||||
{
|
|
||||||
Enqueue(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => checked((int)_count);
|
public RecyclableQueue(IEnumerable<T> source, int blockSize = RecyclableDefaults.BlockSize)
|
||||||
public long LongCount => _count;
|
{
|
||||||
|
BlockSize = blockSize;
|
||||||
|
Memory = new(1);
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
Enqueue(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Enqueue(T item)
|
public RecyclableQueue(int blockSize = RecyclableDefaults.BlockSize)
|
||||||
{
|
{
|
||||||
if (_tail.Top == _tail.Value.Length)
|
BlockSize = blockSize;
|
||||||
{
|
Memory = new(1);
|
||||||
Grow();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_tail.Value[_tail.Top++] = item;
|
public int BlockSize { get; protected set; }
|
||||||
_count++;
|
public long Capacity { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(T item) => Enqueue(item);
|
public int Count
|
||||||
|
{
|
||||||
|
get => checked((int)LongCount);
|
||||||
|
set => LongCount = value;
|
||||||
|
}
|
||||||
|
|
||||||
public T Dequeue()
|
public long LongCount { get; set; }
|
||||||
{
|
public bool IsReadOnly { get; }
|
||||||
if (_count == 0)
|
|
||||||
{
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "Queue is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
_count--;
|
public T this[int index]
|
||||||
var value = _head.Value[_head.Bottom];
|
{
|
||||||
if (_needsClearing)
|
get => GetItem(index);
|
||||||
{
|
set => SetItem(index, value);
|
||||||
_head.Value[_head.Bottom] = default!;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_head.Bottom++;
|
public T this[long index]
|
||||||
if (_head.Bottom == _head.Top && _head.Next != null)
|
{
|
||||||
{
|
get => GetItem(index);
|
||||||
ReleaseHeadChunk();
|
set => SetItem(index, value);
|
||||||
}
|
}
|
||||||
else if (_head.Bottom == _head.Top)
|
|
||||||
{
|
|
||||||
_head.Bottom = 0;
|
|
||||||
_head.Top = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
}
|
private void SetItem(long index, T value)
|
||||||
|
{
|
||||||
|
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
||||||
|
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
|
||||||
|
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
|
||||||
|
Memory[arrayIndex][itemIndex] = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryDequeue(out T? item)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
private T GetItem(long index)
|
||||||
if (_count > 0)
|
{
|
||||||
{
|
long absoluteIndex = GetAbsoluteIndex(index, RemovedCount);
|
||||||
item = Dequeue();
|
int arrayIndex = RecyclableQueueHelpers<T>.ToArrayIndex(absoluteIndex, BlockSize);
|
||||||
return true;
|
long itemIndex = RecyclableQueueHelpers<T>.ToItemIndex(absoluteIndex, BlockSize);
|
||||||
}
|
return Memory[arrayIndex][itemIndex];
|
||||||
|
}
|
||||||
|
|
||||||
item = default;
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
return false;
|
public void Enqueue(T item)
|
||||||
}
|
{
|
||||||
|
if (LongCount == Capacity)
|
||||||
|
{
|
||||||
|
AddBlock();
|
||||||
|
}
|
||||||
|
|
||||||
public void Clear()
|
SetItem(LongCount, item);
|
||||||
{
|
LongCount++;
|
||||||
while (_head.Next != null)
|
}
|
||||||
{
|
|
||||||
ReleaseHeadChunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_needsClearing && _head.Top > _head.Bottom)
|
public void AddBlock()
|
||||||
{
|
{
|
||||||
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
|
T[] newArray = (BlockSize >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
}
|
? _arrayPool.Rent(BlockSize)
|
||||||
|
: new T[BlockSize];
|
||||||
|
|
||||||
_capacity = _head.Value.Length;
|
Memory.Add(newArray);
|
||||||
_head.Top = 0;
|
Capacity += BlockSize;
|
||||||
_head.Bottom = 0;
|
}
|
||||||
_tail = _head;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
public T Dequeue() => Dequeue(this);
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
private void Grow()
|
public void Add(T item) => Enqueue(item);
|
||||||
{
|
|
||||||
int newSize;
|
|
||||||
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
|
|
||||||
{
|
|
||||||
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var doubled = _capacity << 1;
|
|
||||||
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newChunk = RentChunk(newSize, _tail);
|
public void Clear()
|
||||||
_tail.Next = newChunk;
|
{
|
||||||
_tail = newChunk;
|
try
|
||||||
_capacity += newSize;
|
{
|
||||||
}
|
// Remove in reversed order for performance savings
|
||||||
|
while (Memory._count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RemoveBlock(Memory._count - 1);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LongCount = 0;
|
||||||
|
RemovedCount = 0;
|
||||||
|
Memory.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ReleaseHeadChunk()
|
#pragma warning disable CS0618
|
||||||
{
|
public bool Contains(T item) => RecyclableQueueHelpers<T>.Contains(Memory, item);
|
||||||
var toReturn = _head;
|
public void CopyTo(T[] array, int arrayIndex) => RecyclableQueueHelpers<T>.CopyTo(Memory, RemovedCount, BlockSize, checked((int)(LongCount % BlockSize)), array, arrayIndex);
|
||||||
_head = toReturn.Next!;
|
public IEnumerable<T> Enumerate() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount);
|
||||||
_head.Previous = null;
|
public IEnumerator<T> GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||||
_capacity -= toReturn.Value.Length;
|
IEnumerator IEnumerable.GetEnumerator() => RecyclableQueueHelpers<T>.Enumerate(Memory, BlockSize, LongCount).GetEnumerator();
|
||||||
ReturnChunk(toReturn);
|
public int IndexOf(T item) => checked((int)GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer)));
|
||||||
}
|
public long LongIndexOf(T item) => GetRelativeIndex(RecyclableQueueHelpers<T>.LongIndexOf(Memory, BlockSize, item, _equalityComparer));
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
public void Dispose()
|
public void Insert(int index, T item) => throw new NotSupportedException();
|
||||||
{
|
public void RemoveAt(int index) => throw new NotSupportedException();
|
||||||
if (_disposed)
|
public bool TryDequeue(out T? item) => TryDequeue(this, out item);
|
||||||
{
|
public bool Remove(T item) => TryDequeue(out var _);
|
||||||
return;
|
public void RemoveBlock(int index)
|
||||||
}
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Memory[index].LongLength is > RecyclableDefaults.MinPooledArrayLength and <= int.MaxValue)
|
||||||
|
{
|
||||||
|
_arrayPool.Return(Memory[index], NeedsClearing);
|
||||||
|
}
|
||||||
|
|
||||||
var chunk = _head;
|
Memory.RemoveAt(index);
|
||||||
while (chunk != null)
|
}
|
||||||
{
|
finally
|
||||||
var next = chunk.Next;
|
{
|
||||||
DisposeChunk(chunk);
|
Capacity -= BlockSize;
|
||||||
chunk = next;
|
//LongCount -= BlockSize;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_disposed = true;
|
public void Dispose()
|
||||||
GC.SuppressFinalize(this);
|
{
|
||||||
}
|
if (!_disposedValue)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<T>
|
GC.SuppressFinalize(this);
|
||||||
{
|
}
|
||||||
private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start;
|
}
|
||||||
private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk;
|
|
||||||
private int _index;
|
|
||||||
private T? _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableQueue<T> queue)
|
|
||||||
{
|
|
||||||
_start = queue._head;
|
|
||||||
_chunk = queue._head;
|
|
||||||
_index = _chunk != null ? _chunk.Bottom : 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => _current!;
|
|
||||||
object IEnumerator.Current => _current!;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_chunk == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_index >= _chunk.Top)
|
|
||||||
{
|
|
||||||
_chunk = _chunk.Next;
|
|
||||||
if (_chunk == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_index = _chunk.Bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = _chunk.Value[_index++];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_chunk = _start;
|
|
||||||
_index = _chunk != null ? _chunk.Bottom : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_chunk = null;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
internal sealed class RecyclableSortedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
private static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
|
|
||||||
|
|
||||||
private readonly IComparer<TKey> _comparer;
|
|
||||||
private TKey[] _keys;
|
|
||||||
private TValue[] _values;
|
|
||||||
private int _count;
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public RecyclableSortedDictionary(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<TKey>? comparer = null)
|
|
||||||
{
|
|
||||||
if (initialCapacity < 1)
|
|
||||||
{
|
|
||||||
initialCapacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BitOperations.IsPow2((uint)initialCapacity))
|
|
||||||
{
|
|
||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keys = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<TKey>.RentShared(initialCapacity)
|
|
||||||
: new TKey[initialCapacity];
|
|
||||||
_values = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<TValue>.RentShared(initialCapacity)
|
|
||||||
: new TValue[initialCapacity];
|
|
||||||
_count = 0;
|
|
||||||
_comparer = comparer ?? Comparer<TKey>.Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _count;
|
|
||||||
|
|
||||||
public TValue this[TKey key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
|
|
||||||
{
|
|
||||||
return _values[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
|
|
||||||
return default!;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
|
|
||||||
{
|
|
||||||
_values[index] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertAt(index, key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Add(TKey key, TValue value)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("An element with the same key already exists.", nameof(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertAt(index, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool ContainsKey(TKey key)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
return index < _count && _comparer.Compare(_keys[index], key) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Remove(TKey key)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index >= _count || _comparer.Compare(_keys[index], key) != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < _count - 1)
|
|
||||||
{
|
|
||||||
Array.Copy(_keys, index + 1, _keys, index, _count - index - 1);
|
|
||||||
Array.Copy(_values, index + 1, _values, index, _count - index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_count--;
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_keys[_count] = default!;
|
|
||||||
_values[_count] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryGetValue(TKey key, out TValue value)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index < _count && _comparer.Compare(_keys[index], key) == 0)
|
|
||||||
{
|
|
||||||
value = _values[index];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = default!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
Array.Clear(_keys, 0, _count);
|
|
||||||
Array.Clear(_values, 0, _count);
|
|
||||||
}
|
|
||||||
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TKey GetKey(int index) => _keys[index];
|
|
||||||
public TValue GetValue(int index) => _values[index];
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
private void InsertAt(int index, TKey key, TValue value)
|
|
||||||
{
|
|
||||||
if (_count == _keys.Length)
|
|
||||||
{
|
|
||||||
Grow();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < _count)
|
|
||||||
{
|
|
||||||
Array.Copy(_keys, index, _keys, index + 1, _count - index);
|
|
||||||
Array.Copy(_values, index, _values, index + 1, _count - index);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keys[index] = key;
|
|
||||||
_values[index] = value;
|
|
||||||
_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Grow()
|
|
||||||
{
|
|
||||||
int newSize;
|
|
||||||
if (_keys.Length >= RecyclableDefaults.MaxPooledBlockSize)
|
|
||||||
{
|
|
||||||
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int doubled = _keys.Length << 1;
|
|
||||||
newSize = (int)Math.Min(doubled, RecyclableDefaults.MaxPooledBlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newKeys = newSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<TKey>.RentShared(newSize)
|
|
||||||
: new TKey[newSize];
|
|
||||||
var newValues = newSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<TValue>.RentShared(newSize)
|
|
||||||
: new TValue[newSize];
|
|
||||||
Array.Copy(_keys, newKeys, _count);
|
|
||||||
Array.Copy(_values, newValues, _count);
|
|
||||||
|
|
||||||
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
|
|
||||||
}
|
|
||||||
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keys = newKeys;
|
|
||||||
_values = newValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int BinarySearch(TKey key)
|
|
||||||
{
|
|
||||||
int low = 0;
|
|
||||||
int high = _count - 1;
|
|
||||||
while (low <= high)
|
|
||||||
{
|
|
||||||
int mid = (low + high) >> 1;
|
|
||||||
int cmp = _comparer.Compare(_keys[mid], key);
|
|
||||||
if (cmp == 0)
|
|
||||||
{
|
|
||||||
return mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmp < 0)
|
|
||||||
{
|
|
||||||
low = mid + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
high = mid - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return low;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
|
|
||||||
}
|
|
||||||
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keys = Array.Empty<TKey>();
|
|
||||||
_values = Array.Empty<TValue>();
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
|
|
||||||
{
|
|
||||||
private readonly RecyclableSortedDictionary<TKey, TValue> _dictionary;
|
|
||||||
private int _index;
|
|
||||||
private KeyValuePair<TKey, TValue> _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableSortedDictionary<TKey, TValue> dictionary)
|
|
||||||
{
|
|
||||||
_dictionary = dictionary;
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyValuePair<TKey, TValue> Current => _current;
|
|
||||||
object IEnumerator.Current => _current;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_index >= _dictionary._count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = new KeyValuePair<TKey, TValue>(_dictionary._keys[_index], _dictionary._values[_index]);
|
|
||||||
_index++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,281 +1,81 @@
|
|||||||
using System.Collections;
|
using Recyclable.Collections.Linq;
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
internal sealed class RecyclableSortedList<TKey, TValue> : IEnumerable<(TKey Key, TValue Value)>, IDisposable
|
internal class RecyclableSortedList<TKey, TValue> : RecyclableLongList<(TKey Key, TValue Value)>
|
||||||
where TKey : notnull
|
where TKey : notnull
|
||||||
{
|
{
|
||||||
internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType;
|
private static readonly IComparer<(TKey, TValue)> _comparer = new FrequencyTupleComparer<TKey, TValue>();
|
||||||
|
private uint _isUpdating;
|
||||||
|
private readonly SystemRandomNumberGenerator _randomNumberGenerator = new();
|
||||||
|
|
||||||
internal TKey[] _keys;
|
protected void ListUpdated()
|
||||||
internal TValue[] _values;
|
{
|
||||||
internal int _count;
|
QuickSort();
|
||||||
internal readonly IComparer<TKey> _comparer;
|
}
|
||||||
internal bool _disposed;
|
|
||||||
|
|
||||||
public RecyclableSortedList(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<TKey>? comparer = null)
|
public RecyclableSortedList() : base()
|
||||||
{
|
{
|
||||||
if (initialCapacity < 1)
|
}
|
||||||
{
|
|
||||||
initialCapacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BitOperations.IsPow2((uint)initialCapacity))
|
public RecyclableSortedList(Dictionary<TKey, TValue> source, int blockSize = RecyclableDefaults.BlockSize)
|
||||||
{
|
: this(source.Select(x => (x.Key, x.Value)), blockSize)
|
||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
_keys = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
public RecyclableSortedList(IEnumerable<(TKey Key, TValue Value)> source, int blockSize = RecyclableDefaults.BlockSize)
|
||||||
? RecyclableArrayPool<TKey>.RentShared(initialCapacity)
|
: base(source, blockSize)
|
||||||
: new TKey[initialCapacity];
|
{
|
||||||
_values = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
QuickSort();
|
||||||
? RecyclableArrayPool<TValue>.RentShared(initialCapacity)
|
}
|
||||||
: new TValue[initialCapacity];
|
|
||||||
_count = 0;
|
|
||||||
_comparer = comparer ?? Comparer<TKey>.Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _count;
|
public RecyclableSortedList(int blockSize = RecyclableDefaults.BlockSize)
|
||||||
|
: base(blockSize)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public TValue this[TKey key]
|
public bool IsUpdating => _isUpdating > 0;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
return _values[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "Key not found");
|
public void Add(TKey key, TValue value) => Add((key, value));
|
||||||
return default!;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
_values[index] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Insert(~index, key, value);
|
public void BeginUpdate() => _isUpdating++;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public new void Clear()
|
||||||
public void Add(TKey key, TValue value)
|
{
|
||||||
{
|
BeginUpdate();
|
||||||
int index = BinarySearch(key);
|
try
|
||||||
if (index >= 0)
|
{
|
||||||
{
|
base.Clear();
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(key), "An element with the same key already exists.");
|
}
|
||||||
}
|
finally
|
||||||
|
{
|
||||||
|
EndUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Insert(~index, key, value);
|
public void EndUpdate(bool raiseListUpdated = true)
|
||||||
}
|
{
|
||||||
|
if (_isUpdating > 0)
|
||||||
|
{
|
||||||
|
_isUpdating--;
|
||||||
|
if (raiseListUpdated && _isUpdating == 0)
|
||||||
|
{
|
||||||
|
ListUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public void QuickSort()
|
||||||
public bool ContainsKey(TKey key) => BinarySearch(key) >= 0;
|
{
|
||||||
|
BeginUpdate();
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
try
|
||||||
public bool TryGetValue(TKey key, out TValue value)
|
{
|
||||||
{
|
QuickSortExtensions<(TKey, TValue)>.QuickSort(this, _comparer, _randomNumberGenerator);
|
||||||
int index = BinarySearch(key);
|
}
|
||||||
if (index >= 0)
|
finally
|
||||||
{
|
{
|
||||||
value = _values[index];
|
EndUpdate(false);
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
value = default!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Remove(TKey key)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(key);
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < _count - 1)
|
|
||||||
{
|
|
||||||
Array.Copy(_keys, index + 1, _keys, index, _count - index - 1);
|
|
||||||
Array.Copy(_values, index + 1, _values, index, _count - index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_count--;
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_keys[_count] = default!;
|
|
||||||
_values[_count] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (_needsClearing && _count > 0)
|
|
||||||
{
|
|
||||||
Array.Clear(_keys, 0, _count);
|
|
||||||
Array.Clear(_values, 0, _count);
|
|
||||||
}
|
|
||||||
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TKey GetKey(int index) => _keys[index];
|
|
||||||
public TValue GetValue(int index) => _values[index];
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
IEnumerator<(TKey Key, TValue Value)> IEnumerable<(TKey Key, TValue Value)>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keys = Array.Empty<TKey>();
|
|
||||||
_values = Array.Empty<TValue>();
|
|
||||||
_count = 0;
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Insert(int index, TKey key, TValue value)
|
|
||||||
{
|
|
||||||
if (_count == _keys.Length)
|
|
||||||
{
|
|
||||||
Grow(_count + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < _count)
|
|
||||||
{
|
|
||||||
Array.Copy(_keys, index, _keys, index + 1, _count - index);
|
|
||||||
Array.Copy(_values, index, _values, index + 1, _count - index);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keys[index] = key;
|
|
||||||
_values[index] = value;
|
|
||||||
_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Grow(int min)
|
|
||||||
{
|
|
||||||
int newSize = _keys.Length << 1;
|
|
||||||
if (newSize < min)
|
|
||||||
{
|
|
||||||
newSize = min;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newKeys = newSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<TKey>.RentShared(newSize)
|
|
||||||
: new TKey[newSize];
|
|
||||||
var newValues = newSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<TValue>.RentShared(newSize)
|
|
||||||
: new TValue[newSize];
|
|
||||||
|
|
||||||
Array.Copy(_keys, newKeys, _count);
|
|
||||||
Array.Copy(_values, newValues, _count);
|
|
||||||
|
|
||||||
if (_keys.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TKey>.ReturnShared(_keys, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_values.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<TValue>.ReturnShared(_values, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keys = newKeys;
|
|
||||||
_values = newValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int BinarySearch(TKey key)
|
|
||||||
{
|
|
||||||
int low = 0;
|
|
||||||
int high = _count - 1;
|
|
||||||
while (low <= high)
|
|
||||||
{
|
|
||||||
int mid = (low + high) >> 1;
|
|
||||||
int cmp = _comparer.Compare(_keys[mid], key);
|
|
||||||
if (cmp == 0)
|
|
||||||
{
|
|
||||||
return mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmp < 0)
|
|
||||||
{
|
|
||||||
low = mid + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
high = mid - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ~low;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<(TKey Key, TValue Value)>
|
|
||||||
{
|
|
||||||
private readonly RecyclableSortedList<TKey, TValue> _list;
|
|
||||||
private int _index;
|
|
||||||
private (TKey, TValue) _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableSortedList<TKey, TValue> list)
|
|
||||||
{
|
|
||||||
_list = list;
|
|
||||||
_index = 0;
|
|
||||||
_current = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (TKey Key, TValue Value) Current => _current;
|
|
||||||
object IEnumerator.Current => _current;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_index >= _list._count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = (_list._keys[_index], _list._values[_index]);
|
|
||||||
_index++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = 0;
|
|
||||||
_current = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
internal sealed class RecyclableSortedSet<T> : IEnumerable<T>, IDisposable where T : notnull
|
|
||||||
{
|
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
|
||||||
|
|
||||||
internal readonly IComparer<T> _comparer;
|
|
||||||
|
|
||||||
internal T[] _items;
|
|
||||||
internal int _count;
|
|
||||||
internal bool _disposed;
|
|
||||||
|
|
||||||
public RecyclableSortedSet(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer<T>? comparer = null)
|
|
||||||
{
|
|
||||||
if (initialCapacity < 1)
|
|
||||||
{
|
|
||||||
initialCapacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BitOperations.IsPow2((uint)initialCapacity))
|
|
||||||
{
|
|
||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
_items = initialCapacity >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<T>.RentShared(initialCapacity)
|
|
||||||
: new T[initialCapacity];
|
|
||||||
_count = 0;
|
|
||||||
_comparer = comparer ?? Comparer<T>.Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _count;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Add(T item)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(item);
|
|
||||||
if (index < _count && _comparer.Compare(_items[index], item) == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_count == _items.Length)
|
|
||||||
{
|
|
||||||
Grow();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < _count)
|
|
||||||
{
|
|
||||||
Array.Copy(_items, index, _items, index + 1, _count - index);
|
|
||||||
}
|
|
||||||
|
|
||||||
_items[index] = item;
|
|
||||||
_count++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Contains(T item)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(item);
|
|
||||||
return index < _count && _comparer.Compare(_items[index], item) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool Remove(T item)
|
|
||||||
{
|
|
||||||
int index = BinarySearch(item);
|
|
||||||
if (index >= _count || _comparer.Compare(_items[index], item) != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < _count - 1)
|
|
||||||
{
|
|
||||||
Array.Copy(_items, index + 1, _items, index, _count - index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_count--;
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_items[_count] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
Array.Clear(_items, 0, _count);
|
|
||||||
}
|
|
||||||
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
private void Grow()
|
|
||||||
{
|
|
||||||
int newSize;
|
|
||||||
if (_items.Length >= RecyclableDefaults.MaxPooledBlockSize)
|
|
||||||
{
|
|
||||||
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var doubled = _items.Length << 1;
|
|
||||||
newSize = (int)Math.Min(doubled, RecyclableDefaults.MaxPooledBlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newItems = newSize >= RecyclableDefaults.MinPooledArrayLength
|
|
||||||
? RecyclableArrayPool<T>.RentShared(newSize)
|
|
||||||
: new T[newSize];
|
|
||||||
Array.Copy(_items, newItems, _count);
|
|
||||||
|
|
||||||
if (_items.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(_items, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_items = newItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int BinarySearch(T item)
|
|
||||||
{
|
|
||||||
int low = 0;
|
|
||||||
int high = _count - 1;
|
|
||||||
while (low <= high)
|
|
||||||
{
|
|
||||||
int mid = (low + high) >> 1;
|
|
||||||
int cmp = _comparer.Compare(_items[mid], item);
|
|
||||||
if (cmp == 0)
|
|
||||||
{
|
|
||||||
return mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmp < 0)
|
|
||||||
{
|
|
||||||
low = mid + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
high = mid - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return low;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
if (_items.Length >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(_items, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
_items = Array.Empty<T>();
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private readonly RecyclableSortedSet<T> _set;
|
|
||||||
private int _index;
|
|
||||||
private T? _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableSortedSet<T> set)
|
|
||||||
{
|
|
||||||
_set = set;
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => _current!;
|
|
||||||
object IEnumerator.Current => _current!;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_index >= _set._count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = _set._items[_index++];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = 0;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,234 +1,94 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
internal sealed class RecyclableStack<T> : IEnumerable<T>, IDisposable
|
internal class RecyclableStack<T> : IList<T>, IDisposable
|
||||||
{
|
{
|
||||||
internal static readonly bool _needsClearing = !typeof(T).IsValueType;
|
private static readonly bool _needsClearing = !typeof(T).IsValueType;
|
||||||
|
private bool _disposedValue;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
protected RecyclableLongList<T> List { get; }
|
||||||
private static RecyclableArrayPoolChunk<T> RentChunk(int size, RecyclableArrayPoolChunk<T>? previous)
|
public int Count => checked((int)List._longCount);
|
||||||
{
|
public long LongCount
|
||||||
var chunk = RecyclableArrayPoolChunkPool<T>.Rent();
|
{
|
||||||
if (chunk.Value.Length < size)
|
get => List._longCount;
|
||||||
{
|
set => List._longCount = value;
|
||||||
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
|
}
|
||||||
{
|
public bool IsReadOnly => false;
|
||||||
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
|
public T this[int index] { get => this[(long)index]; set => this[(long)index] = value; }
|
||||||
? RecyclableArrayPool<T>.RentShared(size)
|
public T this[long index] { get => List[index]; set => List[index] = value; }
|
||||||
: new T[size];
|
|
||||||
}
|
|
||||||
chunk.Index = 0;
|
|
||||||
chunk.Previous = previous;
|
|
||||||
chunk.Next = null;
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public RecyclableStack(int blockSize = RecyclableDefaults.BlockSize)
|
||||||
private static void ReturnChunk(RecyclableArrayPoolChunk<T> chunk)
|
{
|
||||||
{
|
List = new(blockSize);
|
||||||
RecyclableArrayPoolChunkPool<T>.Return(chunk);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public RecyclableStack(IEnumerable<T> list, int blockSize = RecyclableDefaults.BlockSize)
|
||||||
private static void DisposeChunk(RecyclableArrayPoolChunk<T> chunk)
|
{
|
||||||
{
|
List = new(list, blockSize);
|
||||||
RecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal RecyclableArrayPoolChunk<T> _current;
|
public RecyclableStack(RecyclableLongList<T> list, int blockSize = RecyclableDefaults.BlockSize)
|
||||||
internal bool _disposed;
|
{
|
||||||
internal long _capacity;
|
List = new(list, blockSize);
|
||||||
internal long _count;
|
}
|
||||||
|
|
||||||
public RecyclableStack(int initialCapacity = RecyclableDefaults.InitialCapacity)
|
public void Push(T item)
|
||||||
{
|
{
|
||||||
if (initialCapacity < 1)
|
List.Add(item);
|
||||||
{
|
}
|
||||||
initialCapacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BitOperations.IsPow2((uint)initialCapacity))
|
public T Pop()
|
||||||
{
|
{
|
||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
RecyclableLongList<T> list = List;
|
||||||
}
|
var itemIndex = (int)(list.LongCount & list._blockSizeMinus1) - 1;
|
||||||
|
var blockIndex = (int)(LongCount >> list._blockSizePow2BitShift);
|
||||||
|
var toRemove = list._memoryBlocks[blockIndex][itemIndex];
|
||||||
|
|
||||||
_current = RentChunk(initialCapacity, null);
|
LongCount--;
|
||||||
_capacity = initialCapacity;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecyclableStack(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
|
if (_needsClearing)
|
||||||
{
|
{
|
||||||
foreach (var item in source)
|
#nullable disable
|
||||||
{
|
new Span<T>(list._memoryBlocks[blockIndex])[itemIndex] = default;
|
||||||
Push(item);
|
#nullable restore
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => checked((int)_count);
|
if (itemIndex == 0)
|
||||||
public long LongCount => _count;
|
{
|
||||||
|
list.RemoveBlock(blockIndex);
|
||||||
|
}
|
||||||
|
|
||||||
public void Push(T item)
|
return toRemove;
|
||||||
{
|
}
|
||||||
if (_current.Index == _current.Value.Length)
|
|
||||||
{
|
|
||||||
Grow();
|
|
||||||
}
|
|
||||||
|
|
||||||
_current.Value[_current.Index++] = item;
|
public int IndexOf(T item) => List.IndexOf(item);
|
||||||
_count++;
|
public long LongIndexOf(T item) => List.LongIndexOf(item);
|
||||||
}
|
public void Insert(int index, T item) => List.Insert(index, item);
|
||||||
|
public void RemoveAt(int index) => List.RemoveAt(index);
|
||||||
|
public void Add(T item) => List.Add(item);
|
||||||
|
public void Clear() => List.Clear();
|
||||||
|
public bool Contains(T item) => List.Contains(item);
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) => List.CopyTo(array, arrayIndex);
|
||||||
|
public bool Remove(T item) => List.Remove(item);
|
||||||
|
public IEnumerator<T> GetEnumerator() => List.Reverse().GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
public void Add(T item) => Push(item);
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposedValue)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public T Pop()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_count == 0)
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
{
|
Dispose(disposing: true);
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "Stack is empty");
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_count--;
|
|
||||||
_current.Index--;
|
|
||||||
var value = _current.Value[_current.Index];
|
|
||||||
if (_needsClearing)
|
|
||||||
{
|
|
||||||
_current.Value[_current.Index] = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_current.Index == 0 && _current.Previous != null)
|
|
||||||
{
|
|
||||||
ReleaseCurrentChunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
while (_current.Previous != null)
|
|
||||||
{
|
|
||||||
ReleaseCurrentChunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_needsClearing && _current.Index > 0)
|
|
||||||
{
|
|
||||||
Array.Clear(_current.Value, 0, _current.Index);
|
|
||||||
}
|
|
||||||
|
|
||||||
_capacity = _current.Value.Length;
|
|
||||||
_current.Index = 0;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new(this);
|
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
|
||||||
|
|
||||||
private void Grow()
|
|
||||||
{
|
|
||||||
int newSize;
|
|
||||||
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
|
|
||||||
{
|
|
||||||
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var doubled = _capacity << 1;
|
|
||||||
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newChunk = RentChunk(newSize, _current);
|
|
||||||
_current.Next = newChunk;
|
|
||||||
_current = newChunk;
|
|
||||||
_capacity += newSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReleaseCurrentChunk()
|
|
||||||
{
|
|
||||||
var toReturn = _current;
|
|
||||||
_current = toReturn.Previous!;
|
|
||||||
_current.Next = null;
|
|
||||||
_capacity -= toReturn.Value.Length;
|
|
||||||
ReturnChunk(toReturn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chunk = _current;
|
|
||||||
while (chunk != null)
|
|
||||||
{
|
|
||||||
var previous = chunk.Previous;
|
|
||||||
DisposeChunk(chunk);
|
|
||||||
chunk = previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<T>
|
|
||||||
{
|
|
||||||
private readonly RecyclableArrayPoolChunk<T>? _start;
|
|
||||||
private RecyclableArrayPoolChunk<T>? _chunk;
|
|
||||||
private int _index;
|
|
||||||
private T? _current;
|
|
||||||
|
|
||||||
internal Enumerator(RecyclableStack<T> stack)
|
|
||||||
{
|
|
||||||
_start = stack._current;
|
|
||||||
_chunk = stack._current;
|
|
||||||
_index = _chunk != null ? _chunk.Index - 1 : -1;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => _current!;
|
|
||||||
object IEnumerator.Current => _current!;
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_chunk == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_index < 0)
|
|
||||||
{
|
|
||||||
_chunk = _chunk.Previous;
|
|
||||||
if (_chunk == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_index = _chunk.Index - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = _chunk.Value[_index--];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_chunk = _start;
|
|
||||||
_index = _chunk != null ? _chunk.Index - 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_chunk = null;
|
|
||||||
_current = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user