Skip to content

Commit

Permalink
Move ArrayMarshaller and PointerArrayMarshaller to the v2 design (#71978
Browse files Browse the repository at this point in the history
)
  • Loading branch information
jkoritzinsky authored Jul 12, 2022
1 parent a3a106f commit b4dd16b
Show file tree
Hide file tree
Showing 12 changed files with 371 additions and 516 deletions.
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 numElements)
{
_allocatedMemory = (IntPtr)value;
if (unmanaged is null)
return null;

return new T[numElements];
}

/// <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

0 comments on commit b4dd16b

Please sign in to comment.