diff --git a/Recyclable.Collections/RecyclableDictionary.cs b/Recyclable.Collections/RecyclableDictionary.cs index c8da1e3..33cb849 100644 --- a/Recyclable.Collections/RecyclableDictionary.cs +++ b/Recyclable.Collections/RecyclableDictionary.cs @@ -9,20 +9,20 @@ namespace Recyclable.Collections internal sealed class RecyclableDictionary : IEnumerable>, IDisposable where TKey : notnull { - private static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; + internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; - private readonly int _blockSize; - private readonly int _blockSizeMinus1; - private readonly byte _blockShift; + internal readonly int _blockSize; + internal readonly int _blockSizeMinus1; + internal readonly byte _blockShift; #nullable disable - private Entry[][] _entries; + internal Entry[][] _entries; #nullable restore - private int[] _buckets; - private int _count; - private bool _disposed; + internal int[] _buckets; + internal int _count; + internal bool _disposed; - private struct Entry + internal struct Entry { public int HashCode; public int Next; diff --git a/Recyclable.Collections/RecyclableHashSet.cs b/Recyclable.Collections/RecyclableHashSet.cs index 34ce553..2c532d0 100644 --- a/Recyclable.Collections/RecyclableHashSet.cs +++ b/Recyclable.Collections/RecyclableHashSet.cs @@ -9,20 +9,20 @@ namespace Recyclable.Collections internal sealed class RecyclableHashSet : IEnumerable, IDisposable where T : notnull { - private static readonly bool _needsClearing = !typeof(T).IsValueType; + internal static readonly bool _needsClearing = !typeof(T).IsValueType; - private readonly int _blockSize; - private readonly int _blockSizeMinus1; - private readonly byte _blockShift; + internal readonly int _blockSize; + internal readonly int _blockSizeMinus1; + internal readonly byte _blockShift; #nullable disable - private Entry[][] _entries; + internal Entry[][] _entries; #nullable restore - private int[] _buckets; - private int _count; - private bool _disposed; + internal int[] _buckets; + internal int _count; + internal bool _disposed; - private struct Entry + internal struct Entry { public int HashCode; public int Next; diff --git a/Recyclable.Collections/RecyclableLinkedList.cs b/Recyclable.Collections/RecyclableLinkedList.cs index b832de3..a0a95d0 100644 --- a/Recyclable.Collections/RecyclableLinkedList.cs +++ b/Recyclable.Collections/RecyclableLinkedList.cs @@ -7,7 +7,7 @@ namespace Recyclable.Collections { internal sealed class RecyclableLinkedList : IEnumerable, IDisposable { - private static readonly bool _needsClearing = !typeof(T).IsValueType; + internal static readonly bool _needsClearing = !typeof(T).IsValueType; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static BiDirectionalRecyclableArrayPoolChunk RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk? previous, bool forHead) @@ -53,11 +53,11 @@ namespace Recyclable.Collections BiDirectionalRecyclableArrayPoolChunkPool.Dispose(chunk, _needsClearing); } - private BiDirectionalRecyclableArrayPoolChunk _head; - private BiDirectionalRecyclableArrayPoolChunk _tail; - private bool _disposed; - private long _capacity; - private long _count; + internal BiDirectionalRecyclableArrayPoolChunk _head; + internal BiDirectionalRecyclableArrayPoolChunk _tail; + internal bool _disposed; + internal long _capacity; + internal long _count; public RecyclableLinkedList(int initialCapacity = RecyclableDefaults.InitialCapacity) { diff --git a/Recyclable.Collections/RecyclableList.AddRange.cs b/Recyclable.Collections/RecyclableList.AddRange.cs index 5aa15ee..87b00e3 100644 --- a/Recyclable.Collections/RecyclableList.AddRange.cs +++ b/Recyclable.Collections/RecyclableList.AddRange.cs @@ -162,12 +162,12 @@ namespace Recyclable.Collections } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public static void AddRange(this RecyclableList list, RecyclableLongList items) - { - if (items._longCount == 0) - { - return; - } + public static void AddRange(this RecyclableList list, RecyclableLongList items) + { + if (items._longCount == 0) + { + return; + } int oldCount = list._count; long sourceItemsCount = items._longCount; @@ -208,9 +208,263 @@ namespace Recyclable.Collections itemsSpan.CopyTo(targetSpan); } - list._count = targetCapacity; - list._version++; - } + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList list, RecyclableHashSet items) + { + if (items._count == 0) + { + return; + } + + int oldCount = list._count, + sourceCount = items._count, + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + Span targetSpan = new(list._memoryBlock, oldCount, sourceCount); + var entries = items._entries; + int shift = items._blockShift, + mask = items._blockSizeMinus1; + + for (int i = 0; i < sourceCount; i++) + { + targetSpan[i] = entries[i >> shift][i & mask].Value; + } + + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList list, RecyclableStack items) + { + if (items._count == 0) + { + return; + } + + if (items._count > int.MaxValue - list._count) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList."); + } + + int oldCount = list._count, + sourceCount = checked((int)items._count), + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + Span targetSpan = new(list._memoryBlock, oldCount, sourceCount); + int index = 0; + var chunk = items._current; + while (chunk != null) + { + var array = chunk.Value; + for (int i = chunk.Index - 1; i >= 0; i--) + { + targetSpan[index++] = array[i]; + } + + chunk = chunk.Previous; + } + + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList list, RecyclableSortedSet items) + { + if (items._count == 0) + { + return; + } + + int oldCount = list._count, + sourceCount = items._count, + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + new ReadOnlySpan(items._items, 0, sourceCount).CopyTo(new Span(list._memoryBlock, oldCount, sourceCount)); + + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList list, RecyclableLinkedList items) + { + if (items._count == 0) + { + return; + } + + if (items._count > int.MaxValue - list._count) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList."); + } + + int oldCount = list._count, + sourceCount = checked((int)items._count), + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + Span targetSpan = new(list._memoryBlock, oldCount, sourceCount); + int index = 0; + var chunk = items._head; + while (chunk != null) + { + for (int i = chunk.Bottom; i < chunk.Top; i++) + { + targetSpan[index++] = chunk.Value[i]; + } + + chunk = chunk.Next; + } + + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList list, RecyclablePriorityQueue items) + { + if (items._size == 0) + { + return; + } + + int oldCount = list._count, + sourceCount = items._size, + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + new ReadOnlySpan(items._heap, 0, sourceCount).CopyTo(new Span(list._memoryBlock, oldCount, sourceCount)); + + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList list, RecyclableQueue items) + { + if (items._count == 0) + { + return; + } + + if (items._count > int.MaxValue - list._count) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), "The total number of items exceeds the capacity of RecyclableList."); + } + + int oldCount = list._count, + sourceCount = checked((int)items._count), + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + Span targetSpan = new(list._memoryBlock, oldCount, sourceCount); + int index = 0; + var chunk = items._head; + while (chunk != null) + { + for (int i = chunk.Bottom; i < chunk.Top; i++) + { + targetSpan[index++] = chunk.Value[i]; + } + + chunk = chunk.Next; + } + + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList> list, RecyclableDictionary items) + { + if (items._count == 0) + { + return; + } + + int oldCount = list._count, + sourceCount = items._count, + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + Span> targetSpan = new(list._memoryBlock, oldCount, sourceCount); + var entries = items._entries; + int shift = items._blockShift, + mask = items._blockSizeMinus1; + + for (int i = 0; i < sourceCount; i++) + { + ref var entry = ref entries[i >> shift][i & mask]; + targetSpan[i] = new KeyValuePair(entry.Key, entry.Value); + } + + list._count = targetCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableList<(TKey Key, TValue Value)> list, RecyclableSortedList items) + { + if (items._count == 0) + { + return; + } + + int oldCount = list._count, + sourceCount = items._count, + targetCapacity = oldCount + sourceCount; + + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers<(TKey Key, TValue Value)>.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + Span<(TKey Key, TValue Value)> targetSpan = new(list._memoryBlock, oldCount, sourceCount); + for (int i = 0; i < sourceCount; i++) + { + targetSpan[i] = (items._keys[i], items._values[i]); + } + + list._count = targetCapacity; + list._version++; + } public static IEnumerator AddRange(this RecyclableList list, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength) { @@ -291,15 +545,39 @@ namespace Recyclable.Collections { 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) + else if (items is ICollection sourceICollection) + { + AddRange(list, sourceICollection); + } + else if (items is ICollection sourceICollectionWithObjects) + { + AddRange(list, sourceICollectionWithObjects); + } + else if (items is RecyclableHashSet sourceHashSet) + { + AddRange(list, sourceHashSet); + } + else if (items is RecyclableStack sourceStack) + { + AddRange(list, sourceStack); + } + else if (items is RecyclableSortedSet sourceSortedSet) + { + AddRange(list, sourceSortedSet); + } + else if (items is RecyclableLinkedList sourceLinkedList) + { + AddRange(list, sourceLinkedList); + } + else if (items is RecyclablePriorityQueue sourcePriorityQueue) + { + AddRange(list, sourcePriorityQueue); + } + else if (items is RecyclableQueue sourceQueue) + { + AddRange(list, sourceQueue); + } + else if (items is IReadOnlyList sourceIReadOnlyList) { AddRange(list, sourceIReadOnlyList); } diff --git a/Recyclable.Collections/RecyclableLongList.AddRange.cs b/Recyclable.Collections/RecyclableLongList.AddRange.cs index e8f5b79..92dcef1 100644 --- a/Recyclable.Collections/RecyclableLongList.AddRange.cs +++ b/Recyclable.Collections/RecyclableLongList.AddRange.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Numerics; +using System.Runtime.CompilerServices; using Recyclable.Collections.Pools; namespace Recyclable.Collections @@ -405,13 +406,37 @@ namespace Recyclable.Collections targetList.AddRange(sourceICollection); return; - case ICollection sourceICollectionWithObjects: - targetList.AddRange(sourceICollectionWithObjects); - return; + case ICollection sourceICollectionWithObjects: + targetList.AddRange(sourceICollectionWithObjects); + return; - case IReadOnlyList sourceIReadOnlyList: - targetList.AddRange(sourceIReadOnlyList); - return; + case IReadOnlyList sourceIReadOnlyList: + targetList.AddRange(sourceIReadOnlyList); + return; + + case RecyclableHashSet sourceHashSet: + targetList.AddRange(sourceHashSet); + return; + + case RecyclableStack sourceStack: + targetList.AddRange(sourceStack); + return; + + case RecyclableSortedSet sourceSortedSet: + targetList.AddRange(sourceSortedSet); + return; + + case RecyclableLinkedList sourceLinkedList: + targetList.AddRange(sourceLinkedList); + return; + + case RecyclablePriorityQueue sourcePriorityQueue: + targetList.AddRange(sourcePriorityQueue); + return; + + case RecyclableQueue sourceQueue: + targetList.AddRange(sourceQueue); + return; default: if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) @@ -760,12 +785,12 @@ namespace Recyclable.Collections targetList._version++; } - public static void AddRange(this RecyclableLongList targetList, in T[] items) - { - if (items.LongLength == 0) - { - return; - } + public static void AddRange(this RecyclableLongList targetList, in T[] items) + { + if (items.LongLength == 0) + { + return; + } int targetBlockIndex = targetList._nextItemBlockIndex; @@ -812,8 +837,344 @@ namespace Recyclable.Collections } targetList._longCount = targetCapacity; - targetList._lastBlockWithData = targetBlockIndex; - targetList._version++; - } + targetList._lastBlockWithData = targetBlockIndex; + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList targetList, RecyclableHashSet items) + { + if (items._count == 0) + { + return; + } + + long sourceCount = items._count, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + var entries = items._entries; + int shift = items._blockShift, + mask = items._blockSizeMinus1; + + for (long i = 0; i < sourceCount; i++) + { + blocks[blockIndex][itemIndex++] = entries[i >> shift][(int)(i & mask)].Value; + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList targetList, RecyclableStack items) + { + if (items._count == 0) + { + return; + } + + long sourceCount = items._count, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + var chunk = items._current; + while (chunk != null) + { + var array = chunk.Value; + for (int i = chunk.Index - 1; i >= 0; i--) + { + blocks[blockIndex][itemIndex++] = array[i]; + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + chunk = chunk.Previous; + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList targetList, RecyclableSortedSet items) + { + if (items._count == 0) + { + return; + } + + long sourceCount = items._count, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + int idx = 0; + while (idx < items._count) + { + blocks[blockIndex][itemIndex++] = items._items[idx++]; + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList targetList, RecyclableLinkedList items) + { + if (items._count == 0) + { + return; + } + + long sourceCount = items._count, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + var chunk = items._head; + while (chunk != null) + { + for (int i = chunk.Bottom; i < chunk.Top; i++) + { + blocks[blockIndex][itemIndex++] = chunk.Value[i]; + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + chunk = chunk.Next; + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList targetList, RecyclablePriorityQueue items) + { + if (items._size == 0) + { + return; + } + + long sourceCount = items._size, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + int idx = 0; + while (idx < items._size) + { + blocks[blockIndex][itemIndex++] = items._heap[idx++]; + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList targetList, RecyclableQueue items) + { + if (items._count == 0) + { + return; + } + + long sourceCount = items._count, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + var chunkQ = items._head; + while (chunkQ != null) + { + for (int i = chunkQ.Bottom; i < chunkQ.Top; i++) + { + blocks[blockIndex][itemIndex++] = chunkQ.Value[i]; + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + chunkQ = chunkQ.Next; + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList> targetList, RecyclableDictionary items) + { + if (items._count == 0) + { + return; + } + + long sourceCount = items._count, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + var entries = items._entries; + int shift = items._blockShift, + mask = items._blockSizeMinus1; + + for (long i = 0; i < sourceCount; i++) + { + ref var entry = ref entries[i >> shift][(int)(i & mask)]; + blocks[blockIndex][itemIndex++] = new KeyValuePair(entry.Key, entry.Value); + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclableLongList<(TKey Key, TValue Value)> targetList, RecyclableSortedList items) + { + if (items._count == 0) + { + return; + } + + long sourceCount = items._count, + targetCapacity = targetList._longCount + sourceCount; + + if (targetList._capacity < targetCapacity) + { + targetList._capacity = RecyclableLongList<(TKey Key, TValue Value)>.Helpers.Resize(targetList, targetList._blockSize, targetList._blockSizePow2BitShift, checked((long)BitOperations.RoundUpToPowerOf2((ulong)targetCapacity))); + } + + var blocks = targetList._memoryBlocks; + int blockIndex = targetList._nextItemBlockIndex, + itemIndex = targetList._nextItemIndex, + blockSize = targetList._blockSize; + + int idx2 = 0; + while (idx2 < items._count) + { + blocks[blockIndex][itemIndex++] = (items._keys[idx2], items._values[idx2]); + idx2++; + if (itemIndex == blockSize) + { + itemIndex = 0; + blockIndex++; + } + } + + targetList._longCount = targetCapacity; + targetList._nextItemBlockIndex = blockIndex; + targetList._nextItemIndex = itemIndex; + targetList._lastBlockWithData = blockIndex - (itemIndex > 0 ? 0 : 1); + targetList._version++; + } } } diff --git a/Recyclable.Collections/RecyclablePriorityQueue.cs b/Recyclable.Collections/RecyclablePriorityQueue.cs index c4fe439..ca80fab 100644 --- a/Recyclable.Collections/RecyclablePriorityQueue.cs +++ b/Recyclable.Collections/RecyclablePriorityQueue.cs @@ -7,15 +7,15 @@ namespace Recyclable.Collections { internal sealed class RecyclablePriorityQueue : IEnumerable, IDisposable { - private static readonly bool _needsClearing = !typeof(T).IsValueType; + internal static readonly bool _needsClearing = !typeof(T).IsValueType; - private readonly IComparer _comparer; + internal readonly IComparer _comparer; #nullable disable - private T[] _heap; + internal T[] _heap; #nullable restore - private int _size; - private bool _disposed; + internal int _size; + internal bool _disposed; public RecyclablePriorityQueue(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer? comparer = null) { diff --git a/Recyclable.Collections/RecyclableQueue.cs b/Recyclable.Collections/RecyclableQueue.cs index 30dd59d..6f4c814 100644 --- a/Recyclable.Collections/RecyclableQueue.cs +++ b/Recyclable.Collections/RecyclableQueue.cs @@ -7,7 +7,7 @@ namespace Recyclable.Collections { internal sealed class RecyclableQueue : IEnumerable, IDisposable { - private static readonly bool _needsClearing = !typeof(T).IsValueType; + internal static readonly bool _needsClearing = !typeof(T).IsValueType; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static BiDirectionalRecyclableArrayPoolChunk RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk? previous) @@ -44,11 +44,11 @@ namespace Recyclable.Collections BiDirectionalRecyclableArrayPoolChunkPool.Dispose(chunk, _needsClearing); } - private BiDirectionalRecyclableArrayPoolChunk _head; - private BiDirectionalRecyclableArrayPoolChunk _tail; - private bool _disposed; - private long _capacity; - private long _count; + internal BiDirectionalRecyclableArrayPoolChunk _head; + internal BiDirectionalRecyclableArrayPoolChunk _tail; + internal bool _disposed; + internal long _capacity; + internal long _count; public RecyclableQueue(int initialCapacity = RecyclableDefaults.InitialCapacity) { diff --git a/Recyclable.Collections/RecyclableSortedList.cs b/Recyclable.Collections/RecyclableSortedList.cs index 9a7af04..9b371ea 100644 --- a/Recyclable.Collections/RecyclableSortedList.cs +++ b/Recyclable.Collections/RecyclableSortedList.cs @@ -8,13 +8,13 @@ namespace Recyclable.Collections internal sealed class RecyclableSortedList : IEnumerable<(TKey Key, TValue Value)>, IDisposable where TKey : notnull { - private static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; + internal static readonly bool _needsClearing = !typeof(TKey).IsValueType || !typeof(TValue).IsValueType; - private TKey[] _keys; - private TValue[] _values; - private int _count; - private readonly IComparer _comparer; - private bool _disposed; + internal TKey[] _keys; + internal TValue[] _values; + internal int _count; + internal readonly IComparer _comparer; + internal bool _disposed; public RecyclableSortedList(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer? comparer = null) { diff --git a/Recyclable.Collections/RecyclableSortedSet.cs b/Recyclable.Collections/RecyclableSortedSet.cs index e5ad458..6c7db74 100644 --- a/Recyclable.Collections/RecyclableSortedSet.cs +++ b/Recyclable.Collections/RecyclableSortedSet.cs @@ -7,13 +7,13 @@ namespace Recyclable.Collections { internal sealed class RecyclableSortedSet : IEnumerable, IDisposable where T : notnull { - private static readonly bool _needsClearing = !typeof(T).IsValueType; + internal static readonly bool _needsClearing = !typeof(T).IsValueType; - private readonly IComparer _comparer; + internal readonly IComparer _comparer; - private T[] _items; - private int _count; - private bool _disposed; + internal T[] _items; + internal int _count; + internal bool _disposed; public RecyclableSortedSet(int initialCapacity = RecyclableDefaults.InitialCapacity, IComparer? comparer = null) { diff --git a/Recyclable.Collections/RecyclableStack.cs b/Recyclable.Collections/RecyclableStack.cs index 27b9e04..e5bd8a2 100644 --- a/Recyclable.Collections/RecyclableStack.cs +++ b/Recyclable.Collections/RecyclableStack.cs @@ -7,7 +7,7 @@ namespace Recyclable.Collections { internal sealed class RecyclableStack : IEnumerable, IDisposable { - private static readonly bool _needsClearing = !typeof(T).IsValueType; + internal static readonly bool _needsClearing = !typeof(T).IsValueType; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static RecyclableArrayPoolChunk RentChunk(int size, RecyclableArrayPoolChunk? previous) @@ -42,10 +42,10 @@ namespace Recyclable.Collections RecyclableArrayPoolChunkPool.Dispose(chunk, _needsClearing); } - private RecyclableArrayPoolChunk _current; - private bool _disposed; - private long _capacity; - private long _count; + internal RecyclableArrayPoolChunk _current; + internal bool _disposed; + internal long _capacity; + internal long _count; public RecyclableStack(int initialCapacity = RecyclableDefaults.InitialCapacity) {