diff --git a/src/LibHac/Common/BlitSpan.cs b/src/LibHac/Common/BlitSpan.cs new file mode 100644 index 00000000..78940967 --- /dev/null +++ b/src/LibHac/Common/BlitSpan.cs @@ -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; + } + } +} diff --git a/src/LibHac/Common/BlitStruct.cs b/src/LibHac/Common/BlitStruct.cs index 70e4dad8..e2c6540c 100644 --- a/src/LibHac/Common/BlitStruct.cs +++ b/src/LibHac/Common/BlitStruct.cs @@ -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; diff --git a/src/LibHac/ThrowHelper.cs b/src/LibHac/ThrowHelper.cs index c13fa3e9..2eb6773d 100644 --- a/src/LibHac/ThrowHelper.cs +++ b/src/LibHac/ThrowHelper.cs @@ -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(); + } } }