@@ -10,147 +10,138 @@ namespace System.Runtime.InteropServices.Marshalling
10
10
/// Marshaller for arrays
11
11
/// </summary>
12
12
/// <typeparam name="T">Array element type</typeparam>
13
+ /// <typeparam name="TUnmanagedElement">The unmanaged type for the element type</typeparam>
13
14
[ 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
18
24
{
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 )
31
26
{
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 )
60
28
{
61
- _managedArray = null ;
62
- _span = default ;
63
- return ;
29
+ numElements = 0 ;
30
+ return null ;
64
31
}
65
32
66
- _managedArray = array ;
33
+ numElements = managed . Length ;
67
34
68
35
// 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 ) ;
80
38
}
81
39
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 ;
107
42
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 ) ;
112
45
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 )
137
47
{
138
- _allocatedMemory = ( IntPtr ) value ;
48
+ if ( unmanaged is null )
49
+ return null ;
50
+
51
+ return new T [ numElements ] ;
139
52
}
140
53
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
152
64
{
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
+ }
154
145
}
155
146
}
156
147
}
0 commit comments