#nullable enable
using System;
using System.Buffers;
namespace ShrinkNetwork
{
/// Owns rented memory until Dispose. A sender must await completion before disposing.
public sealed class ShrinkBufferWriter : IBufferWriter, IDisposable
{
private byte[]? _buffer;
public ShrinkBufferWriter(int initialCapacity = 256) => _buffer = ArrayPool.Shared.Rent(Math.Max(1, initialCapacity));
public int WrittenCount { get; private set; }
public ReadOnlyMemory WrittenMemory => Buffer.AsMemory(0, WrittenCount);
internal Span WrittenSpan => Buffer.AsSpan(0, WrittenCount);
private byte[] Buffer => _buffer ?? throw new ObjectDisposedException(nameof(ShrinkBufferWriter));
public void Advance(int count)
{
if (count < 0 || count > Buffer.Length - WrittenCount) throw new ArgumentOutOfRangeException(nameof(count));
WrittenCount += count;
}
public Memory GetMemory(int sizeHint = 0) { Ensure(sizeHint); return Buffer.AsMemory(WrittenCount); }
public Span GetSpan(int sizeHint = 0) { Ensure(sizeHint); return Buffer.AsSpan(WrittenCount); }
private void Ensure(int sizeHint)
{
if (sizeHint < 0) throw new ArgumentOutOfRangeException(nameof(sizeHint));
sizeHint = Math.Max(1, sizeHint);
if (sizeHint <= Buffer.Length - WrittenCount) return;
var next = ArrayPool.Shared.Rent(checked(Math.Max(Buffer.Length * 2, WrittenCount + sizeHint)));
Buffer.AsSpan(0, WrittenCount).CopyTo(next);
ArrayPool.Shared.Return(Buffer, clearArray: true);
_buffer = next;
}
public void Dispose()
{
var buffer = _buffer;
_buffer = null;
if (buffer != null) ArrayPool.Shared.Return(buffer, clearArray: true);
}
}
}