Skip to content

Commit cfe257f

Browse files
AaronRobinsonMSFTelinor-fungjkoritzinsky
authored
Add design details regarding dynamic buffers in struct marshalling (#60374)
* Update StructMarshalling.md with details Co-authored-by: Elinor Fung <[email protected]> Co-authored-by: Jeremy Koritzinsky <[email protected]>
1 parent 8990b0a commit cfe257f

File tree

1 file changed

+8
-6
lines changed

1 file changed

+8
-6
lines changed

docs/design/libraries/DllImportGenerator/StructMarshalling.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,22 +90,24 @@ public struct TMarshaler
9090

9191
Since C# 7.3 added a feature to enable custom pinning logic for user types, we should also add support for custom pinning logic. If the user provides a `GetPinnableReference` method that matches the requirements to be used in a `fixed` statement and the pointed-to type is blittable, then we will support using pinning to marshal the managed value when possible. The analyzer should issue a warning when the pointed-to type would not match the final native type, accounting for the `Value` property on the native type. Since `MarshalUsingAttribute` is applied at usage time instead of at type authoring time, we will not enable the pinning feature since the implementation of `GetPinnableReference` unless the pointed-to return type matches the native type.
9292

93-
#### Stackalloc
93+
#### Caller-allocated memory
9494

95-
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 members with the following signatures, then it will opt in to using a stack-allocated buffer:
95+
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 members with the following signatures, then it will opt in to using a caller-allocated buffer:
9696

9797
```csharp
9898
partial struct TNative
9999
{
100-
public TNative(TManaged managed, Span<byte> stackSpace) {}
100+
public TNative(TManaged managed, Span<byte> buffer) {}
101101

102-
public const int StackBufferSize = /* */;
102+
public const int BufferSize = /* */;
103+
104+
public const bool RequiresStackBuffer = /* */;
103105
}
104106
```
105107

106-
When these members are both present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `StackBufferSize` bytes when a stack-allocated buffer is usable. As a stack-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.
108+
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 omitting the field definition. 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.
107109

108-
Type authors can pass down the `stackSpace` pointer to native code by defining a `GetPinnableReference` method on the native type that returns a reference to the first element of the span.
110+
Type authors can pass down the `buffer` pointer to native code by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span. When the `RequiresStackBuffer` field is set to `true`, the type author is free to use APIs that would be dangerous in non-stack-allocated scenarios such as `MemoryMarshal.GetReference()` and `Unsafe.AsPointer()`.
109111

110112
### Usage
111113

0 commit comments

Comments
 (0)