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