[CoreMidi] Make P/Invokes have blittable signatures. (#19724)
This turned into a rather involved exercise, because the MidiThruConnectionParamsStruct struct had several array fields that needed marshalling to work. The changes include: * Unwrap the array fields with MarshalAs attributes to be individual fields instead. * Write a number of tests to ensure the changes work. * Fix an overflow issue found by the tests for the Controls array if trying to set to an array with more than 65535 elements. * Fix an overflow issue found by the tests for the Maps array if trying to set to an array with more than 65535 elements. * Fix an issue found by the tests where we wouldn't deserialize the Maps array correctly from a byte array / NSData if the Maps array had more than 1 element. * Fix a consistency issue found in the tests where deserializing a serialized structure doesn't yield the same result. * In particular this happens with the ChannelMap property: behavior has changed a little bit where setting the ChannelMap to an array with fewer than 16 elements (including a null array) will not return the same array anymore, but instead an array with 16 elements, where the extra elements are all 0. I've also made the ChannelMap property non-nullable, since the nullability state isn't serializable into the underlying struct. Contributes towards #15684.
This commit is contained in:
Родитель
62905c695c
Коммит
27248b1ddb
|
@ -10,6 +10,7 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
|
@ -131,14 +132,148 @@ namespace CoreMidi {
|
|||
struct MidiThruConnectionParamsStruct {
|
||||
public uint Version;
|
||||
public uint NumSources;
|
||||
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public MidiThruConnectionEndpoint []? Sources;
|
||||
public uint NumDestinations;
|
||||
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public MidiThruConnectionEndpoint []? Destinations;
|
||||
|
||||
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)]
|
||||
public byte []? ChannelMap;
|
||||
MidiThruConnectionEndpoint Sources_0;
|
||||
MidiThruConnectionEndpoint Sources_1;
|
||||
MidiThruConnectionEndpoint Sources_2;
|
||||
MidiThruConnectionEndpoint Sources_3;
|
||||
MidiThruConnectionEndpoint Sources_4;
|
||||
MidiThruConnectionEndpoint Sources_5;
|
||||
MidiThruConnectionEndpoint Sources_6;
|
||||
MidiThruConnectionEndpoint Sources_7;
|
||||
public MidiThruConnectionEndpoint []? Sources {
|
||||
get {
|
||||
if (NumSources == 0)
|
||||
return null;
|
||||
|
||||
var rv = new List<MidiThruConnectionEndpoint> ();
|
||||
if (NumSources > 0)
|
||||
rv.Add (Sources_0);
|
||||
if (NumSources > 1)
|
||||
rv.Add (Sources_1);
|
||||
if (NumSources > 2)
|
||||
rv.Add (Sources_2);
|
||||
if (NumSources > 3)
|
||||
rv.Add (Sources_3);
|
||||
if (NumSources > 4)
|
||||
rv.Add (Sources_4);
|
||||
if (NumSources > 5)
|
||||
rv.Add (Sources_5);
|
||||
if (NumSources > 6)
|
||||
rv.Add (Sources_6);
|
||||
if (NumSources > 7)
|
||||
rv.Add (Sources_7);
|
||||
return rv.ToArray ();
|
||||
}
|
||||
set {
|
||||
if (value?.Length > 8)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 8 endpoints are allowed");
|
||||
Sources_0 = value?.Length > 0 ? value [0] : default (MidiThruConnectionEndpoint);
|
||||
Sources_1 = value?.Length > 1 ? value [1] : default (MidiThruConnectionEndpoint);
|
||||
Sources_2 = value?.Length > 2 ? value [2] : default (MidiThruConnectionEndpoint);
|
||||
Sources_3 = value?.Length > 3 ? value [3] : default (MidiThruConnectionEndpoint);
|
||||
Sources_4 = value?.Length > 4 ? value [4] : default (MidiThruConnectionEndpoint);
|
||||
Sources_5 = value?.Length > 5 ? value [5] : default (MidiThruConnectionEndpoint);
|
||||
Sources_6 = value?.Length > 6 ? value [6] : default (MidiThruConnectionEndpoint);
|
||||
Sources_7 = value?.Length > 7 ? value [7] : default (MidiThruConnectionEndpoint);
|
||||
NumSources = (uint) (value?.Length ?? 0);
|
||||
}
|
||||
}
|
||||
public uint NumDestinations;
|
||||
|
||||
MidiThruConnectionEndpoint Destinations_0;
|
||||
MidiThruConnectionEndpoint Destinations_1;
|
||||
MidiThruConnectionEndpoint Destinations_2;
|
||||
MidiThruConnectionEndpoint Destinations_3;
|
||||
MidiThruConnectionEndpoint Destinations_4;
|
||||
MidiThruConnectionEndpoint Destinations_5;
|
||||
MidiThruConnectionEndpoint Destinations_6;
|
||||
MidiThruConnectionEndpoint Destinations_7;
|
||||
public MidiThruConnectionEndpoint []? Destinations {
|
||||
get {
|
||||
if (NumDestinations == 0)
|
||||
return null;
|
||||
|
||||
var rv = new List<MidiThruConnectionEndpoint> ();
|
||||
if (NumDestinations > 0)
|
||||
rv.Add (Destinations_0);
|
||||
if (NumDestinations > 1)
|
||||
rv.Add (Destinations_1);
|
||||
if (NumDestinations > 2)
|
||||
rv.Add (Destinations_2);
|
||||
if (NumDestinations > 3)
|
||||
rv.Add (Destinations_3);
|
||||
if (NumDestinations > 4)
|
||||
rv.Add (Destinations_4);
|
||||
if (NumDestinations > 5)
|
||||
rv.Add (Destinations_5);
|
||||
if (NumDestinations > 6)
|
||||
rv.Add (Destinations_6);
|
||||
if (NumDestinations > 7)
|
||||
rv.Add (Destinations_7);
|
||||
return rv.ToArray ();
|
||||
}
|
||||
set {
|
||||
if (value?.Length > 8)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 8 endpoints are allowed");
|
||||
Destinations_0 = value?.Length > 0 ? value [0] : default (MidiThruConnectionEndpoint);
|
||||
Destinations_1 = value?.Length > 1 ? value [1] : default (MidiThruConnectionEndpoint);
|
||||
Destinations_2 = value?.Length > 2 ? value [2] : default (MidiThruConnectionEndpoint);
|
||||
Destinations_3 = value?.Length > 3 ? value [3] : default (MidiThruConnectionEndpoint);
|
||||
Destinations_4 = value?.Length > 4 ? value [4] : default (MidiThruConnectionEndpoint);
|
||||
Destinations_5 = value?.Length > 5 ? value [5] : default (MidiThruConnectionEndpoint);
|
||||
Destinations_6 = value?.Length > 6 ? value [6] : default (MidiThruConnectionEndpoint);
|
||||
Destinations_7 = value?.Length > 7 ? value [7] : default (MidiThruConnectionEndpoint);
|
||||
NumDestinations = (uint) (value?.Length ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
byte ChannelMap_00;
|
||||
byte ChannelMap_01;
|
||||
byte ChannelMap_02;
|
||||
byte ChannelMap_03;
|
||||
byte ChannelMap_04;
|
||||
byte ChannelMap_05;
|
||||
byte ChannelMap_06;
|
||||
byte ChannelMap_07;
|
||||
byte ChannelMap_08;
|
||||
byte ChannelMap_09;
|
||||
byte ChannelMap_10;
|
||||
byte ChannelMap_11;
|
||||
byte ChannelMap_12;
|
||||
byte ChannelMap_13;
|
||||
byte ChannelMap_14;
|
||||
byte ChannelMap_15;
|
||||
public byte [] ChannelMap {
|
||||
get {
|
||||
return new byte [] {
|
||||
ChannelMap_00, ChannelMap_01, ChannelMap_02, ChannelMap_03, ChannelMap_04, ChannelMap_05, ChannelMap_06, ChannelMap_07,
|
||||
ChannelMap_08, ChannelMap_09, ChannelMap_10, ChannelMap_11, ChannelMap_12, ChannelMap_13, ChannelMap_14, ChannelMap_15,
|
||||
};
|
||||
}
|
||||
set {
|
||||
if (value.Length > 16)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 16 channels are allowed");
|
||||
|
||||
ChannelMap_00 = value.Length > 00 ? value [00] : (byte) 0;
|
||||
ChannelMap_01 = value.Length > 01 ? value [01] : (byte) 0;
|
||||
ChannelMap_02 = value.Length > 02 ? value [02] : (byte) 0;
|
||||
ChannelMap_03 = value.Length > 03 ? value [03] : (byte) 0;
|
||||
ChannelMap_04 = value.Length > 04 ? value [04] : (byte) 0;
|
||||
ChannelMap_05 = value.Length > 05 ? value [05] : (byte) 0;
|
||||
ChannelMap_06 = value.Length > 06 ? value [06] : (byte) 0;
|
||||
ChannelMap_07 = value.Length > 07 ? value [07] : (byte) 0;
|
||||
ChannelMap_08 = value.Length > 08 ? value [08] : (byte) 0;
|
||||
ChannelMap_09 = value.Length > 09 ? value [09] : (byte) 0;
|
||||
ChannelMap_10 = value.Length > 10 ? value [10] : (byte) 0;
|
||||
ChannelMap_11 = value.Length > 11 ? value [11] : (byte) 0;
|
||||
ChannelMap_12 = value.Length > 12 ? value [12] : (byte) 0;
|
||||
ChannelMap_13 = value.Length > 13 ? value [13] : (byte) 0;
|
||||
ChannelMap_14 = value.Length > 14 ? value [14] : (byte) 0;
|
||||
ChannelMap_15 = value.Length > 15 ? value [15] : (byte) 0;
|
||||
}
|
||||
}
|
||||
|
||||
public byte LowVelocity;
|
||||
public byte HighVelocity;
|
||||
public byte LowNote;
|
||||
|
@ -154,14 +289,19 @@ namespace CoreMidi {
|
|||
public byte FilterOutMtc;
|
||||
public byte FilterOutBeatClock;
|
||||
public byte FilterOutTuneRequest;
|
||||
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
public byte [] Reserved2;
|
||||
|
||||
byte Reserved2_0;
|
||||
byte Reserved2_1;
|
||||
byte Reserved2_2;
|
||||
public byte FilterOutAllControls;
|
||||
|
||||
public ushort NumControlTransforms;
|
||||
public ushort NumMaps;
|
||||
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public ushort [] Reserved3;
|
||||
|
||||
ushort Reserved3_0;
|
||||
ushort Reserved3_1;
|
||||
ushort Reserved3_2;
|
||||
ushort Reserved3_3;
|
||||
|
||||
// FUN: structure is variably-sized. It contains numControlTransform instances of
|
||||
// MidiControlTransform, followed by numMaps instances of MidiValueMap.
|
||||
|
@ -176,39 +316,36 @@ namespace CoreMidi {
|
|||
public class MidiThruConnectionParams {
|
||||
MidiThruConnectionParamsStruct connectionParams;
|
||||
|
||||
MidiControlTransform []? controls;
|
||||
MidiValueMap []? maps;
|
||||
|
||||
[DllImport (Constants.CoreMidiLibrary)]
|
||||
extern static void MIDIThruConnectionParamsInitialize (out MidiThruConnectionParamsStruct inConnectionParams);
|
||||
unsafe extern static void MIDIThruConnectionParamsInitialize (MidiThruConnectionParamsStruct* inConnectionParams);
|
||||
|
||||
public MidiThruConnectionParams ()
|
||||
{
|
||||
// Always create a valid init point
|
||||
MIDIThruConnectionParamsInitialize (out connectionParams);
|
||||
MidiThruConnectionParamsStruct tmpStruct;
|
||||
unsafe {
|
||||
MIDIThruConnectionParamsInitialize (&tmpStruct);
|
||||
}
|
||||
connectionParams = tmpStruct;
|
||||
}
|
||||
|
||||
public MidiThruConnectionEndpoint []? Sources {
|
||||
get { return connectionParams.Sources; }
|
||||
set {
|
||||
if (value?.Length > 8)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 8 endpoints are allowed");
|
||||
connectionParams.Sources = value;
|
||||
}
|
||||
set { connectionParams.Sources = value; }
|
||||
}
|
||||
|
||||
public MidiThruConnectionEndpoint []? Destinations {
|
||||
get { return connectionParams.Destinations; }
|
||||
set {
|
||||
if (value?.Length > 8)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 8 endpoints are allowed");
|
||||
connectionParams.Destinations = value;
|
||||
}
|
||||
set { connectionParams.Destinations = value; }
|
||||
}
|
||||
|
||||
public byte []? ChannelMap {
|
||||
public byte [] ChannelMap {
|
||||
get { return connectionParams.ChannelMap; }
|
||||
set {
|
||||
if (value?.Length > 16)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 16 channels are allowed");
|
||||
connectionParams.ChannelMap = value;
|
||||
connectionParams.ChannelMap = value ?? new byte [16];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,8 +424,23 @@ namespace CoreMidi {
|
|||
set { connectionParams.FilterOutAllControls = value ? (byte) 1 : (byte) 0; }
|
||||
}
|
||||
|
||||
public MidiControlTransform []? Controls { get; set; }
|
||||
public MidiValueMap []? Maps { get; set; }
|
||||
public MidiControlTransform []? Controls {
|
||||
get => controls;
|
||||
set {
|
||||
if (value?.Length > ushort.MaxValue)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 65535 controls are allowed");
|
||||
controls = value;
|
||||
}
|
||||
}
|
||||
|
||||
public MidiValueMap []? Maps {
|
||||
get => maps;
|
||||
set {
|
||||
if (value?.Length > ushort.MaxValue)
|
||||
throw new ArgumentOutOfRangeException (nameof (value), "A maximum of 65535 maps are allowed");
|
||||
maps = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal void ReadStruct (NSData data)
|
||||
{
|
||||
|
@ -319,6 +471,7 @@ namespace CoreMidi {
|
|||
Maps [i].Value = new byte [128];
|
||||
fixed (void* arrAddr = Maps [i].Value)
|
||||
Buffer.MemoryCopy ((void*) bufferEnd, arrAddr, 128, 128);
|
||||
bufferEnd += 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,23 +479,6 @@ namespace CoreMidi {
|
|||
|
||||
internal NSData WriteStruct ()
|
||||
{
|
||||
if (Sources?.Length > 0 && connectionParams.Sources is not null) {
|
||||
connectionParams.NumSources = (uint) Sources.Length;
|
||||
for (int i = 0; i < Sources.Length; i++)
|
||||
connectionParams.Sources [i] = Sources [i];
|
||||
}
|
||||
|
||||
if (Destinations?.Length > 0 && connectionParams.Destinations is not null) {
|
||||
connectionParams.NumDestinations = (uint) Destinations.Length;
|
||||
for (int i = 0; i < Destinations.Length; i++)
|
||||
connectionParams.Destinations [i] = Destinations [i];
|
||||
}
|
||||
|
||||
if (ChannelMap?.Length > 0 && connectionParams.ChannelMap is not null) {
|
||||
for (int i = 0; i < ChannelMap.Length; i++)
|
||||
connectionParams.ChannelMap [i] = ChannelMap [i];
|
||||
}
|
||||
|
||||
connectionParams.NumControlTransforms = Controls is not null ? (ushort) Controls.Length : (ushort) 0;
|
||||
connectionParams.NumMaps = Maps is not null ? (ushort) Maps.Length : (ushort) 0;
|
||||
|
||||
|
|
|
@ -724,7 +724,6 @@ namespace Cecil.Tests {
|
|||
"System.Void CoreGraphics.CGPath::CGPathAddArc(System.IntPtr,CoreGraphics.CGAffineTransform*,System.Runtime.InteropServices.NFloat,System.Runtime.InteropServices.NFloat,System.Runtime.InteropServices.NFloat,System.Runtime.InteropServices.NFloat,System.Runtime.InteropServices.NFloat,System.Boolean)",
|
||||
"System.Void CoreGraphics.CGPDFDocument::CGPDFDocumentGetVersion(System.IntPtr,System.Int32&,System.Int32&)",
|
||||
"System.Void CoreGraphics.CGRectExtensions::CGRectDivide(CoreGraphics.CGRect,CoreGraphics.CGRect&,CoreGraphics.CGRect&,System.Runtime.InteropServices.NFloat,CoreGraphics.CGRectEdge)",
|
||||
"System.Void CoreMidi.MidiThruConnectionParams::MIDIThruConnectionParamsInitialize(CoreMidi.MidiThruConnectionParamsStruct&)",
|
||||
"System.Void CoreText.CTFontManager::CTFontManagerRegisterFontDescriptors(System.IntPtr,CoreText.CTFontManagerScope,System.Boolean,ObjCRuntime.BlockLiteral*)",
|
||||
"System.Void CoreText.CTFontManager::CTFontManagerRegisterFontsWithAssetNames(System.IntPtr,System.IntPtr,CoreText.CTFontManagerScope,System.Boolean,ObjCRuntime.BlockLiteral*)",
|
||||
"System.Void CoreText.CTFontManager::CTFontManagerRegisterFontURLs(System.IntPtr,CoreText.CTFontManagerScope,System.Boolean,ObjCRuntime.BlockLiteral*)",
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче