<PackageReference Include="Azure.Storage.Blobs" Version="12.25.1" />

PooledMemoryStream

Functions like a readable MemoryStream but uses an ArrayPool to supply the backing memory. This stream support buffering long sizes.
using System; using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace Azure.Storage.Shared { internal class PooledMemoryStream : Stream { private class BufferPartition { public byte[] Buffer { get; set; } public int DataLength { get; set; } } private int _newBufferSize; public ArrayPool<byte> ArrayPool { get; } private List<BufferPartition> BufferSet { get; } = new List<BufferPartition>(); public override bool CanRead => true; public override bool CanSeek => true; public override bool CanWrite => true; public override long Length => BufferSet.Sum((BufferPartition tuple) => tuple.DataLength); public override long Position { get; set; } public PooledMemoryStream(ArrayPool<byte> arrayPool, int bufferSize, int? initialBufferSize = default(int?)) { ArrayPool = arrayPool; _newBufferSize = bufferSize; if (initialBufferSize.HasValue) BufferSet.Add(new BufferPartition { Buffer = ArrayPool.Rent(initialBufferSize.Value), DataLength = 0 }); } public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { return ReadImpl(new Span<byte>(buffer, offset, count)); } public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return Task.FromResult(ReadImpl(new Span<byte>(buffer, offset, count))); } public override int Read(Span<byte> buffer) { return ReadImpl(buffer); } public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) { return new ValueTask<int>(ReadImpl(buffer.Span)); } private int ReadImpl(Span<byte> destBuffer) { if (Position >= Length) return 0; int num = 0; while (num < destBuffer.Length && Position < Length) { (byte[] CurrentBuffer, int BufferCount, long OffsetOfBuffer) bufferFromPosition = GetBufferFromPosition(); byte[] item = bufferFromPosition.CurrentBuffer; int item2 = bufferFromPosition.BufferCount; long item3 = bufferFromPosition.OffsetOfBuffer; int num2 = (int)Min(Length - Position, item2 - (Position - item3), destBuffer.Length - num); new Span<byte>(item, (int)(Position - item3), num2).CopyTo(destBuffer.Slice(num)); num += num2; Position += num2; } return num; } public override int ReadByte() { if (Position >= Length) return -1; (byte[] CurrentBuffer, int BufferCount, long OffsetOfBuffer) bufferFromPosition = GetBufferFromPosition(); byte[] item = bufferFromPosition.CurrentBuffer; long item2 = bufferFromPosition.OffsetOfBuffer; byte result = item[Position - item2]; Position++; return result; } private (byte[] CurrentBuffer, int BufferCount, long OffsetOfBuffer) GetBufferFromPosition() { AssertPositionInBounds(); long num = 0; foreach (BufferPartition item in BufferSet) { if (num + item.DataLength > Position) return (item.Buffer, item.DataLength, num); num += item.DataLength; } throw new InvalidOperationException("Incorrect stream partition length."); } public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: Position = offset; break; case SeekOrigin.Current: Position += offset; break; case SeekOrigin.End: Position = Length + offset; break; } return Position; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { WriteImpl(new ReadOnlySpan<byte>(buffer, offset, count)); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { WriteImpl(new ReadOnlySpan<byte>(buffer, offset, count)); return Task.CompletedTask; } public override void Write(ReadOnlySpan<byte> buffer) { WriteImpl(buffer); } public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) { WriteImpl(buffer.Span); return new ValueTask(Task.CompletedTask); } private void WriteImpl(ReadOnlySpan<byte> writeBuffer) { while (writeBuffer.Length > 0) { BufferPartition bufferPartition = GetLatestBufferWithAvailableSpaceOrDefault(); if (bufferPartition == null) { byte[] buffer = ArrayPool.Rent(_newBufferSize); bufferPartition = new BufferPartition { Buffer = buffer, DataLength = 0 }; BufferSet.Add(bufferPartition); } int num = Math.Min(bufferPartition.Buffer.Length - bufferPartition.DataLength, writeBuffer.Length); writeBuffer.Slice(0, Math.Min(num, writeBuffer.Length)).CopyTo(new Span<byte>(bufferPartition.Buffer, bufferPartition.DataLength, num)); bufferPartition.DataLength += num; Position += num; writeBuffer = writeBuffer.Slice(num); } } protected override void Dispose(bool disposing) { base.Dispose(disposing); Clear(); } public void Clear() { foreach (BufferPartition item in BufferSet) { ArrayPool.Return(item.Buffer, false); } BufferSet.Clear(); Position = 0; } private void AssertPositionInBounds() { if (Position >= Length || Position < 0) throw new InvalidOperationException("Cannot read outside the bounds of this stream."); } private BufferPartition GetLatestBufferWithAvailableSpaceOrDefault() { BufferPartition bufferPartition = BufferSet.LastOrDefault(); if (bufferPartition == null || bufferPartition.DataLength >= bufferPartition.Buffer.Length) return null; return bufferPartition; } private static long Min(long val1, long val2, long val3) { return Math.Min(Math.Min(val1, val2), val3); } } }