using System; using System.Buffers.Binary; using System.Runtime.InteropServices; namespace Ryujinx.Horizon.Sdk.Hid { /// /// This is a "marker interface" to add some compile-time safety to a convention-based optimization. /// /// Any struct implementing this interface should: /// - use StructLayoutAttribute (and related attributes) to explicity control how the struct is laid out in memory. /// - ensure that the method ISampledDataStruct.GetSamplingNumberFieldOffset() correctly returns the offset, in bytes, /// to the ulong "Sampling Number" field within the struct. Most types have it as the first field, so the default offset is 0. /// /// Example: /// /// /// [StructLayout(LayoutKind.Sequential, Pack = 8)] /// struct DebugPadState : ISampledDataStruct /// { /// public ulong SamplingNumber; // 1st field, so no need to add special handling to GetSamplingNumberFieldOffset() /// // other members... /// } /// /// [StructLayout(LayoutKind.Sequential, Pack = 8)] /// struct SixAxisSensorState : ISampledDataStruct /// { /// public ulong DeltaTime; /// public ulong SamplingNumber; // Not the first field - needs special handling in GetSamplingNumberFieldOffset() /// // other members... /// } /// /// internal interface ISampledDataStruct { // No Instance Members - marker interface only public static ulong GetSamplingNumber(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct { ReadOnlySpan structSpan = MemoryMarshal.CreateReadOnlySpan(ref sampledDataStruct, 1); ReadOnlySpan byteSpan = MemoryMarshal.Cast(structSpan); int fieldOffset = GetSamplingNumberFieldOffset(ref sampledDataStruct); if (fieldOffset > 0) { byteSpan = byteSpan[fieldOffset..]; } ulong value = BinaryPrimitives.ReadUInt64LittleEndian(byteSpan); return value; } private static int GetSamplingNumberFieldOffset(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct { return sampledDataStruct switch { SixAxisSensorState _ => sizeof(ulong), _ => 0, }; } } }