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

Move ArrayMarshaller and PointerArrayMarshaller to the v2 design #71978

Merged
merged 9 commits into from
Jul 12, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\AnsiStringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ArrayMarshaller.cs" />
<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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,147 +10,138 @@ namespace System.Runtime.InteropServices.Marshalling
/// Marshaller for arrays
/// </summary>
/// <typeparam name="T">Array element type</typeparam>
/// <typeparam name="TUnmanagedElement">The unmanaged type for the element type</typeparam>
[CLSCompliant(false)]
[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]),
CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200,
Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
public unsafe ref struct ArrayMarshaller<T>
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]),
MarshalMode.Default,
typeof(ArrayMarshaller<,>))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]),
MarshalMode.ManagedToUnmanagedIn,
typeof(ArrayMarshaller<,>.ManagedToUnmanagedIn))]
[ContiguousCollectionMarshaller]
public static unsafe class ArrayMarshaller<T, TUnmanagedElement>
where TUnmanagedElement : unmanaged
{
private readonly int _sizeOfNativeElement;

private T[]? _managedArray;
private IntPtr _allocatedMemory;
private Span<byte> _span;

/// <summary>
/// Initializes a new instance of the <see cref="ArrayMarshaller{T}"/>.
/// </summary>
/// <param name="sizeOfNativeElement">Size of the native element in bytes.</param>
public ArrayMarshaller(int sizeOfNativeElement)
: this()
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
{
_sizeOfNativeElement = sizeOfNativeElement;
}

/// <summary>
/// Initializes a new instance of the <see cref="ArrayMarshaller{T}"/>.
/// </summary>
/// <param name="array">Array to be marshalled.</param>
/// <param name="sizeOfNativeElement">Size of the native element in bytes.</param>
public ArrayMarshaller(T[]? array, int sizeOfNativeElement)
: this(array, Span<byte>.Empty, sizeOfNativeElement)
{ }

/// <summary>
/// Initializes a new instance of the <see cref="ArrayMarshaller{T}"/>.
/// </summary>
/// <param name="array">Array to be marshalled.</param>
/// <param name="buffer">Buffer that may be used for marshalling.</param>
/// <param name="sizeOfNativeElement">Size of the native element in bytes.</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>
/// <seealso cref="CustomTypeMarshallerFeatures.CallerAllocatedBuffer"/>
public ArrayMarshaller(T[]? array, Span<byte> buffer, int sizeOfNativeElement)
{
_allocatedMemory = default;
_sizeOfNativeElement = sizeOfNativeElement;
if (array is null)
if (managed is null)
{
_managedArray = null;
_span = default;
return;
numElements = 0;
return null;
}

_managedArray = array;
numElements = managed.Length;

// Always allocate at least one byte when the array is zero-length.
int bufferSize = checked(array.Length * _sizeOfNativeElement);
int spaceToAllocate = Math.Max(bufferSize, 1);
if (spaceToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_span = new Span<byte>((void*)_allocatedMemory, spaceToAllocate);
}
int spaceToAllocate = Math.Max(checked(sizeof(TUnmanagedElement) * numElements), 1);
return (TUnmanagedElement*)Marshal.AllocCoTaskMem(spaceToAllocate);
}

/// <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>
/// <seealso cref="CustomTypeMarshallerDirection.In"/>
public ReadOnlySpan<T> GetManagedValuesSource() => _managedArray;

/// <summary>
/// Gets a span that points to the memory where the unmarshalled managed values of the array should be stored.
/// </summary>
/// <param name="length">Length of the array.</param>
/// <returns>Span where managed values of the array should be stored.</returns>
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
public Span<T> GetManagedValuesDestination(int length) => _allocatedMemory == IntPtr.Zero ? null : _managedArray = new T[length];

/// <summary>
/// Returns a span that points to the memory where the native values of the array are stored after the native call.
/// </summary>
/// <param name="length">Length of the array.</param>
/// <returns>Span over the native values of the array.</returns>
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
public ReadOnlySpan<byte> GetNativeValuesSource(int length)
{
if (_allocatedMemory == IntPtr.Zero)
return default;
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed)
=> managed;

int allocatedSize = checked(length * _sizeOfNativeElement);
_span = new Span<byte>((void*)_allocatedMemory, allocatedSize);
return _span;
}
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
=> new Span<TUnmanagedElement>(unmanaged, numElements);

/// <summary>
/// Returns a span that points to the memory where the native values of the array should be stored.
/// </summary>
/// <returns>Span where native values of the array should be stored.</returns>
/// <seealso cref="CustomTypeMarshallerDirection.In"/>
public Span<byte> GetNativeValuesDestination() => _span;

/// <summary>
/// Returns a reference to the marshalled array.
/// </summary>
public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(_span);

/// <summary>
/// Returns the native value representing the array.
/// </summary>
/// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());

/// <summary>
/// Sets the native value representing the array.
/// </summary>
/// <param name="value">The native value.</param>
/// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
public void FromNativeValue(byte* value)
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int length)
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
{
_allocatedMemory = (IntPtr)value;
if (unmanaged is null)
return null;

return new T[length];
}

/// <summary>
/// Returns the managed array.
/// </summary>
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
public T[]? ToManaged() => _managedArray;

/// <summary>
/// Frees native resources.
/// </summary>
/// <seealso cref="CustomTypeMarshallerFeatures.UnmanagedResources"/>
public void FreeNative()
public static Span<T> GetManagedValuesDestination(T[]? managed)
=> managed;

public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>(unmanagedValue, numElements);

public static void Free(TUnmanagedElement* unmanaged)
=> Marshal.FreeCoTaskMem((IntPtr)unmanaged);

public ref struct ManagedToUnmanagedIn
{
Marshal.FreeCoTaskMem(_allocatedMemory);
// 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 T[]? _managedArray;
private TUnmanagedElement* _allocatedMemory;
private Span<TUnmanagedElement> _span;

/// <summary>
/// Initializes the <see cref="ArrayMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller.
/// </summary>
/// <param name="array">Array 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(T[]? array, Span<TUnmanagedElement> buffer)
{
_allocatedMemory = null;
if (array is null)
{
_managedArray = null;
_span = default;
return;
}

_managedArray = array;

// Always allocate at least one byte when the array is zero-length.
if (array.Length <= buffer.Length)
{
_span = buffer[0..array.Length];
}
else
{
int bufferSize = checked(array.Length * sizeof(TUnmanagedElement));
int spaceToAllocate = Math.Max(bufferSize, 1);
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)spaceToAllocate);
_span = new Span<TUnmanagedElement>(_allocatedMemory, array.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(T[]? array)
{
if (array is null)
{
return ref Unsafe.NullRef<T>();
}
return ref MemoryMarshal.GetArrayDataReference(array);
}
}
}
}
Loading