Files
whitemagic/WhiteMagic/MarshalCache.cs
T
kbe ccde012e45 task 2.1-2.2: implement MarshalCache<T> with tests
Add MarshalCache<T> static class that computes Size, SizeU,
TypeRequiresMarshal, IsIntPtr, TypeCode, and RealType once per type
in the static constructor. Handles bool (size=1), enums (underlying
type), and MarshalAs-attributed fields (TypeRequiresMarshal).

12 new tests covering: blittable sizes, bool size, enum size, struct
size, marshal-required flag, IsIntPtr, computed-once caching.
All passing.
2026-07-21 17:03:23 +02:00

70 lines
2.4 KiB
C#

using System.Reflection;
using System.Runtime.InteropServices;
namespace WhiteMagic;
/// <summary>
/// Computes and caches marshal-related metadata for type <typeparamref name="T"/>
/// exactly once. <see cref="MemoryBase.Read{T}"/> and <see cref="MemoryBase.Write{T}"/>
/// branch on these cached flags to decide between blittable <c>Span</c>/<c>MemoryMarshal</c>
/// paths and the fallback marshal path.
/// </summary>
/// <typeparam name="T">The type to cache metadata for.</typeparam>
public static class MarshalCache<T>
{
/// <summary>The unmanaged size of <typeparamref name="T"/> in bytes.</summary>
public static readonly int Size;
/// <summary>The unmanaged size of <typeparamref name="T"/> as an unsigned integer.</summary>
public static readonly uint SizeU;
/// <summary>
/// <see langword="true"/> when <typeparamref name="T"/> has at least one field
/// decorated with <see cref="MarshalAsAttribute"/>, meaning it cannot be copied
/// via a simple pointer dereference.
/// </summary>
public static readonly bool TypeRequiresMarshal;
/// <summary><see langword="true"/> when <typeparamref name="T"/> is <see cref="IntPtr"/>.</summary>
public static readonly bool IsIntPtr;
/// <summary>The underlying type code of <typeparamref name="T"/>.</summary>
public static readonly TypeCode TypeCode;
/// <summary>
/// The effective type that the marshaler uses. For an enum this is the underlying
/// integer type; for all other types it is <typeparamref name="T"/> itself.
/// </summary>
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).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);
TypeRequiresMarshal =
RealType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Any(f => f.GetCustomAttributes(typeof(MarshalAsAttribute), true).Length != 0);
}
}