Skip to content

Commit

Permalink
Expose ArrayMarshaller and PointerArrayMarshaller (dotnet#68173)
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung authored and directhex committed Apr 21, 2022
1 parent 4d24aac commit 936ccf8
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 197 deletions.
10 changes: 0 additions & 10 deletions eng/generators.targets
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,6 @@
Include="$(CoreLibSharedDir)System\Runtime\InteropServices\LibraryImportAttribute.cs" />
<Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' != '$(TargetFrameworkMoniker)'"
Include="$(CoreLibSharedDir)System\Runtime\InteropServices\StringMarshalling.cs" />

<!-- Only add the following files if we are on the latest TFM (that is, net7) and the project is SPCL or has references to System.Runtime.CompilerServices.Unsafe and System.Memory -->
<Compile Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)'
and (
'$(MSBuildProjectName)' == 'System.Private.CoreLib'
or '$(EnableLibraryImportGenerator)' == 'true'
or ('@(Reference)' != ''
and @(Reference->AnyHaveMetadataValue('Identity', 'System.Memory')))
or ('@(ProjectReference)' != ''
and @(ProjectReference->AnyHaveMetadataValue('Identity', '$(CoreLibProject)'))))" Include="$(LibrariesProjectRoot)Common\src\System\Runtime\InteropServices\ArrayMarshaller.cs" />
</ItemGroup>

<ItemGroup>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\AllowReversePInvokeCallsAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\AnsiStringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Architecture.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ArrayMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ArrayWithOffset.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\BestFitMappingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\BStrWrapper.cs" />
Expand Down Expand Up @@ -872,6 +873,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OSPlatform.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PointerArrayMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignal.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignalContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignalRegistration.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace System.Runtime.InteropServices
{
/// <summary>
/// Marshaller for arrays
/// </summary>
/// <typeparam name="T">Array 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>
{
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()
{
_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.
/// <seealso cref="CustomTypeMarshallerFeatures.CallerAllocatedBuffer"/>
/// </remarks>
public ArrayMarshaller(T[]? array, Span<byte> buffer, int sizeOfNativeElement)
{
_allocatedMemory = default;
_sizeOfNativeElement = sizeOfNativeElement;
if (array is null)
{
_managedArray = null;
_span = default;
return;
}

_managedArray = array;

// Always allocate at least one byte when the array is zero-length.
int spaceToAllocate = Math.Max(array.Length * _sizeOfNativeElement, 1);
if (spaceToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_span = new Span<byte>((void*)_allocatedMemory, 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>
/// <remarks>
/// <seealso cref="CustomTypeMarshallerDirection.In"/>
/// </remarks>
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>
/// <remarks>
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
/// </remarks>
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>
/// <remarks>
/// <seealso cref="CustomTypeMarshallerDirection.Out"/>
/// </remarks>
public ReadOnlySpan<byte> GetNativeValuesSource(int length)
{
return _allocatedMemory == IntPtr.Zero ? default : _span = new Span<byte>((void*)_allocatedMemory, length * _sizeOfNativeElement);
}

/// <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>
/// <remarks>
/// <seealso cref="CustomTypeMarshallerDirection.In"/>
/// </remarks>
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>
/// <remarks>
/// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
/// </remarks>
public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference());

/// <summary>
/// Sets the native value representing the array.
/// </summary>
/// <param name="value">The native value.</param>
/// <remarks>
/// <seealso cref="CustomTypeMarshallerFeatures.TwoStageMarshalling"/>
/// </remarks>
public void FromNativeValue(byte* value)
{
_allocatedMemory = (IntPtr)value;
}

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

/// <summary>
/// Frees native resources.
/// </summary>
/// <remarks>
/// <seealso cref="CustomTypeMarshallerFeatures.UnmanagedResources"/>
/// </remarks>
public void FreeNative()
{
Marshal.FreeCoTaskMem(_allocatedMemory);
}
}
}
Loading

0 comments on commit 936ccf8

Please sign in to comment.