using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Security.Permissions;
using Vanara.Extensions.Reflection;
using Vanara.InteropServices;
using Vanara.PInvoke;
using Vanara.PInvoke.Collections;
namespace Vanara.Extensions
{
/// Extension methods for System.Runtime.InteropServices.
public static partial class InteropExtensions
{
///
/// Aligns the specified pointer to an adjacent memory location that can be accessed by a adding a constant and its multiples.
///
/// The pointer to align.
/// The aligned pointer. This value may be the same as .
public static IntPtr Align(this IntPtr ptr) => new IntPtr((ptr.ToInt64() + IntPtr.Size - 1) & (~(((long)IntPtr.Size) - 1)));
#if ALLOWSPAN
/// Returns the pointer as a .
/// The type of items in the .
/// A pointer to the starting address of a specified number of elements in memory.
/// The number of elements to be included in the .
/// Bytes to skip before starting the span.
/// If known, the total number of bytes allocated to the native memory in .
/// A that represents the memory.
///
public static unsafe ReadOnlySpan AsReadOnlySpan(this IntPtr ptr, int length, int prefixBytes = 0, SizeT allocatedBytes = default)
{
if (ptr == IntPtr.Zero) return null;
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
if (allocatedBytes > 0 && SizeOf() * length + prefixBytes > allocatedBytes)
throw new InsufficientMemoryException();
return new ReadOnlySpan((ptr + prefixBytes).ToPointer(), length);
}
/// Gets a reference to a structure based on this allocated memory.
/// The type of items in the .
/// A pointer to the starting address of a specified number of elements in memory.
/// Bytes to skip before starting the span.
/// If known, the total number of bytes allocated to the native memory in .
/// A referenced structure.
public static ref T AsRef(this IntPtr ptr, int prefixBytes = 0, SizeT allocatedBytes = default) =>
ref MemoryMarshal.GetReference(AsSpan(ptr, 1, prefixBytes, allocatedBytes));
/// Returns the pointer as a .
/// The type of items in the .
/// A pointer to the starting address of a specified number of elements in memory.
/// The number of elements to be included in the .
/// Bytes to skip before starting the span.
/// If known, the total number of bytes allocated to the native memory in .
/// A that represents the memory.
///
public static unsafe Span AsSpan(this IntPtr ptr, int length, int prefixBytes = 0, SizeT allocatedBytes = default)
{
if (ptr == IntPtr.Zero) return null;
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
if (allocatedBytes > 0 && SizeOf() * length + prefixBytes > allocatedBytes)
throw new InsufficientMemoryException();
return new Span((ptr + prefixBytes).ToPointer(), length);
}
#endif
/// Returns the pointer.
/// The type of items.
/// A pointer to the starting address of a specified number of elements in memory.
/// The number of elements to be included in the pointer.
/// Bytes to skip before starting the span.
/// If known, the total number of bytes allocated to the native memory in .
/// A pointer that represents the memory.
///
public static unsafe T* AsUnmanagedArrayPointer(this IntPtr ptr, int length, int prefixBytes = 0, SizeT allocatedBytes = default) where T : unmanaged
{
if (ptr == IntPtr.Zero) return null;
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
if (allocatedBytes > 0 && SizeOf() * length + prefixBytes > allocatedBytes)
throw new InsufficientMemoryException();
return (T*)ptr.Offset(prefixBytes).ToPointer();
}
/// Copies the number of specified bytes from one unmanaged memory block to another.
/// The allocated memory pointer.
/// The allocated memory pointer to copy to.
/// The number of bytes to copy from to .
public static void CopyTo(this IntPtr ptr, IntPtr dest, long length) => CopyTo(ptr, 0L, dest, length);
/// Copies the number of specified bytes from one unmanaged memory block to another.
/// The allocated memory pointer.
/// The offset from at which to start the copying.
/// The allocated memory pointer to copy to.
/// The number of bytes to copy from to .
public static void CopyTo(this IntPtr source, long start, IntPtr dest, long length)
{
if (start < 0 || length < 0) throw new ArgumentOutOfRangeException();
if (source == IntPtr.Zero || dest == IntPtr.Zero) throw new ArgumentNullException();
unsafe
{
byte* psrc = (byte*)source + start, pdest = (byte*)dest;
for (long i = 0; i < length; i++, psrc++, pdest++)
*pdest = *psrc;
}
}
///
/// Fills the memory with a particular byte value. This is a very dangerous function that can cause memory
/// access errors if the provided is bigger than allocated memory of if the is not a
/// valid memory pointer.
///
/// The allocated memory pointer.
/// The byte value with which to fill the memory.
/// The number of bytes to fill with the value.
public static void FillMemory(this IntPtr ptr, byte value, long length)
{
if (ptr == IntPtr.Zero || length <= 0) return;
// Write multiples of 8 bytes first
var lval = value == 0 ? 0L : BitConverter.ToInt64(new[] { value, value, value, value, value, value, value, value }, 0);
for (var ofs = 0L; ofs < length / 8; ofs++)
Marshal.WriteInt64(ptr.Offset(ofs * 8), 0, lval);
// Write remaining bytes
for (var ofs = length - (length % 8); ofs < length; ofs++)
Marshal.WriteByte(ptr.Offset(ofs), 0, value);
}
/// Converts an that points to a C-style array into an .
/// Type of native structure used by the C-style array.
/// The pointing to the native array.
/// The number of items in the native array.
/// Bytes to skip before reading the array.
/// If known, the total number of bytes allocated to the native memory in .
/// An exposing the elements of the native array.
public static IEnumerator GetEnumerator(this IntPtr ptr, int count, int prefixBytes = 0, SizeT allocatedBytes = default) =>
new NativeMemoryEnumerator(ptr, count, prefixBytes, allocatedBytes);
/// Converts an that points to a C-style array into an .
/// The pointing to the native array.
/// Type of native structure used by the C-style array.
/// The number of items in the native array.
/// Bytes to skip before reading the array.
/// If known, the total number of bytes allocated to the native memory in .
/// An exposing the elements of the native array.
public static System.Collections.IEnumerator GetEnumerator(this IntPtr ptr, Type type, int count, int prefixBytes = 0, SizeT allocatedBytes = default) =>
new UntypedNativeMemoryEnumerator(ptr, type, count, prefixBytes, allocatedBytes);
///
/// Gets the length of a null terminated array of pointers. This is a very dangerous function and can result in
/// memory access errors if the does not point to a null-terminated array of pointers.
///
/// The pointing to the native array.
///
/// The number of non-null pointers in the array. If is equal to IntPtr.Zero, this result is 0.
///
public static int GetNulledPtrArrayLength(this IntPtr lptr)
{
if (lptr == IntPtr.Zero) return 0;
var c = 0;
while (Marshal.ReadIntPtr(lptr, IntPtr.Size * c++) != IntPtr.Zero) { }
return c - 1;
}
/// Determines whether this type is formatted or blittable.
/// The type to check.
/// if the specified type is blittable; otherwise, .
public static bool IsBlittable(this Type T)
{
if (T is null) return false;
// if (T.IsArray && T.GetArrayRank() > 1) return false; // Need to find a way to exclude jagged arrays
while (T.IsArray)
T = T.GetElementType();
//
//if (T == typeof(decimal) || T.IsAbstract || T.IsAutoClass || T.IsGenericType) return false;
//if (T.IsEnum || T.IsPrimitive && T != typeof(bool) && T != typeof(char)) return true;
//try
//{
// GCHandle.Alloc(FormatterServices.GetUninitializedObject(T), GCHandleType.Pinned).Free();
// return true;
//}
//catch
//{
// return false;
//}
if (T.IsEnum) return true;
try { Marshal.SizeOf(T); return true; } catch { return false; }
}
/// Determines whether this type is marshalable.
/// The type to check.
/// if the specified type is marshalable; otherwise, .
public static bool IsMarshalable(this Type type)
{
var t = type.IsNullable() ? type.GetGenericArguments()[0] : type;
return t.IsSerializable || VanaraMarshaler.CanMarshal(t, out _) || t.IsBlittable();
}
/// Determines whether this type is nullable (derived from ).
/// The type to check.
/// if the specified type is nullable; otherwise, .
public static bool IsNullable(this Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
/// Marshals an unmanaged linked list of structures to an of that structure.
/// Type of native structure used by the unmanaged linked list.
/// The pointing to the native array.
/// The expression to be used to fetch the pointer to the next item in the list.
/// An exposing the elements of the linked list.
public static IEnumerable LinkedListToIEnum(this IntPtr ptr, Func next)
{
for (var pCurrent = ptr; pCurrent != IntPtr.Zero;)
{
var ret = pCurrent.ToStructure();
yield return ret;
pCurrent = next(ret);
}
}
/// Marshals an unmanaged linked list of structures to an of that structure.
/// Type of native structure used by the unmanaged linked list.
/// The pointing to the native array.
/// The expression to be used to fetch the offset from the current pointer to the next item in the list.
///
/// The number of allocated bytes behind . This value is used to determine when to stop enumerating.
///
/// An exposing the elements of the linked list.
public static IEnumerable LinkedListToIEnum(this IntPtr ptr, Func nextOffset, SizeT allocatedBytes)
{
var pEnd = ptr.Offset(allocatedBytes);
for (var pCurrent = ptr; pCurrent.ToInt64() < pEnd.ToInt64();)
{
var ret = pCurrent.ToStructure();
yield return ret;
pCurrent = pCurrent.Offset(nextOffset(ret));
}
}
///
/// Marshals data from a managed list of objects to an unmanaged block of memory allocated by the method.
///
/// The enumerated list of objects to marshal.
///
/// The function that allocates the memory for the block of objects (typically or .
///
/// The bytes allocated by the method.
///
/// if set to the pointer will be processed by storing a reference to the value; if ,
/// the pointer value will be directly inserted into the array of pointers.
///
/// The character set to use for strings.
/// Number of bytes preceding the allocated objects.
/// Pointer to the allocated native (unmanaged) array of objects stored using the character set defined by .
public static IntPtr MarshalObjectsToPtr(this IEnumerable