/
ArrayMarshaller.cs
201 lines (179 loc) · 8.79 KB
/
ArrayMarshaller.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices.Marshalling
{
/// <summary>
/// Represents a marshaller for arrays.
/// </summary>
/// <typeparam name="T">The array element type.</typeparam>
/// <typeparam name="TUnmanagedElement">The unmanaged type for the element type.</typeparam>
[CLSCompliant(false)]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]),
MarshalMode.Default,
typeof(ArrayMarshaller<,>))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]),
MarshalMode.ManagedToUnmanagedIn,
typeof(ArrayMarshaller<,>.ManagedToUnmanagedIn))]
[ContiguousCollectionMarshaller]
public static unsafe class ArrayMarshaller<T, TUnmanagedElement>
where TUnmanagedElement : unmanaged
{
/// <summary>
/// Allocates memory for the unmanaged representation of the array.
/// </summary>
/// <param name="managed">The managed array.</param>
/// <param name="numElements">The unmanaged element count.</param>
/// <returns>The unmanaged pointer to the allocated memory.</returns>
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
{
if (managed is null)
{
numElements = 0;
return null;
}
numElements = managed.Length;
// Always allocate at least one byte when the array is zero-length.
int spaceToAllocate = Math.Max(checked(sizeof(TUnmanagedElement) * numElements), 1);
return (TUnmanagedElement*)Marshal.AllocCoTaskMem(spaceToAllocate);
}
/// <summary>
/// Gets a source for the managed elements in the array.
/// </summary>
/// <param name="managed">The managed array.</param>
/// <returns>The <see cref="ReadOnlySpan{T}"/> containing the managed elements to marshal.</returns>
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed)
=> managed;
/// <summary>
/// Gets a destination for the unmanaged elements in the array.
/// </summary>
/// <param name="unmanaged">The unmanaged allocation.</param>
/// <param name="numElements">The unmanaged element count.</param>
/// <returns>The <see cref="Span{TUnmanagedElement}"/> of unmanaged elements.</returns>
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
=> new Span<TUnmanagedElement>(unmanaged, numElements);
/// <summary>
/// Allocates memory for the managed representation of the array.
/// </summary>
/// <param name="unmanaged">The unmanaged array.</param>
/// <param name="numElements">The unmanaged element count.</param>
/// <returns>The managed array.</returns>
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
{
if (unmanaged is null)
return null;
return new T[numElements];
}
/// <summary>
/// Gets a destination for the managed elements in the array.
/// </summary>
/// <param name="managed">The managed array.</param>
/// <returns>The <see cref="Span{T}"/> of managed elements.</returns>
public static Span<T> GetManagedValuesDestination(T[]? managed)
=> managed;
/// <summary>
/// Gets a source for the unmanaged elements in the array.
/// </summary>
/// <param name="unmanagedValue">The unmanaged array.</param>
/// <param name="numElements">The unmanaged element count.</param>
/// <returns>The <see cref="ReadOnlySpan{TUnmanagedElement}"/> containing the unmanaged elements to marshal.</returns>
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>(unmanagedValue, numElements);
/// <summary>
/// Frees memory for the unmanaged array.
/// </summary>
/// <param name="unmanaged">The unmanaged array.</param>
public static void Free(TUnmanagedElement* unmanaged)
=> Marshal.FreeCoTaskMem((IntPtr)unmanaged);
/// <summary>
/// Marshaller for marshalling a array from managed to unmanaged.
/// </summary>
public ref struct ManagedToUnmanagedIn
{
/// <summary>
/// Gets the requested caller-allocated buffer size.
/// </summary>
/// <remarks>
/// This property represents a potential optimization for the marshaller.
/// </remarks>
// We'll keep the buffer size at a maximum of 200 bytes to avoid overflowing the stack.
public static int BufferSize { get; } = 0x200 / sizeof(TUnmanagedElement);
private T[]? _managedArray;
private TUnmanagedElement* _allocatedMemory;
private Span<TUnmanagedElement> _span;
/// <summary>
/// Initializes the <see cref="ArrayMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
/// </summary>
/// <param name="array">The array to be marshalled.</param>
/// <param name="buffer">The buffer that may be used for marshalling.</param>
/// <remarks>
/// The <paramref name="buffer"/> must not be movable - that is, it should not be
/// on the managed heap or it should be pinned.
/// </remarks>
public void FromManaged(T[]? array, Span<TUnmanagedElement> buffer)
{
_allocatedMemory = null;
if (array is null)
{
_managedArray = null;
_span = default;
return;
}
_managedArray = array;
// Always allocate at least one byte when the array is zero-length.
if (array.Length <= buffer.Length)
{
_span = buffer[0..array.Length];
}
else
{
int bufferSize = checked(array.Length * sizeof(TUnmanagedElement));
int spaceToAllocate = Math.Max(bufferSize, 1);
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)spaceToAllocate);
_span = new Span<TUnmanagedElement>(_allocatedMemory, array.Length);
}
}
/// <summary>
/// Returns a span that points to the memory where the managed values of the array are stored.
/// </summary>
/// <returns>A span over managed values of the array.</returns>
public ReadOnlySpan<T> GetManagedValuesSource() => _managedArray;
/// <summary>
/// Returns a span that points to the memory where the unmanaged values of the array should be stored.
/// </summary>
/// <returns>A span where unmanaged values of the array should be stored.</returns>
public Span<TUnmanagedElement> GetUnmanagedValuesDestination() => _span;
/// <summary>
/// Returns a reference to the marshalled array.
/// </summary>
/// <returns>A pinnable reference to the unmanaged marshalled array.</returns>
public ref TUnmanagedElement GetPinnableReference() => ref MemoryMarshal.GetReference(_span);
/// <summary>
/// Returns the unmanaged value representing the array.
/// </summary>
/// <returns>A pointer to the beginning of the unmanaged value.</returns>
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference());
/// <summary>
/// Frees resources.
/// </summary>
public void Free()
{
NativeMemory.Free(_allocatedMemory);
}
/// <summary>
/// Gets a pinnable reference to the managed array.
/// </summary>
/// <param name="array">The managed array.</param>
/// <returns>The reference that can be pinned and directly passed to unmanaged code.</returns>
public static ref T GetPinnableReference(T[]? array)
{
if (array is null)
{
return ref Unsafe.NullRef<T>();
}
return ref MemoryMarshal.GetArrayDataReference(array);
}
}
}
}