diff --git a/Recyclable.Collections/RecyclableList.AddRange.cs b/Recyclable.Collections/RecyclableList.AddRange.cs new file mode 100644 index 0000000..2f153c0 --- /dev/null +++ b/Recyclable.Collections/RecyclableList.AddRange.cs @@ -0,0 +1,322 @@ +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 + { + private static void AddRangeEnumerated(this RecyclableList list, IEnumerable items, int growByCount) + { + int targetItemIndex = list._count; + Span memorySpan; + + int i; + var enumerator = items.GetEnumerator(); + + int capacity = list._capacity; + memorySpan = new(list._memoryBlock); + if (enumerator.MoveNext()) + { + int available = capacity - targetItemIndex; + do + { + if (targetItemIndex + growByCount > capacity) + { + capacity = RecyclableListHelpers.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); + memorySpan = new(list._memoryBlock); + available = capacity - targetItemIndex; + } + + for (i = 0; i < available; i++) + { + memorySpan[targetItemIndex++] = enumerator.Current; + if (!enumerator.MoveNext()) + { + break; + } + } + } + while (i >= available); + } + + list._capacity = capacity; + list._count = targetItemIndex; + } + + private static void AddRangeWithKnownCount(this RecyclableList list, IEnumerable items, int currentItemsCount, int requiredAdditionalCapacity) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity)))); + + Span memorySpan = new(list._memoryBlock); + foreach (var item in items) + { + memorySpan[currentItemsCount++] = item; + } + + list._count = currentItemsCount; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, in Array items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + Array.Copy(items, items.GetLowerBound(0), list._memoryBlock, list._count, items.LongLength); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, in T[] items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + new Span(items).CopyTo(new Span(list._memoryBlock, list._count, items.Length)); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, ReadOnlySpan items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + items.CopyTo(new Span(list._memoryBlock, list._count, items.Length)); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, Span items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + items.CopyTo(new Span(list._memoryBlock, list._count, items.Length)); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, List items) + { + if (list._capacity < list._count + items.Count) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); + } + + items.CopyTo(list._memoryBlock, list._count); + list._count += items.Count; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, ICollection items) + { + if (list._capacity < list._count + items.Count) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); + } + + items.CopyTo(list._memoryBlock, list._count); + list._count += items.Count; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, ICollection items) + { + if (list._capacity < list._count + items.Count) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); + } + + items.CopyTo(list._memoryBlock, list._count); + list._count += items.Count; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, RecyclableList items) + { + if (list._capacity < list._count + items._count) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(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++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, RecyclableLongList items) + { + if (items._longCount == 0) + { + return; + } + + int oldCount = list._count; + long sourceItemsCount = items._longCount; + if (oldCount + sourceItemsCount > int.MaxValue) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList)}, instead"); + } + + int targetCapacity = oldCount + (int)sourceItemsCount; + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + // TODO: Avoid unnecessary range operator - pass as arguments instead. + Span targetSpan = new Span(list._memoryBlock)[oldCount..], + itemsSpan; + + int blockIndex, + sourceBlockSize = items._blockSize, + lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1; + + for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++) + { + itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize); + itemsSpan.CopyTo(targetSpan); + targetSpan = targetSpan[sourceBlockSize..]; + } + + if (blockIndex == 0) + { + itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount); + itemsSpan.CopyTo(targetSpan); + } + else if (blockIndex <= items._lastBlockWithData) + { + itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex); + itemsSpan.CopyTo(targetSpan); + } + + list._count = targetCapacity; + list._version++; + } + + public static IEnumerator AddRange(this RecyclableList list, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength) + { + int targetItemIndex = list._count; + Span memorySpan; + + int i; + var enumerator = source.GetEnumerator(); + + int capacity = list._capacity; + memorySpan = new(list._memoryBlock); + if (enumerator.MoveNext()) + { + int available = capacity - targetItemIndex; + do + { + if (targetItemIndex + growByCount > capacity) + { + capacity = RecyclableListHelpers.ResizeAndCopy(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); + memorySpan = new(list._memoryBlock); + available = capacity - targetItemIndex; + } + + for (i = 0; i < available; i++) + { + memorySpan[targetItemIndex++] = (T)enumerator.Current; + if (!enumerator.MoveNext()) + { + break; + } + } + } + while (i >= available); + } + + list._capacity = capacity; + list._count = targetItemIndex; + return enumerator; + } + + public static void AddRange(this RecyclableList list, IReadOnlyList items) + { + int sourceItemsCount = items.Count; + if (list._capacity < list._count + sourceItemsCount) + { + list._capacity = RecyclableListHelpers.ResizeAndCopy(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount)))); + } + + Span memorySpan = new(list._memoryBlock, list._count, sourceItemsCount); + for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++) + { + memorySpan[sourceItemIndex] = items[sourceItemIndex]; + } + + list._count += sourceItemsCount; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void AddRange(this RecyclableList list, IEnumerable items, int growByCount = RecyclableDefaults.MinPooledArrayLength) + { + if (items is RecyclableList sourceRecyclableList) + { + AddRange(list, sourceRecyclableList); + } + else if (items is RecyclableLongList sourceRecyclableLongList) + { + AddRange(list, sourceRecyclableLongList); + } + else if (items is T[] sourceArray) + { + AddRange(list, sourceArray); + } + else if (items is Array sourceArrayWithObjects) + { + AddRange(list, sourceArrayWithObjects); + } + else if (items is List sourceList) + { + AddRange(list, sourceList); + } + else if (items is ICollection sourceICollection) + { + AddRange(list, sourceICollection); + } + else if (items is ICollection sourceICollectionWithObjects) + { + AddRange(list, sourceICollectionWithObjects); + } + else if (items is IReadOnlyList sourceIReadOnlyList) + { + AddRange(list, sourceIReadOnlyList); + } + else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0) + { + AddRangeWithKnownCount(list, items, list._count, requiredAdditionalCapacity); + list._version++; + } + else + { + AddRangeEnumerated(list, items, growByCount); + list._version++; + } + } + } +} \ No newline at end of file diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index ba6bad6..3d1f34a 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -19,57 +19,6 @@ namespace Recyclable.Collections internal ulong _version; public ulong Version => _version; - private void AddRangeEnumerated(IEnumerable items, int growByCount) - { - int targetItemIndex = _count; - Span memorySpan; - - int i; - var enumerator = items.GetEnumerator(); - - int capacity = _capacity; - memorySpan = new(_memoryBlock); - if (enumerator.MoveNext()) - { - int available = capacity - targetItemIndex; - do - { - if (targetItemIndex + growByCount > capacity) - { - capacity = RecyclableListHelpers.ResizeAndCopy(this, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); - memorySpan = new(_memoryBlock); - available = capacity - targetItemIndex; - } - - for (i = 0; i < available; i++) - { - memorySpan[targetItemIndex++] = enumerator.Current; - if (!enumerator.MoveNext()) - { - break; - } - } - } - while (i >= available); - } - - _capacity = capacity; - _count = targetItemIndex; - } - - private void AddRangeWithKnownCount(IEnumerable items, int currentItemsCount, int requiredAdditionalCapacity) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity)))); - - Span memorySpan = new(_memoryBlock); - foreach (var item in items) - { - memorySpan[currentItemsCount++] = item; - } - - _count = currentItemsCount; - } - public RecyclableList() { _capacity = RecyclableDefaults.InitialCapacity; @@ -92,84 +41,67 @@ namespace Recyclable.Collections } } - public void AddRange(IReadOnlyList items) - { - int sourceItemsCount = items.Count; - if (_capacity < _count + sourceItemsCount) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + sourceItemsCount)))); - } - - Span memorySpan = new(_memoryBlock, _count, sourceItemsCount); - for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++) - { - memorySpan[sourceItemIndex] = items[sourceItemIndex]; - } - - _count += sourceItemsCount; - } - public RecyclableList(in Array source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(RecyclableList source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(RecyclableLongList source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(ReadOnlySpan source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(in T[] source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(IReadOnlyList source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(List source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(ICollection source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(ICollection source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(IEnumerable source, int initialCapacity = RecyclableDefaults.InitialCapacity) @@ -187,7 +119,7 @@ namespace Recyclable.Collections _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; } - AddRange(source, RecyclableDefaults.MinPooledArrayLength); + zRecyclableListAddRange.AddRange(this, source, RecyclableDefaults.MinPooledArrayLength); } public RecyclableList(IEnumerable source, int initialCapacity = RecyclableDefaults.InitialCapacity) @@ -205,14 +137,14 @@ namespace Recyclable.Collections _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; } - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public RecyclableList(Span source) { _capacity = RecyclableDefaults.InitialCapacity; _memoryBlock = new T[RecyclableDefaults.InitialCapacity]; - AddRange(source); + zRecyclableListAddRange.AddRange(this, source); } public T this[int index] @@ -259,247 +191,6 @@ namespace Recyclable.Collections _version++; } - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(in Array items) - { - if (_capacity < _count + items.Length) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Length)))); - } - - Array.Copy(items, items.GetLowerBound(0), _memoryBlock, _count, items.LongLength); - _count += items.Length; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(in T[] items) - { - if (_capacity < _count + items.Length) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Length)))); - } - - new Span(items).CopyTo(new Span(_memoryBlock, _count, items.Length)); - _count += items.Length; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(ReadOnlySpan items) - { - if (_capacity < _count + items.Length) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Length)))); - } - - items.CopyTo(new Span(_memoryBlock, _count, items.Length)); - _count += items.Length; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(Span items) - { - if (_capacity < _count + items.Length) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Length)))); - } - - items.CopyTo(new Span(_memoryBlock, _count, items.Length)); - _count += items.Length; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(List items) - { - if (_capacity < _count + items.Count) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Count)))); - } - - items.CopyTo(_memoryBlock, _count); - _count += items.Count; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(ICollection items) - { - if (_capacity < _count + items.Count) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Count)))); - } - - items.CopyTo(_memoryBlock, _count); - _count += items.Count; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(ICollection items) - { - if (_capacity < _count + items.Count) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items.Count)))); - } - - items.CopyTo(_memoryBlock, _count); - _count += items.Count; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(RecyclableList items) - { - if (_capacity < _count + items._count) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, _count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(_count + items._count)))); - } - - new Span(items._memoryBlock, 0, items._count).CopyTo(new Span(_memoryBlock, _count, items._count)); - _count += items._count; - _version++; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(RecyclableLongList items) - { - if (items._longCount == 0) - { - return; - } - - int oldCount = _count; - long sourceItemsCount = items._longCount; - if (oldCount + sourceItemsCount > int.MaxValue) - { - ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList)}, instead"); - } - - int targetCapacity = oldCount + (int)sourceItemsCount; - if (_capacity < targetCapacity) - { - _capacity = RecyclableListHelpers.ResizeAndCopy(this, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); - } - - // TODO: Avoid unnecessary range operator - pass as arguments instead. - Span targetSpan = new Span(_memoryBlock)[oldCount..], - itemsSpan; - - int blockIndex, - sourceBlockSize = items._blockSize, - lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1; - - for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++) - { - itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize); - itemsSpan.CopyTo(targetSpan); - targetSpan = targetSpan[sourceBlockSize..]; - } - - if (blockIndex == 0) - { - itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount); - itemsSpan.CopyTo(targetSpan); - } - else if (blockIndex <= items._lastBlockWithData) - { - itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex); - itemsSpan.CopyTo(targetSpan); - } - - _count = targetCapacity; - _version++; - } - - public IEnumerator AddRange(IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength) - { - int targetItemIndex = _count; - Span memorySpan; - - int i; - var enumerator = source.GetEnumerator(); - - int capacity = _capacity; - memorySpan = new(_memoryBlock); - if (enumerator.MoveNext()) - { - int available = capacity - targetItemIndex; - do - { - if (targetItemIndex + growByCount > capacity) - { - capacity = RecyclableListHelpers.ResizeAndCopy(this, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); - memorySpan = new(_memoryBlock); - available = capacity - targetItemIndex; - } - - for (i = 0; i < available; i++) - { - memorySpan[targetItemIndex++] = (T)enumerator.Current; - if (!enumerator.MoveNext()) - { - break; - } - } - } - while (i >= available); - } - - _capacity = capacity; - _count = targetItemIndex; - return enumerator; - } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public void AddRange(IEnumerable items, int growByCount = RecyclableDefaults.MinPooledArrayLength) - { - if (items is RecyclableList sourceRecyclableList) - { - AddRange(sourceRecyclableList); - } - else if (items is RecyclableLongList sourceRecyclableLongList) - { - AddRange(sourceRecyclableLongList); - } - else if (items is T[] sourceArray) - { - AddRange(sourceArray); - } - else if (items is Array sourceArrayWithObjects) - { - AddRange(sourceArrayWithObjects); - } - else if (items is List sourceList) - { - AddRange(sourceList); - } - else if (items is ICollection sourceICollection) - { - AddRange(sourceICollection); - } - else if (items is ICollection sourceICollectionWithObjects) - { - AddRange(sourceICollectionWithObjects); - } - else if (items is IReadOnlyList sourceIReadOnlyList) - { - AddRange(sourceIReadOnlyList); - } - else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0) - { - AddRangeWithKnownCount(items, _count, requiredAdditionalCapacity); - _version++; - } - else - { - AddRangeEnumerated(items, growByCount); - _version++; - } - } - public T[] AsArray => _memoryBlock; [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/Recyclable.Collections/RecyclableLongList.AddRange.cs b/Recyclable.Collections/RecyclableLongList.AddRange.cs new file mode 100644 index 0000000..dc96591 --- /dev/null +++ b/Recyclable.Collections/RecyclableLongList.AddRange.cs @@ -0,0 +1,819 @@ +using System.Collections; +using System.Numerics; +using Recyclable.Collections.Pools; + +namespace Recyclable.Collections +{ + public static class zRecyclableLongListAddRange + { + private static void AddRangeEnumerated(this RecyclableLongList targetList, IEnumerable items) + { + using IEnumerator enumerator = items.GetEnumerator(); + if (!enumerator.MoveNext()) + { + return; + } + + int blockSize = targetList._blockSize, + targetBlockIdx = targetList._nextItemBlockIndex, + targetItemIdx = targetList._nextItemIndex; + + long capacity = targetList._capacity, + copied = targetList._longCount, + available = capacity - copied, + i; + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span blockArraySpan; + + while (true) + { + if (copied + blockSize > capacity) + { + capacity = RecyclableLongList.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize)))); + memoryBlocksSpan = new(targetList._memoryBlocks); + available = capacity - copied; + } + + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); + for (i = 1; i <= available; i++) + { + blockArraySpan[targetItemIdx++] = enumerator.Current; + + if (!enumerator.MoveNext()) + { + targetList._lastBlockWithData = targetBlockIdx; + targetList._longCount += copied + i - targetList._longCount; + if (targetItemIdx < blockSize) + { + targetList._nextItemBlockIndex = targetBlockIdx; + targetList._nextItemIndex = targetItemIdx; + } + else + { + targetList._nextItemBlockIndex = targetBlockIdx + 1; + targetList._nextItemIndex = 0; + } + + targetList._capacity = capacity; + targetList._version++; + return; + } + + if (targetItemIdx == blockSize) + { + targetBlockIdx++; + targetItemIdx = 0; + if (i == available) + { + break; + } + + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); + } + } + + copied += available; + } + } + + private static void AddRangeWithKnownCount(this RecyclableLongList targetList, IEnumerable items, int requiredAdditionalCapacity) + { + long requiredCapacity = targetList._longCount + requiredAdditionalCapacity; + if (requiredCapacity == 0) + { + return; + } + + int blockSize = targetList._blockSize, + targetBlockIdx = targetList._nextItemBlockIndex, + targetItemIdx = targetList._nextItemIndex; + + if (targetList._capacity < requiredCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity))); + } + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); + foreach (var item in items) + { + blockArraySpan[targetItemIdx++] = item; + if (targetItemIdx == blockSize) + { + targetBlockIdx++; + targetItemIdx = 0; + if (targetBlockIdx == memoryBlocksSpan.Length) + { + break; + } + + blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx]); + } + } + + targetList._longCount = requiredCapacity; + targetList._nextItemBlockIndex = targetBlockIdx; + targetList._nextItemIndex = targetItemIdx; + targetList._lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1); + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, in Array items) + { + if (items.LongLength == 0) + { + return; + } + + int blockSize = targetList._blockSize, + targetBlockIndex = targetList._nextItemBlockIndex; + + long fullBlockItemsCount = items.LongLength - blockSize, + sourceItemIndex = Math.Min(blockSize - targetList._nextItemIndex, items.LongLength), + targetCapacity = targetList._longCount + items.LongLength; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Array.Copy(items, 0, memoryBlocksSpan[targetBlockIndex++], targetList._nextItemIndex, sourceItemIndex); + while (sourceItemIndex < fullBlockItemsCount) + { + Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex++], 0, blockSize); + sourceItemIndex += blockSize; + } + + // We're reusing a variable which is no longer needed. That's to avoid unnecessary + // allocation. + if ((blockSize = (int)(items.LongLength - sourceItemIndex)) > 0) + { + Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex], 0, blockSize); + targetList._lastBlockWithData = targetBlockIndex; + targetList._nextItemBlockIndex = targetBlockIndex; + targetList._nextItemIndex = blockSize; + } + else + { + targetList._lastBlockWithData = targetBlockIndex - 1; + if (targetList._nextItemIndex + sourceItemIndex >= targetList._blockSize) + { + targetList._nextItemBlockIndex = targetBlockIndex; + targetList._nextItemIndex = 0; + } + else + { + targetList._nextItemIndex += (int)sourceItemIndex; + } + } + + targetList._longCount = targetCapacity; + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, ICollection items) + { + if (items.Count == 0) + { + return; + } + + int targetBlockIndex = targetList._nextItemBlockIndex; + long targetCapacity = targetList._longCount + items.Count; + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + // 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 ? RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; + items.CopyTo(itemsBuffer, 0); + + Span itemsSpan = new(itemsBuffer, 0, items.Count); + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); + while (targetBlockArraySpan.Length <= itemsSpan.Length) + { + itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); + itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; + targetBlockIndex++; + if (itemsSpan.Length == 0) + { + break; + } + + targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); + } + + if (itemsSpan.Length > 0) + { + itemsSpan.CopyTo(targetBlockArraySpan); + if (targetList._nextItemBlockIndex != targetBlockIndex) + { + targetList._nextItemIndex = itemsSpan.Length; + targetList._nextItemBlockIndex = targetBlockIndex; + } + else + { + targetList._nextItemIndex += itemsSpan.Length; + } + } + else + { + targetList._nextItemIndex = 0; + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = targetBlockIndex; + targetList._lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1); + + if (items.Count >= RecyclableDefaults.MinPooledArrayLength) + { + RecyclableArrayPool.ReturnShared(itemsBuffer, RecyclableLongList._needsClearing); + } + + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, ICollection items) + { + if (items.Count == 0) + { + return; + } + + int targetBlockIndex = targetList._nextItemBlockIndex; + + long targetCapacity = targetList._longCount + items.Count; + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + // 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 ? RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; + + items.CopyTo(itemsBuffer, 0); + + Span itemsSpan = new(itemsBuffer, 0, items.Count); + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); + + while (targetBlockArraySpan.Length <= itemsSpan.Length) + { + itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); + itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; + targetBlockIndex++; + if (itemsSpan.Length == 0) + { + break; + } + + targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); + } + + if (itemsSpan.Length > 0) + { + itemsSpan.CopyTo(targetBlockArraySpan); + if (targetList._nextItemBlockIndex != targetBlockIndex) + { + targetList._nextItemIndex = itemsSpan.Length; + targetList._nextItemBlockIndex = targetBlockIndex; + } + else + { + targetList._nextItemIndex += itemsSpan.Length; + } + } + else + { + targetList._nextItemIndex = 0; + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = targetBlockIndex; + targetList._lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1); + + if (items.Count >= RecyclableDefaults.MinPooledArrayLength) + { + RecyclableArrayPool.ReturnShared(itemsBuffer, RecyclableLongList._needsClearing); + } + + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, IEnumerable source) + { + IEnumerator enumerator = source.GetEnumerator(); + if (!enumerator.MoveNext()) + { + return; + } + + int blockSize = targetList._blockSize, + targetBlockIdx = targetList._nextItemBlockIndex, + targetItemIdx = targetList._nextItemIndex; + + long capacity = targetList._capacity, + copied = targetList._longCount, + available = capacity - copied, + i; + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span blockArraySpan; + + while (true) + { + if (copied + blockSize > capacity) + { + capacity = RecyclableLongList.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize)))); + memoryBlocksSpan = new(targetList._memoryBlocks); + available = capacity - copied; + } + + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); + for (i = 1; i <= available; i++) + { + blockArraySpan[targetItemIdx++] = (T)enumerator.Current; + + if (!enumerator.MoveNext()) + { + targetList._lastBlockWithData = targetBlockIdx; + targetList._longCount += copied + i - targetList._longCount; + if (targetItemIdx < blockSize) + { + targetList._nextItemBlockIndex = targetBlockIdx; + targetList._nextItemIndex = targetItemIdx; + } + else + { + targetList._nextItemBlockIndex = targetBlockIdx + 1; + targetList._nextItemIndex = 0; + } + + targetList._capacity = capacity; + return; + } + + if (targetItemIdx == blockSize) + { + targetBlockIdx++; + targetItemIdx = 0; + + if (i == available) + { + break; + } + + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); + } + } + + copied += available; + } + } + + public static void AddRange(this RecyclableLongList targetList, IEnumerable items) + { + switch (items) + { + case RecyclableList sourceRecyclableList: + targetList.AddRange(sourceRecyclableList); + return; + + case RecyclableLongList sourceRecyclableLongList: + targetList.AddRange(sourceRecyclableLongList); + return; + + case T[] sourceArray: + targetList.AddRange(sourceArray); + return; + + case Array sourceArrayWithObjects: + targetList.AddRange(sourceArrayWithObjects); + return; + + case List sourceList: + targetList.AddRange(sourceList); + return; + + case ICollection sourceICollection: + targetList.AddRange(sourceICollection); + return; + + case ICollection sourceICollectionWithObjects: + targetList.AddRange(sourceICollectionWithObjects); + return; + + case IReadOnlyList sourceIReadOnlyList: + targetList.AddRange(sourceIReadOnlyList); + return; + + default: + if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) + { + targetList.AddRangeWithKnownCount(items, requiredAdditionalCapacity); + } + else + { + targetList.AddRangeEnumerated(items); + } + + return; + } + } + + public static void AddRange(this RecyclableLongList targetList, IReadOnlyList items) + { + long sourceItemsCount = items.Count, + requiredCapacity = targetList._longCount + sourceItemsCount; + + if (requiredCapacity == 0) + { + return; + } + + int blockSize = targetList._blockSize, + targetBlockIdx = targetList._nextItemBlockIndex, + targetItemIdx = targetList._nextItemIndex; + + if (targetList._capacity < requiredCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity))); + } + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); + + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + blockArraySpan[targetItemIdx++] = items[itemIndex]; + if (targetItemIdx == blockSize) + { + targetItemIdx = 0; + targetBlockIdx++; + if (itemIndex + 1 == sourceItemsCount) + { + break; + } + + blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx]); + } + } + + targetList._longCount = requiredCapacity; + targetList._nextItemBlockIndex = targetBlockIdx; + targetList._nextItemIndex = targetItemIdx; + targetList._lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1); + } + + public static void AddRange(this RecyclableLongList targetList, List items) + { + if (items.Count == 0) + { + return; + } + + int blockSize = targetList._blockSize, + itemsCount = items.Count, + copied = Math.Min(blockSize - targetList._nextItemIndex, itemsCount), + targetBlockIndex = targetList._nextItemBlockIndex; + + long targetCapacity = targetList._longCount + items.Count; + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + items.CopyTo(0, memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, copied); + + if (targetList._nextItemIndex + copied == blockSize) + { + targetBlockIndex++; + targetList._nextItemIndex = 0; + } + else + { + targetList._nextItemIndex += copied; + } + + while (blockSize <= itemsCount - copied) + { + items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex++], 0, blockSize); + copied += blockSize; + } + + if ((itemsCount - copied < blockSize) && (itemsCount - copied != 0)) + { + targetList._nextItemIndex = itemsCount - copied; + items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex], 0, targetList._nextItemIndex); + } + + targetList._nextItemBlockIndex = targetBlockIndex; + targetList._longCount = targetCapacity; + targetList._lastBlockWithData = targetBlockIndex - (targetList._nextItemIndex > 0 ? 0 : 1); + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, ReadOnlySpan items) + { + if (items.Length == 0) + { + return; + } + + int targetBlockIndex = targetList._nextItemBlockIndex; + + long targetCapacity = targetList._longCount + items.Length; + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); + + while (items.Length >= targetBlockArraySpan.Length) + { + items[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); + items = items[targetBlockArraySpan.Length..]; + if (items.Length == 0) + { + break; + } + + targetBlockIndex++; + targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); + } + + if (items.Length > 0) + { + items.CopyTo(targetBlockArraySpan); + if (targetList._nextItemBlockIndex != targetBlockIndex) + { + targetList._nextItemIndex = items.Length; + targetList._nextItemBlockIndex = targetBlockIndex; + } + else + { + targetList._nextItemIndex += items.Length; + } + } + else + { + targetList._nextItemBlockIndex = targetBlockIndex + 1; + targetList._nextItemIndex = 0; + } + + targetList._longCount = targetCapacity; + targetList._lastBlockWithData = targetBlockIndex; + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, RecyclableList items) + { + if (items._count == 0) + { + return; + } + + int targetBlockIndex = targetList._nextItemBlockIndex; + + long targetCapacity = targetList._longCount + items._count; + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + Span itemsSpan = new(items._memoryBlock, 0, items._count); + Span targetMemoryBlocksSpan = new(targetList._memoryBlocks); + Span targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); + + while (itemsSpan.Length >= targetBlockArraySpan.Length) + { + itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); + itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; + targetBlockIndex++; + if (itemsSpan.Length == 0) + { + break; + } + + targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]); + } + + if (itemsSpan.Length > 0) + { + itemsSpan.CopyTo(targetBlockArraySpan); + targetList._lastBlockWithData = targetBlockIndex; + if (targetList._nextItemBlockIndex != targetBlockIndex) + { + targetList._nextItemIndex = itemsSpan.Length; + targetList._nextItemBlockIndex = targetBlockIndex; + } + else + { + targetList._nextItemIndex += itemsSpan.Length; + } + } + else + { + targetList._lastBlockWithData = targetBlockIndex - 1; + targetList._nextItemBlockIndex = targetBlockIndex; + targetList._nextItemIndex = 0; + } + + targetList._longCount = targetCapacity; + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, RecyclableLongList items) + { + if (items._longCount == 0) + { + return; + } + + int sourceBlockIndex = 0, + targetBlockIndex = targetList._nextItemBlockIndex, + toCopy; + + long copiedCount = 0, + itemsCount = items._longCount, + targetCapacity = targetList._longCount + itemsCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + Span sourceMemoryBlocksSpan = new(items._memoryBlocks); + Span itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]); + Span targetMemoryBlocksSpan = new(targetList._memoryBlocks); + Span targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); + + while (copiedCount < itemsCount) + { + toCopy = checked((int)Math.Min(itemsCount - copiedCount, itemsSpan.Length)); + if (targetBlockArraySpan.Length < toCopy) + { + toCopy = targetBlockArraySpan.Length; + itemsSpan[..toCopy].CopyTo(targetBlockArraySpan); + targetBlockIndex++; + targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]); + itemsSpan = itemsSpan[toCopy..]; + copiedCount += toCopy; + } + else + { + itemsSpan[..toCopy].CopyTo(targetBlockArraySpan); + targetBlockArraySpan = targetBlockArraySpan[toCopy..]; + itemsSpan = itemsSpan[toCopy..]; + if (itemsSpan.IsEmpty && sourceBlockIndex + 1 < sourceMemoryBlocksSpan.Length) + { + sourceBlockIndex++; + itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]); + } + + copiedCount += toCopy; + if (targetBlockArraySpan.IsEmpty && copiedCount < itemsCount) + { + targetBlockIndex++; + targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]); + } + } + } + + targetList._lastBlockWithData = targetBlockIndex; + if ((targetCapacity & targetList._blockSizeMinus1) == 0) + { + targetList._nextItemIndex = 0; + targetList._nextItemBlockIndex = targetBlockIndex + 1; + } + else + { + targetList._nextItemIndex = checked((int)(targetCapacity & targetList._blockSizeMinus1)); + targetList._nextItemBlockIndex = targetBlockIndex; + } + + targetList._longCount = targetCapacity; + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, Span items) + { + if (items.Length == 0) + { + return; + } + + int targetBlockIndex = targetList._nextItemBlockIndex; + + long targetCapacity = targetList._longCount + items.Length; + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); + + while (items.Length >= targetBlockArraySpan.Length) + { + items[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); + items = items[targetBlockArraySpan.Length..]; + if (items.Length == 0) + { + break; + } + + targetBlockIndex++; + targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); + } + + if (items.Length > 0) + { + items.CopyTo(targetBlockArraySpan); + if (targetList._nextItemBlockIndex != targetBlockIndex) + { + targetList._nextItemIndex = items.Length; + targetList._nextItemBlockIndex = targetBlockIndex; + } + else + { + targetList._nextItemIndex += items.Length; + } + } + else + { + targetList._nextItemBlockIndex = targetBlockIndex + 1; + targetList._nextItemIndex = 0; + } + + targetList._longCount = targetCapacity; + targetList._lastBlockWithData = targetBlockIndex; + targetList._version++; + } + + public static void AddRange(this RecyclableLongList targetList, in T[] items) + { + if (items.LongLength == 0) + { + return; + } + + int targetBlockIndex = targetList._nextItemBlockIndex; + + long targetCapacity = targetList._longCount + items.LongLength; + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + Span itemsSpan = new(items); + Span memoryBlocksSpan = new(targetList._memoryBlocks); + Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], targetList._nextItemIndex, targetList._blockSize - targetList._nextItemIndex); + + while (itemsSpan.Length >= targetBlockArraySpan.Length) + { + itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); + itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; + if (itemsSpan.Length == 0) + { + break; + } + + targetBlockIndex++; + targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); + } + + if (itemsSpan.Length > 0) + { + itemsSpan.CopyTo(targetBlockArraySpan); + if (targetList._nextItemBlockIndex != targetBlockIndex) + { + targetList._nextItemIndex = itemsSpan.Length; + targetList._nextItemBlockIndex = targetBlockIndex; + } + else + { + targetList._nextItemIndex += itemsSpan.Length; + } + } + else + { + targetList._nextItemBlockIndex = targetBlockIndex + 1; + targetList._nextItemIndex = 0; + } + + targetList._longCount = targetCapacity; + targetList._lastBlockWithData = targetBlockIndex; + targetList._version++; + } + } +} diff --git a/Recyclable.Collections/RecyclableLongList.cs b/Recyclable.Collections/RecyclableLongList.cs index 19add8e..bd23eb9 100644 --- a/Recyclable.Collections/RecyclableLongList.cs +++ b/Recyclable.Collections/RecyclableLongList.cs @@ -11,7 +11,7 @@ namespace Recyclable.Collections public partial class RecyclableLongList : IList, IReadOnlyList, IDisposable { private static readonly bool _defaultIsNull = default(T) == null; - private static readonly bool _needsClearing = !typeof(T).IsValueType; + internal static readonly bool _needsClearing = !typeof(T).IsValueType; internal int _blockSize; internal byte _blockSizePow2BitShift; @@ -52,134 +52,17 @@ namespace Recyclable.Collections public int NextItemIndex => _nextItemIndex; public ulong Version => _version; - private void AddRangeEnumerated(IEnumerable items) - { - using IEnumerator enumerator = items.GetEnumerator(); - if (!enumerator.MoveNext()) + public RecyclableLongList(int minBlockSize = RecyclableDefaults.BlockSize, long initialCapacity = RecyclableDefaults.InitialCapacity) + { + if (!BitOperations.IsPow2(minBlockSize)) { - return; + minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); } - long i, copied = _longCount; - int blockSize = _blockSize, - targetItemIdx = _nextItemIndex, - targetBlockIdx = _nextItemBlockIndex; - - long capacity = _capacity; - long available = capacity - copied; - Span memoryBlocksSpan = new(_memoryBlocks); - Span blockArraySpan; - - while (true) - { - if (copied + blockSize > capacity) - { - capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize)))); - memoryBlocksSpan = new(_memoryBlocks); - available = capacity - copied; - } - - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); - for (i = 1; i <= available; i++) - { - blockArraySpan[targetItemIdx++] = enumerator.Current; - - if (!enumerator.MoveNext()) - { - _lastBlockWithData = targetBlockIdx; - _longCount += copied + i - _longCount; - if (targetItemIdx < blockSize) - { - _nextItemBlockIndex = targetBlockIdx; - _nextItemIndex = targetItemIdx; - } - else - { - _nextItemBlockIndex = targetBlockIdx + 1; - _nextItemIndex = 0; - } - - _capacity = capacity; - return; - } - - if (targetItemIdx == blockSize) - { - targetBlockIdx++; - targetItemIdx = 0; - if (i == available) - { - break; - } - - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); - } - } - - copied += available; - } - } - - private void AddRangeWithKnownCount(IEnumerable items, int requiredAdditionalCapacity) - { - long copied = _longCount; - int blockSize = _blockSize; - int targetItemIdx = _nextItemIndex; - int targetBlockIdx = _nextItemBlockIndex; - - long requiredCapacity = copied + requiredAdditionalCapacity; - if (requiredCapacity == 0) - { - return; - } - - if (_capacity < requiredCapacity) - { - _capacity = Helpers.Resize(this, blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity))); - blockSize = _blockSize; - } - - Span memoryBlocksSpan = new(_memoryBlocks); - Span blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); - foreach (var item in items) - { - blockArraySpan[targetItemIdx++] = item; - if (targetItemIdx == blockSize) - { - targetBlockIdx++; - targetItemIdx = 0; - if (targetBlockIdx == memoryBlocksSpan.Length) - { - break; - } - - blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx]); - } - } - - _longCount = requiredCapacity; - _nextItemBlockIndex = targetBlockIdx; - _nextItemIndex = targetItemIdx; - _lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1); - } - - [MethodImpl(MethodImplOptions.AggressiveOptimization)] - public RecyclableLongList(int minBlockSize = RecyclableDefaults.BlockSize, long? initialCapacity = default) - { - minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); - - if (initialCapacity > 0) - { - Helpers.SetupBlockArrayPooling(this, minBlockSize); - _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity.Value))); - _blockSizeMinus1 = minBlockSize - 1; - } - else - { - Helpers.SetupBlockArrayPooling(this, minBlockSize); - _blockSizeMinus1 = _blockSize - 1; - _memoryBlocks = _emptyMemoryBlocksArray; - } + _blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)minBlockSize)); + _capacity = RecyclableLongList.Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)initialCapacity))); + _blockSize = minBlockSize; + _blockSizeMinus1 = minBlockSize - 1; } public RecyclableLongList(in T[] source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -190,7 +73,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength))); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(in Array source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -201,7 +84,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.LongLength))); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(IReadOnlyList source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -212,7 +95,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count)); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(ReadOnlySpan source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -223,7 +106,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length))); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(RecyclableList source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -234,7 +117,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count)); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(RecyclableLongList source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -245,7 +128,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Count))); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(Span source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -256,7 +139,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)source.Length))); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(List source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -267,7 +150,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count)); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(ICollection source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -278,7 +161,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count)); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(ICollection source, int minBlockSize = RecyclableDefaults.BlockSize) @@ -289,7 +172,7 @@ namespace Recyclable.Collections _capacity = Helpers.Resize(this, minBlockSize, _blockSizePow2BitShift, BitOperations.RoundUpToPowerOf2((uint)source.Count)); _blockSizeMinus1 = minBlockSize - 1; - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(IEnumerable source, int minBlockSize = RecyclableDefaults.BlockSize, long? initialCapacity = default) @@ -306,7 +189,7 @@ namespace Recyclable.Collections _memoryBlocks = _emptyMemoryBlocksArray; } - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public RecyclableLongList(IEnumerable source, int minBlockSize = RecyclableDefaults.BlockSize, long? initialCapacity = default) @@ -323,7 +206,7 @@ namespace Recyclable.Collections _memoryBlocks = _emptyMemoryBlocksArray; } - AddRange(source); + zRecyclableLongListAddRange.AddRange(this, source); } public T this[long index] @@ -376,710 +259,6 @@ namespace Recyclable.Collections _version++; } - public void AddRange(in Array items) - { - if (items.LongLength == 0) - { - return; - } - - long targetCapacity = _longCount + items.LongLength; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex; - - long fullBlockItemsCount = items.LongLength - blockSize, - sourceItemIndex = Math.Min(blockSize - _nextItemIndex, items.LongLength); - - Span memoryBlocksSpan = new(_memoryBlocks); - - Array.Copy(items, 0, memoryBlocksSpan[targetBlockIndex++], _nextItemIndex, sourceItemIndex); - while (sourceItemIndex < fullBlockItemsCount) - { - Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex++], 0, blockSize); - sourceItemIndex += blockSize; - } - - // We're reusing a variable which is no longer needed. That's to avoid unnecessary - // allocation. - if ((blockSize = (int)(items.LongLength - sourceItemIndex)) > 0) - { - Array.Copy(items, sourceItemIndex, memoryBlocksSpan[targetBlockIndex], 0, blockSize); - _lastBlockWithData = targetBlockIndex; - _nextItemBlockIndex = targetBlockIndex; - _nextItemIndex = blockSize; - } - else - { - _lastBlockWithData = targetBlockIndex - 1; - if (_nextItemIndex + sourceItemIndex >= _blockSize) - { - _nextItemBlockIndex = targetBlockIndex; - _nextItemIndex = 0; - } - else - { - _nextItemIndex += (int)sourceItemIndex; - } - } - - _longCount = targetCapacity; - _version++; - } - - public void AddRange(in T[] items) - { - if (items.LongLength == 0) - { - return; - } - - long targetCapacity = _longCount + items.LongLength; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex; - - Span itemsSpan = new(items); - Span memoryBlocksSpan = new(_memoryBlocks); - Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex); - while (itemsSpan.Length >= targetBlockArraySpan.Length) - { - itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); - itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; - if (itemsSpan.Length == 0) - { - break; - } - - targetBlockIndex++; - targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); - } - - if (itemsSpan.Length > 0) - { - itemsSpan.CopyTo(targetBlockArraySpan); - if (_nextItemBlockIndex != targetBlockIndex) - { - _nextItemIndex = itemsSpan.Length; - _nextItemBlockIndex = targetBlockIndex; - } - else - { - _nextItemIndex += itemsSpan.Length; - } - } - else - { - _nextItemBlockIndex = targetBlockIndex + 1; - _nextItemIndex = 0; - } - - _longCount = targetCapacity; - _lastBlockWithData = targetBlockIndex; - _version++; - } - - public void AddRange(RecyclableList items) - { - if (items._count == 0) - { - return; - } - - long targetCapacity = _longCount + items._count; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex; - - Span itemsSpan = new(items._memoryBlock, 0, items._count); - Span targetMemoryBlocksSpan = new(_memoryBlocks); - Span targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex); - while (itemsSpan.Length >= targetBlockArraySpan.Length) - { - itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); - itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; - targetBlockIndex++; - if (itemsSpan.Length == 0) - { - break; - } - - targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]); - } - - if (itemsSpan.Length > 0) - { - itemsSpan.CopyTo(targetBlockArraySpan); - _lastBlockWithData = targetBlockIndex; - if (_nextItemBlockIndex != targetBlockIndex) - { - _nextItemIndex = itemsSpan.Length; - _nextItemBlockIndex = targetBlockIndex; - } - else - { - _nextItemIndex += itemsSpan.Length; - } - } - else - { - _lastBlockWithData = targetBlockIndex - 1; - _nextItemBlockIndex = targetBlockIndex; - _nextItemIndex = 0; - } - - _longCount = targetCapacity; - _version++; - } - - public void AddRange(ReadOnlySpan items) - { - if (items.Length == 0) - { - return; - } - - long targetCapacity = _longCount + items.Length; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex; - - Span memoryBlocksSpan = new(_memoryBlocks); - Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex); - while (items.Length >= targetBlockArraySpan.Length) - { - items[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); - items = items[targetBlockArraySpan.Length..]; - if (items.Length == 0) - { - break; - } - - targetBlockIndex++; - targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); - } - - if (items.Length > 0) - { - items.CopyTo(targetBlockArraySpan); - if (_nextItemBlockIndex != targetBlockIndex) - { - _nextItemIndex = items.Length; - _nextItemBlockIndex = targetBlockIndex; - } - else - { - _nextItemIndex += items.Length; - } - } - else - { - _nextItemBlockIndex = targetBlockIndex + 1; - _nextItemIndex = 0; - } - - _longCount = targetCapacity; - _lastBlockWithData = targetBlockIndex; - _version++; - } - - public void AddRange(RecyclableLongList items) - { - if (items._longCount == 0) - { - return; - } - - long itemsCount = items._longCount, - targetCapacity = _longCount + itemsCount; - - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - sourceBlockIndex = 0, - targetBlockIndex = _nextItemBlockIndex, - toCopy; - - Span sourceMemoryBlocksSpan = new(items._memoryBlocks); - Span itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]); - Span targetMemoryBlocksSpan = new(_memoryBlocks); - Span targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex); - long copiedCount = 0; - while (copiedCount < itemsCount) - { - toCopy = checked((int)Math.Min(itemsCount - copiedCount, itemsSpan.Length)); - if (targetBlockArraySpan.Length < toCopy) - { - toCopy = targetBlockArraySpan.Length; - itemsSpan[..toCopy].CopyTo(targetBlockArraySpan); - targetBlockIndex++; - targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]); - itemsSpan = itemsSpan[toCopy..]; - copiedCount += toCopy; - } - else - { - itemsSpan[..toCopy].CopyTo(targetBlockArraySpan); - targetBlockArraySpan = targetBlockArraySpan[toCopy..]; - itemsSpan = itemsSpan[toCopy..]; - if (itemsSpan.IsEmpty && sourceBlockIndex + 1 < sourceMemoryBlocksSpan.Length) - { - sourceBlockIndex++; - itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex]); - } - - copiedCount += toCopy; - if (targetBlockArraySpan.IsEmpty && copiedCount < itemsCount) - { - targetBlockIndex++; - targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex]); - } - } - } - - _lastBlockWithData = targetBlockIndex; - if ((targetCapacity & _blockSizeMinus1) == 0) - { - _nextItemIndex = 0; - _nextItemBlockIndex = targetBlockIndex + 1; - } - else - { - _nextItemIndex = checked((int)(targetCapacity & _blockSizeMinus1)); - _nextItemBlockIndex = targetBlockIndex; - } - - _longCount = targetCapacity; - _version++; - } - - public void AddRange(Span items) - { - if (items.Length == 0) - { - return; - } - - long targetCapacity = _longCount + items.Length; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex; - - Span memoryBlocksSpan = new(_memoryBlocks); - Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex); - while (items.Length >= targetBlockArraySpan.Length) - { - items[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); - items = items[targetBlockArraySpan.Length..]; - if (items.Length == 0) - { - break; - } - - targetBlockIndex++; - targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); - } - - if (items.Length > 0) - { - items.CopyTo(targetBlockArraySpan); - if (_nextItemBlockIndex != targetBlockIndex) - { - _nextItemIndex = items.Length; - _nextItemBlockIndex = targetBlockIndex; - } - else - { - _nextItemIndex += items.Length; - } - } - else - { - _nextItemBlockIndex = targetBlockIndex + 1; - _nextItemIndex = 0; - } - - _longCount = targetCapacity; - _lastBlockWithData = targetBlockIndex; - _version++; - } - - public void AddRange(List items) - { - if (items.Count == 0) - { - return; - } - - long targetCapacity = _longCount + items.Count; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex, - itemsCount = items.Count, - copied = Math.Min(blockSize - _nextItemIndex, itemsCount); - - Span memoryBlocksSpan = new(_memoryBlocks); - items.CopyTo(0, memoryBlocksSpan[targetBlockIndex], _nextItemIndex, copied); - if (_nextItemIndex + copied == blockSize) - { - targetBlockIndex++; - _nextItemIndex = 0; - } - else - { - _nextItemIndex += copied; - } - - while (blockSize <= itemsCount - copied) - { - items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex++], 0, blockSize); - copied += blockSize; - } - - if ((itemsCount - copied < blockSize) && (itemsCount - copied != 0)) - { - _nextItemIndex = itemsCount - copied; - items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex], 0, _nextItemIndex); - } - - _nextItemBlockIndex = targetBlockIndex; - _longCount = targetCapacity; - _lastBlockWithData = targetBlockIndex - (_nextItemIndex > 0 ? 0 : 1); - _version++; - } - - public void AddRange(ICollection items) - { - if (items.Count == 0) - { - return; - } - - long targetCapacity = _longCount + items.Count; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex; - - // 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 ? RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; - try - { - items.CopyTo(itemsBuffer, 0); - - Span itemsSpan = new(itemsBuffer, 0, items.Count); - Span memoryBlocksSpan = new(_memoryBlocks); - Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex); - while (targetBlockArraySpan.Length <= itemsSpan.Length) - { - itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); - itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; - targetBlockIndex++; - if (itemsSpan.Length == 0) - { - break; - } - - targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); - } - - if (itemsSpan.Length > 0) - { - itemsSpan.CopyTo(targetBlockArraySpan); - if (_nextItemBlockIndex != targetBlockIndex) - { - _nextItemIndex = itemsSpan.Length; - _nextItemBlockIndex = targetBlockIndex; - } - else - { - _nextItemIndex += itemsSpan.Length; - } - } - else - { - _nextItemIndex = 0; - } - - _longCount = targetCapacity; - _nextItemBlockIndex = targetBlockIndex; - _lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1); - } - finally - { - if (items.Count >= RecyclableDefaults.MinPooledArrayLength) - { - RecyclableArrayPool.ReturnShared(itemsBuffer, _needsClearing); - } - - _version++; - } - } - - public void AddRange(ICollection items) - { - if (items.Count == 0) - { - return; - } - - long targetCapacity = _longCount + items.Count; - if (_capacity < targetCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); - } - - int blockSize = _blockSize, - targetBlockIndex = _nextItemBlockIndex; - - // 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 ? RecyclableArrayPool.RentShared((int)BitOperations.RoundUpToPowerOf2((uint)items.Count)) : new T[items.Count]; - try - { - items.CopyTo(itemsBuffer, 0); - - Span itemsSpan = new(itemsBuffer, 0, items.Count); - Span memoryBlocksSpan = new(_memoryBlocks); - Span targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex); - while (targetBlockArraySpan.Length <= itemsSpan.Length) - { - itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan); - itemsSpan = itemsSpan[targetBlockArraySpan.Length..]; - targetBlockIndex++; - if (itemsSpan.Length == 0) - { - break; - } - - targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex]); - } - - if (itemsSpan.Length > 0) - { - itemsSpan.CopyTo(targetBlockArraySpan); - if (_nextItemBlockIndex != targetBlockIndex) - { - _nextItemIndex = itemsSpan.Length; - _nextItemBlockIndex = targetBlockIndex; - } - else - { - _nextItemIndex += itemsSpan.Length; - } - } - else - { - _nextItemIndex = 0; - } - - _longCount = targetCapacity; - _nextItemBlockIndex = targetBlockIndex; - _lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1); - } - finally - { - if (items.Count >= RecyclableDefaults.MinPooledArrayLength) - { - RecyclableArrayPool.ReturnShared(itemsBuffer, _needsClearing); - } - - _version++; - } - } - - public void AddRange(IEnumerable source) - { - long i, copied = _longCount; - int blockSize = _blockSize, - targetItemIdx = _nextItemIndex, - targetBlockIdx = _nextItemBlockIndex; - - IEnumerator enumerator = source.GetEnumerator(); - if (!enumerator.MoveNext()) - { - return; - } - - long capacity = _capacity; - long available = capacity - copied; - - Span memoryBlocksSpan = new(_memoryBlocks); - Span blockArraySpan; - while (true) - { - if (copied + blockSize > capacity) - { - capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)(capacity + blockSize)))); - memoryBlocksSpan = new(_memoryBlocks); - available = capacity - copied; - } - - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); - for (i = 1; i <= available; i++) - { - blockArraySpan[targetItemIdx++] = (T)enumerator.Current; - - if (!enumerator.MoveNext()) - { - _lastBlockWithData = targetBlockIdx; - _longCount += copied + i - _longCount; - if (targetItemIdx < blockSize) - { - _nextItemBlockIndex = targetBlockIdx; - _nextItemIndex = targetItemIdx; - } - else - { - _nextItemBlockIndex = targetBlockIdx + 1; - _nextItemIndex = 0; - } - - _capacity = capacity; - return; - } - - if (targetItemIdx == blockSize) - { - targetBlockIdx++; - targetItemIdx = 0; - - if (i == available) - { - break; - } - - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); - } - } - - copied += available; - } - } - - public void AddRange(IEnumerable items) - { - if (items is RecyclableList sourceRecyclableList) - { - AddRange(sourceRecyclableList); - } - else if (items is RecyclableLongList sourceRecyclableLongList) - { - AddRange(sourceRecyclableLongList); - } - else if (items is T[] sourceArray) - { - AddRange(sourceArray); - } - else if (items is Array sourceArrayWithObjects) - { - AddRange(sourceArrayWithObjects); - } - else if (items is List sourceList) - { - AddRange(sourceList); - } - else if (items is ICollection sourceICollection) - { - AddRange(sourceICollection); - } - else if (items is ICollection sourceICollectionWithObjects) - { - AddRange(sourceICollectionWithObjects); - } - else if (items is IReadOnlyList sourceIReadOnlyList) - { - AddRange(sourceIReadOnlyList); - } - else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) - { - AddRangeWithKnownCount(items, requiredAdditionalCapacity); - _version++; - } - else - { - AddRangeEnumerated(items); - _version++; - } - } - - public void AddRange(IReadOnlyList items) - { - long copied = _longCount; - int blockSize = _blockSize; - int targetItemIdx = _nextItemIndex; - int targetBlockIdx = _nextItemBlockIndex; - - Span memoryBlocksSpan; - Span blockArraySpan; - long sourceItemsCount = items.Count; - long requiredCapacity = copied + sourceItemsCount; - if (requiredCapacity == 0) - { - return; - } - - if (_capacity < requiredCapacity) - { - _capacity = Helpers.Resize(this, _blockSize, _blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)requiredCapacity))); - blockSize = _blockSize; - } - - memoryBlocksSpan = new(_memoryBlocks); - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]); - for(var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) - { - blockArraySpan[targetItemIdx++] = items[itemIndex]; - if (targetItemIdx == blockSize) - { - targetItemIdx = 0; - targetBlockIdx++; - if (itemIndex + 1 == sourceItemsCount) - { - break; - } - - blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx]); - } - } - - _longCount = requiredCapacity; - _nextItemBlockIndex = targetBlockIdx; - _nextItemIndex = targetItemIdx; - _lastBlockWithData = targetBlockIdx - (targetItemIdx > 0 ? 0 : 1); - } - public void Clear() { if (_longCount == 0) @@ -1105,7 +284,6 @@ namespace Recyclable.Collections } } - // TODO: Do we want to initialize with empty arrays or null refs? Null refs may be faster. Array.Fill(_memoryBlocks, null); _capacity = 0; _nextItemBlockIndex = 0;