// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; namespace System.Buffers { /// /// A fast access struct that wraps . /// /// The type of element to be written. internal ref struct BufferWriter where T : IBufferWriter { /// /// The underlying . /// private T _output; /// /// The result of the last call to , less any bytes already "consumed" with . /// Backing field for the property. /// private Span _span; /// /// The number of uncommitted bytes (all the calls to since the last call to ). /// private int _buffered; /// /// The total number of bytes written with this writer. /// Backing field for the property. /// private long _bytesCommitted; /// /// Initializes a new instance of the struct. /// /// The to be wrapped. [MethodImpl(MethodImplOptions.AggressiveInlining)] public BufferWriter(T output) { _buffered = 0; _bytesCommitted = 0; _output = output; _span = output.GetSpan(); } /// /// Gets the result of the last call to . /// public Span Span => _span; /// /// Gets the total number of bytes written with this writer. /// public long BytesCommitted => _bytesCommitted; /// /// Calls on the underlying writer /// with the number of uncommitted bytes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Commit() { var buffered = _buffered; if (buffered > 0) { _bytesCommitted += buffered; _buffered = 0; _output.Advance(buffered); } } /// /// Used to indicate that part of the buffer has been written to. /// /// The number of bytes written to. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Advance(int count) { _buffered += count; _span = _span.Slice(count); } /// /// Copies the caller's buffer into this writer and calls with the length of the source buffer. /// /// The buffer to copy in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(ReadOnlySpan source) { if (_span.Length >= source.Length) { source.CopyTo(_span); Advance(source.Length); } else { WriteMultiBuffer(source); } } /// /// Acquires a new buffer if necessary to ensure that some given number of bytes can be written to a single buffer. /// /// The number of bytes that must be allocated in a single buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Ensure(int count = 1) { if (_span.Length < count) { EnsureMore(count); } } /// /// Gets a fresh span to write to, with an optional minimum size. /// /// The minimum size for the next requested buffer. [MethodImpl(MethodImplOptions.NoInlining)] private void EnsureMore(int count = 0) { if (_buffered > 0) { Commit(); } _span = _output.GetSpan(count); } /// /// Copies the caller's buffer into this writer, potentially across multiple buffers from the underlying writer. /// /// The buffer to copy into this writer. private void WriteMultiBuffer(ReadOnlySpan source) { while (source.Length > 0) { if (_span.Length == 0) { EnsureMore(); } var writable = Math.Min(source.Length, _span.Length); source.Slice(0, writable).CopyTo(_span); source = source.Slice(writable); Advance(writable); } } } }