using System;
using System.Runtime.InteropServices;
namespace CeresSharp.Native;
///
/// Helper class for unsafe code patterns and pointer operations.
/// Provides utilities for array pinning and pointer conversions.
///
internal static class UnsafeHelpers
{
///
/// Executes an operation with a pinned array.
///
/// The unmanaged type of the array elements.
/// The array to pin.
/// The operation to execute with the pinned pointer.
/// Thrown when array is null.
internal static void WithPinnedArray(T[] array, Action operation) where T : unmanaged
{
if (array == null)
throw new ArgumentNullException(nameof(array));
unsafe
{
fixed (T* ptr = array)
{
operation((IntPtr)ptr);
}
}
}
///
/// Executes an operation with a pinned array and returns a result.
///
/// The unmanaged type of the array elements.
/// The return type.
/// The array to pin.
/// The operation to execute with the pinned pointer.
/// The result of the operation.
/// Thrown when array is null.
internal static TResult WithPinnedArray(T[] array, Func operation) where T : unmanaged
{
if (array == null)
throw new ArgumentNullException(nameof(array));
unsafe
{
fixed (T* ptr = array)
{
return operation((IntPtr)ptr);
}
}
}
///
/// Executes an operation with multiple pinned arrays.
///
/// The unmanaged type of the array elements.
/// The arrays to pin.
/// The operation to execute with the pinned pointers.
/// Thrown when arrays is null or contains null elements.
internal static void WithPinnedArrays(T[][] arrays, Action 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();
}
}
}
}
///
/// Pins an array and returns a GCHandle that must be freed by the caller.
///
/// The unmanaged type of the array elements.
/// The array to pin.
/// A GCHandle that pins the array.
/// Thrown when array is null.
internal static GCHandle PinArray(T[] array) where T : unmanaged
{
if (array == null)
throw new ArgumentNullException(nameof(array));
return GCHandle.Alloc(array, GCHandleType.Pinned);
}
///
/// Copies data from an IntPtr array to a managed array of arrays.
///
/// Pointer to array of IntPtr pointers.
/// Array of sizes for each parameter block.
/// Array of parameter blocks.
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;
}
}