Versioning implemented in RecyclableList<T>
This commit is contained in:
1 parent
5d17a259c1
commit
51d3613ffd
5 files changed
+315
-67
No files matched your search
@@ -142,6 +142,10 @@
|
|||||||
1. 🅿️ Review locks
|
1. 🅿️ Review locks
|
||||||
1. 🅿️ Measure multi-threading performance
|
1. 🅿️ Measure multi-threading performance
|
||||||
1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario
|
1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario
|
||||||
|
1. 🅿️ Optimize `RecyclableCollectionVersionObjectPool`
|
||||||
|
1. 🅿️ Implement custom `ObjectPool<T>`
|
||||||
|
1. 🅿️ Measure multi-threading performance
|
||||||
|
1. 🅿️ Implement memory bucket disposal in high RAM pressure scenario
|
||||||
1. 🅿️ Review `RecyclableArrayPool`
|
1. 🅿️ Review `RecyclableArrayPool`
|
||||||
1. 🅿️ Review locks
|
1. 🅿️ Review locks
|
||||||
1. 🅿️ Measure multi-threading performance
|
1. 🅿️ Measure multi-threading performance
|
||||||
@@ -166,9 +170,10 @@
|
|||||||
1. 🅿️ `RecyclableLongListExtensions.CopyTo`
|
1. 🅿️ `RecyclableLongListExtensions.CopyTo`
|
||||||
1. 🅿️ Cleanup
|
1. 🅿️ Cleanup
|
||||||
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
||||||
1. ✅ Cleanup `RecyclableLongListExtensions`
|
1. ✅ `RecyclableLongListExtensions`
|
||||||
1. ✅ `ListExtensions`
|
1. ✅ `ListExtensions`
|
||||||
1. 🅿️ `MathUtils`
|
1. 🅿️ `MathUtils`
|
||||||
|
1. 🅿️ Resolve TODOs
|
||||||
1. 🅿️ Optimize
|
1. 🅿️ Optimize
|
||||||
1. 🅿️ `ListExtensions`
|
1. 🅿️ `ListExtensions`
|
||||||
1. 🅿️ `MathUtils`
|
1. 🅿️ `MathUtils`
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
private ObjectPool<RecyclableCollectionVersion> _pool = new DefaultObjectPool<RecyclableCollectionVersion>(new DefaultPooledObjectPolicy<RecyclableCollectionVersion>());
|
||||||
|
|
||||||
|
public RecyclableCollectionVersion Get() => _pool.Get().Reset();
|
||||||
|
public void Return(RecyclableCollectionVersion version) => _pool.Return(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
public sealed class RecyclableCollectionVersion
|
||||||
|
{
|
||||||
|
private ulong _enumeratorCount;
|
||||||
|
public ulong Version;
|
||||||
|
public bool IsVersioned;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Inc() => _ = unchecked(Version++);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void AddEnumerator()
|
||||||
|
{
|
||||||
|
if (unchecked(_enumeratorCount++) == 0)
|
||||||
|
{
|
||||||
|
IsVersioned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void RemoveEnumerator()
|
||||||
|
{
|
||||||
|
if (unchecked(--_enumeratorCount) == 0)
|
||||||
|
{
|
||||||
|
IsVersioned = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public RecyclableCollectionVersion Reset()
|
||||||
|
{
|
||||||
|
IsVersioned = false;
|
||||||
|
Version = 0;
|
||||||
|
_enumeratorCount = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
public class RecyclableListEnumerator<T> : IEnumerator<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)
|
||||||
|
{
|
||||||
|
_list = list;
|
||||||
|
_listVersion = list._version;
|
||||||
|
_listVersion!.AddEnumerator();
|
||||||
|
_version = _listVersion!.Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.RemoveEnumerator();
|
||||||
|
_listVersion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_list != null)
|
||||||
|
{
|
||||||
|
_list = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_list != null)
|
||||||
|
{
|
||||||
|
if (_list._version!.Version != _version)
|
||||||
|
{
|
||||||
|
ThrowListModifiedException();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (_currentIndex < _list.Count - 1)
|
||||||
|
{
|
||||||
|
_currentIndex++;
|
||||||
|
_current = _list[_currentIndex];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_currentIndex = RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
_current = default;
|
||||||
|
if (_list != null)
|
||||||
|
{
|
||||||
|
_version = _list._version!.Version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Buffers;
|
using Recyclable.Collections.Pools;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -9,10 +10,65 @@ namespace Recyclable.Collections
|
|||||||
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;
|
||||||
public static explicit operator ReadOnlySpan<T>(RecyclableList<T> source) => new(source._memoryBlock, 0, source.Count);
|
|
||||||
protected T[] _memoryBlock;
|
|
||||||
|
|
||||||
private static void ThrowArgumentOutOfRangeException(in string argumentName, in string message) => throw new ArgumentOutOfRangeException(argumentName, message);
|
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;
|
||||||
|
internal RecyclableCollectionVersion? _version;
|
||||||
|
|
||||||
|
public RecyclableCollectionVersion? Version => _version;
|
||||||
|
|
||||||
|
protected IEnumerator<T> AddRangeEnumerated(IEnumerable<T> source, int growByCount)
|
||||||
|
{
|
||||||
|
int targetItemIdx = _count;
|
||||||
|
Span<T> memorySpan;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
var enumerator = source.GetEnumerator();
|
||||||
|
|
||||||
|
int capacity = _capacity;
|
||||||
|
memorySpan = new(_memoryBlock);
|
||||||
|
if (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
int available = capacity - targetItemIdx;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (targetItemIdx + growByCount > capacity)
|
||||||
|
{
|
||||||
|
capacity = EnsureCapacity(this, capacity + growByCount);
|
||||||
|
memorySpan = new(_memoryBlock);
|
||||||
|
available = capacity - targetItemIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < available; i++)
|
||||||
|
{
|
||||||
|
memorySpan[targetItemIdx++] = enumerator.Current;
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (i >= available);
|
||||||
|
}
|
||||||
|
|
||||||
|
_count = targetItemIdx;
|
||||||
|
return enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void AddRangeWithKnownCount(IEnumerable<T> source, int targetItemIdx, int requiredAdditionalCapacity)
|
||||||
|
{
|
||||||
|
_ = EnsureCapacity(this, targetItemIdx + requiredAdditionalCapacity);
|
||||||
|
|
||||||
|
Span<T> memorySpan = new(_memoryBlock);
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
memorySpan[targetItemIdx++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
_count = targetItemIdx;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
protected static T[] Resize(in T[]? source, int newSize)
|
protected static T[] Resize(in T[]? source, int newSize)
|
||||||
@@ -137,7 +193,15 @@ namespace Recyclable.Collections
|
|||||||
get => _memoryBlock[index];
|
get => _memoryBlock[index];
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
set => _memoryBlock[index] = value;
|
set
|
||||||
|
{
|
||||||
|
_memoryBlock[index] = value;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int _capacity;
|
protected int _capacity;
|
||||||
@@ -151,6 +215,11 @@ namespace Recyclable.Collections
|
|||||||
_memoryBlock = Resize(_memoryBlock, value);
|
_memoryBlock = Resize(_memoryBlock, value);
|
||||||
_capacity = _memoryBlock.Length;
|
_capacity = _memoryBlock.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,10 +230,19 @@ namespace Recyclable.Collections
|
|||||||
get => _count;
|
get => _count;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
set => _count = value;
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsReadOnly { get; } = false;
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(T item)
|
public void Add(T item)
|
||||||
@@ -176,6 +254,11 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_memoryBlock[_count++] = item;
|
_memoryBlock[_count++] = item;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -197,6 +280,11 @@ namespace Recyclable.Collections
|
|||||||
Span<T> itemsSpan = new(items);
|
Span<T> itemsSpan = new(items);
|
||||||
itemsSpan.CopyTo(targetSpan);
|
itemsSpan.CopyTo(targetSpan);
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -217,6 +305,11 @@ namespace Recyclable.Collections
|
|||||||
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
||||||
itemsSpan.CopyTo(targetSpan);
|
itemsSpan.CopyTo(targetSpan);
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -236,6 +329,11 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
items.CopyTo(_memoryBlock, _count);
|
items.CopyTo(_memoryBlock, _count);
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -255,6 +353,11 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
items.CopyTo(_memoryBlock, _count);
|
items.CopyTo(_memoryBlock, _count);
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -276,6 +379,11 @@ namespace Recyclable.Collections
|
|||||||
Span<T> itemsSpan = new(items._memoryBlock, 0, sourceItemsCount);
|
Span<T> itemsSpan = new(items._memoryBlock, 0, sourceItemsCount);
|
||||||
itemsSpan.CopyTo(targetSpan);
|
itemsSpan.CopyTo(targetSpan);
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -329,6 +437,11 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -337,72 +450,37 @@ namespace Recyclable.Collections
|
|||||||
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 RecyclableList<T> sourceRecyclableList)
|
||||||
if (source is RecyclableList<T> sourceRecyclableList)
|
|
||||||
{
|
{
|
||||||
AddRange(sourceRecyclableList);
|
AddRange(sourceRecyclableList);
|
||||||
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))
|
||||||
int targetItemIdx = _count;
|
|
||||||
Span<T> memorySpan;
|
|
||||||
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(this, targetItemIdx + requiredAdditionalCapacity);
|
AddRangeWithKnownCount(source, _count, requiredAdditionalCapacity);
|
||||||
memorySpan = new(_memoryBlock);
|
|
||||||
foreach (var item in source)
|
if (_version?.IsVersioned ?? false)
|
||||||
{
|
{
|
||||||
memorySpan[targetItemIdx++] = item;
|
_version.Inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
_count = targetItemIdx;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
int i;
|
|
||||||
using var enumerator = source.GetEnumerator();
|
|
||||||
|
|
||||||
int capacity = _capacity;
|
|
||||||
memorySpan = new(_memoryBlock);
|
|
||||||
if (enumerator.MoveNext())
|
|
||||||
{
|
{
|
||||||
int available = capacity - targetItemIdx;
|
AddRangeEnumerated(source, growByCount);
|
||||||
do
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
{
|
{
|
||||||
if (targetItemIdx + growByCount > capacity)
|
_version.Inc();
|
||||||
{
|
|
||||||
capacity = EnsureCapacity(this, capacity + growByCount);
|
|
||||||
memorySpan = new(_memoryBlock);
|
|
||||||
available = capacity - targetItemIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < available; i++)
|
|
||||||
{
|
|
||||||
memorySpan[targetItemIdx++] = enumerator.Current;
|
|
||||||
if (!enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
while (i >= available);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_count = targetItemIdx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T[] AsArray => _memoryBlock;
|
public T[] AsArray => _memoryBlock;
|
||||||
@@ -416,6 +494,11 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_count = 0;
|
_count = 0;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -424,16 +507,6 @@ namespace Recyclable.Collections
|
|||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public void CopyTo(T[] array, int index) => Array.Copy(_memoryBlock, 0, array, index, _count);
|
public void CopyTo(T[] array, int index) => Array.Copy(_memoryBlock, 0, array, index, _count);
|
||||||
|
|
||||||
protected static IEnumerable<T> Enumerate(RecyclableList<T> list)
|
|
||||||
{
|
|
||||||
int count = list._count;
|
|
||||||
var memory = list._memoryBlock;
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
yield return memory[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T itemToFind) => _count > 0 ? Array.IndexOf(_memoryBlock, itemToFind, 0, _count) : RecyclableDefaults.ItemNotFoundIndex;
|
public int IndexOf(T itemToFind) => _count > 0 ? Array.IndexOf(_memoryBlock, itemToFind, 0, _count) : RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
@@ -456,6 +529,11 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
_memoryBlock[index] = item;
|
_memoryBlock[index] = item;
|
||||||
_count++;
|
_count++;
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -477,6 +555,11 @@ namespace Recyclable.Collections
|
|||||||
#pragma warning restore CS8601
|
#pragma warning restore CS8601
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,13 +586,26 @@ namespace Recyclable.Collections
|
|||||||
_memoryBlock[_count] = default;
|
_memoryBlock[_count] = default;
|
||||||
#pragma warning restore CS8601
|
#pragma warning restore CS8601
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version?.IsVersioned ?? false)
|
||||||
|
{
|
||||||
|
_version.Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public IEnumerator<T> GetEnumerator() => Enumerate(this).GetEnumerator();
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
_version ??= RecyclableCollectionVersionPool.Shared.Get();
|
||||||
|
return new RecyclableListEnumerator<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
IEnumerator IEnumerable.GetEnumerator() => Enumerate(this).GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
_version ??= RecyclableCollectionVersionPool.Shared.Get();
|
||||||
|
return new RecyclableListEnumerator<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@@ -522,6 +618,13 @@ namespace Recyclable.Collections
|
|||||||
_arrayPool.Return(_memoryBlock, NeedsClearing);
|
_arrayPool.Return(_memoryBlock, NeedsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_version != null)
|
||||||
|
{
|
||||||
|
var toReturn = _version;
|
||||||
|
_version = null;
|
||||||
|
RecyclableCollectionVersionPool.Shared.Return(toReturn);
|
||||||
|
}
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user