using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace WhiteMagic;
///
/// Computes and caches the byte size and marshalling decision for type
/// exactly once. and
/// branch on
/// to decide between the blittable Span /
/// path and the
/// path.
///
/// The type to cache metadata for.
public static class MarshalCache
{
///
/// The byte size of . For the blittable path this is
/// the managed layout size — the width that
/// /
/// actually consume. For primitive-sized types (, ,
/// and the underlying of enums) the size matches the CLR primitive width.
///
public static readonly int Size;
///
/// when cannot be copied through
/// the blittable path
/// and must fall back to /
/// . 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;
static MarshalCache()
{
if (typeof(T) == typeof(bool))
{
Size = 1;
}
else if (typeof(T) == typeof(char))
{
// Marshal.SizeOf reports 1 (ANSI char), but the blittable
// MemoryMarshal path reads/writes a char as a 2-byte UTF-16 code unit.
// Use the managed layout width so Size matches what the reader actually uses.
Size = 2;
}
else if (typeof(T).IsEnum)
{
Size = Marshal.SizeOf(typeof(T).GetEnumUnderlyingType());
}
else
{
// The blittable path goes through MemoryMarshal, which uses the CLR managed
// layout. Use Unsafe.SizeOf so Size agrees with that layout —
// Marshal.SizeOf can disagree when a struct contains a `bool` field
// (unmanaged width 4 vs managed width 1).
Size = Unsafe.SizeOf();
}
bool hasMarshalAsField =
typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Any(f => f.GetCustomAttributes(typeof(MarshalAsAttribute), true).Length != 0);
TypeRequiresMarshal =
hasMarshalAsField || RuntimeHelpers.IsReferenceOrContainsReferences();
}
}