using System.Reflection;
using System.Runtime.InteropServices;
namespace WhiteMagic;
///
/// Computes and caches marshal-related metadata for type
/// exactly once. and
/// branch on these cached flags to decide between blittable Span/MemoryMarshal
/// paths and the fallback marshal path.
///
/// The type to cache metadata for.
public static class MarshalCache
{
/// The unmanaged size of in bytes.
public static readonly int Size;
/// The unmanaged size of as an unsigned integer.
public static readonly uint SizeU;
///
/// when cannot be copied through the
/// blittable path and must
/// use / instead.
/// This is the case when a top-level field carries , or
/// when contains a managed reference
/// ().
///
///
/// The check inspects only top-level fields; a
/// on a field of a nested struct is not detected.
/// Reference-containing nested structs are still caught, because the reference check
/// propagates through nested value types.
///
public static readonly bool TypeRequiresMarshal;
/// when is .
public static readonly bool IsIntPtr;
/// The underlying type code of .
public static readonly TypeCode TypeCode;
///
/// The effective type that the marshaler uses. For an enum this is the underlying
/// integer type; for all other types it is itself.
///
public static readonly Type RealType;
static MarshalCache()
{
TypeCode = Type.GetTypeCode(typeof(T));
if (typeof(T) == typeof(bool))
{
Size = 1;
RealType = typeof(T);
}
else if (typeof(T) == typeof(char))
{
// Marshal.SizeOf(char) is 1 (ANSI), but the blittable path reads/writes a
// char as a 2-byte UTF-16 code unit. Size must match the blittable width.
Size = 2;
RealType = typeof(T);
}
else if (typeof(T).IsEnum)
{
Type underlying = typeof(T).GetEnumUnderlyingType();
Size = Marshal.SizeOf(underlying);
RealType = underlying;
TypeCode = Type.GetTypeCode(underlying);
}
else
{
Size = Marshal.SizeOf(typeof(T));
RealType = typeof(T);
}
SizeU = (uint)Size;
IsIntPtr = RealType == typeof(IntPtr);
bool hasMarshalAsField =
RealType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Any(f => f.GetCustomAttributes(typeof(MarshalAsAttribute), true).Length != 0);
TypeRequiresMarshal =
hasMarshalAsField || System.Runtime.CompilerServices.RuntimeHelpers.IsReferenceOrContainsReferences();
}
}