ARchive old spec

This commit is contained in:
kbe
2026-07-21 22:30:10 +02:00
parent 0380705e76
commit 184dec86ca
17 changed files with 550 additions and 191 deletions
+47 -23
View File
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WhiteMagic;
@@ -5,7 +6,7 @@ namespace WhiteMagicTest;
/// <summary>
/// Tests for <see cref="MarshalCache{T}"/>: blittable size, marshal-required flag,
/// IsIntPtr, and computed-once behavior.
/// and computed-once behavior.
/// </summary>
public class MarshalCacheTests
{
@@ -45,12 +46,33 @@ public class MarshalCacheTests
Assert.Equal(8, MarshalCache<BlittableStruct>.Size);
}
[Fact]
public void Size_for_struct_with_bool_is_managed_layout_width()
{
// Regression for the Marshal.SizeOf / Unsafe.SizeOf disagreement on a struct
// whose only field is `bool`: Marshal reports 4 bytes (Win32 BOOL default marshaling),
// but the blittable path (MemoryMarshal.Read<T>) actually lays out a bool as 1 byte.
// MarshalCache.Size must match what the reader actually touches.
Assert.Equal(Unsafe.SizeOf<SingleBoolStruct>(), MarshalCache<SingleBoolStruct>.Size);
Assert.Equal(1, MarshalCache<SingleBoolStruct>.Size);
}
[Fact]
public void Size_for_struct_with_bools_in_sequence_matches_managed_layout()
{
// Sequential struct { bool, bool } — managed width is 2, Marshal width is 8 (two BOOLs).
// The blittable path uses 1 byte per bool, so Size must equal the managed width.
Assert.Equal(Unsafe.SizeOf<SequentialBoolStruct>(), MarshalCache<SequentialBoolStruct>.Size);
Assert.Equal(2, MarshalCache<SequentialBoolStruct>.Size);
}
[Fact]
public void TypeRequiresMarshal_is_false_for_blittable_types()
{
Assert.False(MarshalCache<int>.TypeRequiresMarshal);
Assert.False(MarshalCache<long>.TypeRequiresMarshal);
Assert.False(MarshalCache<BlittableStruct>.TypeRequiresMarshal);
Assert.False(MarshalCache<byte>.TypeRequiresMarshal);
Assert.False(MarshalCache<IntPtr>.TypeRequiresMarshal);
Assert.False(MarshalCache<IntPtr>.TypeRequiresMarshal);
}
[Fact]
@@ -60,39 +82,23 @@ public class MarshalCacheTests
}
[Fact]
public void IsIntPtr_is_true_for_IntPtr()
public void TypeRequiresMarshal_is_true_for_reference_containing_types()
{
Assert.True(MarshalCache<IntPtr>.IsIntPtr);
Assert.True(MarshalCache<string>.TypeRequiresMarshal);
Assert.True(MarshalCache<ClassWithInt>.TypeRequiresMarshal);
}
[Fact]
public void IsIntPtr_is_false_for_non_IntPtr_types()
{
Assert.False(MarshalCache<int>.IsIntPtr);
Assert.False(MarshalCache<long>.IsIntPtr);
Assert.False(MarshalCache<BlittableStruct>.IsIntPtr);
}
[Fact]
public void All_properties_are_computed_once_and_cached()
public void Properties_are_computed_once_and_cached()
{
int size1 = MarshalCache<int>.Size;
bool marshal1 = MarshalCache<int>.TypeRequiresMarshal;
bool intPtr1 = MarshalCache<int>.IsIntPtr;
int size2 = MarshalCache<int>.Size;
bool marshal2 = MarshalCache<int>.TypeRequiresMarshal;
bool intPtr2 = MarshalCache<int>.IsIntPtr;
Assert.Equal(size1, size2);
Assert.Equal(marshal1, marshal2);
Assert.Equal(intPtr1, intPtr2);
}
[Fact]
public void SizeU_matches_Size_as_uint()
{
Assert.Equal((uint)MarshalCache<int>.Size, MarshalCache<int>.SizeU);
}
[StructLayout(LayoutKind.Sequential)]
@@ -108,4 +114,22 @@ public class MarshalCacheTests
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Data;
}
[StructLayout(LayoutKind.Sequential)]
private struct SingleBoolStruct
{
public bool Flag;
}
[StructLayout(LayoutKind.Sequential)]
private struct SequentialBoolStruct
{
public bool A;
public bool B;
}
private class ClassWithInt
{
public int Value = 0;
}
}