RecyclableList.Enumerator performance improvements to match List<T>'s performance. Dirty state.
This commit is contained in:
1 parent
51d3613ffd
commit
15cac80d2f
13 files changed
+683
-192
No files matched your search
@@ -0,0 +1,45 @@
|
|||||||
|
namespace Recyclable.Collections.Benchmarks
|
||||||
|
{
|
||||||
|
public partial class RecyclableCollectionsBenchmarks
|
||||||
|
{
|
||||||
|
public void Array_ForEach()
|
||||||
|
{
|
||||||
|
foreach (var item in TestObjects)
|
||||||
|
{
|
||||||
|
DoNothing(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void List_ForEach()
|
||||||
|
{
|
||||||
|
foreach (var item in TestObjectsAsList)
|
||||||
|
{
|
||||||
|
DoNothing(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PooledList_ForEach()
|
||||||
|
{
|
||||||
|
foreach (var item in TestObjectsAsPooledList)
|
||||||
|
{
|
||||||
|
DoNothing(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableList_ForEach()
|
||||||
|
{
|
||||||
|
foreach (var item in TestObjectsAsRecyclableList)
|
||||||
|
{
|
||||||
|
DoNothing(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecyclableLongList_ForEach()
|
||||||
|
{
|
||||||
|
foreach (var item in TestObjectsAsRecyclableLongList)
|
||||||
|
{
|
||||||
|
DoNothing(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
{
|
{
|
||||||
public enum RecyclableCollectionsBenchmarkType
|
public enum RecyclableCollectionsBenchmarkType
|
||||||
{
|
{
|
||||||
Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
||||||
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, Contains_FirstItems,
|
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, Contains_FirstItems,
|
||||||
Contains_LastItems, Count, Create_WithCapacity, Create, GetItem, IndexOf_BestAndWorstCases, IndexOf_FirstItems,
|
Contains_LastItems, Count, Create_WithCapacity, Create, ForEach, GetItem, IndexOf_BestAndWorstCases, IndexOf_FirstItems,
|
||||||
IndexOf_LastItems, LongCount, Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
|
IndexOf_LastItems, LongCount, Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
|
||||||
SetItem,
|
SetItem,
|
||||||
}
|
}
|
||||||
@@ -17,52 +17,53 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
public RecyclableCollectionsBenchmarks() : base()
|
public RecyclableCollectionsBenchmarks() : base()
|
||||||
{
|
{
|
||||||
TestObjectCount = 1;
|
TestObjectCount = 1;
|
||||||
BaseDataType = RecyclableCollectionsBenchmarkSource.List;
|
//BaseDataType = RecyclableCollectionsBenchmarkSource.List;
|
||||||
DataType = RecyclableCollectionsBenchmarkSource.RecyclableLongList;
|
DataType = RecyclableCollectionsBenchmarkSource.RecyclableList;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Params
|
[Params
|
||||||
(
|
(
|
||||||
RecyclableCollectionsBenchmarkType.Add_WithCapacity,
|
//RecyclableCollectionsBenchmarkType.Add_WithCapacity,
|
||||||
RecyclableCollectionsBenchmarkType.Add,
|
//RecyclableCollectionsBenchmarkType.Add,
|
||||||
RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
|
//RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
|
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
|
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
||||||
RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
||||||
RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
//RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
||||||
RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
//RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
||||||
RecyclableCollectionsBenchmarkType.Count,
|
//RecyclableCollectionsBenchmarkType.Count,
|
||||||
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
//RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
||||||
RecyclableCollectionsBenchmarkType.Create,
|
//RecyclableCollectionsBenchmarkType.Create,
|
||||||
RecyclableCollectionsBenchmarkType.GetItem,
|
RecyclableCollectionsBenchmarkType.ForEach
|
||||||
RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
|
//RecyclableCollectionsBenchmarkType.GetItem,
|
||||||
RecyclableCollectionsBenchmarkType.IndexOf_FirstItems,
|
//RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases,
|
||||||
RecyclableCollectionsBenchmarkType.IndexOf_LastItems,
|
//RecyclableCollectionsBenchmarkType.IndexOf_FirstItems,
|
||||||
RecyclableCollectionsBenchmarkType.LongCount,
|
//RecyclableCollectionsBenchmarkType.IndexOf_LastItems,
|
||||||
RecyclableCollectionsBenchmarkType.Remove_FirstItems,
|
//RecyclableCollectionsBenchmarkType.LongCount,
|
||||||
RecyclableCollectionsBenchmarkType.Remove_LastItems,
|
//RecyclableCollectionsBenchmarkType.Remove_FirstItems,
|
||||||
RecyclableCollectionsBenchmarkType.RemoveAt_FirstItems,
|
//RecyclableCollectionsBenchmarkType.Remove_LastItems,
|
||||||
RecyclableCollectionsBenchmarkType.RemoveAt_LastItems,
|
//RecyclableCollectionsBenchmarkType.RemoveAt_FirstItems,
|
||||||
RecyclableCollectionsBenchmarkType.SetItem
|
//RecyclableCollectionsBenchmarkType.RemoveAt_LastItems,
|
||||||
|
//RecyclableCollectionsBenchmarkType.SetItem
|
||||||
)]
|
)]
|
||||||
|
|
||||||
public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases;
|
public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases;
|
||||||
|
|
||||||
[Params
|
//[Params
|
||||||
(
|
//(
|
||||||
RecyclableCollectionsBenchmarkSource.List
|
// RecyclableCollectionsBenchmarkSource.List
|
||||||
)]
|
//)]
|
||||||
public override RecyclableCollectionsBenchmarkSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
public override RecyclableCollectionsBenchmarkSource BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
|
||||||
|
|
||||||
[Params
|
[Params
|
||||||
(
|
(
|
||||||
RecyclableCollectionsBenchmarkSource.Array,
|
//RecyclableCollectionsBenchmarkSource.Array,
|
||||||
RecyclableCollectionsBenchmarkSource.List,
|
//RecyclableCollectionsBenchmarkSource.List,
|
||||||
RecyclableCollectionsBenchmarkSource.PooledList,
|
//RecyclableCollectionsBenchmarkSource.PooledList,
|
||||||
RecyclableCollectionsBenchmarkSource.RecyclableList,
|
RecyclableCollectionsBenchmarkSource.RecyclableList
|
||||||
RecyclableCollectionsBenchmarkSource.RecyclableLongList
|
//RecyclableCollectionsBenchmarkSource.RecyclableLongList
|
||||||
)]
|
)]
|
||||||
public override RecyclableCollectionsBenchmarkSource DataType { get => base.DataType; set => base.DataType = value; }
|
public override RecyclableCollectionsBenchmarkSource DataType { get => base.DataType; set => base.DataType = value; }
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
// [Params(1, 10, 1024, 10240, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinItemsCountForParallelization)]
|
// [Params(1, 10, 1024, 10240, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinItemsCountForParallelization)]
|
||||||
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
|
// [Params(RecyclableDefaults.MinItemsCountForParallelization * 3)]
|
||||||
// [Params(1_048_576)]
|
// [Params(1_048_576)]
|
||||||
public virtual int TestObjectCount { get; set; } = 1; //33_554_432; //524_288; //131_072;
|
public virtual int TestObjectCount { get; set; } = 131_072; //1; //33_554_432; //524_288; //131_072;
|
||||||
public virtual int TestObjectCountForSlowMethods => (TestObjectCount / 2) + 1;// Math.Min(TestObjectCount, 131_072);
|
public virtual int TestObjectCountForSlowMethods => (TestObjectCount / 2) + 1;// Math.Min(TestObjectCount, 131_072);
|
||||||
|
|
||||||
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)]
|
// [Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)]
|
||||||
|
|||||||
@@ -23,17 +23,16 @@ public static class Program
|
|||||||
// ****************
|
// ****************
|
||||||
// *** Template ***
|
// *** Template ***
|
||||||
// ****************
|
// ****************
|
||||||
//var benchmark = new RecyclableCollectionsBenchmarks();
|
var benchmark = new RecyclableCollectionsBenchmarks();
|
||||||
//benchmark.Setup();
|
benchmark.Setup();
|
||||||
|
|
||||||
//foreach (var _ in Enumerable.Range(1, 1000))
|
//foreach (var _ in Enumerable.Range(1, 1000))
|
||||||
//{
|
//{
|
||||||
// benchmark.RecyclableLongList_Remove_FirstItems();
|
benchmark.List_ForEach();
|
||||||
// benchmark.RecyclableLongList_RemoveAt_LastItems();
|
benchmark.RecyclableList_ForEach();
|
||||||
// benchmark.List_RemoveAt_LastItems();
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//benchmark.Cleanup();
|
benchmark.Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RunPocBenchmarks()
|
static void RunPocBenchmarks()
|
||||||
@@ -53,8 +52,8 @@ public static class Program
|
|||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
RunRecyclableCollectionsBenchmarks();
|
RunRecyclableCollectionsBenchmarks();
|
||||||
// RunPocBenchmarks();
|
//RunPocBenchmarks();
|
||||||
// RunSelectedBenchmarks();
|
//RunSelectedBenchmarks();
|
||||||
// RunAssemblyBenchmarks();
|
//RunAssemblyBenchmarks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,15 @@
|
|||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Pools
|
namespace Recyclable.Collections.Pools
|
||||||
{
|
{
|
||||||
public class RecyclableCollectionVersionPool
|
//public class RecyclableCollectionVersionPool
|
||||||
{
|
//{
|
||||||
private static readonly RecyclableCollectionVersionPool _shared = new();
|
// private static readonly RecyclableCollectionVersionPool _shared = new();
|
||||||
public static RecyclableCollectionVersionPool Shared => _shared;
|
// public static RecyclableCollectionVersionPool Shared => _shared;
|
||||||
|
|
||||||
private ObjectPool<RecyclableCollectionVersion> _pool = new DefaultObjectPool<RecyclableCollectionVersion>(new DefaultPooledObjectPolicy<RecyclableCollectionVersion>());
|
// private readonly ObjectPool<RecyclableCollectionVersion> _pool = new DefaultObjectPool<RecyclableCollectionVersion>(new DefaultPooledObjectPolicy<RecyclableCollectionVersion>());
|
||||||
|
|
||||||
public RecyclableCollectionVersion Get() => _pool.Get().Reset();
|
// public RecyclableCollectionVersion Get() => _pool.Get().Reset();
|
||||||
public void Return(RecyclableCollectionVersion version) => _pool.Return(version);
|
// public void Return(RecyclableCollectionVersion version) => _pool.Return(version);
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
public partial class RecyclableList<T>
|
||||||
|
{
|
||||||
|
//internal class RecyclableListEnumeratorPool
|
||||||
|
//{
|
||||||
|
// private static readonly RecyclableListEnumeratorPool _shared = new();
|
||||||
|
// public static RecyclableListEnumeratorPool Shared => _shared;
|
||||||
|
|
||||||
|
// private readonly ObjectPool<RecyclableListEnumerator> _pool = new DefaultObjectPool<RecyclableListEnumerator>(new DefaultPooledObjectPolicy<RecyclableListEnumerator>());
|
||||||
|
|
||||||
|
// public RecyclableListEnumerator Get(RecyclableList<T> list) => _pool.Get().Reset(this, list);
|
||||||
|
|
||||||
|
// public void Return(RecyclableListEnumerator version) => _pool.Return(version);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,39 +2,88 @@
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
|
public delegate void VersionChangedEventHandler();
|
||||||
|
|
||||||
public sealed class RecyclableCollectionVersion
|
public sealed class RecyclableCollectionVersion
|
||||||
{
|
{
|
||||||
private ulong _enumeratorCount;
|
//private ulong _enumeratorCount;
|
||||||
public ulong Version;
|
|
||||||
|
//private ulong _version;
|
||||||
|
//public ulong Version
|
||||||
|
//{
|
||||||
|
// get => _version;
|
||||||
|
|
||||||
|
// set
|
||||||
|
// {
|
||||||
|
// _version = value;
|
||||||
|
// _versionChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public uint Version { get; set; }
|
||||||
|
|
||||||
public bool IsVersioned;
|
public bool IsVersioned;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private VersionChangedEventHandler? _versionChanged;
|
||||||
public void Inc() => _ = unchecked(Version++);
|
public event VersionChangedEventHandler VersionChanged
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void AddEnumerator()
|
|
||||||
{
|
{
|
||||||
if (unchecked(_enumeratorCount++) == 0)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
add
|
||||||
|
{
|
||||||
|
_versionChanged += value;
|
||||||
|
if (!IsVersioned)
|
||||||
|
{
|
||||||
|
IsVersioned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
remove
|
||||||
{
|
{
|
||||||
IsVersioned = true;
|
_versionChanged -= value;
|
||||||
|
if (_versionChanged == null)
|
||||||
|
{
|
||||||
|
IsVersioned = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void RemoveEnumerator()
|
public void Inc()
|
||||||
{
|
{
|
||||||
if (unchecked(--_enumeratorCount) == 0)
|
if (IsVersioned)
|
||||||
{
|
{
|
||||||
IsVersioned = false;
|
_versionChanged!();
|
||||||
}
|
}
|
||||||
|
//_ = unchecked(Version++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
//public RecyclableCollectionVersion AddEnumerator()
|
||||||
|
//{
|
||||||
|
// if (unchecked(_enumeratorCount++) == 0)
|
||||||
|
// {
|
||||||
|
// IsVersioned = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return this;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
//public void RemoveEnumerator()
|
||||||
|
//{
|
||||||
|
// if (unchecked(--_enumeratorCount) == 0)
|
||||||
|
// {
|
||||||
|
// IsVersioned = false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public RecyclableCollectionVersion Reset()
|
public RecyclableCollectionVersion Reset()
|
||||||
{
|
{
|
||||||
IsVersioned = false;
|
IsVersioned = false;
|
||||||
Version = 0;
|
//Version = 0;
|
||||||
_enumeratorCount = 0;
|
//_enumeratorCount = 0;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,166 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.ConstrainedExecution;
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
public class RecyclableListEnumerator<T> : IEnumerator<T>
|
public partial class RecyclableList<T>
|
||||||
{
|
{
|
||||||
private static void ThrowListModifiedException() => throw new InvalidOperationException("Can't move to the next item, because the collection was modified. You must restart the enumeration by calling Reset(), if you want to enumerate the collection again");
|
|
||||||
|
|
||||||
private int _currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
public struct Enumerator : IEnumerator<T>, IEnumerator
|
||||||
private RecyclableList<T>? _list;
|
|
||||||
private RecyclableCollectionVersion? _listVersion;
|
|
||||||
private ulong _version;
|
|
||||||
|
|
||||||
private T? _current;
|
|
||||||
|
|
||||||
public RecyclableListEnumerator(RecyclableList<T> list)
|
|
||||||
{
|
{
|
||||||
_list = list;
|
#nullable disable
|
||||||
_listVersion = list._version;
|
private readonly RecyclableList<T> _list;
|
||||||
_listVersion!.AddEnumerator();
|
private int _currentIndex;
|
||||||
_version = _listVersion!.Version;
|
private T _current;
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => _current ?? throw new InvalidOperationException("The enumerator wasn't initialized. You need to call MoveNext() before getting the current iterator value");
|
//[Pure]
|
||||||
object? IEnumerator.Current => _current;
|
//private readonly int _listCount;
|
||||||
|
//private readonly int _listCountMinus1;
|
||||||
|
//private readonly T[] _memoryBlock;
|
||||||
|
//private bool _endOfCollectionReached;
|
||||||
|
|
||||||
public void Dispose()
|
//private RecyclableCollectionVersion _listVersion;
|
||||||
{
|
//private ulong _enumeratorVersion;
|
||||||
if (_listVersion != null)
|
//private bool _isVersionChanged;
|
||||||
|
|
||||||
|
//private void VersionChanged() => _isVersionChanged = true;
|
||||||
|
|
||||||
|
//internal RecyclableListEnumeratorPool? _pool;
|
||||||
|
//internal bool _returned;
|
||||||
|
|
||||||
|
//public RecyclableListEnumerator()
|
||||||
|
//{
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[Pure]
|
||||||
|
internal Enumerator(RecyclableList<T> list)
|
||||||
{
|
{
|
||||||
_listVersion.RemoveEnumerator();
|
_current = default;
|
||||||
_listVersion = null;
|
_currentIndex = 0;
|
||||||
|
_list = list;
|
||||||
|
//_memoryBlock = list._memoryBlock;
|
||||||
|
//_listCount = list._count;
|
||||||
|
//_listVersion = list._version!;
|
||||||
|
//_listVersion.VersionChanged += VersionChanged;
|
||||||
|
//_enumeratorVersion = _listVersion.Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_list != null)
|
//{
|
||||||
|
// //[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
// get =>
|
||||||
|
// //if (_currentIndex < 0)
|
||||||
|
// //{
|
||||||
|
// // ThrowHelper.ThrowInvalidOperationException("The enumerator wasn't initialized. You need to call MoveNext() before getting the current iterator value");
|
||||||
|
// //}
|
||||||
|
// //else
|
||||||
|
// //if (_endOfCollectionReached)
|
||||||
|
// //{
|
||||||
|
// // throw new InvalidOperationException("The enumerator reached the end of the collection. You need to call Reset(), if you want to enumerate the collection again");
|
||||||
|
// //}
|
||||||
|
// //else
|
||||||
|
// //{
|
||||||
|
// //if (_currentIndex < 0)
|
||||||
|
// //{
|
||||||
|
// // ThrowHelper.ThrowInvalidOperationException("The enumerator wasn't initialized. You need to call MoveNext() before getting the current iterator value");
|
||||||
|
// //}
|
||||||
|
// //else
|
||||||
|
// _memoryBlock[_currentIndex];//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[Pure]
|
||||||
|
public readonly void Dispose()
|
||||||
{
|
{
|
||||||
_list = null;
|
// if (_listVersion != null)
|
||||||
|
// {
|
||||||
|
// _listVersion.VersionChanged -= VersionChanged;
|
||||||
|
// //_listVersion.RemoveEnumerator(this);
|
||||||
|
//#nullable disable
|
||||||
|
// _listVersion = null;
|
||||||
|
//#nullable restore
|
||||||
|
// }
|
||||||
|
|
||||||
|
//if (_pool != null)
|
||||||
|
//{
|
||||||
|
// if (!_returned)
|
||||||
|
// {
|
||||||
|
// _returned = true;
|
||||||
|
// _pool?.Return(this);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
//_memoryBlock = null;
|
||||||
|
//GC.SuppressFinalize(this);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
}
|
public bool MoveNext()
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_list != null)
|
|
||||||
{
|
{
|
||||||
if (_list._version!.Version != _version)
|
var list = _list;
|
||||||
{
|
//if (_isVersionChanged)
|
||||||
ThrowListModifiedException();
|
//{
|
||||||
return false;
|
// ThrowHelper.ThrowListModifiedException();
|
||||||
}
|
// return false;
|
||||||
else if (_currentIndex < _list.Count - 1)
|
//}
|
||||||
|
//else
|
||||||
|
if (_currentIndex < list._count)
|
||||||
{
|
{
|
||||||
|
_current = list._memoryBlock[_currentIndex];
|
||||||
_currentIndex++;
|
_currentIndex++;
|
||||||
_current = _list[_currentIndex];
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
//else if (!_endOfCollectionReached)
|
||||||
return false;
|
//{
|
||||||
}
|
// _endOfCollectionReached = true;
|
||||||
}
|
// return false;
|
||||||
else
|
//}
|
||||||
{
|
|
||||||
|
_current = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
#nullable restore
|
||||||
{
|
public T Current => _current;
|
||||||
_currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
object IEnumerator.Current => _current;
|
||||||
_current = default;
|
#nullable disable
|
||||||
if (_list != null)
|
|
||||||
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
void IEnumerator.Reset()
|
||||||
{
|
{
|
||||||
_version = _list._version!.Version;
|
_currentIndex = 0;
|
||||||
|
_current = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//_endOfCollectionReached = false;
|
||||||
|
//if (_isVersionChanged)
|
||||||
|
//{
|
||||||
|
//
|
||||||
|
//_enumeratorVersion = _listVersion!.Version;
|
||||||
|
// _isVersionChanged = false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//internal RecyclableListEnumerator Reset(RecyclableListEnumeratorPool pool, RecyclableList<T> list)
|
||||||
|
//{
|
||||||
|
// _returned = false;
|
||||||
|
// if (_pool != pool)
|
||||||
|
// {
|
||||||
|
// _pool = pool;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
// _listVersion = list._version!;
|
||||||
|
// _enumeratorVersion = _listVersion!.Version;
|
||||||
|
// list._version!.AddEnumerator();
|
||||||
|
// return this;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[Pure]
|
||||||
|
//public object Clone() => MemberwiseClone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#nullable restore
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,9 @@ namespace Recyclable.Collections
|
|||||||
public bool IsFixedSize => false;
|
public bool IsFixedSize => false;
|
||||||
public bool IsSynchronized => false;
|
public bool IsSynchronized => false;
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
private object? _syncRoot;
|
private object? _syncRoot;
|
||||||
|
|
||||||
public object SyncRoot
|
public object SyncRoot
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -39,7 +41,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +60,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||||
return RecyclableDefaults.ItemNotFoundIndex;
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +93,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
public partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IDisposable
|
[Serializable]
|
||||||
|
public sealed partial class RecyclableList<T> : IList<T>, IReadOnlyList<T>, IList, IDisposable
|
||||||
{
|
{
|
||||||
private static readonly ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
|
private static readonly ArrayPool<T> _arrayPool = ArrayPool<T>.Create();
|
||||||
private static readonly bool _defaultIsNull = default(T) == null;
|
private static readonly bool _defaultIsNull = default(T) == null;
|
||||||
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
||||||
|
|
||||||
private static void ThrowArgumentOutOfRangeException(in string argumentName, in string message) => throw new ArgumentOutOfRangeException(argumentName, message);
|
|
||||||
public static explicit operator ReadOnlySpan<T>(RecyclableList<T> source) => new(source._memoryBlock, 0, source.Count);
|
public static explicit operator ReadOnlySpan<T>(RecyclableList<T> source) => new(source._memoryBlock, 0, source.Count);
|
||||||
|
|
||||||
protected T[] _memoryBlock;
|
protected T[] _memoryBlock;
|
||||||
@@ -224,23 +224,23 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected int _count;
|
protected int _count;
|
||||||
public int Count
|
public int Count => _count;
|
||||||
{
|
//{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => _count;
|
// get => _count;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
// TODO: Clear items if we're decreasing the size of the list, if T is reference type
|
// // TODO: Clear items if we're decreasing the size of the list, if T is reference type
|
||||||
set
|
// set
|
||||||
{
|
// {
|
||||||
_count = value;
|
// _count = value;
|
||||||
|
|
||||||
if (_version?.IsVersioned ?? false)
|
// if (_version?.IsVersioned ?? false)
|
||||||
{
|
// {
|
||||||
_version.Inc();
|
// _version.Inc();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
public bool IsReadOnly => false;
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
@@ -397,12 +397,12 @@ namespace Recyclable.Collections
|
|||||||
var sourceItemsCount = items.LongCount;
|
var sourceItemsCount = items.LongCount;
|
||||||
if (sourceItemsCount > int.MaxValue)
|
if (sourceItemsCount > int.MaxValue)
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException(nameof(items), $"The number of items exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The number of items exceeds the maximum capacity of {nameof(RecyclableList<T>)}, equal {int.MaxValue}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList<T>)}, instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_count + sourceItemsCount > int.MaxValue)
|
if (_count + sourceItemsCount > int.MaxValue)
|
||||||
{
|
{
|
||||||
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");
|
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 = _count + (int)sourceItemsCount;
|
int targetCapacity = _count + (int)sourceItemsCount;
|
||||||
@@ -571,7 +571,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (index >= _count)
|
if (index >= _count)
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException(nameof(index), $"Provided value {index} exceeds the no. of items on the list {_count}");
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(index), $"Provided value {index} exceeds the no. of items on the list {_count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
_count--;
|
_count--;
|
||||||
@@ -593,19 +593,16 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||||
public IEnumerator<T> GetEnumerator()
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
IEnumerator IEnumerable.GetEnumerator() =>
|
||||||
_version ??= RecyclableCollectionVersionPool.Shared.Get();
|
//_version ??= new(); // RecyclableCollectionVersionPool.Shared.Get();
|
||||||
return new RecyclableListEnumerator<T>(this);
|
new Enumerator(this);//return RecyclableListEnumeratorPool.Shared.Get(this);
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
|
||||||
{
|
//_version ??= new(); // RecyclableCollectionVersionPool.Shared.Get();
|
||||||
_version ??= RecyclableCollectionVersionPool.Shared.Get();
|
new Enumerator(this);//return RecyclableListEnumeratorPool.Shared.Get(this);
|
||||||
return new RecyclableListEnumerator<T>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@@ -620,9 +617,9 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
if (_version != null)
|
if (_version != null)
|
||||||
{
|
{
|
||||||
var toReturn = _version;
|
//var toReturn = _version;
|
||||||
_version = null;
|
_version = null;
|
||||||
RecyclableCollectionVersionPool.Shared.Return(toReturn);
|
//RecyclableCollectionVersionPool.Shared.Return(toReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
public sealed class RecyclableLongListEnumerator<T> : IEnumerator<T>
|
||||||
|
{
|
||||||
|
public static void ThrowListModifiedException() => throw new InvalidOperationException("Can't move to the next item, because the collection was modified. You must restart the enumeration by calling Reset(), if you want to enumerate the collection again");
|
||||||
|
|
||||||
|
private int _blockSize;
|
||||||
|
private int _blockSizeMinus1;
|
||||||
|
private int _currentBlockIndex = 0;
|
||||||
|
private int _currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
private int _lastBlockWithData;
|
||||||
|
private RecyclableLongList<T>? _list;
|
||||||
|
private RecyclableCollectionVersion? _listVersion;
|
||||||
|
private T[][] _memoryBlocks;
|
||||||
|
private int _nextItemBlockIndex;
|
||||||
|
private int _nextItemIndex;
|
||||||
|
//private ulong _version;
|
||||||
|
private bool _isVersionChanged;
|
||||||
|
private void VersionChanged() => _isVersionChanged = true;
|
||||||
|
|
||||||
|
private T? _current;
|
||||||
|
|
||||||
|
public RecyclableLongListEnumerator(RecyclableLongList<T> list)
|
||||||
|
{
|
||||||
|
_listVersion = list._version;
|
||||||
|
list._version!.VersionChanged += VersionChanged;
|
||||||
|
//_listVersion!.AddEnumerator();
|
||||||
|
_blockSize = list._blockSize;
|
||||||
|
_blockSizeMinus1 = list._blockSizeMinus1;
|
||||||
|
_lastBlockWithData = list._lastBlockWithData;
|
||||||
|
_list = list;
|
||||||
|
//_version = _listVersion!.Version;
|
||||||
|
_memoryBlocks = list._memoryBlocks;
|
||||||
|
_nextItemBlockIndex = list._nextItemBlockIndex;
|
||||||
|
_nextItemIndex = _lastBlockWithData == _nextItemBlockIndex ? _list._nextItemIndex : _blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current => _current ?? throw new InvalidOperationException("The enumerator wasn't initialized. You need to call MoveNext() before getting the current iterator value");
|
||||||
|
object? IEnumerator.Current => _current;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_listVersion != null)
|
||||||
|
{
|
||||||
|
_listVersion.VersionChanged -= VersionChanged;
|
||||||
|
//_listVersion.RemoveEnumerator();
|
||||||
|
_listVersion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_list != null)
|
||||||
|
{
|
||||||
|
_list = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_list != null)
|
||||||
|
{
|
||||||
|
if (_isVersionChanged)
|
||||||
|
{
|
||||||
|
_current = default;
|
||||||
|
ThrowListModifiedException();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (_currentBlockIndex < _lastBlockWithData)
|
||||||
|
{
|
||||||
|
if (_currentIndex < _blockSizeMinus1)
|
||||||
|
{
|
||||||
|
_currentIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_currentIndex = 0;
|
||||||
|
_currentBlockIndex++;
|
||||||
|
if (_currentBlockIndex == _lastBlockWithData && _currentIndex >= _nextItemIndex)
|
||||||
|
{
|
||||||
|
_current = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_current = _memoryBlocks[_currentBlockIndex][_currentIndex];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (_currentBlockIndex == _lastBlockWithData && !(_currentIndex >= _nextItemIndex - 1))
|
||||||
|
{
|
||||||
|
_currentIndex++;
|
||||||
|
_current = _memoryBlocks[_currentBlockIndex][_currentIndex];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_current = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_current = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_currentBlockIndex = 0;
|
||||||
|
_currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
_current = default;
|
||||||
|
if (_isVersionChanged)
|
||||||
|
{
|
||||||
|
//_blockSize = _list!._blockSize;
|
||||||
|
//_blockSizeMinus1 = _list._blockSizeMinus1;
|
||||||
|
_isVersionChanged = false;
|
||||||
|
//_lastBlockWithData = _list!._lastBlockWithData;
|
||||||
|
//_listVersion = _list._version;
|
||||||
|
//_memoryBlocks = _list._memoryBlocks;
|
||||||
|
//_nextItemBlockIndex = _list._nextItemBlockIndex;
|
||||||
|
//_nextItemIndex = _lastBlockWithData == _nextItemBlockIndex ? _list._nextItemIndex : _blockSize;
|
||||||
|
//_version = _listVersion!.Version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal RecyclableLongListEnumerator<T> Init()
|
||||||
|
{
|
||||||
|
_currentBlockIndex = 0;
|
||||||
|
_currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
_current = default;
|
||||||
|
if (_list != null)
|
||||||
|
{
|
||||||
|
_blockSize = _list._blockSize;
|
||||||
|
_blockSizeMinus1 = _list._blockSizeMinus1;
|
||||||
|
_isVersionChanged = false;
|
||||||
|
_lastBlockWithData = _list._lastBlockWithData;
|
||||||
|
_listVersion = _list._version;
|
||||||
|
_memoryBlocks = _list._memoryBlocks;
|
||||||
|
_nextItemBlockIndex = _list._nextItemBlockIndex;
|
||||||
|
_nextItemIndex = _lastBlockWithData == _nextItemBlockIndex ? _list._nextItemIndex : _blockSize;
|
||||||
|
//_version = _listVersion!.Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ namespace Recyclable.Collections
|
|||||||
internal int _blockSize;
|
internal int _blockSize;
|
||||||
internal byte _blockSizePow2BitShift;
|
internal byte _blockSizePow2BitShift;
|
||||||
internal int _blockSizeMinus1;
|
internal int _blockSizeMinus1;
|
||||||
private int _nextItemBlockIndex;
|
internal int _nextItemBlockIndex;
|
||||||
internal int _nextItemIndex;
|
internal int _nextItemIndex;
|
||||||
private int _reservedBlockCount;
|
private int _reservedBlockCount;
|
||||||
|
|
||||||
@@ -36,12 +36,34 @@ namespace Recyclable.Collections
|
|||||||
public long Capacity
|
public long Capacity
|
||||||
{
|
{
|
||||||
get => _capacity;
|
get => _capacity;
|
||||||
protected set => _capacity = value;
|
protected set
|
||||||
|
{
|
||||||
|
_capacity = value;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count => checked((int)_longCount);
|
public int Count
|
||||||
public bool IsReadOnly { get; }
|
{
|
||||||
private int _lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
get => checked((int)_longCount);
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_longCount = value;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
internal int _lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
public int LastBlockWithData => _lastBlockWithData;
|
public int LastBlockWithData => _lastBlockWithData;
|
||||||
|
|
||||||
internal long _longCount;
|
internal long _longCount;
|
||||||
@@ -49,7 +71,15 @@ namespace Recyclable.Collections
|
|||||||
public long LongCount
|
public long LongCount
|
||||||
{
|
{
|
||||||
get => _longCount;
|
get => _longCount;
|
||||||
set => _longCount = value;
|
set
|
||||||
|
{
|
||||||
|
_longCount = value;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ReservedBlockCount => _reservedBlockCount;
|
public int ReservedBlockCount => _reservedBlockCount;
|
||||||
@@ -58,6 +88,9 @@ namespace Recyclable.Collections
|
|||||||
public byte BlockSizePow2BitShift => _blockSizePow2BitShift;
|
public byte BlockSizePow2BitShift => _blockSizePow2BitShift;
|
||||||
public int NextItemBlockIndex => _nextItemBlockIndex;
|
public int NextItemBlockIndex => _nextItemBlockIndex;
|
||||||
public int NextItemIndex => _nextItemIndex;
|
public int NextItemIndex => _nextItemIndex;
|
||||||
|
|
||||||
|
internal RecyclableCollectionVersion? _version;
|
||||||
|
public RecyclableCollectionVersion? Version => _version;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private static void CopyFollowingItems(RecyclableLongList<T> list, long destinationItemIndex)
|
private static void CopyFollowingItems(RecyclableLongList<T> list, long destinationItemIndex)
|
||||||
@@ -631,16 +664,32 @@ namespace Recyclable.Collections
|
|||||||
public T this[long index]
|
public T this[long index]
|
||||||
{
|
{
|
||||||
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
||||||
set => new Span<T>(_memoryBlocks[checked((int)(index >> _blockSizePow2BitShift))])[checked((int)(index & _blockSizeMinus1))] = value;
|
set
|
||||||
|
{
|
||||||
|
new Span<T>(_memoryBlocks[checked((int)(index >> _blockSizePow2BitShift))])[checked((int)(index & _blockSizeMinus1))] = value;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T this[int index]
|
public T this[int index]
|
||||||
{
|
{
|
||||||
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
||||||
set => new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = value;
|
set
|
||||||
|
{
|
||||||
|
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = value;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T[][] AsArray { get => _memoryBlocks; }
|
public T[][] AsArray => _memoryBlocks;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(T item)
|
public void Add(T item)
|
||||||
@@ -667,6 +716,11 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_longCount++;
|
_longCount++;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRange(in T[] items)
|
public void AddRange(in T[] items)
|
||||||
@@ -715,6 +769,11 @@ namespace Recyclable.Collections
|
|||||||
_nextItemIndex = itemsSpan.Length;
|
_nextItemIndex = itemsSpan.Length;
|
||||||
_longCount = targetCapacity;
|
_longCount = targetCapacity;
|
||||||
_lastBlockWithData = targetBlockIndex;
|
_lastBlockWithData = targetBlockIndex;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRange(RecyclableList<T> items)
|
public void AddRange(RecyclableList<T> items)
|
||||||
@@ -755,6 +814,11 @@ namespace Recyclable.Collections
|
|||||||
_longCount = targetCapacity;
|
_longCount = targetCapacity;
|
||||||
_nextItemBlockIndex = targetBlockIndex;
|
_nextItemBlockIndex = targetBlockIndex;
|
||||||
_lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
|
_lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRange(ReadOnlySpan<T> itemsSpan)
|
public void AddRange(ReadOnlySpan<T> itemsSpan)
|
||||||
@@ -802,6 +866,11 @@ namespace Recyclable.Collections
|
|||||||
_nextItemIndex = itemsSpan.Length;
|
_nextItemIndex = itemsSpan.Length;
|
||||||
_longCount = targetCapacity;
|
_longCount = targetCapacity;
|
||||||
_lastBlockWithData = targetBlockIndex;
|
_lastBlockWithData = targetBlockIndex;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRange(RecyclableLongList<T> items)
|
public void AddRange(RecyclableLongList<T> items)
|
||||||
@@ -874,6 +943,11 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_longCount = targetCapacity;
|
_longCount = targetCapacity;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRange(List<T> items)
|
public void AddRange(List<T> items)
|
||||||
@@ -921,6 +995,11 @@ namespace Recyclable.Collections
|
|||||||
_nextItemBlockIndex = targetBlockIndex;
|
_nextItemBlockIndex = targetBlockIndex;
|
||||||
_longCount = targetCapacity;
|
_longCount = targetCapacity;
|
||||||
_lastBlockWithData = targetBlockIndex - (_nextItemIndex > 0 ? 0 : 1);
|
_lastBlockWithData = targetBlockIndex - (_nextItemIndex > 0 ? 0 : 1);
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRange(IList<T> items)
|
public void AddRange(IList<T> items)
|
||||||
@@ -975,6 +1054,11 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
_defaultBlockArrayPool.Return(itemsBuffer, NeedsClearing);
|
_defaultBlockArrayPool.Return(itemsBuffer, NeedsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -983,40 +1067,41 @@ namespace Recyclable.Collections
|
|||||||
if (source is RecyclableLongList<T> sourceRecyclableLongList)
|
if (source is RecyclableLongList<T> sourceRecyclableLongList)
|
||||||
{
|
{
|
||||||
AddRange(sourceRecyclableLongList);
|
AddRange(sourceRecyclableLongList);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (source is RecyclableList<T> sourceRecyclableList)
|
||||||
if (source is RecyclableList<T> sourceRecyclableList)
|
|
||||||
{
|
{
|
||||||
AddRange(sourceRecyclableList);
|
AddRange(sourceRecyclableList);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (source is T[] sourceArray)
|
||||||
if (source is T[] sourceArray)
|
|
||||||
{
|
{
|
||||||
AddRange(sourceArray);
|
AddRange(sourceArray);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (source is List<T> sourceList)
|
||||||
if (source is List<T> sourceList)
|
|
||||||
{
|
{
|
||||||
AddRange(sourceList);
|
AddRange(sourceList);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (source is IList<T> sourceIList)
|
||||||
if (source is IList<T> sourceIList)
|
|
||||||
{
|
{
|
||||||
AddRange(sourceIList);
|
AddRange(sourceIList);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||||
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
|
||||||
{
|
{
|
||||||
AddRangeWithKnownCount(source, requiredAdditionalCapacity);
|
AddRangeWithKnownCount(source, requiredAdditionalCapacity);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddRangeEnumerated(source, growByCount);
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddRangeEnumerated(source, growByCount);
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
@@ -1045,6 +1130,11 @@ namespace Recyclable.Collections
|
|||||||
_nextItemIndex = 0;
|
_nextItemIndex = 0;
|
||||||
_lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
_lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
_longCount = 0;
|
_longCount = 0;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -1075,7 +1165,17 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
_version ??= new();
|
||||||
|
return new RecyclableLongListEnumerator<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
_version ??= new();
|
||||||
|
return new RecyclableLongListEnumerator<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public int IndexOf(T item)
|
public int IndexOf(T item)
|
||||||
@@ -1188,6 +1288,11 @@ namespace Recyclable.Collections
|
|||||||
#pragma warning restore CS8601
|
#pragma warning restore CS8601
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1217,6 +1322,11 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_lastBlockWithData--;
|
_lastBlockWithData--;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -1256,6 +1366,11 @@ namespace Recyclable.Collections
|
|||||||
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex] = default;
|
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex] = default;
|
||||||
#pragma warning restore CS8601
|
#pragma warning restore CS8601
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -1294,9 +1409,12 @@ namespace Recyclable.Collections
|
|||||||
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex] = default;
|
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex] = default;
|
||||||
#pragma warning restore CS8601
|
#pragma warning restore CS8601
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
~RecyclableLongList()
|
~RecyclableLongList()
|
||||||
{
|
{
|
||||||
@@ -1315,6 +1433,13 @@ namespace Recyclable.Collections
|
|||||||
_memoryBlocksPool.Return(_memoryBlocks, NeedsClearing);
|
_memoryBlocksPool.Return(_memoryBlocks, NeedsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version != null)
|
||||||
|
{
|
||||||
|
//var toReturn = _version;
|
||||||
|
_version = null;
|
||||||
|
//RecyclableCollectionVersionPool.Shared.Return(toReturn);
|
||||||
|
}
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
internal static class ThrowHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is a helper method to throw <see cref="InvalidOperationException"/>, thrown when the collection was modified during enumeration.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">Always</exception>
|
||||||
|
public static void ThrowListModifiedException() => throw new InvalidOperationException("Can't move to the next item, because the collection was modified. You must restart the enumeration by calling Reset(), if you want to enumerate the collection again");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is a helper method to throw <see cref="InvalidOperationException"/> with the given message.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">Always</exception>
|
||||||
|
public static void ThrowInvalidOperationException(in string message) => throw new InvalidOperationException(message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is a helper method to throw <see cref="ArgumentOutOfRangeException"/> with the given message.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Always</exception>
|
||||||
|
public static void ThrowArgumentOutOfRangeException(in string argumentName, in string message) => throw new ArgumentOutOfRangeException(argumentName, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user