Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the approved span marshallers and remove the v1 model #71989

Merged
merged 8 commits into from
Jul 12, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -877,14 +877,12 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\BStrStringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ContiguousCollectionMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerDirection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerFeatures.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerKind.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\MarshalMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\MarshalUsingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\NativeMarshallingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\PointerArrayMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ReadOnlySpanMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\SpanMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\Utf16StringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\Utf8StringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.Versioning;

using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
Expand All @@ -20,6 +21,9 @@ namespace System
[DebuggerTypeProxy(typeof(SpanDebugView<>))]
[DebuggerDisplay("{ToString(),raw}")]
[NonVersionable]
#pragma warning disable SYSLIB1056 // Specified native type is invalid
[NativeMarshalling(typeof(ReadOnlySpanMarshaller<,>))]
#pragma warning restore SYSLIB1056 // Specified native type is invalid
public readonly ref struct ReadOnlySpan<T>
{
/// <summary>A byref or a native ptr.</summary>
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace System.Runtime.InteropServices.Marshalling
/// This attribute is recognized by the runtime-provided source generators for source-generated interop scenarios.
/// It is not used by the interop marshalling system at runtime.
/// <seealso cref="LibraryImportAttribute"/>
/// <seealso cref="CustomTypeMarshallerAttribute" />
/// <seealso cref="CustomMarshallerAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class MarshalUsingAttribute : Attribute
Expand All @@ -26,15 +26,15 @@ public MarshalUsingAttribute()
/// <summary>
/// Create a <see cref="MarshalUsingAttribute" /> that provides a native marshalling type and optionally size information.
/// </summary>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" /></param>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" /></param>
public MarshalUsingAttribute(Type nativeType)
: this()
{
NativeType = nativeType;
}

/// <summary>
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" />
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" />
/// </summary>
public Type? NativeType { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ namespace System.Runtime.InteropServices.Marshalling
/// This attribute is recognized by the runtime-provided source generators for source-generated interop scenarios.
/// It is not used by the interop marshalling system at runtime.
/// <seealso cref="LibraryImportAttribute"/>
/// <seealso cref="CustomTypeMarshallerAttribute" />
/// <seealso cref="CustomMarshallerAttribute" />
/// </remarks>
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Delegate)]
public sealed class NativeMarshallingAttribute : Attribute
{
/// <summary>
/// Create a <see cref="NativeMarshallingAttribute" /> that provides a native marshalling type.
/// </summary>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" /></param>
/// <param name="nativeType">The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" /></param>
public NativeMarshallingAttribute(Type nativeType)
{
NativeType = nativeType;
}

/// <summary>
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomTypeMarshallerAttribute" />
/// The marshaller type used to convert the attributed type from managed to native code. This type must be attributed with <see cref="CustomMarshallerAttribute" />
/// </summary>
public Type NativeType { get; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// 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
{
[CLSCompliant(false)]
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.ManagedToUnmanagedIn, typeof(ReadOnlySpanMarshaller<,>.ManagedToUnmanagedIn))]
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.UnmanagedToManagedOut, typeof(ReadOnlySpanMarshaller<,>.UnmanagedToManagedOut))]
[ContiguousCollectionMarshaller]
public static unsafe class ReadOnlySpanMarshaller<T, TUnmanagedElement>
where TUnmanagedElement : unmanaged
{
public static unsafe class UnmanagedToManagedOut
{
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(ReadOnlySpan<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)))
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
{
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);
}

public static ReadOnlySpan<T> GetManagedValuesSource(ReadOnlySpan<T> managed)
=> managed;

public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
=> new Span<TUnmanagedElement>(unmanaged, numElements);
}

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 ReadOnlySpan<T> _managedArray;
private TUnmanagedElement* _allocatedMemory;
private Span<TUnmanagedElement> _span;

/// <summary>
/// Initializes the <see cref="SpanMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
/// </summary>
/// <param name="managed">Span to be marshalled.</param>
/// <param name="buffer">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(ReadOnlySpan<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;

// Always allocate at least one byte when the span is zero-length.
if (managed.Length <= buffer.Length)
{
_span = buffer[0..managed.Length];
}
else
{
int bufferSize = checked(managed.Length * sizeof(TUnmanagedElement));
int spaceToAllocate = Math.Max(bufferSize, 1);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)spaceToAllocate);
_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>Span over 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>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);
}

public static ref T GetPinnableReference(ReadOnlySpan<T> managed)
{
return ref MemoryMarshal.GetReference(managed);
}
}
}
}
Loading