diff --git a/Recyclable.Collections/MemoryBucket.cs b/Recyclable.Collections/MemoryBucket.cs index 21552ab..54a228a 100644 --- a/Recyclable.Collections/MemoryBucket.cs +++ b/Recyclable.Collections/MemoryBucket.cs @@ -2,21 +2,23 @@ namespace Recyclable.Collections { internal sealed class MemoryBucket { - public MemoryBucket(Array memory, MemoryBucket nextBucket) + public MemoryBucket(T[] memory, MemoryBucket nextBucket) { _memory = memory; _nextBucket = nextBucket; } - public MemoryBucket(Array memory) + public MemoryBucket(T[] memory) { _memory = memory; } - private Array _memory; - public Array Memory { get => _memory; set => _memory = value; } +#pragma warning disable RCS1085 + private T[] _memory; + public T[] Memory { get => _memory; set => _memory = value; } private MemoryBucket? _nextBucket; public MemoryBucket? NextBucket { get => _nextBucket; set => _nextBucket = value; } +#pragma warning restore RCS1085 } } diff --git a/Recyclable.Collections/Pools/OneSizeArrayPool.cs b/Recyclable.Collections/Pools/OneSizeArrayPool.cs index 146266b..4c50db1 100644 --- a/Recyclable.Collections/Pools/OneSizeArrayPool.cs +++ b/Recyclable.Collections/Pools/OneSizeArrayPool.cs @@ -39,7 +39,7 @@ _ = Task.Run(ReleaseMemoryOnExcess); } - public Array Rent() + public T[] Rent() { MemoryBucket? bucket; Enter(); @@ -55,10 +55,10 @@ } Exit(); - return bucket != null ? bucket.Memory : Array.CreateInstance(typeof(T), _blockSize); + return bucket != null ? bucket.Memory : new T[_blockSize]; } - public void ReturnUnchecked(Array array, bool clearArray) + public void ReturnUnchecked(T[] array, bool clearArray) { MemoryBucket bucket = new(array); if (clearArray) diff --git a/Recyclable.Collections/Pools/RecyclableArrayPool.cs b/Recyclable.Collections/Pools/RecyclableArrayPool.cs index e1f9595..dfe5c66 100644 --- a/Recyclable.Collections/Pools/RecyclableArrayPool.cs +++ b/Recyclable.Collections/Pools/RecyclableArrayPool.cs @@ -4,95 +4,24 @@ namespace Recyclable.Collections.Pools { public readonly struct RecyclableArrayPool { - private const int MaxArraySize = 32; - internal static readonly OneSizeArrayPool[] _pools = new OneSizeArrayPool[MaxArraySize]; - internal static readonly Dictionary[]> _poolsWithBoundaries = new(); - - public static Array RentShared(int blockSize, int lowerBound) - { - OneSizeArrayPool[]? pools; - lock(_poolsWithBoundaries) - { - if (!_poolsWithBoundaries.TryGetValue(lowerBound, out pools)) - { - pools = _poolsWithBoundaries[lowerBound] = new OneSizeArrayPool[MaxArraySize]; - InitializePools(pools); - } - } - - return pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent(); - } - - public static Array RentShared(int blockSize) => _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent(); - public static T[] RentSharedAsTyped(int blockSize) => (T[])_pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent(); - + internal static readonly OneSizeArrayPool[] _pools = new OneSizeArrayPool[32]; + public static T[] RentShared(int blockSize) => _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)].Rent(); public static void ReturnShared(in T[] memoryBlock, bool needsClearing) - { - if (memoryBlock.GetLowerBound(0) == 0) - { - _pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing); - } - else - { - OneSizeArrayPool[] pools; - lock(_poolsWithBoundaries) - { - pools = _poolsWithBoundaries[memoryBlock.GetLowerBound(0)]; - } - - pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing); - } - } - - public static void ReturnShared(in Array memoryBlock, bool needsClearing) - { - if (memoryBlock.GetLowerBound(0) == 0) - { - _pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing); - } - else - { - OneSizeArrayPool[] pools; - lock(_poolsWithBoundaries) - { - pools = _poolsWithBoundaries[memoryBlock.GetLowerBound(0)]; - } - - pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing); - } - } + => _pools[31 - BitOperations.LeadingZeroCount((uint)memoryBlock.Length)].ReturnUnchecked(memoryBlock, needsClearing); public static ref OneSizeArrayPool Shared(int blockSize) => ref _pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)]; - public static ref OneSizeArrayPool SharedWithBoundaries(int blockSize, int lowerBound) - { - OneSizeArrayPool[]? pools; - lock(_poolsWithBoundaries) - { - if (!_poolsWithBoundaries.TryGetValue(lowerBound, out pools)) - { - pools = _poolsWithBoundaries[lowerBound] = new OneSizeArrayPool[MaxArraySize]; - InitializePools(pools); - } - } - - return ref pools[31 - BitOperations.LeadingZeroCount((uint)blockSize)]; - } - static RecyclableArrayPool() { - InitializePools(_pools); - _ = Task.Run(CheckAndReportMemoryPressure); - } - - private static void InitializePools(OneSizeArrayPool[] pools) - { - for (var arraySize = 0; arraySize < MaxArraySize; arraySize++) + var pools = _pools; + for (var arraySize = 0; arraySize < 32; arraySize++) { int blockSize = 1 << arraySize; pools[arraySize] = new OneSizeArrayPool(blockSize, 1000); } + + _ = Task.Run(CheckAndReportMemoryPressure); } public static readonly OneSizeArrayPool Null = new(0, 0); diff --git a/Recyclable.Collections/RecyclableList.AddRange.cs b/Recyclable.Collections/RecyclableList.AddRange.cs index 7bcf4df..2f153c0 100644 --- a/Recyclable.Collections/RecyclableList.AddRange.cs +++ b/Recyclable.Collections/RecyclableList.AddRange.cs @@ -1,10 +1,14 @@ +using System; using System.Collections; +using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; +using System.Threading.Tasks; namespace Recyclable.Collections { - public static class zRecyclableListAddRange + public static class zRecyclableListAddRange { private static void AddRangeEnumerated(this RecyclableList list, IEnumerable items, int growByCount) { @@ -15,7 +19,7 @@ namespace Recyclable.Collections var enumerator = items.GetEnumerator(); int capacity = list._capacity; - memorySpan = new((T[])list._memoryBlock); + memorySpan = new(list._memoryBlock); if (enumerator.MoveNext()) { int available = capacity - targetItemIndex; @@ -24,7 +28,7 @@ namespace Recyclable.Collections if (targetItemIndex + growByCount > capacity) { capacity = RecyclableListHelpers.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); - memorySpan = new((T[])list._memoryBlock); + memorySpan = new(list._memoryBlock); available = capacity - targetItemIndex; } @@ -48,7 +52,7 @@ namespace Recyclable.Collections { list._capacity = RecyclableListHelpers.ResizeAndCopy(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity)))); - Span memorySpan = new((T[])list._memoryBlock); + Span memorySpan = new(list._memoryBlock); foreach (var item in items) { memorySpan[currentItemsCount++] = item; @@ -78,7 +82,7 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); } - new Span(items).CopyTo(new Span((T[])list._memoryBlock, list._count, items.Length)); + new Span(items).CopyTo(new Span(list._memoryBlock, list._count, items.Length)); list._count += items.Length; list._version++; } @@ -91,7 +95,7 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); } - items.CopyTo(new Span((T[])list._memoryBlock, list._count, items.Length)); + items.CopyTo(new Span(list._memoryBlock, list._count, items.Length)); list._count += items.Length; list._version++; } @@ -104,7 +108,7 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); } - items.CopyTo(new Span((T[])list._memoryBlock, list._count, items.Length)); + items.CopyTo(new Span(list._memoryBlock, list._count, items.Length)); list._count += items.Length; list._version++; } @@ -117,7 +121,7 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); } - items.CopyTo((T[])list._memoryBlock, list._count); + items.CopyTo(list._memoryBlock, list._count); list._count += items.Count; list._version++; } @@ -143,7 +147,7 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); } - items.CopyTo((T[])list._memoryBlock, list._count); + items.CopyTo(list._memoryBlock, list._count); list._count += items.Count; list._version++; } @@ -156,7 +160,7 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count)))); } - new Span((T[])items._memoryBlock, 0, items._count).CopyTo(new Span((T[])list._memoryBlock, list._count, items._count)); + new Span(items._memoryBlock, 0, items._count).CopyTo(new Span(list._memoryBlock, list._count, items._count)); list._count += items._count; list._version++; } @@ -183,7 +187,7 @@ namespace Recyclable.Collections } // TODO: Avoid unnecessary range operator - pass as arguments instead. - Span targetSpan = new Span((T[])list._memoryBlock)[oldCount..], + Span targetSpan = new Span(list._memoryBlock)[oldCount..], itemsSpan; int blockIndex, @@ -221,7 +225,7 @@ namespace Recyclable.Collections var enumerator = source.GetEnumerator(); int capacity = list._capacity; - memorySpan = new((T[])list._memoryBlock); + memorySpan = new(list._memoryBlock); if (enumerator.MoveNext()) { int available = capacity - targetItemIndex; @@ -230,7 +234,7 @@ namespace Recyclable.Collections if (targetItemIndex + growByCount > capacity) { capacity = RecyclableListHelpers.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); - memorySpan = new((T[])list._memoryBlock); + memorySpan = new(list._memoryBlock); available = capacity - targetItemIndex; } @@ -259,7 +263,7 @@ namespace Recyclable.Collections list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount)))); } - Span memorySpan = new((T[])list._memoryBlock, list._count, sourceItemsCount); + Span memorySpan = new(list._memoryBlock, list._count, sourceItemsCount); for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++) { memorySpan[sourceItemIndex] = items[sourceItemIndex]; diff --git a/Recyclable.Collections/RecyclableList.Enumerator.cs b/Recyclable.Collections/RecyclableList.Enumerator.cs index e99237c..acaae28 100644 --- a/Recyclable.Collections/RecyclableList.Enumerator.cs +++ b/Recyclable.Collections/RecyclableList.Enumerator.cs @@ -16,9 +16,9 @@ namespace Recyclable.Collections #nullable disable [Pure] - public readonly T Current => (T)_list._memoryBlock.GetValue(_currentItemIndex - 1); + public readonly T Current => _list._memoryBlock[_currentItemIndex - 1]; [Pure] - readonly object IEnumerator.Current => (T)_list._memoryBlock.GetValue(_currentItemIndex - 1); + readonly object IEnumerator.Current => _list._memoryBlock[_currentItemIndex - 1]; #nullable restore public bool MoveNext() diff --git a/Recyclable.Collections/RecyclableList.Helpers.cs b/Recyclable.Collections/RecyclableList.Helpers.cs index 79b6b5d..dd64d43 100644 --- a/Recyclable.Collections/RecyclableList.Helpers.cs +++ b/Recyclable.Collections/RecyclableList.Helpers.cs @@ -108,23 +108,13 @@ namespace Recyclable.Collections { // TODO: Measure performance // T[] newMemoryBlock = RecyclableListHelpers._arrayPool.Rent(_capacity <<= 1); - Array oldMemoryBlock = list._memoryBlock; - if (oldMemoryBlock.GetLowerBound(0) == 0) - { - list._memoryBlock = (list._capacity <<= 1) < RecyclableDefaults.MinPooledArrayLength - ? Array.CreateInstance(typeof(T), list._capacity) - : RecyclableArrayPool.RentShared(list._capacity); - } - else - { - list._memoryBlock = (list._capacity <<= 1) < RecyclableDefaults.MinPooledArrayLength - ? Array.CreateInstance(typeof(T), new[] { list._capacity }, new[] { oldMemoryBlock.GetLowerBound(0) }) - : RecyclableArrayPool.RentShared(list._capacity, oldMemoryBlock.GetLowerBound(0)); - } + T[] oldMemoryBlock = list._memoryBlock; + list._memoryBlock = (list._capacity <<= 1) < RecyclableDefaults.MinPooledArrayLength + ? new T[list._capacity] + : RecyclableArrayPool.RentShared(list._capacity); // & WAS SLOWER WITHOUT - Array.Copy(oldMemoryBlock, list._memoryBlock, list._count); - // new Span((T[])oldMemoryBlock, 0, list._count).CopyTo((T[])list._memoryBlock); + new Span(oldMemoryBlock, 0, list._count).CopyTo(list._memoryBlock); if (oldMemoryBlock.Length >= RecyclableDefaults.MinPooledArrayLength) { @@ -141,7 +131,7 @@ namespace Recyclable.Collections [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] public static int ResizeAndCopy(RecyclableList sourceList, int oldSize, int newSize) { - T[] oldMemoryBlock = (T[])sourceList._memoryBlock; + T[] oldMemoryBlock = sourceList._memoryBlock; sourceList._memoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(newSize) : new T[newSize]; @@ -149,7 +139,7 @@ namespace Recyclable.Collections // & WAS SLOWER WITHOUT // & WAS SLOWER AS ARRAY - new Span(oldMemoryBlock, 0, oldSize).CopyTo((T[])sourceList._memoryBlock); + new Span(oldMemoryBlock, 0, oldSize).CopyTo(sourceList._memoryBlock); if (oldMemoryBlock.Length >= RecyclableDefaults.MinPooledArrayLength) { diff --git a/Recyclable.Collections/RecyclableList.IList.cs b/Recyclable.Collections/RecyclableList.IList.cs index e0506fe..a7537b1 100644 --- a/Recyclable.Collections/RecyclableList.IList.cs +++ b/Recyclable.Collections/RecyclableList.IList.cs @@ -27,18 +27,18 @@ namespace Recyclable.Collections object? IList.this[int index] { - get => _memoryBlock.GetValue(index); + get => _memoryBlock[index]; set { if (value is T typedValue) { - _memoryBlock.SetValue(typedValue, index); + new Span(_memoryBlock)[index] = typedValue; } else if (value == null && _defaultIsNull) { #nullable disable - _memoryBlock.SetValue(default, index); + new Span(_memoryBlock)[index] = default; #nullable restore } else diff --git a/Recyclable.Collections/RecyclableList.VersionedEnumerator.cs b/Recyclable.Collections/RecyclableList.VersionedEnumerator.cs index cfa0001..5e729f7 100644 --- a/Recyclable.Collections/RecyclableList.VersionedEnumerator.cs +++ b/Recyclable.Collections/RecyclableList.VersionedEnumerator.cs @@ -17,10 +17,10 @@ namespace Recyclable.Collections #nullable disable [Pure] - public readonly T Current => _enumeratorVersion == _list._version ? (T)_list._memoryBlock.GetValue(_currentItemIndex - 1) : throw new InvalidOperationException(); + public readonly T Current => _enumeratorVersion == _list._version ? _list._memoryBlock[_currentItemIndex - 1] : throw new InvalidOperationException(); [Pure] - readonly object IEnumerator.Current => _enumeratorVersion == _list._version ? _list._memoryBlock.GetValue(_currentItemIndex - 1) : throw new InvalidOperationException(); + readonly object IEnumerator.Current => _enumeratorVersion == _list._version ? _list._memoryBlock[_currentItemIndex - 1] : throw new InvalidOperationException(); #nullable restore public bool MoveNext() diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index 6c29de3..cc90c49 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -10,10 +10,10 @@ namespace Recyclable.Collections { private static readonly bool _needsClearing = !typeof(T).IsValueType; - public static explicit operator ReadOnlySpan(RecyclableList source) => new((T[])source._memoryBlock, 0, source._count); + public static explicit operator ReadOnlySpan(RecyclableList source) => new(source._memoryBlock, 0, source._count); #nullable disable - internal Array _memoryBlock; + internal T[] _memoryBlock; #nullable restore internal ulong _version; @@ -22,103 +22,85 @@ namespace Recyclable.Collections public RecyclableList() { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; } - public RecyclableList(int initialCapacity = RecyclableDefaults.InitialCapacity, int lowerBound = 0) + public RecyclableList(int initialCapacity) { - if (lowerBound == 0) + if (initialCapacity >= RecyclableDefaults.InitialCapacity) { - if (initialCapacity >= RecyclableDefaults.InitialCapacity) - { - _memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength - ? RecyclableArrayPool.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity))) - : Array.CreateInstance(typeof(T), initialCapacity); - _capacity = _memoryBlock.Length; - } - else - { - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); - _capacity = RecyclableDefaults.InitialCapacity; - } + _memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength + ? RecyclableArrayPool.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity))) + : new T[initialCapacity]; + _capacity = _memoryBlock.Length; } else { - if (initialCapacity >= RecyclableDefaults.InitialCapacity) - { - _memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength - ? RecyclableArrayPool.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity)), lowerBound) - : Array.CreateInstance(typeof(T), new[] { initialCapacity }, new[] { lowerBound }); - _capacity = _memoryBlock.Length; - } - else - { - _memoryBlock = Array.CreateInstance(typeof(T), new[] { RecyclableDefaults.InitialCapacity }, new[] { lowerBound }); - _capacity = RecyclableDefaults.InitialCapacity; - } + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; + _capacity = RecyclableDefaults.InitialCapacity; } } public RecyclableList(in Array source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(RecyclableList source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(RecyclableLongList source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(ReadOnlySpan source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(in T[] source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(IReadOnlyList source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(List source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(ICollection source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(ICollection source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } @@ -128,13 +110,13 @@ namespace Recyclable.Collections { _memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity))) - : Array.CreateInstance(typeof(T), initialCapacity); + : new T[initialCapacity]; _capacity = _memoryBlock.Length; } else { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; } zRecyclableListAddRange.AddRange(this, source, RecyclableDefaults.MinPooledArrayLength); @@ -146,13 +128,13 @@ namespace Recyclable.Collections { _memoryBlock = initialCapacity >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity))) - : Array.CreateInstance(typeof(T), initialCapacity); + : new T[initialCapacity]; _capacity = _memoryBlock.Length; } else { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; } zRecyclableListAddRange.AddRange(this, source); @@ -161,23 +143,21 @@ namespace Recyclable.Collections public RecyclableList(Span source) { _capacity = RecyclableDefaults.InitialCapacity; - _memoryBlock = Array.CreateInstance(typeof(T), RecyclableDefaults.InitialCapacity); + _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; zRecyclableListAddRange.AddRange(this, source); } public T this[int index] { -#nullable disable [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (T)_memoryBlock.GetValue(index); + get => _memoryBlock[index]; [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - _memoryBlock.SetValue(value, index); + _memoryBlock[index] = value; _version++; } -#nullable restore } internal int _capacity; @@ -207,11 +187,11 @@ namespace Recyclable.Collections RecyclableListHelpers.ResizeAndCopy(this); } - _memoryBlock.SetValue(item, _count++); + _memoryBlock[_count++] = item; _version++; } - public Array AsArray => _memoryBlock; + public T[] AsArray => _memoryBlock; [MethodImpl(MethodImplOptions.NoInlining)] public void Clear() @@ -250,10 +230,11 @@ namespace Recyclable.Collections if (index < oldCount) { - Array.Copy(_memoryBlock, index, _memoryBlock, index + 1, oldCount -= index); + new Span(_memoryBlock, index, oldCount -= index) + .CopyTo(new Span(_memoryBlock, index + 1, oldCount)); } - _memoryBlock.SetValue(item, index); + _memoryBlock[index] = item; _count++; _version++; } @@ -272,7 +253,7 @@ namespace Recyclable.Collections if (_needsClearing) { - _memoryBlock.SetValue(default, _count); + _memoryBlock[_count] = default; } _version++; @@ -299,7 +280,7 @@ namespace Recyclable.Collections if (_needsClearing) { - _memoryBlock.SetValue(default, _count); + _memoryBlock[_count] = default; } _version++; diff --git a/Recyclable.Collections/RecyclableLongList.AddRange.cs b/Recyclable.Collections/RecyclableLongList.AddRange.cs index 658b685..dc96591 100644 --- a/Recyclable.Collections/RecyclableLongList.AddRange.cs +++ b/Recyclable.Collections/RecyclableLongList.AddRange.cs @@ -189,7 +189,7 @@ namespace Recyclable.Collections // We're better off to temporarily copy it to a fixed array, // than copying them item by item. We may run into OOMs, though. - T[] itemsBuffer = items.Count >= RecyclableDefaults.MinPooledArrayLength ? (T[])RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; + T[] itemsBuffer = items.Count >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; items.CopyTo(itemsBuffer, 0); Span itemsSpan = new(itemsBuffer, 0, items.Count); @@ -255,7 +255,7 @@ namespace Recyclable.Collections // We're better off to temporarily copy it to a fixed array, // than copying them item by item. We may run into OOMs, though. - T[] itemsBuffer = items.Count >= RecyclableDefaults.MinPooledArrayLength ? (T[])RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; + T[] itemsBuffer = items.Count >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; items.CopyTo(itemsBuffer, 0); @@ -590,7 +590,7 @@ namespace Recyclable.Collections targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); } - Span itemsSpan = new((T[])items._memoryBlock, 0, items._count); + Span itemsSpan = new(items._memoryBlock, 0, items._count); Span targetMemoryBlocksSpan = new(targetList._memoryBlocks); Span targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); diff --git a/Recyclable.Collections/RecyclableLongList.Helpers.cs b/Recyclable.Collections/RecyclableLongList.Helpers.cs index 2187e61..e2c43cf 100644 --- a/Recyclable.Collections/RecyclableLongList.Helpers.cs +++ b/Recyclable.Collections/RecyclableLongList.Helpers.cs @@ -150,7 +150,7 @@ namespace Recyclable.Collections if (requiredBlockCount > sourceBlockCount) { // Allocate new memory block for all arrays - newMemoryBlocks = requiredBlockCount >= RecyclableDefaults.MinPooledArrayLength ? (T[][])RecyclableArrayPool.RentShared(requiredBlockCount) : new T[requiredBlockCount][]; + newMemoryBlocks = requiredBlockCount >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(requiredBlockCount) : new T[requiredBlockCount][]; // Copy arrays from the old memory block for all arrays if (sourceBlockCount > 0) @@ -179,7 +179,7 @@ namespace Recyclable.Collections ref OneSizeArrayPool blockArrayPool = ref RecyclableArrayPool.Shared(minBlockSize); while (blockIndex >= 0 && newMemoryBlocks[blockIndex] == null) { - newMemoryBlocks[blockIndex--] = (T[])blockArrayPool.Rent(); + newMemoryBlocks[blockIndex--] = blockArrayPool.Rent(); } } else diff --git a/Recyclable.CollectionsTests/RecyclableListTests.cs b/Recyclable.CollectionsTests/RecyclableListTests.cs index 14f0008..c75652c 100644 --- a/Recyclable.CollectionsTests/RecyclableListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableListTests.cs @@ -338,13 +338,13 @@ namespace Recyclable.CollectionsTests // Arrange using var list = new RecyclableList(testData, itemsCount); - _ = ((object[])list.AsArray)[..itemsCount].Should().AllSatisfy(x => x.Should().NotBeNull()); + _ = list.AsArray[..itemsCount].Should().AllSatisfy(x => x.Should().NotBeNull()); // Act list.Clear(); // Assert - _ = ((object[])list.AsArray)[..itemsCount].Should().AllSatisfy(x => x.Should().BeNull()); + _ = list.AsArray[..itemsCount].Should().AllSatisfy(x => x.Should().BeNull()); } [Theory] @@ -393,12 +393,6 @@ namespace Recyclable.CollectionsTests _ = list.Should().Equal(testData); } - [Fact] - public void ConstructorShouldCreateArrayWithCustomBoundaries() - { - using var list = new RecyclableList(-10); - } - [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] public void ConstructorSourceShouldInitializeList(string testCase, IEnumerable testData, int itemsCount) @@ -481,14 +475,14 @@ namespace Recyclable.CollectionsTests // Arrange using var list = new RecyclableList(testData, itemsCount); var listArray = list.AsArray; - _ = ((object[])list.AsArray)[..itemsCount].Should().NotBeNull(); - _ = ((object[])listArray)[..itemsCount].Should().AllSatisfy(x => x.Should().NotBeNull()); + _ = list.AsArray[..itemsCount].Should().NotBeNull(); + _ = listArray[..itemsCount].Should().AllSatisfy(x => x.Should().NotBeNull()); // Act list.Dispose(); // Assert - _ = ((object[])listArray)[..itemsCount].Should().AllSatisfy(x => x.Should().BeNull()); + _ = listArray[..itemsCount].Should().AllSatisfy(x => x.Should().BeNull()); } [Fact]