diff options
Diffstat (limited to 'src/Ryujinx.Common/Memory')
| -rw-r--r-- | src/Ryujinx.Common/Memory/ArrayPtr.cs | 123 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/Box.cs | 12 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs | 51 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/ByteMemoryPool.cs | 108 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/IArray.cs | 21 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/MemoryStreamManager.cs | 99 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/PartialUnmaps/NativeReaderWriterLock.cs | 80 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapHelpers.cs | 20 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs | 163 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs | 92 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/Ptr.cs | 68 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/SpanOrArray.cs | 89 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/SpanReader.cs | 56 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/SpanWriter.cs | 45 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/StructArrayHelpers.cs | 654 | ||||
| -rw-r--r-- | src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs | 77 |
16 files changed, 1758 insertions, 0 deletions
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 +{ + /// <summary> + /// Represents an array of unmanaged resources. + /// </summary> + /// <typeparam name="T">Array element type</typeparam> + public unsafe struct ArrayPtr<T> : IEquatable<ArrayPtr<T>>, IArray<T> where T : unmanaged + { + private IntPtr _ptr; + + /// <summary> + /// Null pointer. + /// </summary> + public static ArrayPtr<T> Null => new ArrayPtr<T>() { _ptr = IntPtr.Zero }; + + /// <summary> + /// True if the pointer is null, false otherwise. + /// </summary> + public bool IsNull => _ptr == IntPtr.Zero; + + /// <summary> + /// Number of elements on the array. + /// </summary> + public int Length { get; } + + /// <summary> + /// Gets a reference to the item at the given index. + /// </summary> + /// <remarks> + /// No bounds checks are performed, this allows negative indexing, + /// but care must be taken if the index may be out of bounds. + /// </remarks> + /// <param name="index">Index of the element</param> + /// <returns>Reference to the element at the given index</returns> + public ref T this[int index] => ref Unsafe.AsRef<T>((T*)_ptr + index); + + /// <summary> + /// Creates a new array from a given reference. + /// </summary> + /// <remarks> + /// For data on the heap, proper pinning is necessary during + /// use. Failure to do so will result in memory corruption and crashes. + /// </remarks> + /// <param name="value">Reference of the first array element</param> + /// <param name="length">Number of elements on the array</param> + public ArrayPtr(ref T value, int length) + { + _ptr = (IntPtr)Unsafe.AsPointer(ref value); + Length = length; + } + + /// <summary> + /// Creates a new array from a given pointer. + /// </summary> + /// <param name="ptr">Array base pointer</param> + /// <param name="length">Number of elements on the array</param> + public ArrayPtr(T* ptr, int length) + { + _ptr = (IntPtr)ptr; + Length = length; + } + + /// <summary> + /// Creates a new array from a given pointer. + /// </summary> + /// <param name="ptr">Array base pointer</param> + /// <param name="length">Number of elements on the array</param> + public ArrayPtr(IntPtr ptr, int length) + { + _ptr = ptr; + Length = length; + } + + /// <summary> + /// Splits the array starting at the specified position. + /// </summary> + /// <param name="start">Index where the new array should start</param> + /// <returns>New array starting at the specified position</returns> + public ArrayPtr<T> Slice(int start) => new ArrayPtr<T>(ref this[start], Length - start); + + /// <summary> + /// Gets a span from the array. + /// </summary> + /// <returns>Span of the array</returns> + public Span<T> AsSpan() => Length == 0 ? Span<T>.Empty : MemoryMarshal.CreateSpan(ref this[0], Length); + + /// <summary> + /// Gets the array base pointer. + /// </summary> + /// <returns>Base pointer</returns> + public T* ToPointer() => (T*)_ptr; + + public override bool Equals(object obj) + { + return obj is ArrayPtr<T> other && Equals(other); + } + + public bool Equals([AllowNull] ArrayPtr<T> other) + { + return _ptr == other._ptr && Length == other.Length; + } + + public override int GetHashCode() + { + return HashCode.Combine(_ptr, Length); + } + + public static bool operator ==(ArrayPtr<T> left, ArrayPtr<T> right) + { + return left.Equals(right); + } + + public static bool operator !=(ArrayPtr<T> left, ArrayPtr<T> 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<T> 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 + { + /// <summary> + /// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from + /// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/> + /// with a length of the requested size. + /// </summary> + private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte> + { + private byte[] _array; + private readonly int _length; + + public ByteMemoryPoolBuffer(int length) + { + _array = ArrayPool<byte>.Shared.Rent(length); + _length = length; + } + + /// <summary> + /// Returns a <see cref="Memory{Byte}"/> belonging to this owner. + /// </summary> + public Memory<byte> Memory + { + get + { + byte[] array = _array; + + ObjectDisposedException.ThrowIf(array is null, this); + + return new Memory<byte>(array, 0, _length); + } + } + + public void Dispose() + { + var array = Interlocked.Exchange(ref _array, null); + + if (array != null) + { + ArrayPool<byte>.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 +{ + /// <summary> + /// Provides a pool of re-usable byte array instances. + /// </summary> + public sealed partial class ByteMemoryPool + { + private static readonly ByteMemoryPool _shared = new ByteMemoryPool(); + + /// <summary> + /// Constructs a <see cref="ByteMemoryPool"/> instance. Private to force access through + /// the <see cref="ByteMemoryPool.Shared"/> instance. + /// </summary> + private ByteMemoryPool() + { + // No implementation + } + + /// <summary> + /// Retrieves a shared <see cref="ByteMemoryPool"/> instance. + /// </summary> + public static ByteMemoryPool Shared => _shared; + + /// <summary> + /// Returns the maximum buffer size supported by this pool. + /// </summary> + public int MaxBufferSize => Array.MaxLength; + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer may contain data from a prior use. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> Rent(long length) + => RentImpl(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer may contain data from a prior use. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> Rent(ulong length) + => RentImpl(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer may contain data from a prior use. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> Rent(int length) + => RentImpl(length); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer's contents are cleared (set to all 0s) before returning. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> RentCleared(long length) + => RentCleared(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer's contents are cleared (set to all 0s) before returning. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> RentCleared(ulong length) + => RentCleared(checked((int)length)); + + /// <summary> + /// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>. + /// The buffer's contents are cleared (set to all 0s) before returning. + /// </summary> + /// <param name="length">The buffer's required length in bytes</param> + /// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public IMemoryOwner<byte> 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 +{ + /// <summary> + /// Array interface. + /// </summary> + /// <typeparam name="T">Element type</typeparam> + public interface IArray<T> where T : unmanaged + { + /// <summary> + /// Used to index the array. + /// </summary> + /// <param name="index">Element index</param> + /// <returns>Element at the specified index</returns> + ref T this[int index] { get; } + + /// <summary> + /// Number of elements on the array. + /// </summary> + 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(); + + /// <summary> + /// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x + /// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use + /// and b) return them as <c>RecyclableMemoryStream</c> so we don't have to cast. + /// </summary> + public static class Shared + { + /// <summary> + /// Retrieve a new <c>MemoryStream</c> object with no tag and a default initial capacity. + /// </summary> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream() + => new RecyclableMemoryStream(_shared); + + /// <summary> + /// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream(byte[] buffer) + => GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length); + + /// <summary> + /// Retrieve a new <c>MemoryStream</c> object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream(ReadOnlySpan<byte> buffer) + => GetStream(Guid.NewGuid(), null, buffer); + + /// <summary> + /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks> + /// <param name="id">A unique identifier which can be used to trace usages of the stream</param> + /// <param name="tag">A tag which can be used to track the source of the stream</param> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan<byte> 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; + } + } + + /// <summary> + /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided + /// buffer. The provided buffer is not wrapped or used after construction. + /// </summary> + /// <remarks>The new stream's position is set to the beginning of the stream when returned</remarks> + /// <param name="id">A unique identifier which can be used to trace usages of the stream</param> + /// <param name="tag">A tag which can be used to track the source of the stream</param> + /// <param name="buffer">The byte buffer to copy data from</param> + /// <param name="offset">The offset from the start of the buffer to copy from</param> + /// <param name="count">The number of bytes to copy from the buffer</param> + /// <returns>A <c>RecyclableMemoryStream</c></returns> + 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 +{ + /// <summary> + /// A simple implementation of a ReaderWriterLock which can be used from native code. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct NativeReaderWriterLock + { + public int WriteLock; + public int ReaderCount; + + public static int WriteLockOffset; + public static int ReaderCountOffset; + + /// <summary> + /// Populates the field offsets for use when emitting native code. + /// </summary> + static NativeReaderWriterLock() + { + NativeReaderWriterLock instance = new NativeReaderWriterLock(); + + WriteLockOffset = OffsetOf(ref instance, ref instance.WriteLock); + ReaderCountOffset = OffsetOf(ref instance, ref instance.ReaderCount); + } + + /// <summary> + /// Acquires the reader lock. + /// </summary> + 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); + } + + /// <summary> + /// Releases the reader lock. + /// </summary> + public void ReleaseReaderLock() + { + Interlocked.Decrement(ref ReaderCount); + } + + /// <summary> + /// Upgrades to a writer lock. The reader lock is temporarily released while obtaining the writer lock. + /// </summary> + 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) { } + } + + /// <summary> + /// Downgrades from a writer lock, back to a reader one. + /// </summary> + 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 + { + /// <summary> + /// Calculates a byte offset of a given field within a struct. + /// </summary> + /// <typeparam name="T">Struct type</typeparam> + /// <typeparam name="T2">Field type</typeparam> + /// <param name="storage">Parent struct</param> + /// <param name="target">Field</param> + /// <returns>The byte offset of the given field in the given struct</returns> + public static int OffsetOf<T, T2>(ref T2 storage, ref T target) + { + return (int)Unsafe.ByteOffset(ref Unsafe.As<T2, T>(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 +{ + /// <summary> + /// State for partial unmaps. Intended to be used on Windows. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public partial struct PartialUnmapState + { + public NativeReaderWriterLock PartialUnmapLock; + public int PartialUnmapsCount; + public ThreadLocalMap<int> 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); + + /// <summary> + /// Creates a global static PartialUnmapState and populates the field offsets. + /// </summary> + 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<PartialUnmapState>(); + GlobalState = Marshal.AllocHGlobal(size); + Unsafe.InitBlockUnaligned((void*)GlobalState, 0, (uint)size); + } + + /// <summary> + /// Resets the global state. + /// </summary> + public static unsafe void Reset() + { + int size = Unsafe.SizeOf<PartialUnmapState>(); + Unsafe.InitBlockUnaligned((void*)GlobalState, 0, (uint)size); + } + + /// <summary> + /// Gets a reference to the global state. + /// </summary> + /// <returns>A reference to the global state</returns> + public static unsafe ref PartialUnmapState GetRef() + { + return ref Unsafe.AsRef<PartialUnmapState>((void*)GlobalState); + } + + /// <summary> + /// Checks if an access violation handler should retry execution due to a fault caused by partial unmap. + /// </summary> + /// <remarks> + /// Due to Windows limitations, <see cref="UnmapView"/> 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. + /// </remarks> + /// <returns>True if execution should be retried, false otherwise</returns> + [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; + } + + /// <summary> + /// Iterates and trims threads in the thread -> count map that + /// are no longer active. + /// </summary> + [SupportedOSPlatform("windows")] + public void TrimThreads() + { + const uint ExitCodeStillActive = 259; + const int ThreadQueryInformation = 0x40; + + Span<int> 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 +{ + /// <summary> + /// A simple fixed size thread safe map that can be used from native code. + /// Integer thread IDs map to corresponding structs. + /// </summary> + /// <typeparam name="T">The value type for the map</typeparam> + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ThreadLocalMap<T> where T : unmanaged + { + public const int MapSize = 20; + + public Array20<int> ThreadIds; + public Array20<T> Structs; + + public static int ThreadIdsOffset; + public static int StructsOffset; + + /// <summary> + /// Populates the field offsets for use when emitting native code. + /// </summary> + static ThreadLocalMap() + { + ThreadLocalMap<T> instance = new ThreadLocalMap<T>(); + + ThreadIdsOffset = OffsetOf(ref instance, ref instance.ThreadIds); + StructsOffset = OffsetOf(ref instance, ref instance.Structs); + } + + /// <summary> + /// 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. + /// </summary> + /// <param name="threadId">Thread ID to use as a key</param> + /// <param name="initial">Initial value of the associated struct.</param> + /// <returns>The index of the entry, or -1 if none</returns> + 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; + } + + /// <summary> + /// Gets the struct value for a given map entry. + /// </summary> + /// <param name="index">Index of the entry</param> + /// <returns>A reference to the struct value</returns> + public ref T GetValue(int index) + { + return ref Structs[index]; + } + + /// <summary> + /// Releases an entry from the map. + /// </summary> + /// <param name="index">Index of the entry to release</param> + 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 +{ + /// <summary> + /// Represents a pointer to an unmanaged resource. + /// </summary> + /// <typeparam name="T">Type of the unmanaged resource</typeparam> + public unsafe struct Ptr<T> : IEquatable<Ptr<T>> where T : unmanaged + { + private IntPtr _ptr; + + /// <summary> + /// Null pointer. + /// </summary> + public static Ptr<T> Null => new Ptr<T>() { _ptr = IntPtr.Zero }; + + /// <summary> + /// True if the pointer is null, false otherwise. + /// </summary> + public bool IsNull => _ptr == IntPtr.Zero; + + /// <summary> + /// Gets a reference to the value. + /// </summary> + public ref T Value => ref Unsafe.AsRef<T>((void*)_ptr); + + /// <summary> + /// Creates a new pointer to an unmanaged resource. + /// </summary> + /// <remarks> + /// For data on the heap, proper pinning is necessary during + /// use. Failure to do so will result in memory corruption and crashes. + /// </remarks> + /// <param name="value">Reference to the unmanaged resource</param> + public Ptr(ref T value) + { + _ptr = (IntPtr)Unsafe.AsPointer(ref value); + } + + public override bool Equals(object obj) + { + return obj is Ptr<T> other && Equals(other); + } + + public bool Equals([AllowNull] Ptr<T> other) + { + return _ptr == other._ptr; + } + + public override int GetHashCode() + { + return _ptr.GetHashCode(); + } + + public static bool operator ==(Ptr<T> left, Ptr<T> right) + { + return left.Equals(right); + } + + public static bool operator !=(Ptr<T> left, Ptr<T> 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 +{ + /// <summary> + /// A struct that can represent both a Span and Array. + /// This is useful to keep the Array representation when possible to avoid copies. + /// </summary> + /// <typeparam name="T">Element Type</typeparam> + public readonly ref struct SpanOrArray<T> where T : unmanaged + { + public readonly T[] Array; + public readonly ReadOnlySpan<T> Span; + + /// <summary> + /// Create a new SpanOrArray from an array. + /// </summary> + /// <param name="array">Array to store</param> + public SpanOrArray(T[] array) + { + Array = array; + Span = ReadOnlySpan<T>.Empty; + } + + /// <summary> + /// Create a new SpanOrArray from a readonly span. + /// </summary> + /// <param name="array">Span to store</param> + public SpanOrArray(ReadOnlySpan<T> span) + { + Array = null; + Span = span; + } + + /// <summary> + /// Return the contained array, or convert the span if necessary. + /// </summary> + /// <returns>An array containing the data</returns> + public T[] ToArray() + { + return Array ?? Span.ToArray(); + } + + /// <summary> + /// Return a ReadOnlySpan from either the array or ReadOnlySpan. + /// </summary> + /// <returns>A ReadOnlySpan containing the data</returns> + public ReadOnlySpan<T> AsSpan() + { + return Array ?? Span; + } + + /// <summary> + /// Cast an array to a SpanOrArray. + /// </summary> + /// <param name="array">Source array</param> + public static implicit operator SpanOrArray<T>(T[] array) + { + return new SpanOrArray<T>(array); + } + + /// <summary> + /// Cast a ReadOnlySpan to a SpanOrArray. + /// </summary> + /// <param name="span">Source ReadOnlySpan</param> + public static implicit operator SpanOrArray<T>(ReadOnlySpan<T> span) + { + return new SpanOrArray<T>(span); + } + + /// <summary> + /// Cast a Span to a SpanOrArray. + /// </summary> + /// <param name="span">Source Span</param> + public static implicit operator SpanOrArray<T>(Span<T> span) + { + return new SpanOrArray<T>(span); + } + + /// <summary> + /// Cast a SpanOrArray to a ReadOnlySpan + /// </summary> + /// <param name="spanOrArray">Source SpanOrArray</param> + public static implicit operator ReadOnlySpan<T>(SpanOrArray<T> 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<byte> _input; + + public int Length => _input.Length; + + public SpanReader(ReadOnlySpan<byte> input) + { + _input = input; + } + + public T Read<T>() where T : unmanaged + { + T value = MemoryMarshal.Cast<byte, T>(_input)[0]; + + _input = _input.Slice(Unsafe.SizeOf<T>()); + + return value; + } + + public ReadOnlySpan<byte> GetSpan(int size) + { + ReadOnlySpan<byte> data = _input.Slice(0, size); + + _input = _input.Slice(size); + + return data; + } + + public ReadOnlySpan<byte> GetSpanSafe(int size) + { + return GetSpan((int)Math.Min((uint)_input.Length, (uint)size)); + } + + public T ReadAt<T>(int offset) where T : unmanaged + { + return MemoryMarshal.Cast<byte, T>(_input.Slice(offset))[0]; + } + + public ReadOnlySpan<byte> 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<byte> _output; + + public int Length => _output.Length; + + public SpanWriter(Span<byte> output) + { + _output = output; + } + + public void Write<T>(T value) where T : unmanaged + { + MemoryMarshal.Cast<byte, T>(_output)[0] = value; + _output = _output.Slice(Unsafe.SizeOf<T>()); + } + + public void Write(ReadOnlySpan<byte> data) + { + data.CopyTo(_output.Slice(0, data.Length)); + _output = _output.Slice(data.Length); + } + + public void WriteAt<T>(int offset, T value) where T : unmanaged + { + MemoryMarshal.Cast<byte, T>(_output.Slice(offset))[0] = value; + } + + public void WriteAt(int offset, ReadOnlySpan<byte> 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<T> : IArray<T> where T : unmanaged + { + T _e0; + public int Length => 1; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 1); + } + public struct Array2<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array1<T> _other; +#pragma warning restore CS0169 + public int Length => 2; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 2); + } + public struct Array3<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array2<T> _other; +#pragma warning restore CS0169 + public int Length => 3; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 3); + } + public struct Array4<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array3<T> _other; +#pragma warning restore CS0169 + public int Length => 4; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 4); + } + public struct Array5<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array4<T> _other; +#pragma warning restore CS0169 + public int Length => 5; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 5); + } + public struct Array6<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array5<T> _other; +#pragma warning restore CS0169 + public int Length => 6; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 6); + } + public struct Array7<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array6<T> _other; +#pragma warning restore CS0169 + public int Length => 7; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 7); + } + public struct Array8<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array7<T> _other; +#pragma warning restore CS0169 + public int Length => 8; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 8); + } + public struct Array9<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array8<T> _other; +#pragma warning restore CS0169 + public int Length => 9; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 9); + } + public struct Array10<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array9<T> _other; +#pragma warning restore CS0169 + public int Length => 10; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 10); + } + public struct Array11<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array10<T> _other; +#pragma warning restore CS0169 + public int Length => 11; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 11); + } + public struct Array12<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array11<T> _other; +#pragma warning restore CS0169 + public int Length => 12; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 12); + } + public struct Array13<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array12<T> _other; +#pragma warning restore CS0169 + public int Length => 13; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 13); + } + public struct Array14<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array13<T> _other; +#pragma warning restore CS0169 + public int Length => 14; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 14); + } + public struct Array15<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array14<T> _other; +#pragma warning restore CS0169 + public int Length => 15; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 15); + } + public struct Array16<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array15<T> _other; +#pragma warning restore CS0169 + public int Length => 16; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 16); + } + public struct Array17<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array16<T> _other; +#pragma warning restore CS0169 + public int Length => 17; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 17); + } + public struct Array18<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array17<T> _other; +#pragma warning restore CS0169 + public int Length => 18; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 18); + } + public struct Array19<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array18<T> _other; +#pragma warning restore CS0169 + public int Length => 19; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 19); + } + public struct Array20<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array19<T> _other; +#pragma warning restore CS0169 + public int Length => 20; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 20); + } + public struct Array21<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array20<T> _other; +#pragma warning restore CS0169 + public int Length => 21; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 21); + } + public struct Array22<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array21<T> _other; +#pragma warning restore CS0169 + public int Length => 22; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 22); + } + public struct Array23<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array22<T> _other; +#pragma warning restore CS0169 + public int Length => 23; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 23); + } + public struct Array24<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array23<T> _other; +#pragma warning restore CS0169 + public int Length => 24; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 24); + } + public struct Array25<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array24<T> _other; +#pragma warning restore CS0169 + public int Length => 25; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 25); + } + public struct Array26<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array25<T> _other; +#pragma warning restore CS0169 + public int Length => 26; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 26); + } + public struct Array27<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array26<T> _other; +#pragma warning restore CS0169 + public int Length => 27; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 27); + } + public struct Array28<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array27<T> _other; +#pragma warning restore CS0169 + public int Length => 28; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 28); + } + public struct Array29<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array28<T> _other; +#pragma warning restore CS0169 + public int Length => 29; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 29); + } + public struct Array30<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array29<T> _other; +#pragma warning restore CS0169 + public int Length => 30; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 30); + } + public struct Array31<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array30<T> _other; +#pragma warning restore CS0169 + public int Length => 31; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 31); + } + public struct Array32<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array31<T> _other; +#pragma warning restore CS0169 + public int Length => 32; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 32); + } + public struct Array33<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array32<T> _other; +#pragma warning restore CS0169 + public int Length => 33; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 33); + } + public struct Array34<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array33<T> _other; +#pragma warning restore CS0169 + public int Length => 34; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 34); + } + public struct Array35<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array34<T> _other; +#pragma warning restore CS0169 + public int Length => 35; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 35); + } + public struct Array36<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array35<T> _other; +#pragma warning restore CS0169 + public int Length => 36; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 36); + } + public struct Array37<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array36<T> _other; +#pragma warning restore CS0169 + public int Length => 37; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 37); + } + public struct Array38<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array37<T> _other; +#pragma warning restore CS0169 + public int Length => 38; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 38); + } + public struct Array39<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array38<T> _other; +#pragma warning restore CS0169 + public int Length => 39; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 39); + } + public struct Array40<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array39<T> _other; +#pragma warning restore CS0169 + public int Length => 40; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 40); + } + public struct Array41<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array40<T> _other; +#pragma warning restore CS0169 + public int Length => 41; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 41); + } + public struct Array42<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array41<T> _other; +#pragma warning restore CS0169 + public int Length => 42; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 42); + } + public struct Array43<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array42<T> _other; +#pragma warning restore CS0169 + public int Length => 43; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 43); + } + public struct Array44<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array43<T> _other; +#pragma warning restore CS0169 + public int Length => 44; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 44); + } + public struct Array45<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array44<T> _other; +#pragma warning restore CS0169 + public int Length => 45; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 45); + } + public struct Array46<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array45<T> _other; +#pragma warning restore CS0169 + public int Length => 46; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 46); + } + public struct Array47<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array46<T> _other; +#pragma warning restore CS0169 + public int Length => 47; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 47); + } + public struct Array48<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array47<T> _other; +#pragma warning restore CS0169 + public int Length => 48; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 48); + } + public struct Array49<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array48<T> _other; +#pragma warning restore CS0169 + public int Length => 49; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 49); + } + public struct Array50<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array49<T> _other; +#pragma warning restore CS0169 + public int Length => 50; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 50); + } + public struct Array51<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array50<T> _other; +#pragma warning restore CS0169 + public int Length => 51; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 51); + } + public struct Array52<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array51<T> _other; +#pragma warning restore CS0169 + public int Length => 52; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 52); + } + public struct Array53<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array52<T> _other; +#pragma warning restore CS0169 + public int Length => 53; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 53); + } + public struct Array54<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array53<T> _other; +#pragma warning restore CS0169 + public int Length => 54; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 54); + } + public struct Array55<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array54<T> _other; +#pragma warning restore CS0169 + public int Length => 55; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 55); + } + public struct Array56<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array55<T> _other; +#pragma warning restore CS0169 + public int Length => 56; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 56); + } + public struct Array57<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array56<T> _other; +#pragma warning restore CS0169 + public int Length => 57; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 57); + } + public struct Array58<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array57<T> _other; +#pragma warning restore CS0169 + public int Length => 58; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 58); + } + public struct Array59<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array58<T> _other; +#pragma warning restore CS0169 + public int Length => 59; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 59); + } + public struct Array60<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array59<T> _other; +#pragma warning restore CS0169 + public int Length => 60; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 60); + } + public struct Array61<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array60<T> _other; +#pragma warning restore CS0169 + public int Length => 61; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 61); + } + public struct Array62<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array61<T> _other; +#pragma warning restore CS0169 + public int Length => 62; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 62); + } + public struct Array63<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array62<T> _other; +#pragma warning restore CS0169 + public int Length => 63; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 63); + } + public struct Array64<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array63<T> _other; +#pragma warning restore CS0169 + public int Length => 64; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, 64); + } + public struct Array73<T> : IArray<T> where T : unmanaged + { +#pragma warning disable CS0169 + T _e0; + Array64<T> _other; + Array8<T> _other2; +#pragma warning restore CS0169 + public int Length => 73; + public ref T this[int index] => ref AsSpan()[index]; + public Span<T> 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<byte> + { + private const int Size = 128; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray256 : IArray<byte> + { + private const int Size = 256; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray512 : IArray<byte> + { + private const int Size = 512; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray1024 : IArray<byte> + { + private const int Size = 1024; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray2048 : IArray<byte> + { + private const int Size = 2048; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } + + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)] + public struct ByteArray4096 : IArray<byte> + { + private const int Size = 4096; + + byte _element; + + public int Length => Size; + public ref byte this[int index] => ref AsSpan()[index]; + public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size); + } +} |
