// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Buffers; using System.Diagnostics.Contracts; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Toolkit.HighPerformance.Buffers; using Microsoft.Toolkit.HighPerformance.Streams; namespace Microsoft.Toolkit.HighPerformance { /// /// Helpers for working with the type. /// public static class IBufferWriterExtensions { /// /// Returns a that can be used to write to a target an of instance. /// /// The target instance. /// A wrapping and writing data to its underlying buffer. /// The returned can only be written to and does not support seeking. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Stream AsStream(this IBufferWriter writer) { if (writer.GetType() == typeof(ArrayPoolBufferWriter)) { // If the input writer is of type ArrayPoolBufferWriter, we can use the type // specific buffer writer owner to let the JIT elide callvirts when accessing it. var internalWriter = Unsafe.As>(writer)!; return new IBufferWriterStream(new ArrayBufferWriterOwner(internalWriter)); } return new IBufferWriterStream(new IBufferWriterOwner(writer)); } /// /// Writes a value of a specified type into a target instance. /// /// The type of value to write. /// The target instance to write to. /// The input value to write to . /// Thrown if reaches the end. public static void Write(this IBufferWriter writer, T value) where T : unmanaged { int length = Unsafe.SizeOf(); Span span = writer.GetSpan(1); if (span.Length < length) { ThrowArgumentExceptionForEndOfBuffer(); } ref byte r0 = ref MemoryMarshal.GetReference(span); Unsafe.WriteUnaligned(ref r0, value); writer.Advance(length); } /// /// Writes a value of a specified type into a target instance. /// /// The type of value to write. /// The target instance to write to. /// The input value to write to . /// Thrown if reaches the end. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Write(this IBufferWriter writer, T value) { Span span = writer.GetSpan(1); if (span.Length < 1) { ThrowArgumentExceptionForEndOfBuffer(); } MemoryMarshal.GetReference(span) = value; writer.Advance(1); } /// /// Writes a series of items of a specified type into a target instance. /// /// The type of value to write. /// The target instance to write to. /// The input to write to . /// Thrown if reaches the end. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Write(this IBufferWriter writer, ReadOnlySpan span) where T : unmanaged { ReadOnlySpan source = MemoryMarshal.AsBytes(span); Span destination = writer.GetSpan(source.Length); source.CopyTo(destination); writer.Advance(source.Length); } #if !SPAN_RUNTIME_SUPPORT /// /// Writes a series of items of a specified type into a target instance. /// /// The type of value to write. /// The target instance to write to. /// The input to write to . /// Thrown if reaches the end. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Write(this IBufferWriter writer, ReadOnlySpan span) { Span destination = writer.GetSpan(span.Length); span.CopyTo(destination); writer.Advance(span.Length); } #endif /// /// Throws an when trying to write too many bytes to the target writer. /// private static void ThrowArgumentExceptionForEndOfBuffer() { throw new ArgumentException("The current buffer writer can't contain the requested input data."); } } }