diff options
author | Jeremy Koritzinsky <jekoritz@microsoft.com> | 2021-06-09 20:41:29 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-09 20:41:29 +0300 |
commit | a3d954faa056a68deb7620ca99a28514d400337e (patch) | |
tree | f60e26b3a165daf50145875479276d4d06113f9b /src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop | |
parent | 95552f4b62373528b5bba7c8821990db76beddea (diff) |
Implement collection marshaller spec (dotnet/runtimelab#1197)
Commit migrated from https://github.com/dotnet/runtimelab/commit/b3a0ead76a03a92af3b87dd5486563728fb5ecf8
Diffstat (limited to 'src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop')
2 files changed, 245 insertions, 6 deletions
diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ArrayMarshaller.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ArrayMarshaller.cs new file mode 100644 index 00000000000..370e90efb3d --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ArrayMarshaller.cs @@ -0,0 +1,217 @@ + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.GeneratedMarshalling +{ + public unsafe ref struct ArrayMarshaller<T> + { + private T[]? managedArray; + private readonly int sizeOfNativeElement; + private IntPtr allocatedMemory; + + public ArrayMarshaller(int sizeOfNativeElement) + :this() + { + this.sizeOfNativeElement = sizeOfNativeElement; + } + + public ArrayMarshaller(T[]? managed, int sizeOfNativeElement) + { + allocatedMemory = default; + this.sizeOfNativeElement = sizeOfNativeElement; + if (managed is null) + { + managedArray = null; + NativeValueStorage = default; + return; + } + managedArray = managed; + this.sizeOfNativeElement = sizeOfNativeElement; + // Always allocate at least one byte when the array is zero-length. + int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1); + allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate); + NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate); + } + + public ArrayMarshaller(T[]? managed, Span<byte> stackSpace, int sizeOfNativeElement) + { + allocatedMemory = default; + this.sizeOfNativeElement = sizeOfNativeElement; + if (managed is null) + { + managedArray = null; + NativeValueStorage = default; + return; + } + managedArray = managed; + // Always allocate at least one byte when the array is zero-length. + int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1); + if (spaceToAllocate <= stackSpace.Length) + { + NativeValueStorage = stackSpace[0..spaceToAllocate]; + } + else + { + allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate); + NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate); + } + } + + /// <summary> + /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + /// blow the stack since this is a new optimization in the code-generated interop. + /// </summary> + public const int StackBufferSize = 0x200; + + public Span<T> ManagedValues => managedArray; + + public Span<byte> NativeValueStorage { get; private set; } + + public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); + + public void SetUnmarshalledCollectionLength(int length) + { + managedArray = new T[length]; + } + + public byte* Value + { + get + { + Debug.Assert(managedArray is null || allocatedMemory != IntPtr.Zero); + return (byte*)allocatedMemory; + } + set + { + if (value == null) + { + managedArray = null; + NativeValueStorage = default; + } + else + { + allocatedMemory = (IntPtr)value; + NativeValueStorage = new Span<byte>(value, (managedArray?.Length ?? 0) * sizeOfNativeElement); + } + } + } + + public T[]? ToManaged() => managedArray; + + public void FreeNative() + { + if (allocatedMemory != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(allocatedMemory); + } + } + } + + public unsafe ref struct PtrArrayMarshaller<T> where T : unmanaged + { + private T*[]? managedArray; + private readonly int sizeOfNativeElement; + private IntPtr allocatedMemory; + + public PtrArrayMarshaller(int sizeOfNativeElement) + : this() + { + this.sizeOfNativeElement = sizeOfNativeElement; + } + + public PtrArrayMarshaller(T*[]? managed, int sizeOfNativeElement) + { + allocatedMemory = default; + this.sizeOfNativeElement = sizeOfNativeElement; + if (managed is null) + { + managedArray = null; + NativeValueStorage = default; + return; + } + managedArray = managed; + this.sizeOfNativeElement = sizeOfNativeElement; + // Always allocate at least one byte when the array is zero-length. + int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1); + allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate); + NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate); + } + + public PtrArrayMarshaller(T*[]? managed, Span<byte> stackSpace, int sizeOfNativeElement) + { + allocatedMemory = default; + this.sizeOfNativeElement = sizeOfNativeElement; + if (managed is null) + { + managedArray = null; + NativeValueStorage = default; + return; + } + managedArray = managed; + // Always allocate at least one byte when the array is zero-length. + int spaceToAllocate = Math.Max(managed.Length * sizeOfNativeElement, 1); + if (spaceToAllocate <= stackSpace.Length) + { + NativeValueStorage = stackSpace[0..spaceToAllocate]; + } + else + { + allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate); + NativeValueStorage = new Span<byte>((void*)allocatedMemory, spaceToAllocate); + } + } + + /// <summary> + /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + /// blow the stack since this is a new optimization in the code-generated interop. + /// </summary> + public const int StackBufferSize = 0x200; + + public Span<IntPtr> ManagedValues => Unsafe.As<IntPtr[]>(managedArray); + + public Span<byte> NativeValueStorage { get; private set; } + + public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); + + public void SetUnmarshalledCollectionLength(int length) + { + managedArray = new T*[length]; + } + + public byte* Value + { + get + { + Debug.Assert(managedArray is null || allocatedMemory != IntPtr.Zero); + return (byte*)allocatedMemory; + } + set + { + if (value == null) + { + managedArray = null; + NativeValueStorage = default; + } + else + { + allocatedMemory = (IntPtr)value; + NativeValueStorage = new Span<byte>(value, (managedArray?.Length ?? 0) * sizeOfNativeElement); + } + + } + } + + public T*[]? ToManaged() => managedArray; + + public void FreeNative() + { + if (allocatedMemory != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(allocatedMemory); + } + } + } +}
\ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs index 11e1b29639f..dd605b9e60b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedMarshallingAttribute.cs @@ -2,17 +2,17 @@ namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] - class GeneratedMarshallingAttribute : Attribute + sealed class GeneratedMarshallingAttribute : Attribute { } [AttributeUsage(AttributeTargets.Struct)] - public class BlittableTypeAttribute : Attribute + public sealed class BlittableTypeAttribute : Attribute { } [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] - public class NativeMarshallingAttribute : Attribute + public sealed class NativeMarshallingAttribute : Attribute { public NativeMarshallingAttribute(Type nativeType) { @@ -22,14 +22,36 @@ namespace System.Runtime.InteropServices public Type NativeType { get; } } - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field)] - public class MarshalUsingAttribute : Attribute + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field, AllowMultiple = true)] + public sealed class MarshalUsingAttribute : Attribute { + public MarshalUsingAttribute() + { + CountElementName = string.Empty; + } + public MarshalUsingAttribute(Type nativeType) + :this() { NativeType = nativeType; } - public Type NativeType { get; } + public Type? NativeType { get; } + + public string CountElementName { get; set; } + + public int ConstantElementCount { get; set; } + + public int ElementIndirectionLevel { get; set; } + + public const string ReturnsCountValue = "return-value"; + } + + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] + public sealed class GenericContiguousCollectionMarshallerAttribute : Attribute + { + public GenericContiguousCollectionMarshallerAttribute() + { + } } }
\ No newline at end of file |