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; } }