diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index 19937d3..bbc85bf 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -12,6 +12,8 @@ namespace Recyclable.Collections protected T[] _memoryBlock; + public static explicit operator ReadOnlySpan(RecyclableList source) => new(source._memoryBlock, 0, source.Count); + private static void ThrowArgumentOutOfRangeException(in string message) { throw new ArgumentOutOfRangeException(message, (Exception?)null); @@ -92,6 +94,20 @@ namespace Recyclable.Collections AddRange(source); } +#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added + public RecyclableList(RecyclableLongList source) +#pragma warning restore CS8618 + { + AddRange(source); + } + +#pragma warning disable CS8618 // _memory will be initialized when the 1st item is added + public RecyclableList(ReadOnlySpan source) +#pragma warning restore CS8618 + { + AddRange(source); + } + #pragma warning disable CS8618 // _memory will be initialized when the 1st item is added public RecyclableList(in T[] source) #pragma warning restore CS8618 @@ -188,6 +204,26 @@ namespace Recyclable.Collections _count = targetCapacity; } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public void AddRange(ReadOnlySpan itemsSpan) + { + if (itemsSpan.Length == 0) + { + return; + } + + int sourceItemsCount = itemsSpan.Length; + int targetCapacity = _count + sourceItemsCount; + if (_capacity < targetCapacity) + { + _ = EnsureCapacity(this, targetCapacity); + } + + var targetSpan = new Span(_memoryBlock, _count, sourceItemsCount); + itemsSpan.CopyTo(targetSpan); + _count = targetCapacity; + } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] public void AddRange(List items) { diff --git a/Recyclable.Collections/RecyclableLongList.cs b/Recyclable.Collections/RecyclableLongList.cs index e1ac32c..9394aa9 100644 --- a/Recyclable.Collections/RecyclableLongList.cs +++ b/Recyclable.Collections/RecyclableLongList.cs @@ -417,6 +417,186 @@ namespace Recyclable.Collections } } +#pragma warning disable CS8618 // It's set by SetupBlockArrayPooling + public RecyclableLongList(in T[] source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool? memoryBlocksPool = default, ArrayPool? blockArrayPool = default) +#pragma warning restore CS8618 + { + _memoryBlocksPool = memoryBlocksPool ?? _defaultMemoryBlocksPool; + if (expectedItemsCount > 0) + { + minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); + SetupBlockArrayPooling(this, minBlockSize, blockArrayPool); + _capacity = Resize(this, minBlockSize, _blockSizePow2BitShift, expectedItemsCount.Value); + if (minBlockSize != _memoryBlocks![0].Length && _capacity > 0) + { + SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + } + else + { + _blockSizeMinus1 = minBlockSize - 1; + } + } + else + { + SetupBlockArrayPooling(this, checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)), blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + _memoryBlocks = _emptyMemoryBlocksArray; + } + + AddRange(source); + } + +#pragma warning disable CS8618 // It's set by SetupBlockArrayPooling + public RecyclableLongList(ReadOnlySpan source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool? memoryBlocksPool = default, ArrayPool? blockArrayPool = default) +#pragma warning restore CS8618 + { + _memoryBlocksPool = memoryBlocksPool ?? _defaultMemoryBlocksPool; + if (expectedItemsCount > 0) + { + minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); + SetupBlockArrayPooling(this, minBlockSize, blockArrayPool); + _capacity = Resize(this, minBlockSize, _blockSizePow2BitShift, expectedItemsCount.Value); + if (minBlockSize != _memoryBlocks![0].Length && _capacity > 0) + { + SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + } + else + { + _blockSizeMinus1 = minBlockSize - 1; + } + } + else + { + SetupBlockArrayPooling(this, checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)), blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + _memoryBlocks = _emptyMemoryBlocksArray; + } + + AddRange(source); + } + +#pragma warning disable CS8618 // It's set by SetupBlockArrayPooling + public RecyclableLongList(RecyclableList source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool? memoryBlocksPool = default, ArrayPool? blockArrayPool = default) +#pragma warning restore CS8618 + { + _memoryBlocksPool = memoryBlocksPool ?? _defaultMemoryBlocksPool; + if (expectedItemsCount > 0) + { + minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); + SetupBlockArrayPooling(this, minBlockSize, blockArrayPool); + _capacity = Resize(this, minBlockSize, _blockSizePow2BitShift, expectedItemsCount.Value); + if (minBlockSize != _memoryBlocks![0].Length && _capacity > 0) + { + SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + } + else + { + _blockSizeMinus1 = minBlockSize - 1; + } + } + else + { + SetupBlockArrayPooling(this, checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)), blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + _memoryBlocks = _emptyMemoryBlocksArray; + } + + AddRange(source); + } + +#pragma warning disable CS8618 // It's set by SetupBlockArrayPooling + public RecyclableLongList(RecyclableLongList source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool? memoryBlocksPool = default, ArrayPool? blockArrayPool = default) +#pragma warning restore CS8618 + { + _memoryBlocksPool = memoryBlocksPool ?? _defaultMemoryBlocksPool; + if (expectedItemsCount > 0) + { + minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); + SetupBlockArrayPooling(this, minBlockSize, blockArrayPool); + _capacity = Resize(this, minBlockSize, _blockSizePow2BitShift, expectedItemsCount.Value); + if (minBlockSize != _memoryBlocks![0].Length && _capacity > 0) + { + SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + } + else + { + _blockSizeMinus1 = minBlockSize - 1; + } + } + else + { + SetupBlockArrayPooling(this, checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)), blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + _memoryBlocks = _emptyMemoryBlocksArray; + } + + AddRange(source); + } + +#pragma warning disable CS8618 // It's set by SetupBlockArrayPooling + public RecyclableLongList(List source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool? memoryBlocksPool = default, ArrayPool? blockArrayPool = default) +#pragma warning restore CS8618 + { + _memoryBlocksPool = memoryBlocksPool ?? _defaultMemoryBlocksPool; + if (expectedItemsCount > 0) + { + minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); + SetupBlockArrayPooling(this, minBlockSize, blockArrayPool); + _capacity = Resize(this, minBlockSize, _blockSizePow2BitShift, expectedItemsCount.Value); + if (minBlockSize != _memoryBlocks![0].Length && _capacity > 0) + { + SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + } + else + { + _blockSizeMinus1 = minBlockSize - 1; + } + } + else + { + SetupBlockArrayPooling(this, checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)), blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + _memoryBlocks = _emptyMemoryBlocksArray; + } + + AddRange(source); + } + +#pragma warning disable CS8618 // It's set by SetupBlockArrayPooling + public RecyclableLongList(IList source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool? memoryBlocksPool = default, ArrayPool? blockArrayPool = default) +#pragma warning restore CS8618 + { + _memoryBlocksPool = memoryBlocksPool ?? _defaultMemoryBlocksPool; + if (expectedItemsCount > 0) + { + minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)); + SetupBlockArrayPooling(this, minBlockSize, blockArrayPool); + _capacity = Resize(this, minBlockSize, _blockSizePow2BitShift, expectedItemsCount.Value); + if (minBlockSize != _memoryBlocks![0].Length && _capacity > 0) + { + SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + } + else + { + _blockSizeMinus1 = minBlockSize - 1; + } + } + else + { + SetupBlockArrayPooling(this, checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize)), blockArrayPool); + _blockSizeMinus1 = _blockSize - 1; + _memoryBlocks = _emptyMemoryBlocksArray; + } + + AddRange(source); + } + #pragma warning disable CS8618 // It's set by SetupBlockArrayPooling public RecyclableLongList(IEnumerable source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool? memoryBlocksPool = default, ArrayPool? blockArrayPool = default) #pragma warning restore CS8618 @@ -576,6 +756,53 @@ namespace Recyclable.Collections _lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1); } + public void AddRange(ReadOnlySpan itemsSpan) + { + if (itemsSpan.Length == 0) + { + return; + } + + long targetCapacity = _longCount + itemsSpan.Length; + if (_capacity < targetCapacity) + { + _ = EnsureCapacity(this, targetCapacity); + } + + int blockSize = _blockSize, + targetBlockIndex = _nextItemBlockIndex, + memoryBlockCount = _reservedBlockCount; + + Span memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount); + 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], 0, blockSize); + } + + if (itemsSpan.Length > 0) + { + itemsSpan.CopyTo(targetBlockArraySpan); + _nextItemBlockIndex = targetBlockIndex; + } + else + { + _nextItemBlockIndex = targetBlockIndex + 1; + } + + _nextItemIndex = itemsSpan.Length; + _longCount = targetCapacity; + _lastBlockWithData = targetBlockIndex; + } + public void AddRange(RecyclableLongList items) { if (items.LongCount == 0)