145 lines
5.4 KiB
C#
145 lines
5.4 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace CeresSharp.Native;
|
|
|
|
/// <summary>
|
|
/// Helper class for unsafe code patterns and pointer operations.
|
|
/// Provides utilities for array pinning and pointer conversions.
|
|
/// </summary>
|
|
internal static class UnsafeHelpers
|
|
{
|
|
/// <summary>
|
|
/// Executes an operation with a pinned array.
|
|
/// </summary>
|
|
/// <typeparam name="T">The unmanaged type of the array elements.</typeparam>
|
|
/// <param name="array">The array to pin.</param>
|
|
/// <param name="operation">The operation to execute with the pinned pointer.</param>
|
|
/// <exception cref="ArgumentNullException">Thrown when array is null.</exception>
|
|
internal static void WithPinnedArray<T>(T[] array, Action<IntPtr> operation) where T : unmanaged
|
|
{
|
|
if (array == null)
|
|
throw new ArgumentNullException(nameof(array));
|
|
|
|
unsafe
|
|
{
|
|
fixed (T* ptr = array)
|
|
{
|
|
operation((IntPtr)ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes an operation with a pinned array and returns a result.
|
|
/// </summary>
|
|
/// <typeparam name="T">The unmanaged type of the array elements.</typeparam>
|
|
/// <typeparam name="TResult">The return type.</typeparam>
|
|
/// <param name="array">The array to pin.</param>
|
|
/// <param name="operation">The operation to execute with the pinned pointer.</param>
|
|
/// <returns>The result of the operation.</returns>
|
|
/// <exception cref="ArgumentNullException">Thrown when array is null.</exception>
|
|
internal static TResult WithPinnedArray<T, TResult>(T[] array, Func<IntPtr, TResult> operation) where T : unmanaged
|
|
{
|
|
if (array == null)
|
|
throw new ArgumentNullException(nameof(array));
|
|
|
|
unsafe
|
|
{
|
|
fixed (T* ptr = array)
|
|
{
|
|
return operation((IntPtr)ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes an operation with multiple pinned arrays.
|
|
/// </summary>
|
|
/// <typeparam name="T">The unmanaged type of the array elements.</typeparam>
|
|
/// <param name="arrays">The arrays to pin.</param>
|
|
/// <param name="operation">The operation to execute with the pinned pointers.</param>
|
|
/// <exception cref="ArgumentNullException">Thrown when arrays is null or contains null elements.</exception>
|
|
internal static void WithPinnedArrays<T>(T[][] arrays, Action<IntPtr[]> operation) where T : unmanaged
|
|
{
|
|
if (arrays == null)
|
|
throw new ArgumentNullException(nameof(arrays));
|
|
|
|
unsafe
|
|
{
|
|
var pointers = new IntPtr[arrays.Length];
|
|
var pins = new GCHandle[arrays.Length];
|
|
|
|
try
|
|
{
|
|
for (int i = 0; i < arrays.Length; i++)
|
|
{
|
|
if (arrays[i] == null)
|
|
throw new ArgumentException($"Array at index {i} is null", nameof(arrays));
|
|
|
|
pins[i] = GCHandle.Alloc(arrays[i], GCHandleType.Pinned);
|
|
pointers[i] = pins[i].AddrOfPinnedObject();
|
|
}
|
|
|
|
operation(pointers);
|
|
}
|
|
finally
|
|
{
|
|
foreach (var pin in pins)
|
|
{
|
|
if (pin.IsAllocated)
|
|
pin.Free();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pins an array and returns a GCHandle that must be freed by the caller.
|
|
/// </summary>
|
|
/// <typeparam name="T">The unmanaged type of the array elements.</typeparam>
|
|
/// <param name="array">The array to pin.</param>
|
|
/// <returns>A GCHandle that pins the array.</returns>
|
|
/// <exception cref="ArgumentNullException">Thrown when array is null.</exception>
|
|
internal static GCHandle PinArray<T>(T[] array) where T : unmanaged
|
|
{
|
|
if (array == null)
|
|
throw new ArgumentNullException(nameof(array));
|
|
|
|
return GCHandle.Alloc(array, GCHandleType.Pinned);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies data from an IntPtr array to a managed array of arrays.
|
|
/// </summary>
|
|
/// <param name="parameterPtrs">Pointer to array of IntPtr pointers.</param>
|
|
/// <param name="parameterBlockSizes">Array of sizes for each parameter block.</param>
|
|
/// <returns>Array of parameter blocks.</returns>
|
|
internal static double[][] CopyParameterBlocks(IntPtr parameterPtrs, int[] parameterBlockSizes)
|
|
{
|
|
if (parameterPtrs == IntPtr.Zero)
|
|
throw new ArgumentException("Parameter pointers cannot be zero", nameof(parameterPtrs));
|
|
if (parameterBlockSizes == null || parameterBlockSizes.Length == 0)
|
|
throw new ArgumentException("Parameter block sizes cannot be null or empty", nameof(parameterBlockSizes));
|
|
|
|
var parameterBlocks = new double[parameterBlockSizes.Length][];
|
|
|
|
unsafe
|
|
{
|
|
var paramPtrs = (IntPtr*)parameterPtrs;
|
|
for (int i = 0; i < parameterBlockSizes.Length; i++)
|
|
{
|
|
var size = parameterBlockSizes[i];
|
|
if (size <= 0)
|
|
throw new ArgumentException($"Parameter block size at index {i} must be positive", nameof(parameterBlockSizes));
|
|
|
|
parameterBlocks[i] = new double[size];
|
|
Marshal.Copy(paramPtrs[i], parameterBlocks[i], 0, size);
|
|
}
|
|
}
|
|
|
|
return parameterBlocks;
|
|
}
|
|
}
|
|
|