using System.Collections; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Numerics; using Recyclable.Collections.Pools; namespace Recyclable.Collections { internal static class zRecyclableQueueAddRange { private static class AddRangeHelper { private static readonly Action, object>? _dictionaryAdder; private static readonly Action, object>? _sortedListAdder; private static readonly Type? _dictionaryType; private static readonly Type? _sortedListType; static AddRangeHelper() { Type elementType = typeof(T); if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) { Type[] args = elementType.GetGenericArguments(); _dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args); MethodInfo? method = typeof(zRecyclableQueueAddRange).GetMethod( nameof(AddRange), BindingFlags.NonPublic | BindingFlags.Static, binder: null, types: new[] { typeof(RecyclableQueue<>).MakeGenericType(elementType), _dictionaryType }, modifiers: null); if (method != null) { var queueParam = Expression.Parameter(typeof(RecyclableQueue)); var objParam = Expression.Parameter(typeof(object)); var call = Expression.Call(method, queueParam, Expression.Convert(objParam, _dictionaryType)); _dictionaryAdder = Expression.Lambda, object>>(call, queueParam, objParam).Compile(); } } else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>)) { Type[] args = elementType.GetGenericArguments(); _sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args); MethodInfo? method = typeof(zRecyclableQueueAddRange).GetMethod( nameof(AddRange), BindingFlags.NonPublic | BindingFlags.Static, binder: null, types: new[] { typeof(RecyclableQueue<>).MakeGenericType(elementType), _sortedListType }, modifiers: null); if (method != null) { var queueParam = Expression.Parameter(typeof(RecyclableQueue)); var objParam = Expression.Parameter(typeof(object)); var call = Expression.Call(method, queueParam, Expression.Convert(objParam, _sortedListType)); _sortedListAdder = Expression.Lambda, object>>(call, queueParam, objParam).Compile(); } } } internal static bool TryAddRange(RecyclableQueue queue, IEnumerable items) { if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items)) { _dictionaryAdder(queue, items); return true; } if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items)) { _sortedListAdder(queue, items); return true; } return false; } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, ReadOnlySpan items) { int count = items.Length; if (count == 0) { return; } EnsureCapacity(queue, queue._count + count); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); items.Slice(copied, toCopy).CopyTo(new Span(chunk.Value, index, toCopy)); copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, Span items) => AddRange(queue, (ReadOnlySpan)items); [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, T[] items) => AddRange(queue, (ReadOnlySpan)items); [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, in Array items) { int count = items.Length; if (count == 0) { return; } EnsureCapacity(queue, queue._count + count); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; int lower = items.GetLowerBound(0); while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); Array.Copy(items, lower + copied, chunk.Value, index, toCopy); copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, List items) => AddRange(queue, CollectionsMarshal.AsSpan(items)); [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, ICollection items) { int count = items.Count; if (count == 0) { return; } T[] buffer = count >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)count))) : new T[count]; items.CopyTo(buffer, 0); EnsureCapacity(queue, queue._count + count); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); Array.Copy(buffer, copied, chunk.Value, index, toCopy); copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; if (count >= RecyclableDefaults.MinPooledArrayLength) { RecyclableArrayPool.ReturnShared(buffer, RecyclableQueue._needsClearing); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, ICollection items) { int count = items.Count; if (count == 0) { return; } T[] buffer = count >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(checked((int)BitOperations.RoundUpToPowerOf2((uint)count))) : new T[count]; items.CopyTo(buffer, 0); EnsureCapacity(queue, queue._count + count); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); Array.Copy(buffer, copied, chunk.Value, index, toCopy); copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; if (count >= RecyclableDefaults.MinPooledArrayLength) { RecyclableArrayPool.ReturnShared(buffer, RecyclableQueue._needsClearing); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclableList items) { int count = items._count; if (count == 0) { return; } EnsureCapacity(queue, queue._count + count); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; T[] source = items._memoryBlock; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); Array.Copy(source, copied, chunk.Value, index, toCopy); copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Grow(RecyclableQueue queue) { int newSize; if (queue._capacity >= RecyclableDefaults.MaxPooledBlockSize) { newSize = RecyclableDefaults.MaxPooledBlockSize; } else { long doubled = queue._capacity << 1; newSize = (int)Math.Min(doubled - queue._capacity, RecyclableDefaults.MaxPooledBlockSize); } var newChunk = BiDirectionalRecyclableArrayPoolChunkPool.Rent(); if (newChunk.Value.Length < newSize) { if (newChunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength) { RecyclableArrayPool.ReturnShared(newChunk.Value, RecyclableQueue._needsClearing); } newChunk.Value = newSize >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(newSize) : new T[newSize]; } newChunk.Top = 0; newChunk.Bottom = 0; newChunk.Previous = queue._tail; newChunk.Next = null; queue._tail.Next = newChunk; queue._tail = newChunk; queue._capacity += newSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Grow(RecyclableQueue queue, long additionalCapacity) { int newSize = additionalCapacity >= RecyclableDefaults.MaxPooledBlockSize ? RecyclableDefaults.MaxPooledBlockSize : (int)Math.Min(BitOperations.RoundUpToPowerOf2((uint)additionalCapacity), RecyclableDefaults.MaxPooledBlockSize); var newChunk = BiDirectionalRecyclableArrayPoolChunkPool.Rent(); if (newChunk.Value.Length < newSize) { if (newChunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength) { RecyclableArrayPool.ReturnShared(newChunk.Value, RecyclableQueue._needsClearing); } newChunk.Value = newSize >= RecyclableDefaults.MinPooledArrayLength ? RecyclableArrayPool.RentShared(newSize) : new T[newSize]; } newChunk.Top = 0; newChunk.Bottom = 0; newChunk.Previous = queue._tail; newChunk.Next = null; queue._tail.Next = newChunk; queue._tail = newChunk; queue._capacity += newSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureCapacity(RecyclableQueue queue, long requiredCapacity) { while (queue._capacity < requiredCapacity) { Grow(queue, requiredCapacity - queue._capacity); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclableLongList items) { long longCount = items._longCount; if (longCount == 0) { return; } int blockSize = items._blockSize; int fullBlocks = (int)(longCount >> items._blockSizePow2BitShift); int lastBlockLength = (int)(longCount & items._blockSizeMinus1); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; for (int blockIndex = 0; blockIndex < fullBlocks; blockIndex++) { T[] block = items._memoryBlocks[blockIndex]; int sourceIndex = 0; while (sourceIndex < blockSize) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, blockSize - sourceIndex); Array.Copy(block, sourceIndex, chunk.Value, index, toCopy); sourceIndex += toCopy; index += toCopy; } } if (lastBlockLength > 0) { T[] block = items._memoryBlocks[fullBlocks]; int sourceIndex = 0; while (sourceIndex < lastBlockLength) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, lastBlockLength - sourceIndex); Array.Copy(block, sourceIndex, chunk.Value, index, toCopy); sourceIndex += toCopy; index += toCopy; } } chunk.Top = index; queue._count += longCount; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclableHashSet items) { int count = items._count; if (count == 0) { return; } var entries = items._entries; int shift = items._blockShift, mask = items._blockSizeMinus1; BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); var destination = chunk.Value; for (int i = 0; i < toCopy; i++) { destination[index + i] = entries[(copied + i) >> shift][(copied + i) & mask].Value; } copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclableStack items) { long longCount = items._count; if (longCount == 0) { return; } BiDirectionalRecyclableArrayPoolChunk destChunk = queue._tail; int destIndex = destChunk.Top; int destLength = destChunk.Value.Length; RecyclableArrayPoolChunk sourceChunk = items._current; while (sourceChunk.Previous != null) { sourceChunk = sourceChunk.Previous; } while (sourceChunk != null) { T[] source = sourceChunk.Value; int sourceCount = sourceChunk.Index; int sourceIndex = 0; while (sourceIndex < sourceCount) { if (destIndex == destLength) { destChunk.Top = destIndex; Grow(queue); destChunk = queue._tail; destIndex = destChunk.Top; destLength = destChunk.Value.Length; } int toCopy = Math.Min(destLength - destIndex, sourceCount - sourceIndex); Array.Copy(source, sourceIndex, destChunk.Value, destIndex, toCopy); sourceIndex += toCopy; destIndex += toCopy; } sourceChunk = sourceChunk.Next!; } destChunk.Top = destIndex; queue._count += longCount; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclableSortedSet items) { int count = items._count; if (count == 0) { return; } T[] source = items._items; BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); Array.Copy(source, copied, chunk.Value, index, toCopy); copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclableLinkedList items) { long longCount = items._count; if (longCount == 0) { return; } BiDirectionalRecyclableArrayPoolChunk destChunk = queue._tail; int destIndex = destChunk.Top; int destLength = destChunk.Value.Length; var sourceChunk = items._head; while (sourceChunk != null) { int sourceIndex = sourceChunk.Bottom; int sourceTop = sourceChunk.Top; T[] source = sourceChunk.Value; while (sourceIndex < sourceTop) { if (destIndex == destLength) { destChunk.Top = destIndex; Grow(queue); destChunk = queue._tail; destIndex = destChunk.Top; destLength = destChunk.Value.Length; } int toCopy = Math.Min(destLength - destIndex, sourceTop - sourceIndex); Array.Copy(source, sourceIndex, destChunk.Value, destIndex, toCopy); sourceIndex += toCopy; destIndex += toCopy; } sourceChunk = sourceChunk.Next; } destChunk.Top = destIndex; queue._count += longCount; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclablePriorityQueue items) { int count = items._size; if (count == 0) { return; } T[] source = items._heap; BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); Array.Copy(source, copied, chunk.Value, index, toCopy); copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, RecyclableQueue items) { long longCount = items._count; if (longCount == 0) { return; } BiDirectionalRecyclableArrayPoolChunk destChunk = queue._tail; int destIndex = destChunk.Top; int destLength = destChunk.Value.Length; EnsureCapacity(queue, queue._count + longCount); var sourceChunk = items._head; while (sourceChunk != null) { int sourceIndex = sourceChunk.Bottom; int sourceTop = sourceChunk.Top; T[] source = sourceChunk.Value; while (sourceIndex < sourceTop) { if (destIndex == destLength) { destChunk.Top = destIndex; destChunk = destChunk.Next!; destIndex = destChunk.Top; destLength = destChunk.Value.Length; } int toCopy = Math.Min(destLength - destIndex, sourceTop - sourceIndex); Array.Copy(source, sourceIndex, destChunk.Value, destIndex, toCopy); sourceIndex += toCopy; destIndex += toCopy; } sourceChunk = sourceChunk.Next; } destChunk.Top = destIndex; queue._count += longCount; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue> queue, RecyclableDictionary items) where TKey : notnull { int count = items._count; if (count == 0) { return; } EnsureCapacity(queue, queue._count + count); var entries = items._entries; int shift = items._blockShift, mask = items._blockSizeMinus1; BiDirectionalRecyclableArrayPoolChunk> chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); var destination = chunk.Value; int local = copied; for (int i = 0; i < toCopy; i++) { ref var entry = ref entries[(local + i) >> shift][(local + i) & mask]; destination[index + i] = new KeyValuePair(entry.Key, entry.Value); } copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue<(TKey Key, TValue Value)> queue, RecyclableSortedList items) where TKey : notnull { int count = items._count; if (count == 0) { return; } EnsureCapacity(queue, queue._count + count); TKey[] keys = items._keys; TValue[] values = items._values; BiDirectionalRecyclableArrayPoolChunk<(TKey Key, TValue Value)> chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; int copied = 0; while (copied < count) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } int toCopy = Math.Min(chunkLength - index, count - copied); var destination = chunk.Value; for (int i = 0; i < toCopy; i++) { destination[index + i] = (keys[copied + i], values[copied + i]); } copied += toCopy; index += toCopy; } chunk.Top = index; queue._count += count; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, IReadOnlyList items) { int count = items.Count; if (count == 0) { return; } EnsureCapacity(queue, queue._count + count); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; for (int i = 0; i < count; i++) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } chunk.Value[index++] = items[i]; } chunk.Top = index; queue._count += count; } internal static IEnumerator AddRange(this RecyclableQueue queue, IEnumerable source) { IEnumerator enumerator = source.GetEnumerator(); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; while (enumerator.MoveNext()) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } chunk.Value[index++] = (T)enumerator.Current!; queue._count++; } chunk.Top = index; return enumerator; } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] internal static void AddRange(this RecyclableQueue queue, IEnumerable items) { if (items is RecyclableList recyclableList) { AddRange(queue, recyclableList); } else if (items is RecyclableLongList recyclableLongList) { AddRange(queue, recyclableLongList); } else if (items is T[] array) { AddRange(queue, array); } else if (items is List list) { AddRange(queue, list); } else if (items is ICollection iCollection) { AddRange(queue, iCollection); } else if (items is ICollection collection) { AddRange(queue, collection); } else if (items is RecyclableHashSet hashSet) { AddRange(queue, hashSet); } else if (items is RecyclableStack stack) { AddRange(queue, stack); } else if (items is RecyclableSortedSet sortedSet) { AddRange(queue, sortedSet); } else if (items is RecyclableLinkedList linkedList) { AddRange(queue, linkedList); } else if (items is RecyclablePriorityQueue priorityQueue) { AddRange(queue, priorityQueue); } else if (items is RecyclableQueue queueSource) { AddRange(queue, queueSource); } else if (AddRangeHelper.TryAddRange(queue, items)) { } else if (items is IReadOnlyList readOnlyList) { AddRange(queue, readOnlyList); } else { using IEnumerator enumerator = items.GetEnumerator(); BiDirectionalRecyclableArrayPoolChunk chunk = queue._tail; int index = chunk.Top; int chunkLength = chunk.Value.Length; while (enumerator.MoveNext()) { if (index == chunkLength) { chunk.Top = index; Grow(queue); chunk = queue._tail; index = chunk.Top; chunkLength = chunk.Value.Length; } chunk.Value[index++] = enumerator.Current; queue._count++; } chunk.Top = index; } } } }