diff --git a/Recyclable.Collections/RecyclableList.Enumerator.cs b/Recyclable.Collections/RecyclableList.Enumerator.cs index 89a9be0..7dc6adc 100644 --- a/Recyclable.Collections/RecyclableList.Enumerator.cs +++ b/Recyclable.Collections/RecyclableList.Enumerator.cs @@ -1,87 +1,185 @@ using System.Collections; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; namespace Recyclable.Collections { public partial class RecyclableList { - - public struct Enumerator : IEnumerator, IEnumerator + [StructLayout(LayoutKind.Auto)] + public struct Enumerator : IEnumerator { -#nullable disable - private readonly RecyclableList _list; private int _currentIndex; - private T _current; + private readonly ulong _enumeratorVersion; + private readonly RecyclableList _list; - //[Pure] - //private readonly int _listCount; - //private readonly int _listCountMinus1; - //private readonly T[] _memoryBlock; - //private bool _endOfCollectionReached; + // & WAS SLOWER + // private bool _isInValidState = false; + // private bool _endOfCollectionReached; + // private T _current; - //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 list) +#nullable disable + // [Pure] + public readonly T Current { - _current = default; - _currentIndex = 0; - _list = list; - //_memoryBlock = list._memoryBlock; - //_listCount = list._count; - //_listVersion = list._version!; - //_listVersion.VersionChanged += VersionChanged; - //_enumeratorVersion = _listVersion.Version; + get + { + if (_enumeratorVersion != _list._version) + { + throw new InvalidOperationException(); + // & WAS SLOWER + // ThrowHelper.ThrowListModifiedException(); + } + + if (_currentIndex == 0 || _currentIndex > _list._count) + { + throw new IndexOutOfRangeException(); + // & WAS SLOWER + // ThrowHelper.ThrowCollectionNotInitialized();// new IndexOutOfRangeException();// _current; + } + + Contract.EndContractBlock(); + + return _list._memoryBlock[_currentIndex - 1];// _current; + } } - //{ - // //[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] + object IEnumerator.Current + { + get + { + if (_enumeratorVersion != _list._version) + { + throw new InvalidOperationException(); + // & WAS SLOWER + // ThrowHelper.ThrowListModifiedException(); + } + + if (_currentIndex == 0 || _currentIndex > _list._count) + { + throw new IndexOutOfRangeException(); + // & WAS SLOWER + // ThrowHelper.ThrowCollectionNotInitialized(); + } + + Contract.EndContractBlock(); + + return _list._memoryBlock[_currentIndex - 1]; + + // & WAS SLOWER + // return _current; + } + } + + // & WAS SLOWER + // private readonly int _listCount; + // private RecyclableCollectionVersion _listVersion; + // private readonly T[] _memoryBlock; + // private bool _isVersionChanged; + // private void VersionChanged() => _isVersionChanged = true; + // internal RecyclableListEnumeratorPool? _pool; + // internal bool _returned; + // private readonly int _listCountMinus1; + + // & WAS SLOWER + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // [MethodImpl(MethodImplOptions.NoInlining)] + public bool MoveNext() + { + // & WAS SLOWER + // var list = _list; + if (_enumeratorVersion != _list._version) + { + throw new InvalidOperationException(); + // & WAS SLOWER + // ThrowHelper.ThrowListModifiedException(); + } + + Contract.EndContractBlock(); + + // & WAS SLOWER + // if (_currentIndex == 0) + // { + // _isInValidState = true; + // _currentIndex++; + // return true; + // } + // else + if (_currentIndex < _list._count) + { + // & WAS SLOWER + // _current = _list._memoryBlock[_currentIndex]; + _currentIndex++; + return true; + } + + // & WAS SLOWER + // else if (_currentIndex == _list._count) + // { + // & WAS SLOWER + // This isn't needed in most use-cases, because the enumerator will get disposed, releasing the reference, if any. + // _current = _defaultValue; + // _currentIndex++; + // return false; + // } + // else if (!_endOfCollectionReached) + // { + // // & WAS SLOWER + // // This isn't needed in most use-cases, because the enumerator will get disposed, releasing the reference, if any. + // // _current = _defaultValue; + // // _currentIndex = int.MinValue; + // _endOfCollectionReached = true; + // return false; + // } + + // & WAS SLOWER + // else if (!_endOfCollectionReached) + // { + // _current = default; + // _endOfCollectionReached = true; + // return false; + // } + + // & WAS SLOWER + // _current = default; + // _currentIndex = _list._count + 1; + return false; + } + + public Enumerator(RecyclableList list) + { + _currentIndex = 0; + _list = list; + _enumeratorVersion = list._version; + + // & WAS SLOWER + // _current = default; + // _endOfCollectionReached = false; + // _listCount = list._count; + // _memoryBlock = list._memoryBlock; + // _isVersionChanged = false; + // _listVersion = list.Version; + // _listVersion.AddEnumerator(); + // _enumeratorVersion = list._version.Version; + // list._version.RegisterHandler(VersionChanged); + // _enumeratorVersion = _listVersion.Version; + // _endOfCollectionReached = false; + // _listCountMinus1 = list._count - 1; + } - //[Pure] public readonly void Dispose() { - // if (_listVersion != null) - // { - // _listVersion.VersionChanged -= VersionChanged; - // //_listVersion.RemoveEnumerator(this); - //#nullable disable - // _listVersion = null; - //#nullable restore - // } + // & WAS SLOWER + //if (_listVersion != null) + //{ + //_list._version.RemoveEnumerator(); + //_listVersion.RemoveEnumerator(this); + //} - //if (_pool != null) + // & WAS SLOWER + // if (_pool != null) //{ // if (!_returned) // { @@ -92,74 +190,19 @@ namespace Recyclable.Collections //else //{ //_memoryBlock = null; - //GC.SuppressFinalize(this); + //GC.SuppressFinalize(this); //} } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - var list = _list; - //if (_isVersionChanged) - //{ - // ThrowHelper.ThrowListModifiedException(); - // return false; - //} - //else - if (_currentIndex < list._count) - { - _current = list._memoryBlock[_currentIndex]; - _currentIndex++; - return true; - } - - //else if (!_endOfCollectionReached) - //{ - // _endOfCollectionReached = true; - // return false; - //} - - _current = default; - return false; - } - -#nullable restore - public T Current => _current; - object IEnumerator.Current => _current; -#nullable disable - - //[MethodImpl(MethodImplOptions.AggressiveInlining)] void IEnumerator.Reset() { _currentIndex = 0; - _current = default; + + // & WAS SLOWER + // _endOfCollectionReached = false; + // _isInValidState = false; + // _current = default; } - - //_endOfCollectionReached = false; - //if (_isVersionChanged) - //{ - // - //_enumeratorVersion = _listVersion!.Version; - // _isVersionChanged = false; - //} - - //internal RecyclableListEnumerator Reset(RecyclableListEnumeratorPool pool, RecyclableList 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 diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index e823ef4..6e1518f 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -15,9 +15,12 @@ namespace Recyclable.Collections public static explicit operator ReadOnlySpan(RecyclableList source) => new(source._memoryBlock, 0, source.Count); protected T[] _memoryBlock; - internal RecyclableCollectionVersion? _version; + // & WAS SLOWER + // internal RecyclableCollectionVersion _version; + // public RecyclableCollectionVersion Version => _version; - public RecyclableCollectionVersion? Version => _version; + protected ulong _version; + public ulong Version => _version; protected IEnumerator AddRangeEnumerated(IEnumerable source, int growByCount) { @@ -196,11 +199,7 @@ namespace Recyclable.Collections set { _memoryBlock[index] = value; - - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } } @@ -214,11 +213,7 @@ namespace Recyclable.Collections { _memoryBlock = Resize(_memoryBlock, value); _capacity = _memoryBlock.Length; - } - - if (_version?.IsVersioned ?? false) - { - _version.Inc(); + _version++; } } } @@ -254,11 +249,7 @@ namespace Recyclable.Collections } _memoryBlock[_count++] = item; - - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] @@ -281,10 +272,7 @@ namespace Recyclable.Collections itemsSpan.CopyTo(targetSpan); _count = targetCapacity; - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] @@ -306,10 +294,7 @@ namespace Recyclable.Collections itemsSpan.CopyTo(targetSpan); _count = targetCapacity; - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] @@ -330,10 +315,7 @@ namespace Recyclable.Collections items.CopyTo(_memoryBlock, _count); _count = targetCapacity; - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] @@ -354,10 +336,7 @@ namespace Recyclable.Collections items.CopyTo(_memoryBlock, _count); _count = targetCapacity; - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] @@ -380,10 +359,7 @@ namespace Recyclable.Collections itemsSpan.CopyTo(targetSpan); _count = targetCapacity; - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] @@ -438,10 +414,7 @@ namespace Recyclable.Collections _count = targetCapacity; - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] @@ -467,19 +440,13 @@ namespace Recyclable.Collections { AddRangeWithKnownCount(source, _count, requiredAdditionalCapacity); - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } else { AddRangeEnumerated(source, growByCount); - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } } @@ -494,11 +461,7 @@ namespace Recyclable.Collections } _count = 0; - - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -529,11 +492,7 @@ namespace Recyclable.Collections _memoryBlock[index] = item; _count++; - - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] @@ -555,10 +514,7 @@ namespace Recyclable.Collections #pragma warning restore CS8601 } - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; return true; } @@ -587,22 +543,17 @@ namespace Recyclable.Collections #pragma warning restore CS8601 } - if (_version?.IsVersioned ?? false) - { - _version.Inc(); - } + _version++; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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 ??= new(); // RecyclableCollectionVersionPool.Shared.Get(); - new Enumerator(this);//return RecyclableListEnumeratorPool.Shared.Get(this); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); public void Dispose() { @@ -615,13 +566,6 @@ namespace Recyclable.Collections _arrayPool.Return(_memoryBlock, NeedsClearing); } - if (_version != null) - { - //var toReturn = _version; - _version = null; - //RecyclableCollectionVersionPool.Shared.Return(toReturn); - } - GC.SuppressFinalize(this); } } diff --git a/Recyclable.CollectionsTests/RecyclableListTests.cs b/Recyclable.CollectionsTests/RecyclableListTests.cs index b5a4dba..d47ca01 100644 --- a/Recyclable.CollectionsTests/RecyclableListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableListTests.cs @@ -349,7 +349,11 @@ namespace Recyclable.CollectionsTests // Act var yieldedItems = new List((int)itemsCount); - yieldedItems.AddRange(list); + using var enumerator = list.GetEnumerator(); + while (enumerator.MoveNext()) + { + yieldedItems.Add(enumerator.Current); + } // Validate _ = yieldedItems.Count.Should().Be((int)itemsCount);