mirror of
https://github.com/chylex/.NET-Community-Toolkit.git
synced 2024-11-24 07:42:45 +01:00
134 lines
5.7 KiB
C#
134 lines
5.7 KiB
C#
// 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.IO;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using CommunityToolkit.HighPerformance.Buffers;
|
|
using CommunityToolkit.HighPerformance.Streams;
|
|
|
|
namespace CommunityToolkit.HighPerformance;
|
|
|
|
/// <summary>
|
|
/// Helpers for working with the <see cref="IBufferWriter{T}"/> type.
|
|
/// </summary>
|
|
public static class IBufferWriterExtensions
|
|
{
|
|
/// <summary>
|
|
/// Returns a <see cref="Stream"/> that can be used to write to a target an <see cref="IBufferWriter{T}"/> of <see cref="byte"/> instance.
|
|
/// </summary>
|
|
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance.</param>
|
|
/// <returns>A <see cref="Stream"/> wrapping <paramref name="writer"/> and writing data to its underlying buffer.</returns>
|
|
/// <remarks>The returned <see cref="Stream"/> can only be written to and does not support seeking.</remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static Stream AsStream(this IBufferWriter<byte> writer)
|
|
{
|
|
if (writer.GetType() == typeof(ArrayPoolBufferWriter<byte>))
|
|
{
|
|
// If the input writer is of type ArrayPoolBufferWriter<byte>, we can use the type
|
|
// specific buffer writer owner to let the JIT elide callvirts when accessing it.
|
|
ArrayPoolBufferWriter<byte>? internalWriter = Unsafe.As<ArrayPoolBufferWriter<byte>>(writer)!;
|
|
|
|
return new IBufferWriterStream<ArrayBufferWriterOwner>(new ArrayBufferWriterOwner(internalWriter));
|
|
}
|
|
|
|
return new IBufferWriterStream<IBufferWriterOwner>(new IBufferWriterOwner(writer));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a value of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to write.</typeparam>
|
|
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
|
|
/// <param name="value">The input value to write to <paramref name="writer"/>.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="writer"/> reaches the end.</exception>
|
|
public static void Write<T>(this IBufferWriter<byte> writer, T value)
|
|
where T : unmanaged
|
|
{
|
|
int length = Unsafe.SizeOf<T>();
|
|
Span<byte> span = writer.GetSpan(1);
|
|
|
|
if (span.Length < length)
|
|
{
|
|
ThrowArgumentExceptionForEndOfBuffer();
|
|
}
|
|
|
|
ref byte r0 = ref MemoryMarshal.GetReference(span);
|
|
|
|
Unsafe.WriteUnaligned(ref r0, value);
|
|
|
|
writer.Advance(length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a value of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to write.</typeparam>
|
|
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
|
|
/// <param name="value">The input value to write to <paramref name="writer"/>.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="writer"/> reaches the end.</exception>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static void Write<T>(this IBufferWriter<T> writer, T value)
|
|
{
|
|
Span<T> span = writer.GetSpan(1);
|
|
|
|
if (span.Length < 1)
|
|
{
|
|
ThrowArgumentExceptionForEndOfBuffer();
|
|
}
|
|
|
|
MemoryMarshal.GetReference(span) = value;
|
|
|
|
writer.Advance(1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a series of items of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to write.</typeparam>
|
|
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
|
|
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> to write to <paramref name="writer"/>.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="writer"/> reaches the end.</exception>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static void Write<T>(this IBufferWriter<byte> writer, ReadOnlySpan<T> span)
|
|
where T : unmanaged
|
|
{
|
|
ReadOnlySpan<byte> source = MemoryMarshal.AsBytes(span);
|
|
Span<byte> destination = writer.GetSpan(source.Length);
|
|
|
|
source.CopyTo(destination);
|
|
|
|
writer.Advance(source.Length);
|
|
}
|
|
|
|
#if !NETSTANDARD2_1_OR_GREATER
|
|
/// <summary>
|
|
/// Writes a series of items of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to write.</typeparam>
|
|
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
|
|
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> to write to <paramref name="writer"/>.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="writer"/> reaches the end.</exception>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static void Write<T>(this IBufferWriter<T> writer, ReadOnlySpan<T> span)
|
|
{
|
|
Span<T> destination = writer.GetSpan(span.Length);
|
|
|
|
span.CopyTo(destination);
|
|
|
|
writer.Advance(span.Length);
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Throws an <see cref="ArgumentException"/> when trying to write too many bytes to the target writer.
|
|
/// </summary>
|
|
private static void ThrowArgumentExceptionForEndOfBuffer()
|
|
{
|
|
throw new ArgumentException("The current buffer writer can't contain the requested input data.");
|
|
}
|
|
}
|