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
|
||||
{
|
||||
Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
||||
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
||||
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,
|
||||
SetItem,
|
||||
}
|
||||
@@ -17,52 +17,53 @@ namespace Recyclable.Collections.Benchmarks
|
||||
public RecyclableCollectionsBenchmarks() : base()
|
||||
{
|
||||
TestObjectCount = 1;
|
||||
BaseDataType = RecyclableCollectionsBenchmarkSource.List;
|
||||
DataType = RecyclableCollectionsBenchmarkSource.RecyclableLongList;
|
||||
//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.Contains_FirstItems,
|
||||
RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
||||
RecyclableCollectionsBenchmarkType.Count,
|
||||
RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
||||
RecyclableCollectionsBenchmarkType.Create,
|
||||
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.Add_WithCapacity,
|
||||
//RecyclableCollectionsBenchmarkType.Add,
|
||||
//RecyclableCollectionsBenchmarkType.AddRange_WithCapacity,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsArray,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIEnumerable,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsIList,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsList,
|
||||
//RecyclableCollectionsBenchmarkType.AddRangeWhenSourceIsSameType,
|
||||
//RecyclableCollectionsBenchmarkType.Contains_FirstItems,
|
||||
//RecyclableCollectionsBenchmarkType.Contains_LastItems,
|
||||
//RecyclableCollectionsBenchmarkType.Count,
|
||||
//RecyclableCollectionsBenchmarkType.Create_WithCapacity,
|
||||
//RecyclableCollectionsBenchmarkType.Create,
|
||||
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
|
||||
)]
|
||||
|
||||
public RecyclableCollectionsBenchmarkType TestCase { get; set; } = RecyclableCollectionsBenchmarkType.IndexOf_BestAndWorstCases;
|
||||
|
||||
[Params
|
||||
(
|
||||
RecyclableCollectionsBenchmarkSource.List
|
||||
)]
|
||||
//[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
|
||||
//RecyclableCollectionsBenchmarkSource.Array,
|
||||
//RecyclableCollectionsBenchmarkSource.List,
|
||||
//RecyclableCollectionsBenchmarkSource.PooledList,
|
||||
RecyclableCollectionsBenchmarkSource.RecyclableList
|
||||
//RecyclableCollectionsBenchmarkSource.RecyclableLongList
|
||||
)]
|
||||
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(RecyclableDefaults.MinItemsCountForParallelization * 3)]
|
||||
// [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);
|
||||
|
||||
// [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 ***
|
||||
// ****************
|
||||
//var benchmark = new RecyclableCollectionsBenchmarks();
|
||||
//benchmark.Setup();
|
||||
var benchmark = new RecyclableCollectionsBenchmarks();
|
||||
benchmark.Setup();
|
||||
|
||||
//foreach (var _ in Enumerable.Range(1, 1000))
|
||||
//{
|
||||
// benchmark.RecyclableLongList_Remove_FirstItems();
|
||||
// benchmark.RecyclableLongList_RemoveAt_LastItems();
|
||||
// benchmark.List_RemoveAt_LastItems();
|
||||
benchmark.List_ForEach();
|
||||
benchmark.RecyclableList_ForEach();
|
||||
//}
|
||||
|
||||
//benchmark.Cleanup();
|
||||
benchmark.Cleanup();
|
||||
}
|
||||
|
||||
static void RunPocBenchmarks()
|
||||
@@ -53,8 +52,8 @@ public static class Program
|
||||
static void Main(string[] args)
|
||||
{
|
||||
RunRecyclableCollectionsBenchmarks();
|
||||
// RunPocBenchmarks();
|
||||
// RunSelectedBenchmarks();
|
||||
// RunAssemblyBenchmarks();
|
||||
//RunPocBenchmarks();
|
||||
//RunSelectedBenchmarks();
|
||||
//RunAssemblyBenchmarks();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,15 @@
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Recyclable.Collections.Pools
|
||||
{
|
||||
public class RecyclableCollectionVersionPool
|
||||
{
|
||||
private static readonly RecyclableCollectionVersionPool _shared = new();
|
||||
public static RecyclableCollectionVersionPool Shared => _shared;
|
||||
//public class RecyclableCollectionVersionPool
|
||||
//{
|
||||
// private static readonly RecyclableCollectionVersionPool _shared = new();
|
||||
// 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 void Return(RecyclableCollectionVersion version) => _pool.Return(version);
|
||||
}
|
||||
// public RecyclableCollectionVersion Get() => _pool.Get().Reset();
|
||||
// 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
|
||||
{
|
||||
public delegate void VersionChangedEventHandler();
|
||||
|
||||
public sealed class RecyclableCollectionVersion
|
||||
{
|
||||
private ulong _enumeratorCount;
|
||||
public ulong Version;
|
||||
//private ulong _enumeratorCount;
|
||||
|
||||
//private ulong _version;
|
||||
//public ulong Version
|
||||
//{
|
||||
// get => _version;
|
||||
|
||||
// set
|
||||
// {
|
||||
// _version = value;
|
||||
// _versionChanged?.Invoke(this, EventArgs.Empty);
|
||||
// }
|
||||
//}
|
||||
|
||||
//public uint Version { get; set; }
|
||||
|
||||
public bool IsVersioned;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Inc() => _ = unchecked(Version++);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddEnumerator()
|
||||
private VersionChangedEventHandler? _versionChanged;
|
||||
public event VersionChangedEventHandler VersionChanged
|
||||
{
|
||||
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)]
|
||||
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)]
|
||||
public RecyclableCollectionVersion Reset()
|
||||
{
|
||||
IsVersioned = false;
|
||||
Version = 0;
|
||||
_enumeratorCount = 0;
|
||||
//Version = 0;
|
||||
//_enumeratorCount = 0;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,166 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
|
||||
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;
|
||||
private RecyclableList<T>? _list;
|
||||
private RecyclableCollectionVersion? _listVersion;
|
||||
private ulong _version;
|
||||
|
||||
private T? _current;
|
||||
|
||||
public RecyclableListEnumerator(RecyclableList<T> list)
|
||||
public struct Enumerator : IEnumerator<T>, IEnumerator
|
||||
{
|
||||
_list = list;
|
||||
_listVersion = list._version;
|
||||
_listVersion!.AddEnumerator();
|
||||
_version = _listVersion!.Version;
|
||||
}
|
||||
#nullable disable
|
||||
private readonly RecyclableList<T> _list;
|
||||
private int _currentIndex;
|
||||
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");
|
||||
object? IEnumerator.Current => _current;
|
||||
//[Pure]
|
||||
//private readonly int _listCount;
|
||||
//private readonly int _listCountMinus1;
|
||||
//private readonly T[] _memoryBlock;
|
||||
//private bool _endOfCollectionReached;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_listVersion != null)
|
||||
//private RecyclableCollectionVersion _listVersion;
|
||||
//private ulong _enumeratorVersion;
|
||||
//private bool _isVersionChanged;
|
||||
|
||||
//private void VersionChanged() => _isVersionChanged = true;
|
||||
|
||||
//internal RecyclableListEnumeratorPool? _pool;
|
||||
//internal bool _returned;
|
||||
|
||||
//public RecyclableListEnumerator()
|
||||
//{
|
||||
//}
|
||||
|
||||
//[Pure]
|
||||
internal Enumerator(RecyclableList<T> list)
|
||||
{
|
||||
_listVersion.RemoveEnumerator();
|
||||
_listVersion = null;
|
||||
_current = default;
|
||||
_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);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_list != null)
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_list._version!.Version != _version)
|
||||
{
|
||||
ThrowListModifiedException();
|
||||
return false;
|
||||
}
|
||||
else if (_currentIndex < _list.Count - 1)
|
||||
var list = _list;
|
||||
//if (_isVersionChanged)
|
||||
//{
|
||||
// ThrowHelper.ThrowListModifiedException();
|
||||
// return false;
|
||||
//}
|
||||
//else
|
||||
if (_currentIndex < list._count)
|
||||
{
|
||||
_current = list._memoryBlock[_currentIndex];
|
||||
_currentIndex++;
|
||||
_current = _list[_currentIndex];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//else if (!_endOfCollectionReached)
|
||||
//{
|
||||
// _endOfCollectionReached = true;
|
||||
// return false;
|
||||
//}
|
||||
|
||||
_current = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
||||
_current = default;
|
||||
if (_list != null)
|
||||
#nullable restore
|
||||
public T Current => _current;
|
||||
object IEnumerator.Current => _current;
|
||||
#nullable disable
|
||||
|
||||
//[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 IsSynchronized => false;
|
||||
|
||||
[NonSerialized]
|
||||
private object? _syncRoot;
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
@@ -39,7 +41,7 @@ namespace Recyclable.Collections
|
||||
}
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -91,7 +93,7 @@ namespace Recyclable.Collections
|
||||
}
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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 bool _defaultIsNull = default(T) == null;
|
||||
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);
|
||||
|
||||
protected T[] _memoryBlock;
|
||||
@@ -224,23 +224,23 @@ namespace Recyclable.Collections
|
||||
}
|
||||
|
||||
protected int _count;
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _count;
|
||||
public int Count => _count;
|
||||
//{
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// get => _count;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// TODO: Clear items if we're decreasing the size of the list, if T is reference type
|
||||
set
|
||||
{
|
||||
_count = value;
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// // TODO: Clear items if we're decreasing the size of the list, if T is reference type
|
||||
// set
|
||||
// {
|
||||
// _count = value;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (_version?.IsVersioned ?? false)
|
||||
// {
|
||||
// _version.Inc();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
@@ -397,12 +397,12 @@ namespace Recyclable.Collections
|
||||
var sourceItemsCount = items.LongCount;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
@@ -571,7 +571,7 @@ namespace Recyclable.Collections
|
||||
{
|
||||
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--;
|
||||
@@ -593,19 +593,16 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
_version ??= RecyclableCollectionVersionPool.Shared.Get();
|
||||
return new RecyclableListEnumerator<T>(this);
|
||||
}
|
||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
IEnumerator IEnumerable.GetEnumerator() =>
|
||||
//_version ??= new(); // RecyclableCollectionVersionPool.Shared.Get();
|
||||
new Enumerator(this);//return RecyclableListEnumeratorPool.Shared.Get(this);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
_version ??= RecyclableCollectionVersionPool.Shared.Get();
|
||||
return new RecyclableListEnumerator<T>(this);
|
||||
}
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
|
||||
//_version ??= new(); // RecyclableCollectionVersionPool.Shared.Get();
|
||||
new Enumerator(this);//return RecyclableListEnumeratorPool.Shared.Get(this);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -620,9 +617,9 @@ namespace Recyclable.Collections
|
||||
|
||||
if (_version != null)
|
||||
{
|
||||
var toReturn = _version;
|
||||
//var toReturn = _version;
|
||||
_version = null;
|
||||
RecyclableCollectionVersionPool.Shared.Return(toReturn);
|
||||
//RecyclableCollectionVersionPool.Shared.Return(toReturn);
|
||||
}
|
||||
|
||||
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 byte _blockSizePow2BitShift;
|
||||
internal int _blockSizeMinus1;
|
||||
private int _nextItemBlockIndex;
|
||||
internal int _nextItemBlockIndex;
|
||||
internal int _nextItemIndex;
|
||||
private int _reservedBlockCount;
|
||||
|
||||
@@ -36,12 +36,34 @@ namespace Recyclable.Collections
|
||||
public long Capacity
|
||||
{
|
||||
get => _capacity;
|
||||
protected set => _capacity = value;
|
||||
protected set
|
||||
{
|
||||
_capacity = value;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => checked((int)_longCount);
|
||||
public bool IsReadOnly { get; }
|
||||
private int _lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
||||
public int Count
|
||||
{
|
||||
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;
|
||||
|
||||
internal long _longCount;
|
||||
@@ -49,7 +71,15 @@ namespace Recyclable.Collections
|
||||
public long LongCount
|
||||
{
|
||||
get => _longCount;
|
||||
set => _longCount = value;
|
||||
set
|
||||
{
|
||||
_longCount = value;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int ReservedBlockCount => _reservedBlockCount;
|
||||
@@ -58,6 +88,9 @@ namespace Recyclable.Collections
|
||||
public byte BlockSizePow2BitShift => _blockSizePow2BitShift;
|
||||
public int NextItemBlockIndex => _nextItemBlockIndex;
|
||||
public int NextItemIndex => _nextItemIndex;
|
||||
|
||||
internal RecyclableCollectionVersion? _version;
|
||||
public RecyclableCollectionVersion? Version => _version;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void CopyFollowingItems(RecyclableLongList<T> list, long destinationItemIndex)
|
||||
@@ -631,16 +664,32 @@ namespace Recyclable.Collections
|
||||
public T this[long index]
|
||||
{
|
||||
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]
|
||||
{
|
||||
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)]
|
||||
public void Add(T item)
|
||||
@@ -667,6 +716,11 @@ namespace Recyclable.Collections
|
||||
}
|
||||
|
||||
_longCount++;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(in T[] items)
|
||||
@@ -715,6 +769,11 @@ namespace Recyclable.Collections
|
||||
_nextItemIndex = itemsSpan.Length;
|
||||
_longCount = targetCapacity;
|
||||
_lastBlockWithData = targetBlockIndex;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(RecyclableList<T> items)
|
||||
@@ -755,6 +814,11 @@ namespace Recyclable.Collections
|
||||
_longCount = targetCapacity;
|
||||
_nextItemBlockIndex = targetBlockIndex;
|
||||
_lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(ReadOnlySpan<T> itemsSpan)
|
||||
@@ -802,6 +866,11 @@ namespace Recyclable.Collections
|
||||
_nextItemIndex = itemsSpan.Length;
|
||||
_longCount = targetCapacity;
|
||||
_lastBlockWithData = targetBlockIndex;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(RecyclableLongList<T> items)
|
||||
@@ -874,6 +943,11 @@ namespace Recyclable.Collections
|
||||
}
|
||||
|
||||
_longCount = targetCapacity;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(List<T> items)
|
||||
@@ -921,6 +995,11 @@ namespace Recyclable.Collections
|
||||
_nextItemBlockIndex = targetBlockIndex;
|
||||
_longCount = targetCapacity;
|
||||
_lastBlockWithData = targetBlockIndex - (_nextItemIndex > 0 ? 0 : 1);
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IList<T> items)
|
||||
@@ -975,6 +1054,11 @@ namespace Recyclable.Collections
|
||||
{
|
||||
_defaultBlockArrayPool.Return(itemsBuffer, NeedsClearing);
|
||||
}
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,40 +1067,41 @@ namespace Recyclable.Collections
|
||||
if (source is RecyclableLongList<T> sourceRecyclableLongList)
|
||||
{
|
||||
AddRange(sourceRecyclableLongList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is RecyclableList<T> sourceRecyclableList)
|
||||
else if (source is RecyclableList<T> sourceRecyclableList)
|
||||
{
|
||||
AddRange(sourceRecyclableList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is T[] sourceArray)
|
||||
else if (source is T[] sourceArray)
|
||||
{
|
||||
AddRange(sourceArray);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is List<T> sourceList)
|
||||
else if (source is List<T> sourceList)
|
||||
{
|
||||
AddRange(sourceList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source is IList<T> sourceIList)
|
||||
else if (source is IList<T> sourceIList)
|
||||
{
|
||||
AddRange(sourceIList);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||
else if (source.TryGetNonEnumeratedCount(out var 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()
|
||||
@@ -1045,6 +1130,11 @@ namespace Recyclable.Collections
|
||||
_nextItemIndex = 0;
|
||||
_lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
|
||||
_longCount = 0;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
[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 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)]
|
||||
public int IndexOf(T item)
|
||||
@@ -1188,6 +1288,11 @@ namespace Recyclable.Collections
|
||||
#pragma warning restore CS8601
|
||||
}
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1217,6 +1322,11 @@ namespace Recyclable.Collections
|
||||
}
|
||||
|
||||
_lastBlockWithData--;
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -1256,6 +1366,11 @@ namespace Recyclable.Collections
|
||||
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex] = default;
|
||||
#pragma warning restore CS8601
|
||||
}
|
||||
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -1294,9 +1409,12 @@ namespace Recyclable.Collections
|
||||
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex] = default;
|
||||
#pragma warning restore CS8601
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => RecyclableLongList<T>.RecyclableLongListHelpers.Enumerate(_memoryBlocks, _blockSize, LongCount).GetEnumerator();
|
||||
if (_version?.IsVersioned ?? false)
|
||||
{
|
||||
_version.Inc();
|
||||
}
|
||||
}
|
||||
|
||||
~RecyclableLongList()
|
||||
{
|
||||
@@ -1315,6 +1433,13 @@ namespace Recyclable.Collections
|
||||
_memoryBlocksPool.Return(_memoryBlocks, NeedsClearing);
|
||||
}
|
||||
|
||||
if (_version != null)
|
||||
{
|
||||
//var toReturn = _version;
|
||||
_version = null;
|
||||
//RecyclableCollectionVersionPool.Shared.Return(toReturn);
|
||||
}
|
||||
|
||||
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