From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:22:00 +0200 Subject: Move solution and projects to src --- src/Ryujinx.Common/Memory/ArrayPtr.cs | 123 ++++ src/Ryujinx.Common/Memory/Box.cs | 12 + .../Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs | 51 ++ src/Ryujinx.Common/Memory/ByteMemoryPool.cs | 108 ++++ src/Ryujinx.Common/Memory/IArray.cs | 21 + src/Ryujinx.Common/Memory/MemoryStreamManager.cs | 99 ++++ .../Memory/PartialUnmaps/NativeReaderWriterLock.cs | 80 +++ .../Memory/PartialUnmaps/PartialUnmapHelpers.cs | 20 + .../Memory/PartialUnmaps/PartialUnmapState.cs | 163 +++++ .../Memory/PartialUnmaps/ThreadLocalMap.cs | 92 +++ src/Ryujinx.Common/Memory/Ptr.cs | 68 +++ src/Ryujinx.Common/Memory/SpanOrArray.cs | 89 +++ src/Ryujinx.Common/Memory/SpanReader.cs | 56 ++ src/Ryujinx.Common/Memory/SpanWriter.cs | 45 ++ src/Ryujinx.Common/Memory/StructArrayHelpers.cs | 654 +++++++++++++++++++++ .../Memory/StructByteArrayHelpers.cs | 77 +++ 16 files changed, 1758 insertions(+) create mode 100644 src/Ryujinx.Common/Memory/ArrayPtr.cs create mode 100644 src/Ryujinx.Common/Memory/Box.cs create mode 100644 src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs create mode 100644 src/Ryujinx.Common/Memory/ByteMemoryPool.cs create mode 100644 src/Ryujinx.Common/Memory/IArray.cs create mode 100644 src/Ryujinx.Common/Memory/MemoryStreamManager.cs create mode 100644 src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs create mode 100644 src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs create mode 100644 src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs create mode 100644 src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs create mode 100644 src/Ryujinx.Common/Memory/Ptr.cs create mode 100644 src/Ryujinx.Common/Memory/SpanOrArray.cs create mode 100644 src/Ryujinx.Common/Memory/SpanReader.cs create mode 100644 src/Ryujinx.Common/Memory/SpanWriter.cs create mode 100644 src/Ryujinx.Common/Memory/StructArrayHelpers.cs create mode 100644 src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs (limited to 'src/Ryujinx.Common/Memory') diff --git a/src/Ryujinx.Common/Memory/ArrayPtr.cs b/src/Ryujinx.Common/Memory/ArrayPtr.cs new file mode 100644 index 00000000..9e95f75e --- /dev/null +++ b/src/Ryujinx.Common/Memory/ArrayPtr.cs @@ -0,0 +1,123 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Memory +{ + /// + /// Represents an array of unmanaged resources. + /// + /// Array element type + public unsafe struct ArrayPtr : IEquatable>, IArray where T : unmanaged + { + private IntPtr _ptr; + + /// + /// Null pointer. + /// + public static ArrayPtr Null => new ArrayPtr() { _ptr = IntPtr.Zero }; + + /// + /// True if the pointer is null, false otherwise. + /// + public bool IsNull => _ptr == IntPtr.Zero; + + /// + /// Number of elements on the array. + /// + public int Length { get; } + + /// + /// Gets a reference to the item at the given index. + /// + /// + /// No bounds checks are performed, this allows negative indexing, + /// but care must be taken if the index may be out of bounds. + /// + /// Index of the element + /// Reference to the element at the given index + public ref T this[int index] => ref Unsafe.AsRef((T*)_ptr + index); + + /// + /// Creates a new array from a given reference. + /// + /// + /// For data on the heap, proper pinning is necessary during + /// use. Failure to do so will result in memory corruption and crashes. + /// + /// Reference of the first array element + /// Number of elements on the array + public ArrayPtr(ref T value, int length) + { + _ptr = (IntPtr)Unsafe.AsPointer(ref value); + Length = length; + } + + /// + /// Creates a new array from a given pointer. + /// + /// Array base pointer + /// Number of elements on the array + public ArrayPtr(T* ptr, int length) + { + _ptr = (IntPtr)ptr; + Length = length; + } + + /// + /// Creates a new array from a given pointer. + /// + /// Array base pointer + /// Number of elements on the array + public ArrayPtr(IntPtr ptr, int length) + { + _ptr = ptr; + Length = length; + } + + /// + /// Splits the array starting at the specified position. + /// + /// Index where the new array should start + /// New array starting at the specified position + public ArrayPtr Slice(int start) => new ArrayPtr(ref this[start], Length - start); + + /// + /// Gets a span from the array. + /// + /// Span of the array + public Span AsSpan() => Length == 0 ? Span.Empty : MemoryMarshal.CreateSpan(ref this[0], Length); + + /// + /// Gets the array base pointer. + /// + /// Base pointer + public T* ToPointer() => (T*)_ptr; + + public override bool Equals(object obj) + { + return obj is ArrayPtr other && Equals(other); + } + + public bool Equals([AllowNull] ArrayPtr other) + { + return _ptr == other._ptr && Length == other.Length; + } + + public override int GetHashCode() + { + return HashCode.Combine(_ptr, Length); + } + + public static bool operator ==(ArrayPtr left, ArrayPtr right) + { + return left.Equals(right); + } + + public static bool operator !=(ArrayPtr left, ArrayPtr right) + { + return !(left == right); + } + } +} diff --git a/src/Ryujinx.Common/Memory/Box.cs b/src/Ryujinx.Common/Memory/Box.cs new file mode 100644 index 00000000..743cc31d --- /dev/null +++ b/src/Ryujinx.Common/Memory/Box.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Common.Memory +{ + public class Box where T : unmanaged + { + public T Data; + + public Box() + { + Data = new T(); + } + } +} diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs new file mode 100644 index 00000000..eda350bd --- /dev/null +++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs @@ -0,0 +1,51 @@ +using System; +using System.Buffers; +using System.Threading; + +namespace Ryujinx.Common.Memory +{ + public sealed partial class ByteMemoryPool + { + /// + /// Represents a that wraps an array rented from + /// and exposes it as + /// with a length of the requested size. + /// + private sealed class ByteMemoryPoolBuffer : IMemoryOwner + { + private byte[] _array; + private readonly int _length; + + public ByteMemoryPoolBuffer(int length) + { + _array = ArrayPool.Shared.Rent(length); + _length = length; + } + + /// + /// Returns a belonging to this owner. + /// + public Memory Memory + { + get + { + byte[] array = _array; + + ObjectDisposedException.ThrowIf(array is null, this); + + return new Memory(array, 0, _length); + } + } + + public void Dispose() + { + var array = Interlocked.Exchange(ref _array, null); + + if (array != null) + { + ArrayPool.Shared.Return(array); + } + } + } + } +} diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs new file mode 100644 index 00000000..2910f408 --- /dev/null +++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs @@ -0,0 +1,108 @@ +using System; +using System.Buffers; + +namespace Ryujinx.Common.Memory +{ + /// + /// Provides a pool of re-usable byte array instances. + /// + public sealed partial class ByteMemoryPool + { + private static readonly ByteMemoryPool _shared = new ByteMemoryPool(); + + /// + /// Constructs a instance. Private to force access through + /// the instance. + /// + private ByteMemoryPool() + { + // No implementation + } + + /// + /// Retrieves a shared instance. + /// + public static ByteMemoryPool Shared => _shared; + + /// + /// Returns the maximum buffer size supported by this pool. + /// + public int MaxBufferSize => Array.MaxLength; + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner Rent(long length) + => RentImpl(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner Rent(ulong length) + => RentImpl(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer may contain data from a prior use. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner Rent(int length) + => RentImpl(length); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner RentCleared(long length) + => RentCleared(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner RentCleared(ulong length) + => RentCleared(checked((int)length)); + + /// + /// Rents a byte memory buffer from . + /// The buffer's contents are cleared (set to all 0s) before returning. + /// + /// The buffer's required length in bytes + /// A wrapping the rented memory + /// + public IMemoryOwner RentCleared(int length) + { + var buffer = RentImpl(length); + + buffer.Memory.Span.Clear(); + + return buffer; + } + + private static ByteMemoryPoolBuffer RentImpl(int length) + { + if ((uint)length > Array.MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(length), length, null); + } + + return new ByteMemoryPoolBuffer(length); + } + } +} diff --git a/src/Ryujinx.Common/Memory/IArray.cs b/src/Ryujinx.Common/Memory/IArray.cs new file mode 100644 index 00000000..8f17fade --- /dev/null +++ b/src/Ryujinx.Common/Memory/IArray.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Common.Memory +{ + /// + /// Array interface. + /// + /// Element type + public interface IArray where T : unmanaged + { + /// + /// Used to index the array. + /// + /// Element index + /// Element at the specified index + ref T this[int index] { get; } + + /// + /// Number of elements on the array. + /// + int Length { get; } + } +} diff --git a/src/Ryujinx.Common/Memory/MemoryStreamManager.cs b/src/Ryujinx.Common/Memory/MemoryStreamManager.cs new file mode 100644 index 00000000..68b82999 --- /dev/null +++ b/src/Ryujinx.Common/Memory/MemoryStreamManager.cs @@ -0,0 +1,99 @@ +using Microsoft.IO; +using System; + +namespace Ryujinx.Common.Memory +{ + public static class MemoryStreamManager + { + private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager(); + + /// + /// We don't expose the RecyclableMemoryStreamManager directly because version 2.x + /// returns them as MemoryStream. This Shared class is here to a) offer only the GetStream() versions we use + /// and b) return them as RecyclableMemoryStream so we don't have to cast. + /// + public static class Shared + { + /// + /// Retrieve a new MemoryStream object with no tag and a default initial capacity. + /// + /// A RecyclableMemoryStream + public static RecyclableMemoryStream GetStream() + => new RecyclableMemoryStream(_shared); + + /// + /// Retrieve a new MemoryStream object with the contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// + /// The new stream's position is set to the beginning of the stream when returned. + /// The byte buffer to copy data from + /// A RecyclableMemoryStream + public static RecyclableMemoryStream GetStream(byte[] buffer) + => GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length); + + /// + /// Retrieve a new MemoryStream object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// + /// The new stream's position is set to the beginning of the stream when returned. + /// The byte buffer to copy data from + /// A RecyclableMemoryStream + public static RecyclableMemoryStream GetStream(ReadOnlySpan buffer) + => GetStream(Guid.NewGuid(), null, buffer); + + /// + /// Retrieve a new RecyclableMemoryStream object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// + /// The new stream's position is set to the beginning of the stream when returned. + /// A unique identifier which can be used to trace usages of the stream + /// A tag which can be used to track the source of the stream + /// The byte buffer to copy data from + /// A RecyclableMemoryStream + public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan buffer) + { + RecyclableMemoryStream stream = null; + try + { + stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length); + stream.Write(buffer); + stream.Position = 0; + return stream; + } + catch + { + stream?.Dispose(); + throw; + } + } + + /// + /// Retrieve a new RecyclableMemoryStream object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// + /// The new stream's position is set to the beginning of the stream when returned + /// A unique identifier which can be used to trace usages of the stream + /// A tag which can be used to track the source of the stream + /// The byte buffer to copy data from + /// The offset from the start of the buffer to copy from + /// The number of bytes to copy from the buffer + /// A RecyclableMemoryStream + public static RecyclableMemoryStream GetStream(Guid id, string tag, byte[] buffer, int offset, int count) + { + RecyclableMemoryStream stream = null; + try + { + stream = new RecyclableMemoryStream(_shared, id, tag, count); + stream.Write(buffer, offset, count); + stream.Position = 0; + return stream; + } + catch + { + stream?.Dispose(); + throw; + } + } + } + } +} diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs new file mode 100644 index 00000000..78eeb16f --- /dev/null +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs @@ -0,0 +1,80 @@ +using System.Runtime.InteropServices; +using System.Threading; + +using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; + +namespace Ryujinx.Common.Memory.PartialUnmaps +{ + /// + /// A simple implementation of a ReaderWriterLock which can be used from native code. + /// + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct NativeReaderWriterLock + { + public int WriteLock; + public int ReaderCount; + + public static int WriteLockOffset; + public static int ReaderCountOffset; + + /// + /// Populates the field offsets for use when emitting native code. + /// + static NativeReaderWriterLock() + { + NativeReaderWriterLock instance = new NativeReaderWriterLock(); + + WriteLockOffset = OffsetOf(ref instance, ref instance.WriteLock); + ReaderCountOffset = OffsetOf(ref instance, ref instance.ReaderCount); + } + + /// + /// Acquires the reader lock. + /// + public void AcquireReaderLock() + { + // Must take write lock for a very short time to become a reader. + + while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0) { } + + Interlocked.Increment(ref ReaderCount); + + Interlocked.Exchange(ref WriteLock, 0); + } + + /// + /// Releases the reader lock. + /// + public void ReleaseReaderLock() + { + Interlocked.Decrement(ref ReaderCount); + } + + /// + /// Upgrades to a writer lock. The reader lock is temporarily released while obtaining the writer lock. + /// + public void UpgradeToWriterLock() + { + // Prevent any more threads from entering reader. + // If the write lock is already taken, wait for it to not be taken. + + Interlocked.Decrement(ref ReaderCount); + + while (Interlocked.CompareExchange(ref WriteLock, 1, 0) != 0) { } + + // Wait for reader count to drop to 0, then take the lock again as the only reader. + + while (Interlocked.CompareExchange(ref ReaderCount, 1, 0) != 0) { } + } + + /// + /// Downgrades from a writer lock, back to a reader one. + /// + public void DowngradeFromWriterLock() + { + // Release the WriteLock. + + Interlocked.Exchange(ref WriteLock, 0); + } + } +} diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs new file mode 100644 index 00000000..e650de06 --- /dev/null +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; + +namespace Ryujinx.Common.Memory.PartialUnmaps +{ + static class PartialUnmapHelpers + { + /// + /// Calculates a byte offset of a given field within a struct. + /// + /// Struct type + /// Field type + /// Parent struct + /// Field + /// The byte offset of the given field in the given struct + public static int OffsetOf(ref T2 storage, ref T target) + { + return (int)Unsafe.ByteOffset(ref Unsafe.As(ref storage), ref target); + } + } +} diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs new file mode 100644 index 00000000..5b0bc07e --- /dev/null +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs @@ -0,0 +1,163 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Runtime.Versioning; +using System.Threading; + +using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; + +namespace Ryujinx.Common.Memory.PartialUnmaps +{ + /// + /// State for partial unmaps. Intended to be used on Windows. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public partial struct PartialUnmapState + { + public NativeReaderWriterLock PartialUnmapLock; + public int PartialUnmapsCount; + public ThreadLocalMap LocalCounts; + + public readonly static int PartialUnmapLockOffset; + public readonly static int PartialUnmapsCountOffset; + public readonly static int LocalCountsOffset; + + public readonly static IntPtr GlobalState; + + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32.dll")] + private static partial int GetCurrentThreadId(); + + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32.dll", SetLastError = true)] + private static partial IntPtr OpenThread(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId); + + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + private static partial bool CloseHandle(IntPtr hObject); + + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode); + + /// + /// Creates a global static PartialUnmapState and populates the field offsets. + /// + static unsafe PartialUnmapState() + { + PartialUnmapState instance = new PartialUnmapState(); + + PartialUnmapLockOffset = OffsetOf(ref instance, ref instance.PartialUnmapLock); + PartialUnmapsCountOffset = OffsetOf(ref instance, ref instance.PartialUnmapsCount); + LocalCountsOffset = OffsetOf(ref instance, ref instance.LocalCounts); + + int size = Unsafe.SizeOf(); + GlobalState = Marshal.AllocHGlobal(size); + Unsafe.InitBlockUnaligned((void*)GlobalState, 0, (uint)size); + } + + /// + /// Resets the global state. + /// + public static unsafe void Reset() + { + int size = Unsafe.SizeOf(); + Unsafe.InitBlockUnaligned((void*)GlobalState, 0, (uint)size); + } + + /// + /// Gets a reference to the global state. + /// + /// A reference to the global state + public static unsafe ref PartialUnmapState GetRef() + { + return ref Unsafe.AsRef((void*)GlobalState); + } + + /// + /// Checks if an access violation handler should retry execution due to a fault caused by partial unmap. + /// + /// + /// Due to Windows limitations, might need to unmap more memory than requested. + /// The additional memory that was unmapped is later remapped, however this leaves a time gap where the + /// memory might be accessed but is unmapped. Users of the API must compensate for that by catching the + /// access violation and retrying if it happened between the unmap and remap operation. + /// This method can be used to decide if retrying in such cases is necessary or not. + /// + /// This version of the function is not used, but serves as a reference for the native + /// implementation in ARMeilleure. + /// + /// True if execution should be retried, false otherwise + [SupportedOSPlatform("windows")] + public bool RetryFromAccessViolation() + { + PartialUnmapLock.AcquireReaderLock(); + + int threadID = GetCurrentThreadId(); + int threadIndex = LocalCounts.GetOrReserve(threadID, 0); + + if (threadIndex == -1) + { + // Out of thread local space... try again later. + + PartialUnmapLock.ReleaseReaderLock(); + + return true; + } + + ref int threadLocalPartialUnmapsCount = ref LocalCounts.GetValue(threadIndex); + + bool retry = threadLocalPartialUnmapsCount != PartialUnmapsCount; + if (retry) + { + threadLocalPartialUnmapsCount = PartialUnmapsCount; + } + + PartialUnmapLock.ReleaseReaderLock(); + + return retry; + } + + /// + /// Iterates and trims threads in the thread -> count map that + /// are no longer active. + /// + [SupportedOSPlatform("windows")] + public void TrimThreads() + { + const uint ExitCodeStillActive = 259; + const int ThreadQueryInformation = 0x40; + + Span ids = LocalCounts.ThreadIds.AsSpan(); + + for (int i = 0; i < ids.Length; i++) + { + int id = ids[i]; + + if (id != 0) + { + IntPtr handle = OpenThread(ThreadQueryInformation, false, (uint)id); + + if (handle == IntPtr.Zero) + { + Interlocked.CompareExchange(ref ids[i], 0, id); + } + else + { + GetExitCodeThread(handle, out uint exitCode); + + if (exitCode != ExitCodeStillActive) + { + Interlocked.CompareExchange(ref ids[i], 0, id); + } + + CloseHandle(handle); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs new file mode 100644 index 00000000..a3bd5be8 --- /dev/null +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs @@ -0,0 +1,92 @@ +using System.Runtime.InteropServices; +using System.Threading; + +using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; + +namespace Ryujinx.Common.Memory.PartialUnmaps +{ + /// + /// A simple fixed size thread safe map that can be used from native code. + /// Integer thread IDs map to corresponding structs. + /// + /// The value type for the map + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ThreadLocalMap where T : unmanaged + { + public const int MapSize = 20; + + public Array20 ThreadIds; + public Array20 Structs; + + public static int ThreadIdsOffset; + public static int StructsOffset; + + /// + /// Populates the field offsets for use when emitting native code. + /// + static ThreadLocalMap() + { + ThreadLocalMap instance = new ThreadLocalMap(); + + ThreadIdsOffset = OffsetOf(ref instance, ref instance.ThreadIds); + StructsOffset = OffsetOf(ref instance, ref instance.Structs); + } + + /// + /// Gets the index of a given thread ID in the map, or reserves one. + /// When reserving a struct, its value is set to the given initial value. + /// Returns -1 when there is no space to reserve a new entry. + /// + /// Thread ID to use as a key + /// Initial value of the associated struct. + /// The index of the entry, or -1 if none + public int GetOrReserve(int threadId, T initial) + { + // Try get a match first. + + for (int i = 0; i < MapSize; i++) + { + int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, threadId); + + if (compare == threadId) + { + return i; + } + } + + // Try get a free entry. Since the id is assumed to be unique to this thread, we know it doesn't exist yet. + + for (int i = 0; i < MapSize; i++) + { + int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, 0); + + if (compare == 0) + { + Structs[i] = initial; + return i; + } + } + + return -1; + } + + /// + /// Gets the struct value for a given map entry. + /// + /// Index of the entry + /// A reference to the struct value + public ref T GetValue(int index) + { + return ref Structs[index]; + } + + /// + /// Releases an entry from the map. + /// + /// Index of the entry to release + public void Release(int index) + { + Interlocked.Exchange(ref ThreadIds[index], 0); + } + } +} diff --git a/src/Ryujinx.Common/Memory/Ptr.cs b/src/Ryujinx.Common/Memory/Ptr.cs new file mode 100644 index 00000000..66bcf569 --- /dev/null +++ b/src/Ryujinx.Common/Memory/Ptr.cs @@ -0,0 +1,68 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Common.Memory +{ + /// + /// Represents a pointer to an unmanaged resource. + /// + /// Type of the unmanaged resource + public unsafe struct Ptr : IEquatable> where T : unmanaged + { + private IntPtr _ptr; + + /// + /// Null pointer. + /// + public static Ptr Null => new Ptr() { _ptr = IntPtr.Zero }; + + /// + /// True if the pointer is null, false otherwise. + /// + public bool IsNull => _ptr == IntPtr.Zero; + + /// + /// Gets a reference to the value. + /// + public ref T Value => ref Unsafe.AsRef((void*)_ptr); + + /// + /// Creates a new pointer to an unmanaged resource. + /// + /// + /// For data on the heap, proper pinning is necessary during + /// use. Failure to do so will result in memory corruption and crashes. + /// + /// Reference to the unmanaged resource + public Ptr(ref T value) + { + _ptr = (IntPtr)Unsafe.AsPointer(ref value); + } + + public override bool Equals(object obj) + { + return obj is Ptr other && Equals(other); + } + + public bool Equals([AllowNull] Ptr other) + { + return _ptr == other._ptr; + } + + public override int GetHashCode() + { + return _ptr.GetHashCode(); + } + + public static bool operator ==(Ptr left, Ptr right) + { + return left.Equals(right); + } + + public static bool operator !=(Ptr left, Ptr right) + { + return !(left == right); + } + } +} diff --git a/src/Ryujinx.Common/Memory/SpanOrArray.cs b/src/Ryujinx.Common/Memory/SpanOrArray.cs new file mode 100644 index 00000000..a9798d27 --- /dev/null +++ b/src/Ryujinx.Common/Memory/SpanOrArray.cs @@ -0,0 +1,89 @@ +using System; + +namespace Ryujinx.Common.Memory +{ + /// + /// A struct that can represent both a Span and Array. + /// This is useful to keep the Array representation when possible to avoid copies. + /// + /// Element Type + public readonly ref struct SpanOrArray where T : unmanaged + { + public readonly T[] Array; + public readonly ReadOnlySpan Span; + + /// + /// Create a new SpanOrArray from an array. + /// + /// Array to store + public SpanOrArray(T[] array) + { + Array = array; + Span = ReadOnlySpan.Empty; + } + + /// + /// Create a new SpanOrArray from a readonly span. + /// + /// Span to store + public SpanOrArray(ReadOnlySpan span) + { + Array = null; + Span = span; + } + + /// + /// Return the contained array, or convert the span if necessary. + /// + /// An array containing the data + public T[] ToArray() + { + return Array ?? Span.ToArray(); + } + + /// + /// Return a ReadOnlySpan from either the array or ReadOnlySpan. + /// + /// A ReadOnlySpan containing the data + public ReadOnlySpan AsSpan() + { + return Array ?? Span; + } + + /// + /// Cast an array to a SpanOrArray. + /// + /// Source array + public static implicit operator SpanOrArray(T[] array) + { + return new SpanOrArray(array); + } + + /// + /// Cast a ReadOnlySpan to a SpanOrArray. + /// + /// Source ReadOnlySpan + public static implicit operator SpanOrArray(ReadOnlySpan span) + { + return new SpanOrArray(span); + } + + /// + /// Cast a Span to a SpanOrArray. + /// + /// Source Span + public static implicit operator SpanOrArray(Span span) + { + return new SpanOrArray(span); + } + + /// + /// Cast a SpanOrArray to a ReadOnlySpan + /// + /// Source SpanOrArray + public static implicit operator ReadOnlySpan(SpanOrArray spanOrArray) + { + return spanOrArray.AsSpan(); + } + } +} diff --git a/src/Ryujinx.Common/Memory/SpanReader.cs b/src/Ryujinx.Common/Memory/SpanReader.cs new file mode 100644 index 00000000..673932d0 --- /dev/null +++ b/src/Ryujinx.Common/Memory/SpanReader.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Memory +{ + public ref struct SpanReader + { + private ReadOnlySpan _input; + + public int Length => _input.Length; + + public SpanReader(ReadOnlySpan input) + { + _input = input; + } + + public T Read() where T : unmanaged + { + T value = MemoryMarshal.Cast(_input)[0]; + + _input = _input.Slice(Unsafe.SizeOf()); + + return value; + } + + public ReadOnlySpan GetSpan(int size) + { + ReadOnlySpan data = _input.Slice(0, size); + + _input = _input.Slice(size); + + return data; + } + + public ReadOnlySpan GetSpanSafe(int size) + { + return GetSpan((int)Math.Min((uint)_input.Length, (uint)size)); + } + + public T ReadAt(int offset) where T : unmanaged + { + return MemoryMarshal.Cast(_input.Slice(offset))[0]; + } + + public ReadOnlySpan GetSpanAt(int offset, int size) + { + return _input.Slice(offset, size); + } + + public void Skip(int size) + { + _input = _input.Slice(size); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Common/Memory/SpanWriter.cs b/src/Ryujinx.Common/Memory/SpanWriter.cs new file mode 100644 index 00000000..5c35569d --- /dev/null +++ b/src/Ryujinx.Common/Memory/SpanWriter.cs @@ -0,0 +1,45 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Memory +{ + public ref struct SpanWriter + { + private Span _output; + + public int Length => _output.Length; + + public SpanWriter(Span output) + { + _output = output; + } + + public void Write(T value) where T : unmanaged + { + MemoryMarshal.Cast(_output)[0] = value; + _output = _output.Slice(Unsafe.SizeOf()); + } + + public void Write(ReadOnlySpan data) + { + data.CopyTo(_output.Slice(0, data.Length)); + _output = _output.Slice(data.Length); + } + + public void WriteAt(int offset, T value) where T : unmanaged + { + MemoryMarshal.Cast(_output.Slice(offset))[0] = value; + } + + public void WriteAt(int offset, ReadOnlySpan data) + { + data.CopyTo(_output.Slice(offset, data.Length)); + } + + public void Skip(int size) + { + _output = _output.Slice(size); + } + } +} diff --git a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs new file mode 100644 index 00000000..a039d04e --- /dev/null +++ b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs @@ -0,0 +1,654 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Memory +{ + public struct Array1 : IArray where T : unmanaged + { + T _e0; + public int Length => 1; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 1); + } + public struct Array2 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array1 _other; +#pragma warning restore CS0169 + public int Length => 2; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 2); + } + public struct Array3 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array2 _other; +#pragma warning restore CS0169 + public int Length => 3; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 3); + } + public struct Array4 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array3 _other; +#pragma warning restore CS0169 + public int Length => 4; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 4); + } + public struct Array5 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array4 _other; +#pragma warning restore CS0169 + public int Length => 5; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 5); + } + public struct Array6 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array5 _other; +#pragma warning restore CS0169 + public int Length => 6; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 6); + } + public struct Array7 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array6 _other; +#pragma warning restore CS0169 + public int Length => 7; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 7); + } + public struct Array8 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array7 _other; +#pragma warning restore CS0169 + public int Length => 8; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 8); + } + public struct Array9 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array8 _other; +#pragma warning restore CS0169 + public int Length => 9; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 9); + } + public struct Array10 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array9 _other; +#pragma warning restore CS0169 + public int Length => 10; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 10); + } + public struct Array11 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array10 _other; +#pragma warning restore CS0169 + public int Length => 11; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 11); + } + public struct Array12 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array11 _other; +#pragma warning restore CS0169 + public int Length => 12; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 12); + } + public struct Array13 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array12 _other; +#pragma warning restore CS0169 + public int Length => 13; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 13); + } + public struct Array14 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array13 _other; +#pragma warning restore CS0169 + public int Length => 14; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 14); + } + public struct Array15 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array14 _other; +#pragma warning restore CS0169 + public int Length => 15; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 15); + } + public struct Array16 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array15 _other; +#pragma warning restore CS0169 + public int Length => 16; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 16); + } + public struct Array17 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array16 _other; +#pragma warning restore CS0169 + public int Length => 17; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 17); + } + public struct Array18 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array17 _other; +#pragma warning restore CS0169 + public int Length => 18; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 18); + } + public struct Array19 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array18 _other; +#pragma warning restore CS0169 + public int Length => 19; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 19); + } + public struct Array20 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array19 _other; +#pragma warning restore CS0169 + public int Length => 20; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 20); + } + public struct Array21 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array20 _other; +#pragma warning restore CS0169 + public int Length => 21; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 21); + } + public struct Array22 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array21 _other; +#pragma warning restore CS0169 + public int Length => 22; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 22); + } + public struct Array23 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array22 _other; +#pragma warning restore CS0169 + public int Length => 23; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 23); + } + public struct Array24 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array23 _other; +#pragma warning restore CS0169 + public int Length => 24; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 24); + } + public struct Array25 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array24 _other; +#pragma warning restore CS0169 + public int Length => 25; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 25); + } + public struct Array26 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array25 _other; +#pragma warning restore CS0169 + public int Length => 26; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 26); + } + public struct Array27 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array26 _other; +#pragma warning restore CS0169 + public int Length => 27; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 27); + } + public struct Array28 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array27 _other; +#pragma warning restore CS0169 + public int Length => 28; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 28); + } + public struct Array29 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array28 _other; +#pragma warning restore CS0169 + public int Length => 29; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 29); + } + public struct Array30 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array29 _other; +#pragma warning restore CS0169 + public int Length => 30; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 30); + } + public struct Array31 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array30 _other; +#pragma warning restore CS0169 + public int Length => 31; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 31); + } + public struct Array32 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array31 _other; +#pragma warning restore CS0169 + public int Length => 32; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 32); + } + public struct Array33 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array32 _other; +#pragma warning restore CS0169 + public int Length => 33; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 33); + } + public struct Array34 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array33 _other; +#pragma warning restore CS0169 + public int Length => 34; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 34); + } + public struct Array35 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array34 _other; +#pragma warning restore CS0169 + public int Length => 35; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 35); + } + public struct Array36 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array35 _other; +#pragma warning restore CS0169 + public int Length => 36; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 36); + } + public struct Array37 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array36 _other; +#pragma warning restore CS0169 + public int Length => 37; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 37); + } + public struct Array38 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array37 _other; +#pragma warning restore CS0169 + public int Length => 38; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 38); + } + public struct Array39 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array38 _other; +#pragma warning restore CS0169 + public int Length => 39; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 39); + } + public struct Array40 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array39 _other; +#pragma warning restore CS0169 + public int Length => 40; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 40); + } + public struct Array41 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array40 _other; +#pragma warning restore CS0169 + public int Length => 41; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 41); + } + public struct Array42 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array41 _other; +#pragma warning restore CS0169 + public int Length => 42; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 42); + } + public struct Array43 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array42 _other; +#pragma warning restore CS0169 + public int Length => 43; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 43); + } + public struct Array44 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array43 _other; +#pragma warning restore CS0169 + public int Length => 44; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 44); + } + public struct Array45 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array44 _other; +#pragma warning restore CS0169 + public int Length => 45; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 45); + } + public struct Array46 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array45 _other; +#pragma warning restore CS0169 + public int Length => 46; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 46); + } + public struct Array47 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array46 _other; +#pragma warning restore CS0169 + public int Length => 47; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 47); + } + public struct Array48 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array47 _other; +#pragma warning restore CS0169 + public int Length => 48; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 48); + } + public struct Array49 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array48 _other; +#pragma warning restore CS0169 + public int Length => 49; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 49); + } + public struct Array50 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array49 _other; +#pragma warning restore CS0169 + public int Length => 50; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 50); + } + public struct Array51 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array50 _other; +#pragma warning restore CS0169 + public int Length => 51; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 51); + } + public struct Array52 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array51 _other; +#pragma warning restore CS0169 + public int Length => 52; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 52); + } + public struct Array53 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array52 _other; +#pragma warning restore CS0169 + public int Length => 53; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 53); + } + public struct Array54 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array53 _other; +#pragma warning restore CS0169 + public int Length => 54; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 54); + } + public struct Array55 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array54 _other; +#pragma warning restore CS0169 + public int Length => 55; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 55); + } + public struct Array56 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array55 _other; +#pragma warning restore CS0169 + public int Length => 56; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 56); + } + public struct Array57 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array56 _other; +#pragma warning restore CS0169 + public int Length => 57; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 57); + } + public struct Array58 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array57 _other; +#pragma warning restore CS0169 + public int Length => 58; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 58); + } + public struct Array59 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array58 _other; +#pragma warning restore CS0169 + public int Length => 59; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 59); + } + public struct Array60 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array59 _other; +#pragma warning restore CS0169 + public int Length => 60; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 60); + } + public struct Array61 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array60 _other; +#pragma warning restore CS0169 + public int Length => 61; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 61); + } + public struct Array62 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array61 _other; +#pragma warning restore CS0169 + public int Length => 62; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 62); + } + public struct Array63 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array62 _other; +#pragma warning restore CS0169 + public int Length => 63; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 63); + } + public struct Array64 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array63 _other; +#pragma warning restore CS0169 + public int Length => 64; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 64); + } + public struct Array73 : IArray where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array64 _other; + Array8 _other2; +#pragma warning restore CS0169 + public int Length => 73; + public ref T this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 73); + } +} diff --git a/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs new file mode 100644 index 00000000..8693f5b8 --- /dev/null +++ b/src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs @@ -0,0 +1,77 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Memory +{ + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray128 : IArray + { + private const int Size = 128; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray256 : IArray + { + private const int Size = 256; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray512 : IArray + { + private const int Size = 512; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray1024 : IArray + { + private const int Size = 1024; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray2048 : IArray + { + private const int Size = 2048; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray4096 : IArray + { + private const int Size = 4096; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } +} -- cgit v1.2.3