Skip to content

Proposal: Interop marshaling for Span<T> #9113

@morganbr

Description

@morganbr

One of the valuable aspects of Span is it keeps code safe and largely pointer-free. However, at the interop boundary, Span isn't supported, which leads to code like:

fixed (byte* bufPtr = &buffer.DangerousGetPinnableReference())
{
    bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length));
}

This both forces the method to become unsafe and is a lot of extra work. We should add marshaling support for Span to allow it to be used end-to-end.

For the most part, existing array marshaling semantics are a good match for Spans. For example:

[DllImport("kernel32.dll")]
static extern uint GetTempPath(uint nBufferLength, Span<char>lpBuffer);

would marshal the Span's underlying pointer (pinning as necessary). Reverse interop is a bit trickier. For example, the native signature of HeapAlloc is:
LPVOID WINAPI HeapAlloc(_In_ HANDLE hHeap, _In_ DWORD dwFlags, _In_ SIZE_T dwBytes);
If we wanted a span wrapping the returned pointer, an ideal P/Invoke would look like:

static extern Span<byte> HeapAlloc(SafeHandle hHeap, uint dwFlags, UIntPtr dwBytes);

but that doesn't tell the Span its length. It may be enough to add MarshalAsAttribute with SizeParamIndex (or SizeConst):

static extern [MarshalAs(SizeParamIndex=3)] Span<byte> HeapAlloc(SafeHandle hHeap, uint dwFlags, UIntPtr dwBytes);

Open questions:

  1. Should the Span's elements be marshaled as we would for an array? For example, a bool is 1 byte in managed code, but defaults to marshaling to 4 bytes. If we apply this with Spans, it requires copying in those cases.
  2. How should InAttribute and OutAttribute apply? Should they match what we do with arrays?
  3. In reverse interop cases, the Span may hold native memory that now needs to be freed. What's a good pattern for freeing the memory and getting rid of the now-wild pointer in the Span?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions