Просмотр исходного кода

Reduce usage of Marshal.PtrToStructure and Marshal.StructureToPtr (#3805)

* common: Make BinaryReaderExtensions Read & Write take unamanged types

This allows us to not rely on Marshal.PtrToStructure and Marshal.StructureToPtr for those.

* common: Make MemoryHelper Read & Write takes unamanged types

* Update Marshal.SizeOf => Unsafe.SizeOf when appropriate and start moving software applet to unmanaged types
Mary-nyan 3 лет назад
Родитель
Сommit
f4e879a1e6

+ 5 - 38
Ryujinx.Common/Extensions/BinaryReaderExtensions.cs

@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.Common
@@ -7,49 +8,15 @@ namespace Ryujinx.Common
     public static class BinaryReaderExtensions
     {
         public unsafe static T ReadStruct<T>(this BinaryReader reader)
-            where T : struct
+            where T : unmanaged
         {
-            int size = Marshal.SizeOf<T>();
-
-            byte[] data = reader.ReadBytes(size);
-
-            fixed (byte* ptr = data)
-            {
-                return Marshal.PtrToStructure<T>((IntPtr)ptr);
-            }
-        }
-
-        public unsafe static T[] ReadStructArray<T>(this BinaryReader reader, int count)
-            where T : struct
-        {
-            int size = Marshal.SizeOf<T>();
-
-            T[] result = new T[count];
-
-            for (int i = 0; i < count; i++)
-            {
-                byte[] data = reader.ReadBytes(size);
-
-                fixed (byte* ptr = data)
-                {
-                    result[i] = Marshal.PtrToStructure<T>((IntPtr)ptr);
-                }
-            }
-
-            return result;
+            return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
         }
 
         public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
-            where T : struct
+            where T : unmanaged
         {
-            long size = Marshal.SizeOf<T>();
-
-            byte[] data = new byte[size];
-
-            fixed (byte* ptr = data)
-            {
-                Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
-            }
+            ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
 
             writer.Write(data);
         }

+ 6 - 21
Ryujinx.Cpu/MemoryHelper.cs

@@ -1,6 +1,7 @@
 using Ryujinx.Memory;
 using System;
 using System.IO;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
 
@@ -23,34 +24,18 @@ namespace Ryujinx.Cpu
             }
         }
 
-        public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : struct
+        public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : unmanaged
         {
-            long size = Marshal.SizeOf<T>();
-
-            byte[] data = new byte[size];
-
-            memory.Read(position, data);
-
-            fixed (byte* ptr = data)
-            {
-                return Marshal.PtrToStructure<T>((IntPtr)ptr);
-            }
+            return MemoryMarshal.Cast<byte, T>(memory.GetSpan(position, Unsafe.SizeOf<T>()))[0];
         }
 
-        public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : struct
+        public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : unmanaged
         {
-            long size = Marshal.SizeOf<T>();
-
-            byte[] data = new byte[size];
-
-            fixed (byte* ptr = data)
-            {
-                Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
-            }
+            ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
 
             memory.Write(position, data);
 
-            return (ulong)size;
+            return (ulong)data.Length;
         }
 
         public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)

+ 11 - 12
Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs

@@ -1,4 +1,5 @@
-using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
 using Ryujinx.HLE.HOS.Services.Am.AppletAE;
@@ -9,6 +10,7 @@ using Ryujinx.Memory;
 using System;
 using System.Diagnostics;
 using System.IO;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
 
@@ -78,13 +80,13 @@ namespace Ryujinx.HLE.HOS.Applets
                 var launchParams   = _normalSession.Pop();
                 var keyboardConfig = _normalSession.Pop();
 
-                _isBackground = keyboardConfig.Length == Marshal.SizeOf<SoftwareKeyboardInitialize>();
+                _isBackground = keyboardConfig.Length == Unsafe.SizeOf<SoftwareKeyboardInitialize>();
 
                 if (_isBackground)
                 {
                     // Initialize the keyboard applet in background mode.
 
-                    _keyboardBackgroundInitialize = ReadStruct<SoftwareKeyboardInitialize>(keyboardConfig);
+                    _keyboardBackgroundInitialize = MemoryMarshal.Read<SoftwareKeyboardInitialize>(keyboardConfig);
                     _backgroundState              = InlineKeyboardState.Uninitialized;
 
                     if (_device.UiHandler == null)
@@ -342,7 +344,7 @@ namespace Ryujinx.HLE.HOS.Applets
                         else
                         {
                             int wordsCount = reader.ReadInt32();
-                            int wordSize = Marshal.SizeOf<SoftwareKeyboardUserWord>();
+                            int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>();
                             remaining = stream.Length - stream.Position;
 
                             if (wordsCount > MaxUserWords)
@@ -359,8 +361,7 @@ namespace Ryujinx.HLE.HOS.Applets
 
                                 for (int word = 0; word < wordsCount; word++)
                                 {
-                                    byte[] wordData = reader.ReadBytes(wordSize);
-                                    _keyboardBackgroundUserWords[word] = ReadStruct<SoftwareKeyboardUserWord>(wordData);
+                                    _keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>();
                                 }
                             }
                         }
@@ -369,27 +370,25 @@ namespace Ryujinx.HLE.HOS.Applets
                     case InlineKeyboardRequest.SetCustomizeDic:
                         // Read the custom dic data.
                         remaining = stream.Length - stream.Position;
-                        if (remaining != Marshal.SizeOf<SoftwareKeyboardCustomizeDic>())
+                        if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>())
                         {
                             Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes");
                         }
                         else
                         {
-                            var keyboardDicData = reader.ReadBytes((int)remaining);
-                            _keyboardBackgroundDic = ReadStruct<SoftwareKeyboardCustomizeDic>(keyboardDicData);
+                            _keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>();
                         }
                         break;
                     case InlineKeyboardRequest.SetCustomizedDictionaries:
                         // Read the custom dictionaries data.
                         remaining = stream.Length - stream.Position;
-                        if (remaining != Marshal.SizeOf<SoftwareKeyboardDictSet>())
+                        if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>())
                         {
                             Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes");
                         }
                         else
                         {
-                            var keyboardDictData = reader.ReadBytes((int)remaining);
-                            _keyboardBackgroundDictSet = ReadStruct<SoftwareKeyboardDictSet>(keyboardDictData);
+                            _keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>();
                         }
                         break;
                     case InlineKeyboardRequest.Calc:

+ 2 - 3
Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardCustomizeDic.cs

@@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
     /// <summary>
     /// A structure used by SetCustomizeDic request to software keyboard.
     /// </summary>
-    [StructLayout(LayoutKind.Sequential, Pack = 4)]
+    [StructLayout(LayoutKind.Sequential, Size = 0x70)]
     struct SoftwareKeyboardCustomizeDic
     {
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 112)]
-        public byte[] Unknown;
+        // Unknown
     }
 }

+ 3 - 3
Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardDictSet.cs

@@ -1,4 +1,5 @@
-using System.Runtime.InteropServices;
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
 {
@@ -21,8 +22,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
         /// <summary>
         /// Array of word entries in the buffer.
         /// </summary>
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
-        public ulong[] Entries;
+        public Array24<ulong> Entries;
 
         /// <summary>
         /// Number of used entries in the Entries field.

+ 2 - 3
Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUserWord.cs

@@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
     /// <summary>
     /// A structure used by SetUserWordInfo request to the software keyboard.
     /// </summary>
-    [StructLayout(LayoutKind.Sequential, Pack = 4)]
+    [StructLayout(LayoutKind.Sequential, Size = 0x64)]
     struct SoftwareKeyboardUserWord
     {
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
-        public byte[] Unknown;
+        // Unknown
     }
 }

+ 15 - 7
Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs

@@ -1,9 +1,12 @@
-using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+using System;
 using System.Runtime.InteropServices;
+using System.Text;
 
 namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
 {
-    [StructLayout(LayoutKind.Sequential, Pack = 0x8, CharSet = CharSet.Ansi)]
+    [StructLayout(LayoutKind.Sequential, Pack = 0x8)]
     struct UserPresence
     {
         public UserId         UserId;
@@ -13,15 +16,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
         [MarshalAs(UnmanagedType.I1)]
         public bool SamePresenceGroupApplication;
 
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)]
-        public char[] Unknown;
+        public Array3<byte> Unknown;
+        private AppKeyValueStorageHolder _appKeyValueStorage;
 
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xC0)]
-        public char[] AppKeyValueStorage;
+        public Span<byte> AppKeyValueStorage => MemoryMarshal.Cast<AppKeyValueStorageHolder, byte>(MemoryMarshal.CreateSpan(ref _appKeyValueStorage, AppKeyValueStorageHolder.Size));
+
+        [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = Size)]
+        private struct AppKeyValueStorageHolder
+        {
+            public const int Size = 0xC0;
+        }
 
         public override string ToString()
         {
-            return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {AppKeyValueStorage} }}";
+            return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {Encoding.ASCII.GetString(AppKeyValueStorage)} }}";
         }
     }
 }

+ 2 - 11
Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs

@@ -236,23 +236,14 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
             ulong position = context.Request.PtrBuff[0].Position;
             ulong size     = context.Request.PtrBuff[0].Size;
 
-            byte[] bufferContent = new byte[size];
-
-            context.Memory.Read(position, bufferContent);
+            ReadOnlySpan<UserPresence> userPresenceInputArray = MemoryMarshal.Cast<byte, UserPresence>(context.Memory.GetSpan(position, (int)size));
 
             if (uuid.IsNull)
             {
                 return ResultCode.InvalidArgument;
             }
 
-            int elementCount = bufferContent.Length / Marshal.SizeOf<UserPresence>();
-
-            using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(bufferContent)))
-            {
-                UserPresence[] userPresenceInputArray = bufferReader.ReadStructArray<UserPresence>(elementCount);
-
-                Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray });
-            }
+            Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray = userPresenceInputArray.ToArray() });
 
             return ResultCode.Success;
         }

+ 4 - 6
Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs

@@ -1,15 +1,13 @@
-using System.Runtime.InteropServices;
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
 {
-    [StructLayout(LayoutKind.Sequential, Pack = 0x8, Size = 0x10)]
+    [StructLayout(LayoutKind.Sequential, Size = 0x10)]
     struct NotificationInfo
     {
         public NotificationEventType Type;
-
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4)]
-        public char[] Padding;
-
+        private Array4<byte> _padding;
         public long NetworkUserIdPlaceholder;
     }
 }

+ 2 - 1
Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs

@@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
@@ -75,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
 
             for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++)
             {
-                MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Marshal.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value);
+                MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Unsafe.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value);
             }
 
             context.ResponseData.Write(filteredApplicationPlayStatistics.Count());

+ 11 - 2
Ryujinx.HLE/HOS/Services/Time/Clock/Types/ClockSnapshot.cs

@@ -1,4 +1,5 @@
 using Ryujinx.HLE.HOS.Services.Time.TimeZone;
+using System;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.HLE.HOS.Services.Time.Clock
@@ -16,14 +17,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
         public CalendarAdditionalInfo NetworkCalendarAdditionalTime;
         public SteadyClockTimePoint   SteadyClockTimePoint;
 
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24)]
-        public char[] LocationName;
+        private LocationNameStorageHolder _locationName;
+
+        public Span<byte> LocationName => MemoryMarshal.Cast<LocationNameStorageHolder, byte>(MemoryMarshal.CreateSpan(ref _locationName, LocationNameStorageHolder.Size));
 
         [MarshalAs(UnmanagedType.I1)]
         public bool   IsAutomaticCorrectionEnabled;
         public byte   Type;
         public ushort Unknown;
 
+
+        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)]
+        private struct LocationNameStorageHolder
+        {
+            public const int Size = 0x24;
+        }
+
         public static ResultCode GetCurrentTime(out long currentTime, SteadyClockTimePoint steadyClockTimePoint, SystemClockContext context)
         {
             currentTime = 0;

+ 6 - 7
Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs

@@ -8,7 +8,9 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
 using System;
 using System.Diagnostics;
 using System.IO;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Text;
 
 namespace Ryujinx.HLE.HOS.Services.Time
 {
@@ -281,7 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         {
             byte type = context.RequestData.ReadByte();
 
-            context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ClockSnapshot>());
+            context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<ClockSnapshot>());
 
             context.RequestData.BaseStream.Position += 7;
 
@@ -372,12 +374,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
                 return result;
             }
 
-            char[] tzName       = deviceLocationName.ToCharArray();
-            char[] locationName = new char[0x24];
-
-            Array.Copy(tzName, locationName, tzName.Length);
+            ReadOnlySpan<byte> tzName = Encoding.ASCII.GetBytes(deviceLocationName);
 
-            clockSnapshot.LocationName = locationName;
+            tzName.CopyTo(clockSnapshot.LocationName);
 
             result = ClockSnapshot.GetCurrentTime(out clockSnapshot.UserTime, currentTimePoint, clockSnapshot.UserContext);
 
@@ -414,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
 
         private ClockSnapshot ReadClockSnapshotFromBuffer(ServiceCtx context, IpcPtrBuffDesc ipcDesc)
         {
-            Debug.Assert(ipcDesc.Size == (ulong)Marshal.SizeOf<ClockSnapshot>());
+            Debug.Assert(ipcDesc.Size == (ulong)Unsafe.SizeOf<ClockSnapshot>());
 
             byte[] temp = new byte[ipcDesc.Size];
 

+ 3 - 2
Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs

@@ -5,6 +5,7 @@ using Ryujinx.HLE.Utilities;
 using System;
 using System.Buffers.Binary;
 using System.IO;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
 
@@ -890,14 +891,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
 
             long streamLength = reader.BaseStream.Length;
 
-            if (streamLength < Marshal.SizeOf<TzifHeader>())
+            if (streamLength < Unsafe.SizeOf<TzifHeader>())
             {
                 return false;
             }
 
             TzifHeader header = reader.ReadStruct<TzifHeader>();
 
-            streamLength -= Marshal.SizeOf<TzifHeader>();
+            streamLength -= Unsafe.SizeOf<TzifHeader>();
 
             int ttisGMTCount = Detzcode32(header.TtisGMTCount);
             int ttisSTDCount = Detzcode32(header.TtisSTDCount);

+ 7 - 11
Ryujinx.Input/Motion/CemuHook/Protocol/ControllerData.cs

@@ -1,16 +1,15 @@
-using System.Runtime.InteropServices;
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.Input.Motion.CemuHook.Protocol
 {
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
     struct ControllerDataRequest
     {
-        public MessageType    Type;
+        public MessageType Type;
         public SubscriberType SubscriberType;
-        public byte           Slot;
-
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
-        public byte[] MacAddress;
+        public byte Slot;
+        public Array6<byte> MacAddress;
     }
 
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
@@ -27,11 +26,8 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol
         public uint           DPadAnalog;
         public ulong          MainButtonsAnalog;
 
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
-        public byte[] Touch1;
-
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
-        public byte[] Touch2;
+        public Array6<byte> Touch1;
+        public Array6<byte> Touch2;
 
         public ulong MotionTimestamp;
         public float AccelerometerX;

+ 6 - 7
Ryujinx.Input/Motion/CemuHook/Protocol/ControllerInfo.cs

@@ -1,21 +1,20 @@
-using System.Runtime.InteropServices;
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.Input.Motion.CemuHook.Protocol
 {
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
     public struct ControllerInfoResponse
     {
-        public  SharedResponse Shared;
-        private byte           _zero;
+        public SharedResponse Shared;
+        private byte _zero;
     }
 
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
     public struct ControllerInfoRequest
     {
         public MessageType Type;
-        public int         PortsCount;
-
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
-        public byte[] PortIndices;
+        public int PortsCount;
+        public Array4<byte> PortIndices;
     }
 }

+ 3 - 3
Ryujinx.Input/Motion/CemuHook/Protocol/SharedResponse.cs

@@ -1,4 +1,5 @@
-using System.Runtime.InteropServices;
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.Input.Motion.CemuHook.Protocol
 {
@@ -11,8 +12,7 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol
         public DeviceModelType ModelType;
         public ConnectionType  ConnectionType;
 
-        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
-        public byte[]        MacAddress;
+        public Array6<byte> MacAddress;
         public BatteryStatus BatteryStatus;
     }