using System; using System.Runtime.InteropServices; public class StructLayoutTests { // Test structs with various layouts [StructLayout(LayoutKind.Sequential)] struct SequentialStruct { public int A; public byte B; public long C; } [StructLayout(LayoutKind.Explicit)] struct ExplicitUnion { [FieldOffset(0)] public int AsInt; [FieldOffset(0)] public float AsFloat; [FieldOffset(0)] public byte Byte0; [FieldOffset(1)] public byte Byte1; [FieldOffset(2)] public byte Byte2; [FieldOffset(3)] public byte Byte3; } [StructLayout(LayoutKind.Explicit, Size = 16)] struct FixedSizeStruct { [FieldOffset(0)] public long First; [FieldOffset(8)] public int Second; [FieldOffset(12)] public short Third; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct PackedStruct { public byte A; public int B; public byte C; } [StructLayout(LayoutKind.Auto)] struct AutoLayoutStruct { public int X; public string Y; public double Z; } [StructLayout(LayoutKind.Explicit)] struct NestedUnion { [FieldOffset(0)] public ExplicitUnion Inner; [FieldOffset(0)] public long AsLong; [FieldOffset(4)] public int UpperInt; } [StructLayout(LayoutKind.Explicit)] struct LargeUnion { [FieldOffset(0)] public long Long1; [FieldOffset(8)] public long Long2; [FieldOffset(0)] public double Double1; [FieldOffset(8)] public double Double2; [FieldOffset(0)] public decimal AsDecimal; } // Static fields for testing static SequentialStruct staticSequential; static ExplicitUnion staticUnion; static FixedSizeStruct staticFixed; // Instance fields for testing class FieldContainer { public SequentialStruct instanceSequential; public ExplicitUnion instanceUnion; public PackedStruct instancePacked; public NestedUnion instanceNested; } static int TestSequentialLayout() { var s = new SequentialStruct { A = 42, B = 255, C = long.MaxValue }; // Test field access if (s.A != 42) return 1; if (s.B != 255) return 2; if (s.C != long.MaxValue) return 3; // Test copy semantics var s2 = s; s2.A = 100; if (s.A != 42) return 4; // Should be unchanged (value type) if (s2.A != 100) return 5; // Test static field storage staticSequential = s; if (staticSequential.A != 42) return 6; if (staticSequential.C != long.MaxValue) return 7; return 0; } static int TestExplicitUnion() { var u = new ExplicitUnion(); // Test overlapping int/float u.AsInt = 0x3F800000; // IEEE 754 representation of 1.0f if (Math.Abs(u.AsFloat - 1.0f) > 0.0001f) return 10; // Test byte-level access u.AsInt = 0x12345678; bool isLittleEndian = BitConverter.IsLittleEndian; if (isLittleEndian) { if (u.Byte0 != 0x78) return 11; if (u.Byte1 != 0x56) return 12; if (u.Byte2 != 0x34) return 13; if (u.Byte3 != 0x12) return 14; } else { if (u.Byte0 != 0x12) return 11; if (u.Byte1 != 0x34) return 12; if (u.Byte2 != 0x56) return 13; if (u.Byte3 != 0x78) return 14; } // Test static field staticUnion = u; if (staticUnion.AsInt != 0x12345678) return 15; return 0; } static int TestFixedSizeStruct() { var f = new FixedSizeStruct { First = -1, Second = 42, Third = 1000 }; if (f.First != -1) return 20; if (f.Second != 42) return 21; if (f.Third != 1000) return 22; // Test size is respected int size = Marshal.SizeOf(typeof(FixedSizeStruct)); if (size != 16) return 23; staticFixed = f; if (staticFixed.Second != 42) return 24; return 0; } static int TestPackedStruct() { var p = new PackedStruct { A = 1, B = 0x12345678, C = 2 }; if (p.A != 1) return 30; if (p.B != 0x12345678) return 31; if (p.C != 2) return 32; // Packed struct should be 6 bytes (1 + 4 + 1) int size = Marshal.SizeOf(typeof(PackedStruct)); if (size != 6) return 33; return 0; } static int TestInstanceFields() { var container = new FieldContainer(); container.instanceSequential = new SequentialStruct { A = 111, B = 222, C = 333 }; if (container.instanceSequential.A != 111) return 40; container.instanceUnion = new ExplicitUnion { AsInt = unchecked((int)0xDEADBEEF) }; if (container.instanceUnion.AsInt != unchecked((int)0xDEADBEEF)) return 41; container.instancePacked = new PackedStruct { A = 10, B = 20, C = 30 }; if (container.instancePacked.B != 20) return 42; container.instanceNested = new NestedUnion(); container.instanceNested.Inner.AsInt = 100; if (container.instanceNested.Inner.AsInt != 100) return 43; return 0; } static int TestStructPassing() { var s = new SequentialStruct { A = 500, B = 50, C = 5005 }; int result = ProcessSequential(s); if (result != 555) return 50; // 500 + 50 + 5 (C % 1000) var u = new ExplicitUnion { AsInt = 1000 }; u = TransformUnion(u); if (u.AsInt != 2000) return 51; return 0; } static int ProcessSequential(SequentialStruct s) { return s.A + s.B + (int)(s.C % 1000); } static ExplicitUnion TransformUnion(ExplicitUnion u) { u.AsInt *= 2; return u; } static int TestNestedUnion() { var n = new NestedUnion(); n.Inner.AsInt = 0x12345678; // Lower 32 bits should match Inner.AsInt if ((n.AsLong & 0xFFFFFFFF) != 0x12345678) return 60; // Modify upper int n.UpperInt = unchecked((int)0xABCDEF00); // Check both parts if (n.Inner.AsInt != 0x12345678) return 61; if (n.UpperInt != unchecked((int)0xABCDEF00)) return 62; return 0; } static int TestLargeUnion() { var l = new LargeUnion(); // Test double/long overlap l.Double1 = 1.0; l.Double2 = 2.0; // IEEE 754: 1.0 = 0x3FF0000000000000 if (l.Long1 != 0x3FF0000000000000) return 70; // IEEE 754: 2.0 = 0x4000000000000000 if (l.Long2 != 0x4000000000000000) return 71; // Test decimal overlap (decimal is 128 bits) l.AsDecimal = 42m; // Just verify it doesn't crash and maintains some structure if (l.AsDecimal != 42m) return 72; return 0; } static int TestAutoLayout() { // Auto layout structs can't use FieldOffset, but we can still test basic functionality var a = new AutoLayoutStruct { X = 100, Y = "test", Z = 3.14159 }; if (a.X != 100) return 80; if (a.Y != "test") return 81; if (Math.Abs(a.Z - 3.14159) > 0.00001) return 82; // Test copy var a2 = a; a2.X = 200; if (a.X != 100) return 83; // Original should be unchanged if (a2.X != 200) return 84; return 0; } static int TestStructArray() { var arr = new ExplicitUnion[3]; arr[0].AsInt = 10; arr[1].AsInt = 20; arr[2].AsInt = 30; if (arr[0].AsInt != 10) return 90; if (arr[1].AsInt != 20) return 91; if (arr[2].AsInt != 30) return 92; // Modify through float view arr[1].AsFloat = 2.5f; if (Math.Abs(arr[1].AsFloat - 2.5f) > 0.0001f) return 93; return 0; } static int TestBoxingUnboxing() { ExplicitUnion u = new ExplicitUnion { AsInt = 999 }; object boxed = u; // Box ExplicitUnion unboxed = (ExplicitUnion)boxed; // Unbox if (unboxed.AsInt != 999) return 100; // Modify original, boxed should remain unchanged u.AsInt = 111; ExplicitUnion fromBoxed = (ExplicitUnion)boxed; if (fromBoxed.AsInt != 999) return 101; // Should still be 999 return 0; } static int TestDefaultValues() { // Test that default struct initialization zeroes memory var s = new SequentialStruct(); if (s.A != 0) return 110; if (s.B != 0) return 111; if (s.C != 0) return 112; var u = new ExplicitUnion(); if (u.AsInt != 0) return 113; if (u.AsFloat != 0.0f) return 114; return 0; } public static int Main(string[] argv) { int result = 0; result = TestSequentialLayout(); if (result != 0) return result; result = TestExplicitUnion(); if (result != 0) return result; result = TestFixedSizeStruct(); if (result != 0) return result; result = TestPackedStruct(); if (result != 0) return result; result = TestInstanceFields(); if (result != 0) return result; result = TestStructPassing(); if (result != 0) return result; result = TestNestedUnion(); if (result != 0) return result; result = TestLargeUnion(); if (result != 0) return result; result = TestAutoLayout(); if (result != 0) return result; result = TestStructArray(); if (result != 0) return result; result = TestBoxingUnboxing(); if (result != 0) return result; result = TestDefaultValues(); if (result != 0) return result; return 0; // All tests passed } }