/
SpanMarshaller.cs
194 lines (172 loc) · 9.08 KB
/
SpanMarshaller.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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace System.Runtime.InteropServices.Marshalling
{
/// <summary>
/// Supports marshalling a <see cref="Span{T}"/> from managed value
/// to a contiguous native array of the unmanaged values of the elements.
/// </summary>
/// <typeparam name="T">The managed element type of the span.</typeparam>
/// <typeparam name="TUnmanagedElement">The unmanaged type for the elements of the span.</typeparam>
/// <remarks>
/// A <see cref="Span{T}"/> marshalled with this marshaller will match the semantics of <see cref="MemoryMarshal.GetReference{T}(Span{T})"/>.
/// In particular, this marshaller will pass a non-null value for a zero-length span if the span was constructed with a non-null value.
/// </remarks>
[CLSCompliant(false)]
[CustomMarshaller(typeof(Span<>), MarshalMode.Default, typeof(SpanMarshaller<,>))]
[CustomMarshaller(typeof(Span<>), MarshalMode.ManagedToUnmanagedIn, typeof(SpanMarshaller<,>.ManagedToUnmanagedIn))]
[ContiguousCollectionMarshaller]
public static unsafe class SpanMarshaller<T, TUnmanagedElement>
where TUnmanagedElement : unmanaged
{
/// <summary>
/// Allocates the space to store the unmanaged elements.
/// </summary>
/// <param name="managed">The managed span.</param>
/// <param name="numElements">The number of elements in the span.</param>
/// <returns>A pointer to the block of memory for the unmanaged elements.</returns>
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(Span<T> managed, out int numElements)
{
// Emulate the pinning behavior:
// If the span is over a null reference, then pass a null pointer.
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)))
{
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 span of the managed collection elements.
/// </summary>
/// <param name="managed">The managed collection.</param>
/// <returns>A span of the managed collection elements.</returns>
public static ReadOnlySpan<T> GetManagedValuesSource(Span<T> managed)
=> managed;
/// <summary>
/// Gets a span of the space where the unmanaged collection elements should be stored.
/// </summary>
/// <param name="unmanaged">The pointer to the block of memory for the unmanaged elements.</param>
/// <param name="numElements">The number of elements that will be copied into the memory block.</param>
/// <returns>A span over the unmanaged memory that can contain the specified number of elements.</returns>
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
=> new Span<TUnmanagedElement>(unmanaged, numElements);
/// <summary>
/// Allocates space to store the managed elements.
/// </summary>
/// <param name="unmanaged">The unmanaged value.</param>
/// <param name="numElements">The number of elements in the unmanaged collection.</param>
/// <returns>A span over enough memory to contain <paramref name="numElements"/> elements.</returns>
public static Span<T> AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
{
if (unmanaged is null)
return null;
return new T[numElements];
}
/// <summary>
/// Gets a span of the space where the managed collection elements should be stored.
/// </summary>
/// <param name="managed">A span over the space to store the managed elements.</param>
/// <returns>A span over the managed memory that can contain the specified number of elements.</returns>
public static Span<T> GetManagedValuesDestination(Span<T> managed)
=> managed;
/// <summary>
/// Gets a span of the native collection elements.
/// </summary>
/// <param name="unmanaged">The unmanaged value.</param>
/// <param name="numElements">The number of elements in the unmanaged collection.</param>
/// <returns>A span over the native collection elements.</returns>
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>(unmanaged, numElements);
/// <summary>
/// Frees the allocated unmanaged memory.
/// </summary>
/// <param name="unmanaged">A pointer to the allocated unmanaged memory.</param>
public static void Free(TUnmanagedElement* unmanaged)
=> Marshal.FreeCoTaskMem((IntPtr)unmanaged);
/// <summary>
/// Supports marshalling from managed into unmanaged in a call from managed code to unmanaged code.
/// </summary>
public ref struct ManagedToUnmanagedIn
{
// 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 Span<T> _managedArray;
private TUnmanagedElement* _allocatedMemory;
private Span<TUnmanagedElement> _span;
/// <summary>
/// Initializes the <see cref="SpanMarshaller{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>
/// <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(Span<T> managed, Span<TUnmanagedElement> buffer)
{
_allocatedMemory = null;
// Emulate the pinning behavior:
// If the span is over a null reference, then pass a null pointer.
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)))
{
_managedArray = null;
_span = default;
return;
}
_managedArray = managed;
if (managed.Length <= buffer.Length)
{
_span = buffer[0..managed.Length];
}
else
{
int bufferSize = checked(managed.Length * sizeof(TUnmanagedElement));
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)bufferSize);
_span = new Span<TUnmanagedElement>(_allocatedMemory, managed.Length);
}
}
/// <summary>
/// Gets a span that points to the memory where the managed values of the array are stored.
/// </summary>
/// <returns>A span over the 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>
public ref TUnmanagedElement GetPinnableReference() => ref MemoryMarshal.GetReference(_span);
/// <summary>
/// Returns the unmanaged value representing the array.
/// </summary>
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 span.
/// </summary>
/// <param name="managed">The managed span.</param>
/// <returns>A reference that can be pinned and directly passed to unmanaged code.</returns>
public static ref T GetPinnableReference(Span<T> managed)
{
return ref MemoryMarshal.GetReference(managed);
}
}
}
}