// 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 { /// <summary> /// Supports marshalling a <see cref="Span{T}"/> from managed value /// to a contiguous native array of the unmanaged values of the elements. /// </summary> /// <typeparam name="T">The managed element type of the span.</typeparam> /// <typeparam name="TUnmanagedElement">The unmanaged type for the elements of the span.</typeparam> /// <remarks> /// A <see cref="Span{T}"/> marshalled with this marshaller will match the semantics of <see cref="MemoryMarshal.GetReference{T}(Span{T})"/>. /// In particular, this marshaller will pass a non-null value for a zero-length span if the span was constructed with a non-null value. /// </remarks> [CLSCompliant(false)] [CustomMarshaller(typeof(Span<>), MarshalMode.Default, typeof(SpanMarshaller<,>))] [CustomMarshaller(typeof(Span<>), MarshalMode.ManagedToUnmanagedIn, typeof(SpanMarshaller<,>.ManagedToUnmanagedIn))] [ContiguousCollectionMarshaller] public static unsafe class SpanMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged { /// <summary> /// Allocates the space to store the unmanaged elements. /// </summary> /// <param name="managed">The managed span.</param> /// <param name="numElements">The number of elements in the span.</param> /// <returns>A pointer to the block of memory for the unmanaged elements.</returns> public static TUnmanagedElement* AllocateContainerForUnmanagedElements(Span<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))) { 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); } /// <summary> /// Gets a span of the managed collection elements. /// </summary> /// <param name="managed">The managed collection.</param> /// <returns>A span of the managed collection elements.</returns> public static ReadOnlySpan<T> GetManagedValuesSource(Span<T> managed) => managed; /// <summary> /// Gets a span of the space where the unmanaged collection elements should be stored. /// </summary> /// <param name="unmanaged">The pointer to the block of memory for the unmanaged elements.</param> /// <param name="numElements">The number of elements that will be copied into the memory block.</param> /// <returns>A span over the unmanaged memory that can contain the specified number of elements.</returns> public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) => new Span<TUnmanagedElement>(unmanaged, numElements); /// <summary> /// Allocates space to store the managed elements. /// </summary> /// <param name="unmanaged">The unmanaged value.</param> /// <param name="numElements">The number of elements in the unmanaged collection.</param> /// <returns>A span over enough memory to contain <paramref name="numElements"/> elements.</returns> public static Span<T> AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) { if (unmanaged is null) return null; return new T[numElements]; } /// <summary> /// Gets a span of the space where the managed collection elements should be stored. /// </summary> /// <param name="managed">A span over the space to store the managed elements.</param> /// <returns>A span over the managed memory that can contain the specified number of elements.</returns> public static Span<T> GetManagedValuesDestination(Span<T> managed) => managed; /// <summary> /// Gets a span of the native collection elements. /// </summary> /// <param name="unmanaged">The unmanaged value.</param> /// <param name="numElements">The number of elements in the unmanaged collection.</param> /// <returns>A span over the native collection elements.</returns> public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) => new ReadOnlySpan<TUnmanagedElement>(unmanaged, numElements); /// <summary> /// Frees the allocated unmanaged memory. /// </summary> /// <param name="unmanaged">A pointer to the allocated unmanaged memory.</param> public static void Free(TUnmanagedElement* unmanaged) => Marshal.FreeCoTaskMem((IntPtr)unmanaged); /// <summary> /// Supports marshalling from managed into unmanaged in a call from managed code to unmanaged code. /// </summary> public ref struct ManagedToUnmanagedIn { // We'll keep the buffer size at a maximum of 512 bytes to avoid overflowing the stack. public static int BufferSize { get; } = 0x200 / sizeof(TUnmanagedElement); private Span<T> _managedArray; private TUnmanagedElement* _allocatedMemory; private Span<TUnmanagedElement> _span; /// <summary> /// Initializes the <see cref="SpanMarshaller{T, TUnmanagedElement}.ManagedToUnmanagedIn"/> marshaller. /// </summary> /// <param name="managed">The span to be marshalled.</param> /// <param name="buffer">The 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(Span<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; if (managed.Length <= buffer.Length) { _span = buffer[0..managed.Length]; } else { int bufferSize = checked(managed.Length * sizeof(TUnmanagedElement)); _allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)bufferSize); _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>A span over the 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>A 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() { // Unsafe.AsPointer is safe since buffer must be pinned return (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); } /// <summary> /// Frees resources. /// </summary> public void Free() { NativeMemory.Free(_allocatedMemory); } /// <summary> /// Gets a pinnable reference to the managed span. /// </summary> /// <param name="managed">The managed span.</param> /// <returns>A reference that can be pinned and directly passed to unmanaged code.</returns> public static ref T GetPinnableReference(Span<T> managed) { return ref MemoryMarshal.GetReference(managed); } } } } |