using System; using System.Buffers; namespace MagicOnion.Utils { internal sealed class ArrayPoolBufferWriter : IBufferWriter, IDisposable { [ThreadStatic] static ArrayPoolBufferWriter staticInstance; public static ArrayPoolBufferWriter RentThreadStaticWriter() { if (staticInstance == null) { staticInstance = new ArrayPoolBufferWriter(); } staticInstance.Prepare(); return staticInstance; } const int MinimumBufferSize = 32767; // use 32k buffer. byte[] buffer; int index; void Prepare() { if (buffer == null) { buffer = ArrayPool.Shared.Rent(MinimumBufferSize); } index = 0; } public ReadOnlyMemory WrittenMemory => buffer.AsMemory(0, index); public ReadOnlySpan WrittenSpan => buffer.AsSpan(0, index); public int WrittenCount => index; public int Capacity => buffer.Length; public int FreeCapacity => buffer.Length - index; public void Advance(int count) { if (count < 0) throw new ArgumentException(nameof(count)); index += count; } public Memory GetMemory(int sizeHint = 0) { CheckAndResizeBuffer(sizeHint); return buffer.AsMemory(index); } public Span GetSpan(int sizeHint = 0) { CheckAndResizeBuffer(sizeHint); return buffer.AsSpan(index); } void CheckAndResizeBuffer(int sizeHint) { if (sizeHint < 0) throw new ArgumentException(nameof(sizeHint)); if (sizeHint == 0) { sizeHint = MinimumBufferSize; } int availableSpace = buffer.Length - index; if (sizeHint > availableSpace) { int growBy = Math.Max(sizeHint, buffer.Length); int newSize = checked(buffer.Length + growBy); byte[] oldBuffer = buffer; buffer = ArrayPool.Shared.Rent(newSize); Span previousBuffer = oldBuffer.AsSpan(0, index); previousBuffer.CopyTo(buffer); ArrayPool.Shared.Return(oldBuffer); } } public void Dispose() { if (buffer == null) { return; } ArrayPool.Shared.Return(buffer); buffer = null; } } }