Add BlitStruct<T> and BlitSpan<T>

This commit is contained in:
Alex Barney 2019-10-29 00:08:23 -05:00
parent bfaf95026a
commit 5b6ca9c160
3 changed files with 150 additions and 25 deletions

View file

@ -0,0 +1,102 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common
{
/// <summary>
/// Provides a representation of a region of memory as if it were a series of blittable structs
/// of type <typeparamref name="T"/>. Also allows viewing the memory as a <see cref="Span{T}"/> of bytes.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
public ref struct BlitSpan<T> where T : unmanaged
{
private readonly Span<T> _buffer;
/// <summary>
/// The number of elements of type <typeparamref name="T"/> in the <see cref="BlitSpan{T}"/>.
/// </summary>
public int Length => _buffer.Length;
/// <summary>
/// A reference to the first element in this collection.
/// </summary>
public ref T Value => ref _buffer[0];
/// <summary>
/// A reference to the element at index <paramref name="index"/>.
/// </summary>
public ref T this[int index] => ref _buffer[index];
/// <summary>
/// Creates a new <see cref="BlitSpan{T}"/> using the provided <see cref="Span{T}"/>.
/// </summary>
/// <param name="data">The span from which to create the <see cref="BlitSpan{T}"/>.
/// Must have a length of at least 1.</param>
public BlitSpan(Span<T> data)
{
if (data.Length == 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
_buffer = data;
}
/// <summary>
/// Creates a new <see cref="BlitSpan{T}"/> using the provided <see cref="Span{T}"/> of bytes.
/// </summary>
/// <param name="data">The byte span from which to create the <see cref="BlitSpan{T}"/>.
/// Must be long enough to hold at least one struct of type <typeparamref name="T"/></param>
public BlitSpan(Span<byte> data)
{
if (data.Length < Unsafe.SizeOf<T>())
ThrowHelper.ThrowArgumentOutOfRangeException();
_buffer = MemoryMarshal.Cast<byte, T>(data);
}
/// <summary>
/// Creates a new <see cref="BlitSpan{T}"/> over a struct of type <typeparamref name="T"/>.
/// </summary>
/// <param name="data">The struct from which to create the <see cref="BlitSpan{T}"/>.</param>
public BlitSpan(ref T data)
{
_buffer = SpanHelpers.AsSpan(ref data);
}
/// <summary>
/// A <see cref="Span{T}"/> of the elements in the <see cref="BlitSpan{T}"/>.
/// </summary>
public Span<T> Span => _buffer;
/// <summary>
/// Returns a view of the <see cref="BlitSpan{T}"/> as a <see cref="Span{T}"/> of bytes.
/// </summary>
/// <returns>A byte span representation of the <see cref="BlitSpan{T}"/>.</returns>
public Span<byte> GetByteSpan()
{
return MemoryMarshal.Cast<T, byte>(_buffer);
}
/// <summary>
/// Returns a view of the element at index <see cref="elementIndex"/> as a <see cref="Span{T}"/> of bytes.
/// </summary>
/// <param name="elementIndex">The zero-based index of the element.</param>
/// <returns>A byte span representation of the element.</returns>
public Span<byte> GetByteSpan(int elementIndex)
{
Span<T> element = _buffer.Slice(elementIndex, 1);
return MemoryMarshal.Cast<T, byte>(element);
}
/// <summary>
/// Calculates the length of memory in bytes that would be needed to store <paramref name="elementCount"/>
/// elements of type <typeparamref name="T"/>.
/// </summary>
/// <param name="elementCount">The number of elements.</param>
/// <returns>The number of bytes required.</returns>
public static int QueryByteLength(int elementCount)
{
return Unsafe.SizeOf<T>() * elementCount;
}
}
}

View file

@ -5,45 +5,63 @@ using System.Runtime.InteropServices;
namespace LibHac.Common
{
public ref struct BlitStruct<T> where T : unmanaged
/// <summary>
/// Handles storing a blittable struct or a series of blittable structs in a byte array.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
public readonly struct BlitStruct<T> where T : unmanaged
{
private readonly Span<T> _buffer;
private readonly byte[] _buffer;
public int Length => _buffer.Length;
public int Length => _buffer.Length / Unsafe.SizeOf<T>();
public ref T Value => ref _buffer[0];
public ref T this[int index] => ref _buffer[index];
public BlitStruct(Span<T> data)
/// <summary>
/// A reference to the first element in this collection.
/// </summary>
public ref T Value
{
_buffer = data;
get
{
Debug.Assert(_buffer.Length >= Unsafe.SizeOf<T>());
Debug.Assert(_buffer.Length != 0);
return ref Unsafe.As<byte, T>(ref _buffer[0]);
}
}
public BlitStruct(Span<byte> data)
/// <summary>
/// Initializes a new <see cref="BlitStruct{T}"/> that can hold the specified number
/// of elements of type <typeparamref name="T"/>.
/// </summary>
/// <param name="elementCount">The number of elements the <see cref="BlitStruct{T}"/> will be able to store.</param>
public BlitStruct(int elementCount)
{
_buffer = MemoryMarshal.Cast<byte, T>(data);
if (elementCount <= 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
Debug.Assert(_buffer.Length != 0);
_buffer = new byte[QueryByteLength(elementCount)];
}
public BlitStruct(ref T data)
{
_buffer = SpanHelpers.AsSpan(ref data);
}
/// <summary>
/// Returns a <see cref="Span"/> view of the elements in the current <see cref="BlitStruct{T}"/> as type <typeparamref name="T"/>.
/// </summary>
public Span<T> Span => MemoryMarshal.Cast<byte, T>(_buffer);
public Span<byte> GetByteSpan()
{
return MemoryMarshal.Cast<T, byte>(_buffer);
}
/// <summary>
/// Returns a <see cref="Span"/> view of the elements in the current <see cref="BlitStruct{T}"/> as <see cref="byte"/>s.
/// </summary>
public Span<byte> ByteSpan => _buffer;
public Span<byte> GetByteSpan(int elementIndex)
{
Span<T> element = _buffer.Slice(elementIndex, 1);
return MemoryMarshal.Cast<T, byte>(element);
}
/// <summary>
/// Creates a <see cref="BlitSpan{T}"/> from the current <see cref="BlitStruct{T}"/>.
/// </summary>
public BlitSpan<T> BlitSpan => new BlitSpan<T>(_buffer);
/// <summary>
/// Calculates the length of memory in bytes that would be needed to store <paramref name="elementCount"/>
/// elements of type <typeparamref name="T"/>.
/// </summary>
/// <param name="elementCount">The number of elements.</param>
/// <returns>The number of bytes required.</returns>
public static int QueryByteLength(int elementCount)
{
return Unsafe.SizeOf<T>() * elementCount;

View file

@ -14,5 +14,10 @@ namespace LibHac
public static void ThrowResult(Result result, string message, Exception innerException) =>
throw new HorizonResultException(result, message, innerException);
internal static void ThrowArgumentOutOfRangeException()
{
throw new ArgumentOutOfRangeException();
}
}
}