Skip to content

Commit b4dd16b

Browse files
authored
Move ArrayMarshaller and PointerArrayMarshaller to the v2 design (#71978)
1 parent a3a106f commit b4dd16b

File tree

12 files changed

+371
-516
lines changed

12 files changed

+371
-516
lines changed

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,7 @@
875875
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\AnsiStringMarshaller.cs" />
876876
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ArrayMarshaller.cs" />
877877
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\BStrStringMarshaller.cs" />
878+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ContiguousCollectionMarshallerAttribute.cs" />
878879
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomMarshallerAttribute.cs" />
879880
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerAttribute.cs" />
880881
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerDirection.cs" />

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ArrayMarshaller.cs

Lines changed: 116 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -10,147 +10,138 @@ namespace System.Runtime.InteropServices.Marshalling
1010
/// Marshaller for arrays
1111
/// </summary>
1212
/// <typeparam name="T">Array element type</typeparam>
13+
/// <typeparam name="TUnmanagedElement">The unmanaged type for the element type</typeparam>
1314
[CLSCompliant(false)]
14-
[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]),
15-
CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200,
16-
Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
17-
public unsafe ref struct ArrayMarshaller<T>
15+
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]),
16+
MarshalMode.Default,
17+
typeof(ArrayMarshaller<,>))]
18+
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]),
19+
MarshalMode.ManagedToUnmanagedIn,
20+
typeof(ArrayMarshaller<,>.ManagedToUnmanagedIn))]
21+
[ContiguousCollectionMarshaller]
22+
public static unsafe class ArrayMarshaller<T, TUnmanagedElement>
23+
where TUnmanagedElement : unmanaged
1824
{
19-
private readonly int _sizeOfNativeElement;
20-
21-
private T[]? _managedArray;
22-
private IntPtr _allocatedMemory;
23-
private Span<byte> _span;
24-
25-
/// <summary>
26-
/// Initializes a new instance of the <see cref="ArrayMarshaller{T}"/>.
27-
/// </summary>
28-
/// <param name="sizeOfNativeElement">Size of the native element in bytes.</param>
29-
public ArrayMarshaller(int sizeOfNativeElement)
30-
: this()
25+
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
3126
{
32-
_sizeOfNativeElement = sizeOfNativeElement;
33-
}
34-
35-
/// <summary>
36-
/// Initializes a new instance of the <see cref="ArrayMarshaller{T}"/>.
37-
/// </summary>
38-
/// <param name="array">Array to be marshalled.</param>
39-
/// <param name="sizeOfNativeElement">Size of the native element in bytes.</param>
40-
public ArrayMarshaller(T[]? array, int sizeOfNativeElement)
41-
: this(array, Span<byte>.Empty, sizeOfNativeElement)
42-
{ }
43-
44-
/// <summary>
45-
/// Initializes a new instance of the <see cref="ArrayMarshaller{T}"/>.
46-
/// </summary>
47-
/// <param name="array">Array to be marshalled.</param>
48-
/// <param name="buffer">Buffer that may be used for marshalling.</param>
49-
/// <param name="sizeOfNativeElement">Size of the native element in bytes.</param>
50-
/// <remarks>
51-
/// The <paramref name="buffer"/> must not be movable - that is, it should not be
52-
/// on the managed heap or it should be pinned.
53-
/// </remarks>
54-
/// <seealso cref="CustomTypeMarshallerFeatures.CallerAllocatedBuffer"/>
55-
public ArrayMarshaller(T[]? array, Span<byte> buffer, int sizeOfNativeElement)
56-
{
57-
_allocatedMemory = default;
58-
_sizeOfNativeElement = sizeOfNativeElement;
59-
if (array is null)
27+
if (managed is null)
6028
{
61-
_managedArray = null;
62-
_span = default;
63-
return;
29+
numElements = 0;
30+
return null;
6431
}
6532

66-
_managedArray = array;
33+
numElements = managed.Length;
6734

6835
// Always allocate at least one byte when the array is zero-length.
69-
int bufferSize = checked(array.Length * _sizeOfNativeElement);
70-
int spaceToAllocate = Math.Max(bufferSize, 1);
71-
if (spaceToAllocate <= buffer.Length)
72-
{
73-
_span = buffer[0..spaceToAllocate];
74-
}
75-
else
76-
{
77-
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
78-
_span = new Span<byte>((void*)_allocatedMemory, spaceToAllocate);
79-
}
36+
int spaceToAllocate = Math.Max(checked(sizeof(TUnmanagedElement) * numElements), 1);
37+
return (TUnmanagedElement*)Marshal.AllocCoTaskMem(spaceToAllocate);
8038
}
8139

82-
/// <summary>
83-
/// Gets a span that points to the memory where the managed values of the array are stored.
84-
/// </summary>
85-
/// <returns>Span over managed values of the array.</returns>
86-
/// <seealso cref="CustomTypeMarshallerDirection.In"/>
87-
public ReadOnlySpan<T> GetManagedValuesSource() => _managedArray;
88-
89-
/// <summary>
90-
/// Gets a span that points to the memory where the unmarshalled managed values of the array should be stored.
91-
/// </summary>
92-
/// <param name="length">Length of the array.</param>
93-
/// <returns>Span where managed values of the array should be stored.</returns>
94-
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
95-
public Span<T> GetManagedValuesDestination(int length) => _allocatedMemory == IntPtr.Zero ? null : _managedArray = new T[length];
96-
97-
/// <summary>
98-
/// Returns a span that points to the memory where the native values of the array are stored after the native call.
99-
/// </summary>
100-
/// <param name="length">Length of the array.</param>
101-
/// <returns>Span over the native values of the array.</returns>
102-
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
103-
public ReadOnlySpan<byte> GetNativeValuesSource(int length)
104-
{
105-
if (_allocatedMemory == IntPtr.Zero)
106-
return default;
40+
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed)
41+
=> managed;
10742

108-
int allocatedSize = checked(length * _sizeOfNativeElement);
109-
_span = new Span<byte>((void*)_allocatedMemory, allocatedSize);
110-
return _span;
111-
}
43+
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
44+
=> new Span<TUnmanagedElement>(unmanaged, numElements);
11245

113-
/// <summary>
114-
/// Returns a span that points to the memory where the native values of the array should be stored.
115-
/// </summary>
116-
/// <returns>Span where native values of the array should be stored.</returns>
117-
/// <seealso cref="CustomTypeMarshallerDirection.In"/>
118-
public Span<byte> GetNativeValuesDestination() => _span;
119-
120-
/// <summary>
121-
/// Returns a reference to the marshalled array.
122-
/// </summary>
123-
public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(_span);
124-
125-
/// <summary>
126-
/// Returns the native value representing the array.
127-
/// </summary>
128-
/// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
129-
public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());
130-
131-
/// <summary>
132-
/// Sets the native value representing the array.
133-
/// </summary>
134-
/// <param name="value">The native value.</param>
135-
/// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
136-
public void FromNativeValue(byte* value)
46+
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
13747
{
138-
_allocatedMemory = (IntPtr)value;
48+
if (unmanaged is null)
49+
return null;
50+
51+
return new T[numElements];
13952
}
14053

141-
/// <summary>
142-
/// Returns the managed array.
143-
/// </summary>
144-
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
145-
public T[]? ToManaged() => _managedArray;
146-
147-
/// <summary>
148-
/// Frees native resources.
149-
/// </summary>
150-
/// <seealso cref="CustomTypeMarshallerFeatures.UnmanagedResources"/>
151-
public void FreeNative()
54+
public static Span<T> GetManagedValuesDestination(T[]? managed)
55+
=> managed;
56+
57+
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
58+
=> new ReadOnlySpan<TUnmanagedElement>(unmanagedValue, numElements);
59+
60+
public static void Free(TUnmanagedElement* unmanaged)
61+
=> Marshal.FreeCoTaskMem((IntPtr)unmanaged);
62+
63+
public ref struct ManagedToUnmanagedIn
15264
{
153-
Marshal.FreeCoTaskMem(_allocatedMemory);
65+
// We'll keep the buffer size at a maximum of 200 bytes to avoid overflowing the stack.
66+
public static int BufferSize { get; } = 0x200 / sizeof(TUnmanagedElement);
67+
68+
private T[]? _managedArray;
69+
private TUnmanagedElement* _allocatedMemory;
70+
private Span<TUnmanagedElement> _span;
71+
72+
/// <summary>
73+
/// Initializes the <see cref="ArrayMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
74+
/// </summary>
75+
/// <param name="array">Array to be marshalled.</param>
76+
/// <param name="buffer">Buffer that may be used for marshalling.</param>
77+
/// <remarks>
78+
/// The <paramref name="buffer"/> must not be movable - that is, it should not be
79+
/// on the managed heap or it should be pinned.
80+
/// </remarks>
81+
public void FromManaged(T[]? array, Span<TUnmanagedElement> buffer)
82+
{
83+
_allocatedMemory = null;
84+
if (array is null)
85+
{
86+
_managedArray = null;
87+
_span = default;
88+
return;
89+
}
90+
91+
_managedArray = array;
92+
93+
// Always allocate at least one byte when the array is zero-length.
94+
if (array.Length <= buffer.Length)
95+
{
96+
_span = buffer[0..array.Length];
97+
}
98+
else
99+
{
100+
int bufferSize = checked(array.Length * sizeof(TUnmanagedElement));
101+
int spaceToAllocate = Math.Max(bufferSize, 1);
102+
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)spaceToAllocate);
103+
_span = new Span<TUnmanagedElement>(_allocatedMemory, array.Length);
104+
}
105+
}
106+
107+
/// <summary>
108+
/// Gets a span that points to the memory where the managed values of the array are stored.
109+
/// </summary>
110+
/// <returns>Span over managed values of the array.</returns>
111+
public ReadOnlySpan<T> GetManagedValuesSource() => _managedArray;
112+
113+
/// <summary>
114+
/// Returns a span that points to the memory where the unmanaged values of the array should be stored.
115+
/// </summary>
116+
/// <returns>Span where unmanaged values of the array should be stored.</returns>
117+
public Span<TUnmanagedElement> GetUnmanagedValuesDestination() => _span;
118+
119+
/// <summary>
120+
/// Returns a reference to the marshalled array.
121+
/// </summary>
122+
public ref TUnmanagedElement GetPinnableReference() => ref MemoryMarshal.GetReference(_span);
123+
124+
/// <summary>
125+
/// Returns the unmanaged value representing the array.
126+
/// </summary>
127+
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference());
128+
129+
/// <summary>
130+
/// Frees resources.
131+
/// </summary>
132+
public void Free()
133+
{
134+
NativeMemory.Free(_allocatedMemory);
135+
}
136+
137+
public static ref T GetPinnableReference(T[]? array)
138+
{
139+
if (array is null)
140+
{
141+
return ref Unsafe.NullRef<T>();
142+
}
143+
return ref MemoryMarshal.GetArrayDataReference(array);
144+
}
154145
}
155146
}
156147
}

0 commit comments

Comments
 (0)