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