Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create ReadOnlySpan marshaller for ManagedToUnmanagedOut scenarios #101095

Merged
merged 5 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace System.Runtime.InteropServices.Marshalling
/// </remarks>
[CLSCompliant(false)]
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.ManagedToUnmanagedIn, typeof(ReadOnlySpanMarshaller<,>.ManagedToUnmanagedIn))]
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.ManagedToUnmanagedOut, typeof(ReadOnlySpanMarshaller<,>.ManagedToUnmanagedOut))]
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.UnmanagedToManagedOut, typeof(ReadOnlySpanMarshaller<,>.UnmanagedToManagedOut))]
[ContiguousCollectionMarshaller]
public static unsafe class ReadOnlySpanMarshaller<T, TUnmanagedElement>
Expand Down Expand Up @@ -87,7 +88,7 @@ public ref struct ManagedToUnmanagedIn
private Span<TUnmanagedElement> _span;

/// <summary>
/// Initializes the <see cref="SpanMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
/// Initializes the <see cref="ReadOnlySpanMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
/// </summary>
/// <param name="managed">The span to be marshalled.</param>
/// <param name="buffer">The buffer that may be used for marshalling.</param>
Expand Down Expand Up @@ -166,5 +167,58 @@ public static ref T GetPinnableReference(ReadOnlySpan<T> managed)
return ref MemoryMarshal.GetReference(managed);
}
}

/// <summary>
/// Supports marshalling from unmanaged to managed in a call from managed code to unmanaged code. For example, return values and `out` parameters in P/Invoke methods.
/// </summary>
public struct ManagedToUnmanagedOut
{
private TUnmanagedElement* _unmanagedArray;
private T[]? _managedValues;

/// <summary>
/// Initializes the <see cref="ReadOnlySpanMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedOut"/> marshaller.
/// </summary>
/// <param name="unmanaged">A pointer to the array to be unmarshalled from native to managed.</param>
public void FromUnmanaged(TUnmanagedElement* unmanaged)
{
_unmanagedArray = unmanaged;
}

/// <summary>
/// Returns the managed value representing the native array.
/// </summary>
public ReadOnlySpan<T> ToManaged()
{
return new ReadOnlySpan<T>(_managedValues!);
}

/// <summary>
/// Returns a span that points to the memory where the unmanaged elements of the array are stored.
/// </summary>
/// <returns>A span over unmanaged values of the array.</returns>
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements)
{
return new ReadOnlySpan<TUnmanagedElement> ( _unmanagedArray, numElements);
}

/// <summary>
/// Returns a span that points to the memory where the managed elements of the array should be stored.
/// </summary>
/// <returns>A span where managed values of the array should be stored.</returns>
public Span<T> GetManagedValuesDestination(int numElements)
{
_managedValues = new T[numElements];
return _managedValues;
}

/// <summary>
/// Frees resources.
/// </summary>
public void Free()
{
NativeMemory.Free(_unmanagedArray);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
Expand Down Expand Up @@ -115,13 +116,24 @@ public partial class Stateful
[return: MarshalUsing(typeof(ListMarshallerStateful<,>), CountElementName = "numValues")]
public static partial List<int> CreateRange(int start, int end, out int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array")]
[return: MarshalUsing(CountElementName = nameof(numValues))]
public static partial ReadOnlySpan<int> CreateRangeReadOnlySpan(int start, int end, out int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array_out")]
public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(ListMarshallerStateful<,>), CountElementName = "numValues")] out List<int> res);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "create_range_array_out")]
public static partial void CreateRange_Out_ReadOnlySpan(int start, int end, out int numValues, [MarshalUsing(CountElementName = nameof(numValues))] out ReadOnlySpan<int> res);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
[return: MarshalUsing(typeof(ListMarshallerStateful<,>), ConstantElementCount = sizeof(long))]
public static partial List<byte> GetLongBytes(long l);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")]
[return: MarshalUsing(ConstantElementCount = sizeof(long))]
public static partial ReadOnlySpan<byte> GetLongBytesReadOnlySpan(long l);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "and_bool_struct_array")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool AndAllMembers([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<BoolStruct> pArray, int length);
Expand Down Expand Up @@ -171,7 +183,7 @@ public List<T> ToManagedFinally()
public Span<T> GetManagedValuesDestination(int length) => default;
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int length) => default;
public void FromUnmanaged(byte* value) { }
public void Free() {}
public void Free() { }
}
}
}
Expand Down Expand Up @@ -235,10 +247,11 @@ public void BlittableElementCollection_OutReturn()
{
int start = 5;
int end = 20;
IEnumerable<int> expected = Enumerable.Range(start, end - start);
int[] expected = Enumerable.Range(start, end - start).ToArray();

Assert.Equal(expected, NativeExportsNE.Collections.Stateless.CreateRange(start, end, out _));
Assert.Equal(expected, NativeExportsNE.Collections.Stateful.CreateRange(start, end, out _));
Assert.Equal(expected, NativeExportsNE.Collections.Stateful.CreateRangeReadOnlySpan(start, end, out _));

{
List<int> res;
Expand All @@ -249,6 +262,9 @@ public void BlittableElementCollection_OutReturn()
List<int> res;
NativeExportsNE.Collections.Stateful.CreateRange_Out(start, end, out _, out res);
Assert.Equal(expected, res);
ReadOnlySpan<int> res2;
NativeExportsNE.Collections.Stateful.CreateRange_Out_ReadOnlySpan(start, end, out _, out res2);
Assert.Equal(expected, res2);
jtschuster marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -267,6 +283,9 @@ public void BlittableElementCollection_OutReturn_Null()
List<int> res;
NativeExportsNE.Collections.Stateful.CreateRange_Out(1, 0, out _, out res);
Assert.Null(res);
ReadOnlySpan<int> res2;
NativeExportsNE.Collections.Stateful.CreateRange_Out_ReadOnlySpan(1, 0, out _, out res2);
Assert.True(res2.IsEmpty);
}
}

Expand All @@ -289,6 +308,7 @@ public void ConstantSizeCollection()

Assert.Equal(longVal, MemoryMarshal.Read<long>(CollectionsMarshal.AsSpan(NativeExportsNE.Collections.Stateless.GetLongBytes(longVal))));
Assert.Equal(longVal, MemoryMarshal.Read<long>(CollectionsMarshal.AsSpan(NativeExportsNE.Collections.Stateful.GetLongBytes(longVal))));
Assert.Equal(longVal, MemoryMarshal.Read<long>(NativeExportsNE.Collections.Stateful.GetLongBytesReadOnlySpan(longVal)));
}

[Theory]
Expand Down
11 changes: 11 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14130,6 +14130,7 @@ public NativeMarshallingAttribute(System.Type nativeType) { }
[System.CLSCompliantAttribute(false)]
[System.Runtime.InteropServices.Marshalling.ContiguousCollectionMarshallerAttribute]
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.ReadOnlySpan<>), System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedIn, typeof(System.Runtime.InteropServices.Marshalling.ReadOnlySpanMarshaller<,>.ManagedToUnmanagedIn))]
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.ReadOnlySpan<>), System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedOut, typeof(System.Runtime.InteropServices.Marshalling.ReadOnlySpanMarshaller<,>.ManagedToUnmanagedOut))]
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.ReadOnlySpan<>), System.Runtime.InteropServices.Marshalling.MarshalMode.UnmanagedToManagedOut, typeof(System.Runtime.InteropServices.Marshalling.ReadOnlySpanMarshaller<,>.UnmanagedToManagedOut))]
public static unsafe partial class ReadOnlySpanMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
{
Expand All @@ -14146,6 +14147,16 @@ public void FromManaged(System.ReadOnlySpan<T> managed, System.Span<TUnmanagedEl
public System.Span<TUnmanagedElement> GetUnmanagedValuesDestination() { throw null; }
public unsafe TUnmanagedElement* ToUnmanaged() { throw null; }
}
public partial struct ManagedToUnmanagedOut
{
private object _dummy;
private int _dummyPrimitive;
public void FromUnmanaged(TUnmanagedElement* unmanaged) { throw null; }
public ReadOnlySpan<T> ToManaged() { throw null; }
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements) { throw null; }
public Span<T> GetManagedValuesDestination(int numElements) { throw null; }
jtschuster marked this conversation as resolved.
Show resolved Hide resolved
public void Free() { throw null; }
}
public static partial class UnmanagedToManagedOut
{
public unsafe static TUnmanagedElement* AllocateContainerForUnmanagedElements(System.ReadOnlySpan<T> managed, out int numElements) { throw null; }
Expand Down
Loading