You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md
+37-16Lines changed: 37 additions & 16 deletions
Original file line number
Diff line number
Diff line change
@@ -24,11 +24,11 @@ We have decided to match the managed semantics of `(ReadOnly)Span<T>` to provide
24
24
25
25
As part of this design, we would also want to include some in-box marshallers that follow the design laid out in the [Struct Marshalling design doc](./StructMarshalling.md) to support some additional scenarios:
26
26
27
-
- A marshaler that marshals an empty span as a non-null pointer.
27
+
- A marshaller that marshals an empty span as a non-null pointer.
28
28
- This marshaller would only support empty spans as it cannot correctly represent non-empty spans of non-blittable types.
29
-
- A marshaler that marshals out a pointer to the native memory as a Span instead of copying the data into a managed array.
29
+
- A marshaller that marshals out a pointer to the native memory as a Span instead of copying the data into a managed array.
30
30
- This marshaller would only support blittable spans by design.
31
-
- This marshaler will require the user to manually release the memory. Since this will be an opt-in marshaler, this scenario is already advanced and that additional requirement should be understandable to users who use this marshaler.
31
+
- This marshaller will require the user to manually release the memory. Since this will be an opt-in marshaller, this scenario is already advanced and that additional requirement should be understandable to users who use this marshaller.
32
32
- Since there is no mechansim to provide a collection length, the question of how to provide the span's length in this case is still unresolved. One option would be to always provide a length 1 span and require the user to create a new span with the correct size, but that feels like a bad design.
33
33
34
34
### Pros/Cons of Design 1
@@ -40,7 +40,7 @@ Pros:
40
40
41
41
Cons:
42
42
43
-
- Defining custom marshalers for non-empty spans of non-blittable types generically is impossible since the marshalling rules of the element's type cannot be known.
43
+
- Defining custom marshallers for non-empty spans of non-blittable types generically is impossible since the marshalling rules of the element's type cannot be known.
44
44
- Custom non-default marshalling of the span element types is impossible for non-built-in types.
45
45
- Inlining the span marshalling fully into the stub increases on-disk IL size.
46
46
- This design does not enable developers to easily define custom marshalling support for their own collection types, which may be desireable.
The `CustomTypeMarshallerKind.LinearCollection` kind is applied to a generic marshaler type with the "LinearCollection marshaller shape" described below.
99
+
The `CustomTypeMarshallerKind.LinearCollection` kind is applied to a generic marshaller type with the "LinearCollection marshaller shape" described below.
100
100
101
101
#### Supporting generics
102
102
103
-
Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` and the `CustomTypeMarshallerAttribute` as long as they have the same arity as the type the attribute is applied to and generic parameters provided to the applied-to type can also be used to construct the type passed as a parameter.
103
+
Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` and the `CustomTypeMarshallerAttribute` as long as they have the same arity as the type with the attribute and generic parameters provided to the type with the attribute can also be used to construct the type passed as a parameter.
104
104
105
105
If a `CustomTypeMarshaller`-attributed type is a marshaller for a type for a pointer, an array, or a combination of pointers and arrays, the `CustomTypeMarshallerAttribute.GenericPlaceholder` type can be used in the place of the first generic parameter of the marshaller type.
A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaler types used with the `CustomTypeMarshallerKind.Value` shape, excluding the constructors.
134
+
A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaller types used with the `CustomTypeMarshallerKind.Value` shape, excluding the constructors.
@@ -118,8 +143,6 @@ public struct GenericContiguousCollectionMarshallerImpl<T, U, V,...>
118
143
publicGenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, intnativeSizeOfElement);
119
144
publicGenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, Span<byte> stackSpace, intnativeSizeOfElement); // optional
120
145
121
-
publicconstintStackBufferSize=/**/; // required if the span-based constructor is supplied.
122
-
123
146
/// <summary>
124
147
/// A span that points to the memory where the managed values of the collection are stored (in the marshalling case) or should be stored (in the unmarshalling case).
125
148
/// </summary>
@@ -180,7 +203,7 @@ To support supplying information about collection element counts, a parameterles
180
203
The `ElementIndirectionLevel` property is added to support supplying marshalling info for element types in a collection. For example, if the user is passing a `List<List<Foo>>` from managed to native code, they could provide the following attributes to specify marshalling rules for the outer and inner lists and `Foo` separately:
Multiple `MarshalUsing` attributes can only be supplied on the same parameter or return value if the `ElementIndirectionLevel` property is set to distinct values. One `MarshalUsing` attribute per parameter or return value can leave the `ElementIndirectionLevel` property unset. This attribute controls the marshalling of the collection object passed in as the parameter. The sequence of managed types for `ElementIndirectionLevel` is based on the elements of the `ManagedValues` span on the collection marshaller of the previous indirection level. For example, for the marshalling info for `ElementIndirectionLevel = 1` above, the managed type is the type of the following C# expression: `ListAsArrayMarshaller<List<Foo>>.ManagedValues[0]`.
@@ -193,13 +216,13 @@ This design could be used to provide a default marshaller for spans and arrays.
@@ -290,8 +313,6 @@ public struct GenericContiguousCollectionMarshallerImpl<T, U, V,...>
290
313
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, int nativeSizeOfElements);
291
314
public GenericContiguousCollectionMarshallerImpl(GenericCollection<T, U, V, ...> collection, Span<byte> stackSpace, int nativeSizeOfElements); // optional
292
315
293
-
public const int StackBufferSize = /* */; // required if the span-based constructor is supplied.
294
-
295
316
- public Span<TCollectionElement> ManagedValues { get; }
296
317
297
318
- public void SetUnmarshalledCollectionLength(int length);
@@ -85,16 +84,16 @@ If a `Value` property is provided, the developer may also provide a ref-returnin
85
84
A `ref` or `ref readonly` typed `Value` property is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type to pin the desired `ref` to return and then use `System.Runtime.CompilerServices.Unsafe.AsPointer` to get a pointer from the `ref` that will have already been pinned by the time the `Value` getter is called.
86
85
87
86
```csharp
88
-
[NativeMarshalling(typeof(TMarshaler))]
87
+
[NativeMarshalling(typeof(TMarshaller))]
89
88
publicstructTManaged
90
89
{
91
90
// ...
92
91
}
93
92
94
93
[CustomTypeMarshaller(typeof(TManaged))]
95
-
publicstructTMarshaler
94
+
publicstructTMarshaller
96
95
{
97
-
publicTMarshaler(TManagedmanaged) {}
96
+
publicTMarshaller(TManagedmanaged) {}
98
97
publicTManagedToManaged() {}
99
98
100
99
publicvoidFreeNative() {}
@@ -117,14 +116,14 @@ Since C# 7.3 added a feature to enable custom pinning logic for user types, we s
117
116
Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `TNative` type provides additional constructor with the following signature and sets the `BufferSize` field on the `CustomTypeMarshallerAttribute`, then it will opt in to using a caller-allocated buffer:
When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. If a stack-allocated buffer is a requirement, the `RequiresStackBuffer` field should be set to `true` and the `buffer` will be guaranteed to be allocated on the stack. Setting the `RequiresStackBuffer` field to `false` is the same as not specifying the value in the attribute. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. This may also be provided by providing a two-parameter constructor with a default value for the second parameter.
126
+
When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios.
128
127
129
128
Type authors can pass down the `buffer` pointer to native code by defining a `Value` property that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. If `RequiresStackBuffer` is not provided or set to `false`, the `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span.
0 commit comments