// 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.");
}
}
}