diff --git a/ObjectLayoutInspector.Tests.X64/AsyncStateMachineLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/AsyncStateMachineLayoutTests.cs new file mode 100644 index 0000000..accf8b6 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/AsyncStateMachineLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class AsyncStateMachineLayoutTests : Tests.AsyncStateMachineLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/ClassLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/ClassLayoutTests.cs new file mode 100644 index 0000000..a4c3f15 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/ClassLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class ClassLayoutTests : Tests.ClassLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/ClassWithExplicitLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/ClassWithExplicitLayoutTests.cs new file mode 100644 index 0000000..dd3e1f7 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/ClassWithExplicitLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class ClassWithExplicitLayoutTests : Tests.ClassWithExplicitLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/ClassWithNestedStructPrinterTests.cs b/ObjectLayoutInspector.Tests.X64/ClassWithNestedStructPrinterTests.cs new file mode 100644 index 0000000..0b0cbea --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/ClassWithNestedStructPrinterTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class ClassWithNestedStructPrinterTests : Tests.ClassWithNestedStructPrinterTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/ComplexUnsafeLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/ComplexUnsafeLayoutTests.cs new file mode 100644 index 0000000..9ce7074 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/ComplexUnsafeLayoutTests.cs @@ -0,0 +1,12 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class ComplexUnsafeLayoutTests : Tests.ComplexUnsafeLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/CorefxTypesTests.cs b/ObjectLayoutInspector.Tests.X64/CorefxTypesTests.cs new file mode 100644 index 0000000..6272828 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/CorefxTypesTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class CorefxTypesTests : Tests.CorefxTypesTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/ExcessivePaddings.cs b/ObjectLayoutInspector.Tests.X64/ExcessivePaddings.cs new file mode 100644 index 0000000..9cdac40 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/ExcessivePaddings.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class ExcessivePaddings : Tests.ExcessivePaddings + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/FixedTests.cs b/ObjectLayoutInspector.Tests.X64/FixedTests.cs new file mode 100644 index 0000000..bd93392 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/FixedTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class FixedTests : Tests.FixedTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/GenericStructsTests.cs b/ObjectLayoutInspector.Tests.X64/GenericStructsTests.cs new file mode 100644 index 0000000..37006e4 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/GenericStructsTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class GenericStructsTests : Tests.GenericStructsTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/InstanceSizeTests.cs b/ObjectLayoutInspector.Tests.X64/InstanceSizeTests.cs new file mode 100644 index 0000000..8db8976 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/InstanceSizeTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class InstanceSizeTests : Tests.InstanceSizeTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/NullableTests.cs b/ObjectLayoutInspector.Tests.X64/NullableTests.cs new file mode 100644 index 0000000..4d7cebd --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/NullableTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class NullableTests : Tests.NullableTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/ObjectLayoutInspector.Tests.X64.csproj b/ObjectLayoutInspector.Tests.X64/ObjectLayoutInspector.Tests.X64.csproj new file mode 100644 index 0000000..7ae51c3 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/ObjectLayoutInspector.Tests.X64.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.0;netcoreapp3.1;net6;net9;net8;net481 + 8.0 + enable + true + x64 + + + + + + + + + + + + diff --git a/ObjectLayoutInspector.Tests.X64/PaddingTests.cs b/ObjectLayoutInspector.Tests.X64/PaddingTests.cs new file mode 100644 index 0000000..f78060a --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/PaddingTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class PaddingTests : Tests.PaddingTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/RawLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/RawLayoutTests.cs new file mode 100644 index 0000000..afb3c66 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/RawLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class RawLayoutTests : Tests.RawLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/StructLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/StructLayoutTests.cs new file mode 100644 index 0000000..fedcd94 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/StructLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class StructLayoutTests : Tests.StructLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/StructSizeTests.cs b/ObjectLayoutInspector.Tests.X64/StructSizeTests.cs new file mode 100644 index 0000000..3fe157d --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/StructSizeTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class StructSizeTests : Tests.StructSizeTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X64/StructWithExplicitLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/StructWithExplicitLayoutTests.cs new file mode 100644 index 0000000..a3505d2 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/StructWithExplicitLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class StructWithExplicitLayoutTests : Tests.StructWithExplicitLayoutTests + { + } +} diff --git a/ObjectLayoutInspector.Tests.X64/UnsafeLayoutTests.cs b/ObjectLayoutInspector.Tests.X64/UnsafeLayoutTests.cs new file mode 100644 index 0000000..e8562ef --- /dev/null +++ b/ObjectLayoutInspector.Tests.X64/UnsafeLayoutTests.cs @@ -0,0 +1,10 @@ +using NUnit.Framework; +using NUnit.Framework.Internal; + +namespace ObjectLayoutInspector.Tests.X64 +{ + [TestFixture] + public class UnsafeLayoutTests : Tests.UnsafeLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/AsyncStateMachineLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/AsyncStateMachineLayoutTests.cs new file mode 100644 index 0000000..2662aa9 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/AsyncStateMachineLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class AsyncStateMachineLayoutTests : Tests.AsyncStateMachineLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/ClassLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/ClassLayoutTests.cs new file mode 100644 index 0000000..8b35396 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/ClassLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class ClassLayoutTests : Tests.ClassLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/ClassWithExplicitLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/ClassWithExplicitLayoutTests.cs new file mode 100644 index 0000000..1db9cac --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/ClassWithExplicitLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class ClassWithExplicitLayoutTests : Tests.ClassWithExplicitLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/ClassWithNestedStructPrinterTests.cs b/ObjectLayoutInspector.Tests.X86/ClassWithNestedStructPrinterTests.cs new file mode 100644 index 0000000..c37bb57 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/ClassWithNestedStructPrinterTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class ClassWithNestedStructPrinterTests : Tests.ClassWithNestedStructPrinterTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/ComplexUnsafeLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/ComplexUnsafeLayoutTests.cs new file mode 100644 index 0000000..cd3dc50 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/ComplexUnsafeLayoutTests.cs @@ -0,0 +1,12 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class ComplexUnsafeLayoutTests : Tests.ComplexUnsafeLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/CorefxTypesTests.cs b/ObjectLayoutInspector.Tests.X86/CorefxTypesTests.cs new file mode 100644 index 0000000..da5412e --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/CorefxTypesTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class CorefxTypesTests : Tests.CorefxTypesTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/ExcessivePaddings.cs b/ObjectLayoutInspector.Tests.X86/ExcessivePaddings.cs new file mode 100644 index 0000000..68c72d4 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/ExcessivePaddings.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class ExcessivePaddings : Tests.ExcessivePaddings + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/FixedTests.cs b/ObjectLayoutInspector.Tests.X86/FixedTests.cs new file mode 100644 index 0000000..26adc81 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/FixedTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class FixedTests : Tests.FixedTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/GenericStructsTests.cs b/ObjectLayoutInspector.Tests.X86/GenericStructsTests.cs new file mode 100644 index 0000000..fc4051a --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/GenericStructsTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class GenericStructsTests : Tests.GenericStructsTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/InstanceSizeTests.cs b/ObjectLayoutInspector.Tests.X86/InstanceSizeTests.cs new file mode 100644 index 0000000..8c11249 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/InstanceSizeTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class InstanceSizeTests : Tests.InstanceSizeTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/NullableTests.cs b/ObjectLayoutInspector.Tests.X86/NullableTests.cs new file mode 100644 index 0000000..d0d0e3f --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/NullableTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class NullableTests : Tests.NullableTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/ObjectLayoutInspector.Tests.X86.csproj b/ObjectLayoutInspector.Tests.X86/ObjectLayoutInspector.Tests.X86.csproj new file mode 100644 index 0000000..f598492 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/ObjectLayoutInspector.Tests.X86.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.0;netcoreapp3.1;net9;net8;net481 + 8.0 + enable + true + x86 + + + + + + + + + + + + + diff --git a/ObjectLayoutInspector.Tests.X86/PaddingTests.cs b/ObjectLayoutInspector.Tests.X86/PaddingTests.cs new file mode 100644 index 0000000..9b5a3c9 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/PaddingTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class PaddingTests : Tests.PaddingTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/RawLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/RawLayoutTests.cs new file mode 100644 index 0000000..a132728 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/RawLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class RawLayoutTests : Tests.RawLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/StructLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/StructLayoutTests.cs new file mode 100644 index 0000000..96ce979 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/StructLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class StructLayoutTests : Tests.StructLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/StructSizeTests.cs b/ObjectLayoutInspector.Tests.X86/StructSizeTests.cs new file mode 100644 index 0000000..81047bc --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/StructSizeTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class StructSizeTests : Tests.StructSizeTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.Tests.X86/StructWithExplicitLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/StructWithExplicitLayoutTests.cs new file mode 100644 index 0000000..d5cb2a7 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/StructWithExplicitLayoutTests.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class StructWithExplicitLayoutTests : Tests.StructWithExplicitLayoutTests + { + } +} diff --git a/ObjectLayoutInspector.Tests.X86/UnsafeLayoutTests.cs b/ObjectLayoutInspector.Tests.X86/UnsafeLayoutTests.cs new file mode 100644 index 0000000..09dbc61 --- /dev/null +++ b/ObjectLayoutInspector.Tests.X86/UnsafeLayoutTests.cs @@ -0,0 +1,10 @@ +using NUnit.Framework; +using NUnit.Framework.Internal; + +namespace ObjectLayoutInspector.Tests.X86 +{ + [TestFixture] + public class UnsafeLayoutTests : Tests.UnsafeLayoutTests + { + } +} \ No newline at end of file diff --git a/ObjectLayoutInspector.sln b/ObjectLayoutInspector.sln index 7197cdb..131407f 100644 --- a/ObjectLayoutInspector.sln +++ b/ObjectLayoutInspector.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 16 -VisualStudioVersion = 16.0.28803.156 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35728.132 d17.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObjectLayoutInspector", "src\ObjectLayoutInspector\ObjectLayoutInspector.csproj", "{D89F0520-ED92-4413-BCB2-5365C13447EB}" EndProject @@ -9,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectLayoutInspector.Tests EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectLayoutInspector.Benchmarks", "src\ObjectLayoutInspector.Benchmarks\ObjectLayoutInspector.Benchmarks.csproj", "{B7CA1732-19EB-43B7-9D51-04A7984F03D7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectLayoutInspector.Tests.X86", "ObjectLayoutInspector.Tests.X86\ObjectLayoutInspector.Tests.X86.csproj", "{172B168F-FE58-49CB-AF90-669FB96FC077}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectLayoutInspector.Tests.X64", "ObjectLayoutInspector.Tests.X64\ObjectLayoutInspector.Tests.X64.csproj", "{0780619B-6219-4C3D-81F9-CE59A9498B10}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +31,14 @@ Global {B7CA1732-19EB-43B7-9D51-04A7984F03D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {B7CA1732-19EB-43B7-9D51-04A7984F03D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7CA1732-19EB-43B7-9D51-04A7984F03D7}.Release|Any CPU.Build.0 = Release|Any CPU + {172B168F-FE58-49CB-AF90-669FB96FC077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {172B168F-FE58-49CB-AF90-669FB96FC077}.Debug|Any CPU.Build.0 = Debug|Any CPU + {172B168F-FE58-49CB-AF90-669FB96FC077}.Release|Any CPU.ActiveCfg = Release|Any CPU + {172B168F-FE58-49CB-AF90-669FB96FC077}.Release|Any CPU.Build.0 = Release|Any CPU + {0780619B-6219-4C3D-81F9-CE59A9498B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0780619B-6219-4C3D-81F9-CE59A9498B10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0780619B-6219-4C3D-81F9-CE59A9498B10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0780619B-6219-4C3D-81F9-CE59A9498B10}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ObjectLayoutInspector.Benchmarks/TypeVsRecursiveVsFlat.cs b/src/ObjectLayoutInspector.Benchmarks/TypeVsRecursiveVsFlat.cs index 5038bde..2887f1c 100644 --- a/src/ObjectLayoutInspector.Benchmarks/TypeVsRecursiveVsFlat.cs +++ b/src/ObjectLayoutInspector.Benchmarks/TypeVsRecursiveVsFlat.cs @@ -1,6 +1,6 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; -using ObjectLayoutInspector.Tests; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Benchmarks { diff --git a/src/ObjectLayoutInspector.Tests/AsyncStateMachineLayoutTests.cs b/src/ObjectLayoutInspector.Tests/AsyncStateMachineLayoutTests.cs index d9b06b0..de14297 100644 --- a/src/ObjectLayoutInspector.Tests/AsyncStateMachineLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/AsyncStateMachineLayoutTests.cs @@ -15,11 +15,13 @@ public async Task WithTask() return 42; } +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER public async ValueTask WithValueTask() { await Task.Yield(); return 42; } +#endif } [TestFixture] @@ -32,12 +34,14 @@ public void AsyncTaskStateMachineLayout() TypeLayout.PrintLayout(taskStateMachine); } +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER [Test] public void AsyncValueTaskStateMachineLayout() { var (_, valueTask) = GetStateMachineTypes(); TypeLayout.PrintLayout(valueTask); } +#endif private static (Type taskStateMachine, Type valueTaskStateMachine) GetStateMachineTypes() { @@ -45,8 +49,14 @@ private static (Type taskStateMachine, Type valueTaskStateMachine) GetStateMachi t.FullName!.Contains("AsyncSample") && t.FullName.Contains(">d__")).ToList(); var taskStateMachine = types.First(t => t.FullName!.Contains(nameof(AsyncSample.WithTask))); + +#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER var valueTaskStateMachine = types.First(t => t.FullName!.Contains(nameof(AsyncSample.WithValueTask))); + return (taskStateMachine, valueTaskStateMachine); +#else + return (taskStateMachine, null!); +#endif } } -} \ No newline at end of file + } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/ClassLayoutTests.cs b/src/ObjectLayoutInspector.Tests/ClassLayoutTests.cs index a07162b..bc0b6ec 100644 --- a/src/ObjectLayoutInspector.Tests/ClassLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/ClassLayoutTests.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using NUnit.Framework.Internal; +using ObjectLayoutInspector.Tests.Objects; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector.Tests/ClassWithExplicitLayoutTests.cs b/src/ObjectLayoutInspector.Tests/ClassWithExplicitLayoutTests.cs index acc9637..866c3db 100644 --- a/src/ObjectLayoutInspector.Tests/ClassWithExplicitLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/ClassWithExplicitLayoutTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using ObjectLayoutInspector.Tests.Objects; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector.Tests/ClassWithNestedStructPrinterTests.cs b/src/ObjectLayoutInspector.Tests/ClassWithNestedStructPrinterTests.cs index 6227825..e9dc833 100644 --- a/src/ObjectLayoutInspector.Tests/ClassWithNestedStructPrinterTests.cs +++ b/src/ObjectLayoutInspector.Tests/ClassWithNestedStructPrinterTests.cs @@ -1,4 +1,7 @@ using NUnit.Framework; +using ObjectLayoutInspector.Tests.Objects; +using ObjectLayoutInspector.Tests.Structs; +using System; namespace ObjectLayoutInspector.Tests { @@ -9,6 +12,35 @@ public class ClassWithNestedStructPrinterTests public void Print() { TypeLayout.PrintLayout(); + var lo = TypeLayout.GetLayout(); + } + + [Test] + public void PrintString() + { + TypeLayout.PrintLayout(); + var lo = TypeLayout.GetLayout(); + } + + [Test] + public void PrintObject() + { + TypeLayout.PrintLayout(); + var lo = TypeLayout.GetLayout(); + } + + [Test] + public void PrintNullableInt() + { + TypeLayout.PrintLayout(); + var lo = TypeLayout.GetLayout(); + } + + [Test] + public void PrintNullable() + { + TypeLayout.PrintLayout(); + var lo = TypeLayout.GetLayout(); } } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/ComplexUnsafeLayoutTests.cs b/src/ObjectLayoutInspector.Tests/ComplexUnsafeLayoutTests.cs index cec1b31..3c6c223 100644 --- a/src/ObjectLayoutInspector.Tests/ComplexUnsafeLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/ComplexUnsafeLayoutTests.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; +using System.Runtime.CompilerServices; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { @@ -19,13 +21,24 @@ public class ComplexUnsafeLayoutTests : TestsBase [Test] public void ComplexStruct() => AssertNonRecursive(); + [Test] + public void DoubleFloatStruct() => AssertNonRecursive(); + + [Test] + public void EnumIntStruct() => AssertNonRecursive(); + [Test] public void VeryVeryComplexStructFields() { + LayoutPrinter.Print(true); var structLayout = UnsafeLayout.GetFieldsLayout(recursive:true); var six = structLayout[5]; +#if NET6_0_OR_GREATER //The layout changes on .NET 6 Assert.AreEqual(16, six.Offset); +#else + Assert.AreEqual(8 + IntPtr.Size, six.Offset); +#endif Assert.AreEqual(8, six.Size); Assert.AreEqual(typeof(double), six.FieldInfo.FieldType); Assert.AreEqual(2, structLayout.Where(x=> x.FieldInfo.FieldType == (typeof(double))).Count()); @@ -37,8 +50,12 @@ public void VeryVeryComplexStructFields() [Test] public void MASTER_STRUCTFieldsRecursive() { + LayoutPrinter.Print(true); var structLayout = UnsafeLayout.GetLayout(recursive: true, new List { typeof(Guid) }); - Assert.AreEqual(7, structLayout.Count()); + Assert.AreEqual(8 + (IntPtr.Size is 8 ? 1: 0), structLayout.Count());//In x86 there is less padding at end + //Not perfect but better assertion due to padding interleaving + //different .NET versions use different approach to layout the STRUCT2 + //sometimes the padding in inside the struct instead of end } [Test] @@ -59,9 +76,9 @@ public void ExplicitHolderOfSequentialIntObjectStructFields() var layout = UnsafeLayout.GetFieldsLayout(); Assert.AreEqual(2, layout.Count); Assert.AreEqual(0, layout[0].Offset); - Assert.AreEqual(8, layout[0].Size); + Assert.AreEqual(IntPtr.Size, layout[0].Size); Assert.AreEqual(typeof(object), layout[0].FieldInfo.FieldType); - Assert.AreEqual(8, layout[1].Offset); + Assert.AreEqual(IntPtr.Size, layout[1].Offset); Assert.AreEqual(4, layout[1].Size); Assert.AreEqual(typeof(int), layout[1].FieldInfo.FieldType); } @@ -76,7 +93,7 @@ public void GetFieldsLayoutStructsWorkFineAndThisIsMostImportantForSerialization Assert.AreEqual(typeof(double), structLayout[0].FieldInfo.FieldType); Assert.AreEqual(8, structLayout[1].Offset); Assert.AreEqual(4, structLayout[1].Size); - Assert.AreEqual(typeof(Single), structLayout[1].FieldInfo.FieldType); + Assert.AreEqual(typeof(float), structLayout[1].FieldInfo.FieldType); Assert.AreEqual(16, structLayout[2].Offset); Assert.AreEqual(1, structLayout[2].Size); Assert.AreEqual(typeof(ByteEnum), structLayout[2].FieldInfo.FieldType); @@ -84,5 +101,14 @@ public void GetFieldsLayoutStructsWorkFineAndThisIsMostImportantForSerialization Assert.AreEqual(4, structLayout[3].Size); Assert.AreEqual(typeof(int), structLayout[3].FieldInfo.FieldType); } + + [Test] + public unsafe void UnionHasCorrectSize() + { + Assert.AreEqual(Math.Max(sizeof(STRUCT1), sizeof(STRUCT2)), sizeof(MASTER_STRUCT_UNION)); + Assert.AreEqual(Math.Max(sizeof(STRUCT1), sizeof(STRUCT2)), sizeof(MASTER_STRUCT)); + Assert.AreEqual(Math.Max(Unsafe.SizeOf(), Unsafe.SizeOf()), Unsafe.SizeOf()); + Assert.AreEqual(Math.Max(Unsafe.SizeOf(), Unsafe.SizeOf()), Unsafe.SizeOf()); + } } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/CorefxTypesTests.cs b/src/ObjectLayoutInspector.Tests/CorefxTypesTests.cs index 51d9c9d..cf2dc86 100644 --- a/src/ObjectLayoutInspector.Tests/CorefxTypesTests.cs +++ b/src/ObjectLayoutInspector.Tests/CorefxTypesTests.cs @@ -4,6 +4,7 @@ using System.Numerics; using NUnit.Framework; using NUnit.Framework.Internal; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector.Tests/FixedTests.cs b/src/ObjectLayoutInspector.Tests/FixedTests.cs index e88c80d..9ee80c9 100644 --- a/src/ObjectLayoutInspector.Tests/FixedTests.cs +++ b/src/ObjectLayoutInspector.Tests/FixedTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { @@ -11,6 +12,7 @@ public class FixedTests : TestsBase [Test] public void FixedBytesNoPadding() { + TypeLayout.PrintLayout(recursively: true); var typeLayout = TypeLayout.GetLayout(includePaddings: true); Assert.That(typeLayout.Paddings, Is.EqualTo(0)); } diff --git a/src/ObjectLayoutInspector.Tests/GenericStructsTests.cs b/src/ObjectLayoutInspector.Tests/GenericStructsTests.cs index 37d0989..aa24ad0 100644 --- a/src/ObjectLayoutInspector.Tests/GenericStructsTests.cs +++ b/src/ObjectLayoutInspector.Tests/GenericStructsTests.cs @@ -1,5 +1,6 @@ using System.Linq; using NUnit.Framework; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector.Tests/InstanceSizeTests.cs b/src/ObjectLayoutInspector.Tests/InstanceSizeTests.cs index d6ee63a..6601044 100644 --- a/src/ObjectLayoutInspector.Tests/InstanceSizeTests.cs +++ b/src/ObjectLayoutInspector.Tests/InstanceSizeTests.cs @@ -1,5 +1,7 @@ using System; +using System.Linq; using NUnit.Framework; +using ObjectLayoutInspector.Tests.Objects; namespace ObjectLayoutInspector.Tests { @@ -17,7 +19,7 @@ public void SizeForEmptyClassIs3PtrSizes() public void NoFieldsForEmptyClass() { var layout = TypeLayout.GetLayout(); - Assert.That(layout.Fields, Is.Empty); + Assert.That(layout.Fields.OfType(), Is.Empty); } } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/NullableTests.cs b/src/ObjectLayoutInspector.Tests/NullableTests.cs index 86adf24..974895a 100644 --- a/src/ObjectLayoutInspector.Tests/NullableTests.cs +++ b/src/ObjectLayoutInspector.Tests/NullableTests.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using NUnit.Framework; using NUnit.Framework.Internal; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector.Tests/ObjectLayoutInspector.Tests.csproj b/src/ObjectLayoutInspector.Tests/ObjectLayoutInspector.Tests.csproj index 554fb0a..b34410f 100644 --- a/src/ObjectLayoutInspector.Tests/ObjectLayoutInspector.Tests.csproj +++ b/src/ObjectLayoutInspector.Tests/ObjectLayoutInspector.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.0;netcoreapp3.1;net6;net9;net8;net481 8.0 enable true diff --git a/src/ObjectLayoutInspector.Tests/Objects/Base.cs b/src/ObjectLayoutInspector.Tests/Objects/Base.cs index 2cb2c16..291cdfc 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/Base.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/Base.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Objects { #pragma warning disable 169 // unused fields class Base diff --git a/src/ObjectLayoutInspector.Tests/Objects/ByteAndInt.cs b/src/ObjectLayoutInspector.Tests/Objects/ByteAndInt.cs index 08c6f27..ab96468 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/ByteAndInt.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/ByteAndInt.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Objects { class ByteAndInt { diff --git a/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayout.cs b/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayout.cs index 8dae21a..f6bd271 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayout.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayout.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Objects { [StructLayout(LayoutKind.Explicit)] public class ClassWithExplicitLayout diff --git a/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayoutAndOffsetForFirstField.cs b/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayoutAndOffsetForFirstField.cs index 050aada..f94fa0b 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayoutAndOffsetForFirstField.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/ClassWithExplicitLayoutAndOffsetForFirstField.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Objects { [StructLayout(LayoutKind.Explicit, Size = 100)] public class ClassWithExplicitLayoutAndOffsetForFirstField diff --git a/src/ObjectLayoutInspector.Tests/Objects/ClassWithNestedCustomStruct.cs b/src/ObjectLayoutInspector.Tests/Objects/ClassWithNestedCustomStruct.cs index 70234f6..0638119 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/ClassWithNestedCustomStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/ClassWithNestedCustomStruct.cs @@ -1,4 +1,6 @@ -namespace ObjectLayoutInspector.Tests +using ObjectLayoutInspector.Tests.Structs; + +namespace ObjectLayoutInspector.Tests.Objects { public class ClassWithNestedCustomStruct { diff --git a/src/ObjectLayoutInspector.Tests/Objects/Derived.cs b/src/ObjectLayoutInspector.Tests/Objects/Derived.cs index 3ff1034..9b883e4 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/Derived.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/Derived.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Objects { #pragma warning disable 169 // unused fields class Derived : Base diff --git a/src/ObjectLayoutInspector.Tests/Objects/EmptyClass.cs b/src/ObjectLayoutInspector.Tests/Objects/EmptyClass.cs index eb72363..d7d5a9f 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/EmptyClass.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/EmptyClass.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Objects { class EmptyClass { } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/Objects/ListLayout.cs b/src/ObjectLayoutInspector.Tests/Objects/ListLayout.cs new file mode 100644 index 0000000..c4b5bb8 --- /dev/null +++ b/src/ObjectLayoutInspector.Tests/Objects/ListLayout.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ObjectLayoutInspector.Tests.Objects +{ + internal class ListLayout + { + } +} diff --git a/src/ObjectLayoutInspector.Tests/Objects/NotAlignedClass.cs b/src/ObjectLayoutInspector.Tests/Objects/NotAlignedClass.cs index e058cc6..b490f93 100644 --- a/src/ObjectLayoutInspector.Tests/Objects/NotAlignedClass.cs +++ b/src/ObjectLayoutInspector.Tests/Objects/NotAlignedClass.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Objects { [StructLayout(LayoutKind.Auto)] public class NotAlignedClass diff --git a/src/ObjectLayoutInspector.Tests/PaddingTests.cs b/src/ObjectLayoutInspector.Tests/PaddingTests.cs index 1ba6976..49d1cff 100644 --- a/src/ObjectLayoutInspector.Tests/PaddingTests.cs +++ b/src/ObjectLayoutInspector.Tests/PaddingTests.cs @@ -1,5 +1,6 @@ using System.Linq; using NUnit.Framework; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector.Tests/RawLayoutTests.cs b/src/ObjectLayoutInspector.Tests/RawLayoutTests.cs index 732492b..1d3c912 100644 --- a/src/ObjectLayoutInspector.Tests/RawLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/RawLayoutTests.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Linq; using NUnit.Framework; +using ObjectLayoutInspector.Tests.Objects; namespace ObjectLayoutInspector.Tests { @@ -22,6 +23,8 @@ public void PrivateBaseMembersShouldBeIncluded() { var offsets = TypeInspector.GetFieldOffsets(typeof(Derived)); Assert.That(offsets.Length, Is.EqualTo(2)); + Assert.That(offsets[0].fieldInfo.DeclaringType, Is.EqualTo(typeof(Base))); + Assert.That(offsets[1].fieldInfo.DeclaringType, Is.EqualTo(typeof(Derived))); } } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/RunManually/LayoutsForAllKnownTypes.cs b/src/ObjectLayoutInspector.Tests/RunManually/LayoutsForAllKnownTypes.cs index 3208156..3806851 100644 --- a/src/ObjectLayoutInspector.Tests/RunManually/LayoutsForAllKnownTypes.cs +++ b/src/ObjectLayoutInspector.Tests/RunManually/LayoutsForAllKnownTypes.cs @@ -45,7 +45,7 @@ public void GetLayoutsForAllTypes() var layouts = GetAllLoadedTypes() //.Where(t => t.Assembly.FullName.Contains("mscorlib")) .Select(t => TypeLayout.TryGetLayout(t, cache)) - .Where(t => t != null) + .Where(t => t.HasValue) .Select(t => t!.Value) .ToList(); diff --git a/src/ObjectLayoutInspector.Tests/StructLayoutTests.cs b/src/ObjectLayoutInspector.Tests/StructLayoutTests.cs index 312d305..c4f7350 100644 --- a/src/ObjectLayoutInspector.Tests/StructLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/StructLayoutTests.cs @@ -1,4 +1,9 @@ using NUnit.Framework; +using System.Runtime.CompilerServices; +using System; +using ObjectLayoutInspector.Tests.Structs; +using System.Linq; +using System.Numerics; namespace ObjectLayoutInspector.Tests { @@ -45,7 +50,149 @@ public void Print_WithVolatile() TypeLayout.PrintLayout(); var typeLayout = TypeLayout.GetLayout(); - Assert.That(typeLayout.FullSize, Is.EqualTo(16)); + Assert.That(typeLayout.FullSize, Is.EqualTo(8 + IntPtr.Size)); } + +#if NET8_0_OR_GREATER + [Test] + public void Print_Inline1_byte() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(sizeof(byte))); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline10_byte() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(10 * sizeof(byte))); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline1_long() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(sizeof(long))); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline10_long() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(10 * sizeof(long))); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline1_object() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(IntPtr.Size)); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline10_object() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(10 * IntPtr.Size)); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline1_Complex() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline10_Complex() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(10 * Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline1_ComplexNullable() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline10_ComplexNullable() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(10 * Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline1_BoolBoolStruct() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline10_BoolBoolStruct() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(10 * Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline1_EmptyStruct() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } + + [Test] + public void Print_Inline10_EmptyStruct() + { + TypeLayout.PrintLayout>(); + var unsafeLayout = UnsafeLayout.GetLayout>(); + var typeLayout = TypeLayout.GetLayout>(); + Assert.That(typeLayout.FullSize, Is.EqualTo(10 * Unsafe.SizeOf())); + Assert.That(typeLayout.Fields.All(x => x is FieldLayout), "Has no padding"); + } +#endif } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/StructSizeTests.cs b/src/ObjectLayoutInspector.Tests/StructSizeTests.cs index dc7d72d..42fb7c1 100644 --- a/src/ObjectLayoutInspector.Tests/StructSizeTests.cs +++ b/src/ObjectLayoutInspector.Tests/StructSizeTests.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using NUnit.Framework; using NUnit.Framework.Internal; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { @@ -13,5 +14,11 @@ public void TestStructWithExplicitSize() { Console.WriteLine(Marshal.SizeOf(typeof(MyStruct))); } + + [Test] + public void TestStructWithPointer() + { + Console.WriteLine(Marshal.SizeOf(typeof(NintStruct))); + } } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/StructWithExplicitLayoutTests.cs b/src/ObjectLayoutInspector.Tests/StructWithExplicitLayoutTests.cs index ea0cef5..7183b9b 100644 --- a/src/ObjectLayoutInspector.Tests/StructWithExplicitLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/StructWithExplicitLayoutTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector.Tests/Structs/BoolBoolStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/BoolBoolStruct.cs index 238e936..2c0f73e 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/BoolBoolStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/BoolBoolStruct.cs @@ -1,8 +1,8 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct BoolBoolStruct { public bool one; public bool two; - } + } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/Structs/ByteEnum.cs b/src/ObjectLayoutInspector.Tests/Structs/ByteEnum.cs index c639736..6cb3970 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ByteEnum.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ByteEnum.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public enum ByteEnum : byte { diff --git a/src/ObjectLayoutInspector.Tests/Structs/ClrWillReorderThisStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/ClrWillReorderThisStruct.cs index de7b678..8ac39d6 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ClrWillReorderThisStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ClrWillReorderThisStruct.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { // The following attribute has no effect on the layout! [StructLayout(LayoutKind.Sequential, Pack = 1)] diff --git a/src/ObjectLayoutInspector.Tests/Structs/ComplexFixedStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/ComplexFixedStruct.cs index cf6f287..65df72a 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ComplexFixedStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ComplexFixedStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public unsafe struct ComplexFixedStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/ComplexStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/ComplexStruct.cs index 230fe7e..81d5faa 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ComplexStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ComplexStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct ComplexStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/ComplexUnionCaseFromSO.cs b/src/ObjectLayoutInspector.Tests/Structs/ComplexUnionCaseFromSO.cs index a3b3e7f..4e76fab 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ComplexUnionCaseFromSO.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ComplexUnionCaseFromSO.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Sequential)] @@ -9,8 +9,8 @@ internal struct STRUCT1 { public Guid guid; - public String str1; - public String str2; + public string str1; + public string str2; } [StructLayout(LayoutKind.Sequential)] @@ -18,10 +18,10 @@ internal struct STRUCT2 { public Guid guid; - public String str1; - public String str2; + public string str1; + public string str2; - public Int32 i1; + public int i1; } [StructLayout(LayoutKind.Explicit)] diff --git a/src/ObjectLayoutInspector.Tests/Structs/CustomPrimitive.cs b/src/ObjectLayoutInspector.Tests/Structs/CustomPrimitive.cs index 6439560..aef76d3 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/CustomPrimitive.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/CustomPrimitive.cs @@ -1,6 +1,6 @@ using System.Numerics; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct CustomPrimitive { diff --git a/src/ObjectLayoutInspector.Tests/Structs/DoubleByteStructByteStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/DoubleByteStructByteStruct.cs index 093be1d..04541d8 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/DoubleByteStructByteStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/DoubleByteStructByteStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct DoubleByteStructByteStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/DoubleFloatStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/DoubleFloatStruct.cs index 955b3a3..1fa1e0a 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/DoubleFloatStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/DoubleFloatStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct DoubleFloatStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/EmptyStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/EmptyStruct.cs index bc6ceb7..87f4b08 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/EmptyStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/EmptyStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct EmptyStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/EnumIntStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/EnumIntStruct.cs index e03edb3..7e5819c 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/EnumIntStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/EnumIntStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct EnumIntStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/EnumStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/EnumStruct.cs index ad8bfb1..5a5a3c8 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/EnumStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/EnumStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct EnumStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/ExplicitHolderOfSequentialIntObject.cs b/src/ObjectLayoutInspector.Tests/Structs/ExplicitHolderOfSequentialIntObject.cs index fa43a43..c46211d 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ExplicitHolderOfSequentialIntObject.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ExplicitHolderOfSequentialIntObject.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Explicit)] internal struct ExplicitHolderOfSequentialIntObjectStruct diff --git a/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutNoOverlapStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutNoOverlapStruct.cs index b053208..c4e3788 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutNoOverlapStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutNoOverlapStruct.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Explicit)] public struct ExplicitLayoutNoOverlapStruct diff --git a/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutOverlapStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutOverlapStruct.cs index 246222b..6cc259e 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutOverlapStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/ExplicitLayoutOverlapStruct.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Explicit)] public struct ExplicitLayoutOverlapStruct diff --git a/src/ObjectLayoutInspector.Tests/Structs/FixedBytes.cs b/src/ObjectLayoutInspector.Tests/Structs/FixedBytes.cs index e00d84d..17a72bc 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/FixedBytes.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/FixedBytes.cs @@ -1,6 +1,6 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { - public unsafe struct FixedBytes + public unsafe struct FixedBytes { public const int MaxLength = 33; private fixed byte _bytes[MaxLength]; diff --git a/src/ObjectLayoutInspector.Tests/Structs/FloatFloatFloatStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/FloatFloatFloatStruct.cs index e51740d..9b9e007 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/FloatFloatFloatStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/FloatFloatFloatStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct FloatFloatFloatStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/FloatFloatStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/FloatFloatStruct.cs index e0626e9..1d885d3 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/FloatFloatStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/FloatFloatStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct FloatFloatStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/GenericStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/GenericStruct.cs index 537fbd9..bbf97eb 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/GenericStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/GenericStruct.cs @@ -1,6 +1,6 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { - public struct GenericStruct where T: struct + public struct GenericStruct where T : struct { public bool one; public T two; diff --git a/src/ObjectLayoutInspector.Tests/Structs/InlineArray.cs b/src/ObjectLayoutInspector.Tests/Structs/InlineArray.cs new file mode 100644 index 0000000..06e3cc8 --- /dev/null +++ b/src/ObjectLayoutInspector.Tests/Structs/InlineArray.cs @@ -0,0 +1,16 @@ +#if NET8_0_OR_GREATER +namespace ObjectLayoutInspector.Tests.Structs +{ + [System.Runtime.CompilerServices.InlineArray(10)] + struct Inline10 + { + public T item; + } + + [System.Runtime.CompilerServices.InlineArray(1)] + struct Inline1 + { + public T item; + } +} +#endif diff --git a/src/ObjectLayoutInspector.Tests/Structs/LongByteStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/LongByteStruct.cs index 8baf956..1f56300 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/LongByteStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/LongByteStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct LongByteStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/LongStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/LongStruct.cs index fc1bd99..c150f5c 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/LongStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/LongStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct LongStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/MyStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/MyStruct.cs index f215347..ca9df93 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/MyStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/MyStruct.cs @@ -1,10 +1,10 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Sequential, Size = 42)] struct MyStruct { - + } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/Structs/NestedStructWithOneField.cs b/src/ObjectLayoutInspector.Tests/Structs/NestedStructWithOneField.cs index 838dded..7c2be7e 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/NestedStructWithOneField.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/NestedStructWithOneField.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct NestedStructWithOneField { diff --git a/src/ObjectLayoutInspector.Tests/Structs/NintStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/NintStruct.cs new file mode 100644 index 0000000..8438a7c --- /dev/null +++ b/src/ObjectLayoutInspector.Tests/Structs/NintStruct.cs @@ -0,0 +1,9 @@ +using System; + +namespace ObjectLayoutInspector.Tests.Structs +{ + internal unsafe struct NintStruct + { + IntPtr ptr; + } +} diff --git a/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStruct.cs index 16e4c02..9a3f346 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStruct.cs @@ -1,6 +1,6 @@ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Auto)] public struct NotAlignedStruct diff --git a/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithAutoLayout.cs b/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithAutoLayout.cs index 51dea61..06a7760 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithAutoLayout.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithAutoLayout.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Auto)] public struct NotAlignedStructWithAutoLayout diff --git a/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithPack1.cs b/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithPack1.cs index 06134cc..28a8699 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithPack1.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/NotAlignedStructWithPack1.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct NotAlignedStructWithPack1 { diff --git a/src/ObjectLayoutInspector.Tests/Structs/SequentialIntObject.cs b/src/ObjectLayoutInspector.Tests/Structs/SequentialIntObject.cs index d16f567..2962ba6 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/SequentialIntObject.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/SequentialIntObject.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Sequential)] internal struct SequentialIntObjectStruct diff --git a/src/ObjectLayoutInspector.Tests/Structs/SingleByteStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/SingleByteStruct.cs index cb51b2a..b60755c 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/SingleByteStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/SingleByteStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct SingleByteStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayout.cs b/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayout.cs index 39bb086..bbfd48e 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayout.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayout.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Explicit)] public struct StructWithExplicitLayout diff --git a/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayoutAndOffsetForFirstField.cs b/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayoutAndOffsetForFirstField.cs index 6e0e566..d5b5b62 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayoutAndOffsetForFirstField.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/StructWithExplicitLayoutAndOffsetForFirstField.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Explicit, Size = 100)] public struct StructWithExplicitLayoutAndOffsetForFirstField diff --git a/src/ObjectLayoutInspector.Tests/Structs/UnionStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/UnionStruct.cs index 9f5402a..de64fc3 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/UnionStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/UnionStruct.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { [StructLayout(LayoutKind.Explicit)] public struct UnionStruct diff --git a/src/ObjectLayoutInspector.Tests/Structs/UshortEnum.cs b/src/ObjectLayoutInspector.Tests/Structs/UshortEnum.cs index 7f3a2e3..8c3d954 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/UshortEnum.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/UshortEnum.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public enum UshortEnum : ushort { diff --git a/src/ObjectLayoutInspector.Tests/Structs/VeryVeryComplexStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/VeryVeryComplexStruct.cs index f671bc7..c6e50c2 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/VeryVeryComplexStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/VeryVeryComplexStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public unsafe struct VeryVeryComplexStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeStruct.cs index 4cf4416..58c76fd 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithNestedUnsafeStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeUnsafeStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeUnsafeStruct.cs index 44b8161..bce6ff6 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeUnsafeStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithNestedUnsafeUnsafeStruct.cs @@ -1,9 +1,9 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithNestedUnsafeUnsafeStruct { public WithNestedUnsafeStruct fb; public WithNestedUnsafeUnsafeStruct(WithNestedUnsafeStruct fb) => this.fb = fb; - } + } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStruct.cs index 4a5c0e5..f53182d 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStruct.cs @@ -1,9 +1,9 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithNullableBoolBoolStructStruct { public BoolBoolStruct? one; public WithNullableBoolBoolStructStruct(BoolBoolStruct? one) => this.one = one; - } + } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStructStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStructStruct.cs index 34dd5ff..dd10eaf 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStructStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithNullableBoolBoolStructStructStruct.cs @@ -1,9 +1,9 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithNullableBoolBoolStructStructStruct { public WithNullableBoolBoolStructStruct? one; public WithNullableBoolBoolStructStructStruct(WithNullableBoolBoolStructStruct? one) => this.one = one; - } + } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntNullableBoolIntStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntNullableBoolIntStruct.cs index 122d99b..960aa0a 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntNullableBoolIntStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntNullableBoolIntStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithNullableIntIntStruct { @@ -15,5 +15,5 @@ public struct WithNullableIntNullableBoolIntStruct public int three; public WithNullableIntNullableBoolIntStruct(int? one, bool? two, int three) => (this.one, this.two, this.three) = (one, two, three); - } + } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntStruct.cs b/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntStruct.cs index d5260e8..71aeb9f 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntStruct.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithNullableIntStruct.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithNullableIntStruct { diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithString.cs b/src/ObjectLayoutInspector.Tests/Structs/WithString.cs index 3839bbc..0c29ade 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithString.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithString.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithString { diff --git a/src/ObjectLayoutInspector.Tests/Structs/WithVolatile.cs b/src/ObjectLayoutInspector.Tests/Structs/WithVolatile.cs index 2fff16e..86b3e2a 100644 --- a/src/ObjectLayoutInspector.Tests/Structs/WithVolatile.cs +++ b/src/ObjectLayoutInspector.Tests/Structs/WithVolatile.cs @@ -1,4 +1,4 @@ -namespace ObjectLayoutInspector.Tests +namespace ObjectLayoutInspector.Tests.Structs { public struct WithVolatile { diff --git a/src/ObjectLayoutInspector.Tests/TestsBase.cs b/src/ObjectLayoutInspector.Tests/TestsBase.cs index ad71f4c..da07101 100644 --- a/src/ObjectLayoutInspector.Tests/TestsBase.cs +++ b/src/ObjectLayoutInspector.Tests/TestsBase.cs @@ -5,22 +5,22 @@ namespace ObjectLayoutInspector.Tests { public abstract class TestsBase { - protected void AssertNonRecursiveWithPadding() where T : struct + protected static void AssertNonRecursiveWithPadding() where T : struct { TypeLayout.PrintLayout(); var structLayout = UnsafeLayout.GetLayout(recursive: false); var typeLayout = TypeLayout.GetLayout(includePaddings: true); - CollectionAssert.AreEquivalent(typeLayout.Fields, structLayout); Assert.AreEqual(Unsafe.SizeOf(), typeLayout.Size); + CollectionAssert.AreEquivalent(typeLayout.Fields, structLayout); } - protected void AssertNonRecursive() where T : struct + protected static void AssertNonRecursive() where T : struct { TypeLayout.PrintLayout(); var structLayout = UnsafeLayout.GetFieldsLayout(recursive: false); var typeLayout = TypeLayout.GetLayout(includePaddings: false); - CollectionAssert.AreEquivalent(typeLayout.Fields, structLayout); Assert.AreEqual(Unsafe.SizeOf(), typeLayout.Size); + CollectionAssert.AreEquivalent(typeLayout.Fields, structLayout); } } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector.Tests/UnsafeLayoutTests.cs b/src/ObjectLayoutInspector.Tests/UnsafeLayoutTests.cs index 8688983..4a50e81 100644 --- a/src/ObjectLayoutInspector.Tests/UnsafeLayoutTests.cs +++ b/src/ObjectLayoutInspector.Tests/UnsafeLayoutTests.cs @@ -1,6 +1,7 @@ using System.Linq; using NUnit.Framework; using NUnit.Framework.Internal; +using ObjectLayoutInspector.Tests.Structs; namespace ObjectLayoutInspector.Tests { diff --git a/src/ObjectLayoutInspector/AssemblyInfo.cs b/src/ObjectLayoutInspector/AssemblyInfo.cs index a209f2b..5c07178 100644 --- a/src/ObjectLayoutInspector/AssemblyInfo.cs +++ b/src/ObjectLayoutInspector/AssemblyInfo.cs @@ -1,4 +1,4 @@  using System.Runtime.CompilerServices; -[assembly:InternalsVisibleToAttribute("ObjectLayoutInspector.Tests")] \ No newline at end of file +[assembly:InternalsVisibleTo("ObjectLayoutInspector.Tests")] \ No newline at end of file diff --git a/src/ObjectLayoutInspector/BitArrayExtensions.cs b/src/ObjectLayoutInspector/BitArrayExtensions.cs new file mode 100644 index 0000000..7c2878e --- /dev/null +++ b/src/ObjectLayoutInspector/BitArrayExtensions.cs @@ -0,0 +1,43 @@ +using System.Collections; + +namespace ObjectLayoutInspector +{ + internal static class BitArrayExtensions + { + internal static BitArray SetRange(this BitArray bitArray, FieldLayoutBase fieldLayout, int additionalOffset = 0) + { + for (int i = fieldLayout.Offset; i < fieldLayout.Offset + fieldLayout.Size; i++) + { + bitArray.Set(i + additionalOffset, true); + } + return bitArray; + } + + internal static int GetRange(this BitArray bitArray, int offset) + { + int size = 0; + + while (offset < bitArray.Length) + { + if (bitArray.Get(offset)) + break; + bitArray.Set(offset, true); + offset++; + size++; + } + + return size; + } + + internal static int GetSetCount(this BitArray bitArray) + { + int count = 0; + for (int i = 0; i < bitArray.Length; i++) + { + if (bitArray.Get(i)) + count++; + } + return count; + } + } +} diff --git a/src/ObjectLayoutInspector/Detectors.cs b/src/ObjectLayoutInspector/Detectors.cs index 0caa6b2..4a91975 100644 --- a/src/ObjectLayoutInspector/Detectors.cs +++ b/src/ObjectLayoutInspector/Detectors.cs @@ -13,7 +13,7 @@ public static bool IsFixed(FieldInfo fieldInfo, out int fixedBuffer) .CustomAttributes.Where(x => x.AttributeType.Equals(typeof(FixedBufferAttribute))) .Select(x => x.ConstructorArguments) .FirstOrDefault(); - fixedBuffer = fixedCheck != null ? (int)fixedCheck[1].Value : 0; + fixedBuffer = fixedCheck is null ? 0 : (int)fixedCheck[1].Value; return fixedBuffer != 0; } @@ -21,7 +21,8 @@ public static bool IsPrimitive(FieldInfo fieldInfo) => fieldInfo.FieldType.IsPrimitive || fieldInfo.FieldType.IsEnum || fieldInfo.FieldType == typeof(decimal); public static bool IsNullable(FieldInfo fieldInfo) => IsNullable(fieldInfo.FieldType); + public static bool IsNullable(Type type) => - type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector/FieldLayoutBase.cs b/src/ObjectLayoutInspector/FieldLayoutBase.cs index 385bfca..be695ac 100644 --- a/src/ObjectLayoutInspector/FieldLayoutBase.cs +++ b/src/ObjectLayoutInspector/FieldLayoutBase.cs @@ -1,4 +1,7 @@ -using System.Reflection; +using ObjectLayoutInspector.Helpers; +using System; +using System.Diagnostics; +using System.Reflection; namespace ObjectLayoutInspector { @@ -10,16 +13,29 @@ public abstract class FieldLayoutBase /// /// Size of a field. /// +#if NET8_0_OR_GREATER + public virtual int Size { get; } +#else public int Size { get; } +#endif /// /// An offset of a field from the beginning of a struct. /// public int Offset { get; } + /// + /// Type which declared this field + /// + public abstract Type? DeclaringType { get; } + /// protected FieldLayoutBase(int offset, int size) { + if (size <= 0) + throw new ArgumentOutOfRangeException(nameof(size), size, "Must be positive"); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(size), size, "Must be not negative"); Offset = offset; Size = size; } @@ -28,13 +44,14 @@ protected FieldLayoutBase(int offset, int size) public override string ToString() { int fieldSize = Size; + string byteOrBytes = fieldSize == 1 ? "byte" : "bytes"; - string offsetStr = $"{Offset}-{Offset - 1 + fieldSize}"; + string offsetStr; if (fieldSize == 1) - { offsetStr = Offset.ToString(); - } + else + offsetStr = $"{Offset}-{Offset - 1 + fieldSize}"; return $"{offsetStr,5}: {NameOrDescription} ({fieldSize} {byteOrBytes})"; } @@ -49,14 +66,21 @@ public override string ToString() public sealed class FieldLayout : FieldLayoutBase { /// - public FieldLayout(int offset, FieldInfo fieldInfo, int size) + public FieldLayout(int offset, FieldInfo? fieldInfo, int size) : base(offset, size) { FieldInfo = fieldInfo; +#if NET8_0_OR_GREATER + InlineArrayLength = DeclaringType?.InlineArrayLength() ?? 0; +#endif } + /// - public FieldInfo FieldInfo { get; } + public FieldInfo? FieldInfo { get; } + + /// + public override Type? DeclaringType => FieldInfo?.DeclaringType; /// public override bool Equals(object obj) => @@ -66,14 +90,22 @@ obj is FieldLayout fieldLayout && FieldInfo == fieldLayout.FieldInfo; /// - public override int GetHashCode() - { - return (Offset, Size, FieldInfo).GetHashCode(); - } + public override int GetHashCode() => (Offset, Size, FieldInfo).GetHashCode(); /// protected override string NameOrDescription => - $"{FieldInfo.FieldType.Name} {FieldInfo.Name}"; +#if NET8_0_OR_GREATER + InlineArrayLength > 0 ? $"{FieldInfo?.FieldType.Name}[{InlineArrayLength}] {FieldInfo?.Name} for {DeclaringType?.Name}" : +#endif + $"{FieldInfo?.FieldType.Name} {FieldInfo?.Name} for {DeclaringType?.Name}"; + +#if NET8_0_OR_GREATER + /// + public sealed override int Size => InlineArrayLength > 0 ? InlineArrayLength * base.Size : base.Size; + + /// + public int InlineArrayLength { get; } +#endif } /// @@ -82,23 +114,26 @@ public override int GetHashCode() public sealed class Padding : FieldLayoutBase { /// - public Padding(int offset, int size) : base(offset, size) + public Padding(int offset, int size, Type? declaringType) : base(offset, size) { + DeclaringType = declaringType; } + /// + public override Type? DeclaringType { get; } + /// - protected override string NameOrDescription => "padding"; + protected override string NameOrDescription => + $"padding for {DeclaringType?.Name}"; /// public override bool Equals(object obj) => obj is Padding padding && Offset == padding.Offset - && Size == padding.Size; + && Size == padding.Size + && DeclaringType == padding.DeclaringType; /// - public override int GetHashCode() - { - return (Offset, Size).GetHashCode(); - } + public override int GetHashCode() => (Offset, Size, DeclaringType).GetHashCode(); } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector/Helpers/ReflectionHelper.cs b/src/ObjectLayoutInspector/Helpers/ReflectionHelper.cs index 90bbb84..17caa0c 100644 --- a/src/ObjectLayoutInspector/Helpers/ReflectionHelper.cs +++ b/src/ObjectLayoutInspector/Helpers/ReflectionHelper.cs @@ -12,13 +12,15 @@ internal static class ReflectionHelper /// /// Returns all instance fields including the fields declared in all base types. /// - public static FieldInfo[] GetInstanceFields(this Type type) + public static (FieldInfo[] fields, Type[] types) GetInstanceFields(this Type type) { - return GetBaseTypesAndThis(type).SelectMany(t => GetDeclaredFields(t)).Where(fi => !fi.IsStatic).ToArray(); + var types = GetBaseTypesAndThis(type).Reverse().ToArray(); + + return (types.SelectMany(t => GetDeclaredFields(t)).Where(fi => !fi.IsStatic).ToArray(), types); IEnumerable GetBaseTypesAndThis(Type t) { - while (t != null) + while (t is object) { yield return t; @@ -33,8 +35,7 @@ IEnumerable GetDeclaredFields(Type t) return ti.DeclaredFields; } - return t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | - BindingFlags.NonPublic); + return t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } } @@ -57,6 +58,12 @@ public static (object? result, bool success) TryCreateInstanceSafe(Type t) // Value types are handled separately if (t.IsValueType) { + var nullableType = Nullable.GetUnderlyingType(t); + if (nullableType is object) + { + return Success(GetUninitializedObject(t)); + } + return Success(Activator.CreateInstance(t)); } @@ -70,7 +77,7 @@ public static (object? result, bool success) TryCreateInstanceSafe(Type t) // I've got null for some security related types. return Success(GetUninitializedObject(t)); - (object? result, bool success) Success(object? o) => (o, o != null); + static (object? result, bool success) Success(object? o) => (o, o is object); } private static object? GetUninitializedObject(Type t) @@ -112,7 +119,8 @@ public static bool CanCreateInstance(this Type t) } return true; - bool IsOpenGenericType(Type type) + + static bool IsOpenGenericType(Type type) { return type.IsGenericTypeDefinition && !type.IsConstructedGenericType; } @@ -121,9 +129,15 @@ bool IsOpenGenericType(Type type) /// /// Returns true if a given type is unsafe. /// - public static bool IsUnsafeValueType(this Type t) - { - return t.GetCustomAttribute(typeof(UnsafeValueTypeAttribute)) != null; - } + public static bool IsUnsafeValueType(this Type t) => t.GetCustomAttribute() is object; + +#if NET8_0_OR_GREATER + /// + /// Checks if the type is inline array, returns the + /// + /// + /// + public static int InlineArrayLength(this Type t) => t.GetCustomAttribute()?.Length ?? 0; +#endif } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector/ObjectLayoutInspector.csproj b/src/ObjectLayoutInspector/ObjectLayoutInspector.csproj index 2cac5ea..46d3367 100644 --- a/src/ObjectLayoutInspector/ObjectLayoutInspector.csproj +++ b/src/ObjectLayoutInspector/ObjectLayoutInspector.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;netcoreapp3.1;netcoreapp3.0;net481;net6;net8;net9 True 8.0 enable @@ -10,6 +10,7 @@ true true snupkg + true diff --git a/src/ObjectLayoutInspector/Padder.cs b/src/ObjectLayoutInspector/Padder.cs index 8ab30d2..5e24a36 100644 --- a/src/ObjectLayoutInspector/Padder.cs +++ b/src/ObjectLayoutInspector/Padder.cs @@ -1,37 +1,68 @@ +using System; +using System.Collections; using System.Collections.Generic; namespace ObjectLayoutInspector { internal static class Padder { - public static void AddPaddings(bool includePaddings, int size, FieldLayout[] fieldsOffsets, List layouts) + public static void AddPaddings(bool includePaddings, int size, FieldLayout[] fieldsOffsets, List layouts, Type type) { - if (includePaddings && fieldsOffsets.Length != 0 && fieldsOffsets[0].Offset != 0) + if (includePaddings) { - layouts.Add(new Padding(0, fieldsOffsets[0].Offset)); - } + var allBits = new BitArray(size); + var dict = new Dictionary fields, BitArray usedBytes)>(); - for (var index = 0; index < fieldsOffsets.Length; index++) - { - var fieldOffset = fieldsOffsets[index]; - layouts.Add(fieldOffset); + foreach (var fieldOffset in fieldsOffsets) + { + if (dict.TryGetValue(fieldOffset.DeclaringType, out var range)) + { + range.fields.Add(fieldOffset); + range = (range.fields, range.usedBytes.SetRange(fieldOffset)); + } + else + { + range = (new List() { fieldOffset }, new BitArray(size).SetRange(fieldOffset)); + } - if (includePaddings) + dict[fieldOffset.DeclaringType] = range; + } + + foreach (var item in dict) { - int nextOffsetOrSize = size; - if (index != fieldsOffsets.Length - 1) + var startPaddingSize = item.Value.usedBytes.GetRange(0); + if (startPaddingSize > 0) + layouts.Add(new Padding(0, startPaddingSize, item.Key)); + + foreach (var field in item.Value.fields) { - // This is not a last field. - nextOffsetOrSize = fieldsOffsets[index + 1].Offset; + layouts.Add(field); + var paddingSize = item.Value.usedBytes.GetRange(field.Offset + field.Size); + if (paddingSize > 0) + layouts.Add(new Padding(field.Offset + field.Size, paddingSize, item.Key)); } - var nextSectionOffsetCandidate = fieldOffset.Offset + fieldOffset.Size; - if (nextSectionOffsetCandidate < nextOffsetOrSize) + allBits.Or(item.Value.usedBytes); + } + + int start = -1, i = 0; + for (; i < size; i++) + { + var notSet = !allBits.Get(i); + if(notSet && start is -1) + start=i; + else if (!notSet && !(start is -1)) { - // we have padding - layouts.Add(new Padding(nextSectionOffsetCandidate, nextOffsetOrSize - nextSectionOffsetCandidate)); + layouts.Add(new Padding(start, i - start, type)); + start = -1; } } + if (!(start is -1)) + layouts.Add(new Padding(start, size - start, type)); + } + else + { + layouts.AddRange(fieldsOffsets); } } } diff --git a/src/ObjectLayoutInspector/TypeInspector.cs b/src/ObjectLayoutInspector/TypeInspector.cs index dc00fdd..c46509b 100644 --- a/src/ObjectLayoutInspector/TypeInspector.cs +++ b/src/ObjectLayoutInspector/TypeInspector.cs @@ -27,7 +27,7 @@ public static (int size, int overhead) GetSize(Type type) } var size = GetSizeOfReferenceTypeInstance(type); - return (size, 2 * IntPtr.Size); + return (size, overhead: 2 * IntPtr.Size); } /// @@ -47,7 +47,7 @@ public static int GetSizeOfReferenceTypeInstance(Type type) // The size of the reference type is computed in the following way: // MaxFieldOffset + SizeOfThatField - // and round that number to closest point size boundary + // and round that number to closest pointer size boundary var maxValue = fields.MaxBy(tpl => tpl.offset); int sizeCandidate = maxValue.offset + GetFieldSize(maxValue.fieldInfo.FieldType); @@ -113,7 +113,7 @@ public static (FieldInfo fieldInfo, int offset)[] GetFieldOffsets(Type t) { // GetFields does not return private fields from the base types. // Need to use a custom helper function. - var fields = t.GetInstanceFields(); + var (fields, types) = t.GetInstanceFields(); //var fields2 = t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); Func fieldOffsetInspector = GenerateFieldOffsetInspectionFunction(fields); @@ -136,10 +136,19 @@ public static (FieldInfo fieldInfo, int offset)[] GetFieldOffsets(Type t) // Converting field addresses to offsets using the first field as a baseline return fields .Select((field, index) => (field: field, offset: (int)(addresses[index + 1] - baseLine))) - .OrderBy(tpl => tpl.offset) + .OrderBy(tpl => GetIndexOf(tpl.field.DeclaringType)).ThenBy(tpl => tpl.offset) .ToArray(); long GetBaseLine(long referenceAddress) => t.IsValueType ? referenceAddress : referenceAddress + IntPtr.Size; + int GetIndexOf(Type type) + { + for (int i = types.Length - 1; i >= 0; i--) + { + if (types[i] == type) + return i; + } + return types.Length; + } } private static Func GenerateFieldOffsetInspectionFunction(FieldInfo[] fields) diff --git a/src/ObjectLayoutInspector/TypeLayout.cs b/src/ObjectLayoutInspector/TypeLayout.cs index 3642935..b1ae664 100644 --- a/src/ObjectLayoutInspector/TypeLayout.cs +++ b/src/ObjectLayoutInspector/TypeLayout.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -9,7 +10,7 @@ namespace ObjectLayoutInspector /// /// Represents layout of a given type. /// - public struct TypeLayout : IEquatable + public readonly struct TypeLayout : IEquatable { /// /// A CLR type of the layout. @@ -32,41 +33,133 @@ public struct TypeLayout : IEquatable public int Overhead { get; } /// - /// Size of an empty space in the instance. + /// Size of an always empty space in the instance. /// public int Paddings { get; } + /// + /// Size of an always used space in the instance. + /// + public int Used { get; } + + /// + /// Size of empty/or used space in the instance. + /// + public int Mixed { get; } + + /// + /// Array holding used bytes + /// + internal BitArray UsedBytes { get; } + + /// + /// Array holding padding bytes + /// + internal BitArray PaddingBytes { get; } + + /// + /// Array holding mixed bytes + /// + internal BitArray MixedBytes { get; } + + /// + /// Is the inspected type unsafe, if so the is unknown + /// + public bool IsUnsafeValueType { get; } + /// public FieldLayoutBase[] Fields { get; } private TypeLayout(Type type, int size, int overhead, FieldLayoutBase[] fields, TypeLayoutCache? cache) { + if (fields.FirstOrDefault(fl => fl.DeclaringType != type) is FieldLayoutBase fl) + throw new ArgumentOutOfRangeException(nameof(fields), (fl, fl.DeclaringType.FullName, type.FullName), "Detected field from other type"); + Type = type; Size = size; Overhead = overhead; Fields = fields; + IsUnsafeValueType = type.IsUnsafeValueType(); + + cache ??= TypeLayoutCache.Create(); + + UsedBytes = new BitArray(size); + PaddingBytes = new BitArray(size); + + SetByteBits(fields, UsedBytes, PaddingBytes, cache); + + MixedBytes = UsedBytes.And(PaddingBytes); // We can't get padding information for unsafe structs. // Assuming there is no one. - var thisInstancePaddings = type.IsUnsafeValueType() ? 0 : fields.OfType().Sum(p => p.Size); + Paddings = IsUnsafeValueType ? 0 : PaddingBytes.GetSetCount() - MixedBytes.GetSetCount(); + Used = IsUnsafeValueType ? size : UsedBytes.GetSetCount() - MixedBytes.GetSetCount(); + Mixed = IsUnsafeValueType ? 0 : MixedBytes.GetSetCount(); - cache ??= TypeLayoutCache.Create(); - var nestedPaddings = fields - // Need to include paddings for value types only - // because we can't tell if the reference is exclusive or shared. - .OfType() - // Primitive types can be recursive. - .Where(fl => fl.FieldInfo.FieldType.IsValueType && !fl.FieldInfo.FieldType.IsPrimitive) - .Select(fl => GetLayout(fl.FieldInfo.FieldType, cache, includePaddings: true)) - .Sum(tl => tl.Paddings); - - Paddings = thisInstancePaddings + nestedPaddings; + if (Paddings > Size) + throw new ArgumentOutOfRangeException(nameof(fields), (size, Paddings), "Calculated padding was too big"); // Updating the cache. cache.LayoutCache.AddOrUpdate(type, this, (t, layout) => layout); } +#if NET8_0_OR_GREATER + internal static void SetByteBits(FieldLayoutBase[] fields, BitArray used, BitArray padding, TypeLayoutCache? cache, int offset = 0, FieldLayout flParent = default) +#else + internal static void SetByteBits(FieldLayoutBase[] fields, BitArray used, BitArray padding, TypeLayoutCache? cache, int offset = 0) +#endif + { + foreach (var field in fields) + { + if (field is FieldLayout fl) + { + // Need to include paddings for value types only + // because we can't tell if the reference is exclusive or shared. + // Primitive types can be recursive. + if (fl.FieldInfo.FieldType.IsValueType && + !fl.FieldInfo.FieldType.IsPrimitive && + GetLayout(fl.FieldInfo.FieldType, cache, includePaddings: true) is var lo && + !lo.IsUnsafeValueType) + { +#if NET8_0_OR_GREATER + SetByteBits(lo.Fields, used, padding, cache, offset + fl.Offset, fl); +#else + SetByteBits(lo.Fields, used, padding, cache, offset + fl.Offset); +#endif + } + else + { +#if NET8_0_OR_GREATER + if(flParent?.InlineArrayLength is int ial && ial > 0) + { + for (int i = 0; i < ial; i++) + { + used.SetRange(field, offset + i * flParent.Size / ial); + } + } + else +#endif + used.SetRange(field, offset); + } + } + else + { +#if NET8_0_OR_GREATER + if (flParent?.InlineArrayLength is int ial && ial > 0) + { + for (int i = 0; i < ial; i++) + { + padding.SetRange(field, offset + i * flParent.Size / ial); + } + } + else +#endif + padding.SetRange(field, offset); + } + } + } + /// /// /// @@ -127,7 +220,7 @@ public static TypeLayout GetLayout(TypeLayoutCache? cache = null, bool includ /// public static TypeLayout GetLayout(Type type, TypeLayoutCache? cache = null, bool includePaddings = true) { - if (cache != null && cache.LayoutCache.TryGetValue(type, out var result)) + if (cache?.LayoutCache.TryGetValue(type, out var result) ?? false) { return result; } @@ -142,7 +235,7 @@ public static TypeLayout GetLayout(Type type, TypeLayoutCache? cache = null, boo { Console.WriteLine($"Failed to create an instance of type {type}: {e}."); throw; - } + } TypeLayout DoGetLayout() { @@ -157,28 +250,19 @@ TypeLayout DoGetLayout() var layouts = new List(); - Padder.AddPaddings(includePaddings, size, fieldsOffsets, layouts); + Padder.AddPaddings(includePaddings, size, fieldsOffsets, layouts, type); return new TypeLayout(type, size, overhead, layouts.ToArray(), cache); } } /// - public bool Equals(TypeLayout other) - { - return Type == other.Type && Size == other.Size && Overhead == other.Overhead && Paddings == other.Paddings; - } + public bool Equals(TypeLayout other) => Type == other.Type && Size == other.Size && Overhead == other.Overhead && Paddings == other.Paddings; /// - public override bool Equals(object obj) - { - return obj is TypeLayout && Equals((TypeLayout)obj); - } + public override bool Equals(object obj) => obj is TypeLayout layout && Equals(layout); /// - public override int GetHashCode() - { - return (Type, Size, Overhead, Paddings).GetHashCode(); - } + public override int GetHashCode() => (Type, Size, Overhead, Paddings).GetHashCode(); } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector/UnsafeLayout.cs b/src/ObjectLayoutInspector/UnsafeLayout.cs index 31dfb1b..20d9dba 100644 --- a/src/ObjectLayoutInspector/UnsafeLayout.cs +++ b/src/ObjectLayoutInspector/UnsafeLayout.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using ValueGetter = System.Func; using ValueComparer = System.Func; +using ObjectLayoutInspector.Helpers; namespace ObjectLayoutInspector { @@ -63,12 +64,13 @@ public static IReadOnlyList GetLayout(bool recursive = true, } var tree = GetLayoutTree(); - IEnumerable fieldsLayout = + var fieldsLayout = GetFieldsLayoutInternal(in tree, recursive, considerPrimitives) - .OrderBy(x => x.Offset); + .OrderBy(x => x.Offset) + .ToArray(); var layouts = new List(); - Padder.AddPaddings(true, Unsafe.SizeOf(), fieldsLayout.OrderBy(x => x.Offset).ToArray(), layouts); + Padder.AddPaddings(true, Unsafe.SizeOf(), fieldsLayout, layouts, typeof(T)); return layouts; } @@ -82,7 +84,7 @@ private static IReadOnlyList GetFieldsLayoutInternal(in RootNode } else if (!recursive) { - if (primitives != null && primitives.Contains(typeof(T))) + if (primitives?.Contains(typeof(T)) ?? false) { fieldsLayout.Add(new FieldLayout(tree.totalOffset, tree.info, tree.size)); return fieldsLayout; @@ -98,7 +100,7 @@ private static IReadOnlyList GetFieldsLayoutInternal(in RootNode } else { - if (primitives != null && primitives.Contains(typeof(T))) + if (primitives?.Contains(typeof(T)) ?? false) { fieldsLayout.Add(new FieldLayout(tree.totalOffset, tree.info, tree.size)); return fieldsLayout; @@ -106,8 +108,8 @@ private static IReadOnlyList GetFieldsLayoutInternal(in RootNode for (var i = 0; i < tree.children.Length; i++) { - IsTerminal check = (n) => primitives != null ? primitives.Contains(n.Type) : false; - GetLayout(ref tree.children[i], fieldsLayout, check); + bool Check(FieldNode n) => primitives?.Contains(n.Type) ?? false; + GetLayout(ref tree.children[i], fieldsLayout, Check); } return fieldsLayout; @@ -189,11 +191,7 @@ private static FieldLayout GetLayout( case NodeKind.Fixed: return new FieldLayout(node.fixedNode.totalOffset, node.fixedNode.info, node.fixedNode.size); case NodeKind.Complex: - { - var lastNodeKind = node.complexNode.children[node.complexNode.children.Length - 1].kind; - var layout = new FieldLayout(node.complexNode.totalOffset, node.complexNode.info, node.complexNode.size); - return lastNodeKind != NodeKind.Fixed ? FixPadding(layout) : layout; - } + return FixSizePadding(ref node.complexNode); default: throw new NotImplementedException($"{node.kind} is not supported"); @@ -204,23 +202,37 @@ private static FieldLayout GetLayout( // counts padding in the end of first struct as part of it // Unsafe can support padding in the end, but not in the end and state (doubt this happens) // https://en.wikipedia.org/wiki/Data_structure_alignment - private static FieldLayout FixPadding(FieldLayout x) + private static bool ShouldFixSize(ref ComplexNode node, bool ignoreSizeModulo) + { + if (node.children.Length == 0) + return false; + var lastNodeKind = node.children[node.children.Length - 1].kind; + return lastNodeKind != NodeKind.Fixed && node.size > IntPtr.Size && (ignoreSizeModulo || node.size % IntPtr.Size != 0) && !Detectors.IsFixed(node.info, out var _); + } + + private static FieldLayout FixSizePadding(ref ComplexNode node) { - if (x.Size > 8 && x.Size % 8 != 0 && !Detectors.IsFixed(x.FieldInfo, out var _)) + var fl = new FieldLayout(node.totalOffset, node.info, node.size); + + if (ShouldFixSize(ref node, ignoreSizeModulo: false)) { - return new FieldLayout(x.Offset, x.FieldInfo, x.Size + x.Size % 8); + var runtimeSize = SizeOf(node.Type); + var computedSize = fl.Size + fl.Size % IntPtr.Size; + if (computedSize != runtimeSize) + throw new InvalidOperationException($"Runtime Size {runtimeSize} is not equal to computed size with padding {computedSize}"); + return new FieldLayout(fl.Offset, fl.FieldInfo, computedSize); } - return x; + return fl; } private static RootNode GetLayoutTree() where T : struct { var type = typeof(T); - var fields = FieldNode.GetFieldNodes(type); + var (fields, _) = FieldNode.GetFieldNodes(type); var root = new FieldNode { kind = NodeKind.Root, rootNode = new RootNode { children = fields, totalOffset = 0, size = Unsafe.SizeOf() } }; var previous = 0; - GetLayout(ref previous, ref root, new List(), new List?> { null }); + GetLayout(ref previous, ref root, new List(), new List?> { null }); return root.rootNode; } @@ -228,7 +240,7 @@ private static RootNode GetLayoutTree() where T : struct private static void GetLayout( ref int previous, ref FieldNode node, - List> getterHierarchy, + List getterHierarchy, List?> twiddlerHierarchy) where T : struct { @@ -239,13 +251,13 @@ private static void GetLayout( break; case NodeKind.Primitive: var pType = node.primitiveNode.Type; - Func primitiveEmptyComparator = x => Activator.CreateInstance(pType).Equals(x); + ValueComparer primitiveEmptyComparator = x => Activator.CreateInstance(pType).Equals(x); if (!Detectors.IsNullable(node.info.DeclaringType)) { getterHierarchy.Add(node.info.GetValue); } - FindPrimitiveOffset(ref node.primitiveNode, getterHierarchy, primitiveEmptyComparator, twiddlerHierarchy); + FindPrimitiveOffset(ref node.primitiveNode, getterHierarchy, primitiveEmptyComparator, twiddlerHierarchy); if (!Detectors.IsNullable(node.info.DeclaringType)) { @@ -257,26 +269,26 @@ private static void GetLayout( // Activator.CreateInstance creates null for nullable // cannot FieldInfo.GetValue fields of Value and HasValue as System.NotSupportedException : Specified method is not supported. 0 - var nullable = FieldNode.GetFieldNodes(node.nullableNode.Type); - node.nullableNode.hasValue = new Ref(nullable[0]); - node.nullableNode.value = new Ref(nullable[1]); + var (fields, _) = FieldNode.GetFieldNodes(node.nullableNode.Type); + node.nullableNode.hasValue = new Ref(fields[0]); + node.nullableNode.value = new Ref(fields[1]); ref var hasValue = ref node.nullableNode.hasValue.value; getterHierarchy.Add(node.info.GetValue); var propertyGetter = node.info.FieldType.GetProperty("HasValue"); - Func hasValueGetter = x => x != null ? propertyGetter.GetValue(x) : null; + ValueGetter hasValueGetter = x => x is null ? null : propertyGetter.GetValue(x); getterHierarchy.Add(hasValueGetter); - ValueComparer hasValueEmptyComparator = x => x == null; - FindPrimitiveOffset(ref hasValue.primitiveNode, getterHierarchy, hasValueEmptyComparator, twiddlerHierarchy); + ValueComparer hasValueEmptyComparator = x => x is null; + FindPrimitiveOffset(ref hasValue.primitiveNode, getterHierarchy, hasValueEmptyComparator, twiddlerHierarchy); getterHierarchy.RemoveAt(getterHierarchy.Count - 1); var underType = Nullable.GetUnderlyingType(node.nullableNode.Type); - ValueComparer nullableEmptyComparator = x => Activator.CreateInstance(underType).Equals(x); + ValueComparer nullableEmptyComparator = x => Activator.CreateInstance(underType).Equals(x);//unused? var valueProperty = node.info.FieldType.GetProperty("Value"); // may cache handles getterHierarchy.Add(valueProperty.GetValue); var t = new NullableTwiddler(hasValue.totalOffset); twiddlerHierarchy.Add(t.Twiddle); - GetLayout(ref previous, ref node.nullableNode.value.value, getterHierarchy, twiddlerHierarchy); + GetLayout(ref previous, ref node.nullableNode.value.value, getterHierarchy, twiddlerHierarchy); twiddlerHierarchy.RemoveAt(twiddlerHierarchy.Count - 1); node.totalOffset = hasValue.primitiveNode.totalOffset; node.nullableNode.size = node.nullableNode.value.value.totalOffset + node.nullableNode.value.value.size - node.totalOffset; @@ -289,7 +301,7 @@ private static void GetLayout( var fType = node.primitiveNode.Type; ValueComparer fixedEmptyComparator = x => Activator.CreateInstance(fType).Equals(x); getterHierarchy.Add(node.info.GetValue); - FindPrimitiveOffset(ref node.primitiveNode, getterHierarchy, fixedEmptyComparator, twiddlerHierarchy); + FindPrimitiveOffset(ref node.primitiveNode, getterHierarchy, fixedEmptyComparator, twiddlerHierarchy); getterHierarchy.RemoveAt(getterHierarchy.Count - 1); node.fixedNode.size = node.fixedNode.size * node.fixedNode.length; break; @@ -299,7 +311,7 @@ private static void GetLayout( for (var i = 0; i < node.rootNode.children.Length; i++) { ref var child = ref node.rootNode.children[i]; - GetLayout(ref previous, ref child, getterHierarchy, twiddlerHierarchy); + GetLayout(ref previous, ref child, getterHierarchy, twiddlerHierarchy); } } break; @@ -310,24 +322,53 @@ private static void GetLayout( getterHierarchy.Add(node.info.GetValue); } - node.complexNode.children = FieldNode.GetFieldNodes(node.complexNode.Type); + node.complexNode.children = FieldNode.GetFieldNodes(node.complexNode.Type).fields; for (var i = 0; i < node.complexNode.children.Length; i++) { ref var child = ref node.complexNode.children[i]; - GetLayout(ref previous, ref child, getterHierarchy, twiddlerHierarchy); + GetLayout(ref previous, ref child, getterHierarchy, twiddlerHierarchy); } //TODO: use ref compare-sort https://github.com/dotnet/corefx/issues/33927 node.complexNode.children = node.complexNode.children.OrderBy(x => x.totalOffset).ThenByDescending(x => x.size).ToArray(); - node.totalOffset = node.complexNode.children[0].totalOffset; + node.totalOffset = node.complexNode.children.Length > 0 ? node.complexNode.children[0].totalOffset : 0; for (var i = 0; i < node.complexNode.children.Length; i++) { ref var child = ref node.complexNode.children[i]; - var possibleEnd = child.totalOffset + child.size; - node.complexNode.size = Math.Max(node.complexNode.size, possibleEnd - node.complexNode.totalOffset); + var possibleEnd = child.totalOffset + child.size;//might be smaller than actual struct size + var size = possibleEnd - node.complexNode.totalOffset; + node.complexNode.size = Math.Max(node.complexNode.size, size); + } + + if (node.complexNode.Type.IsValueType) + { + var unsafeSize = SizeOf(node.complexNode.Type); + if (ShouldFixSize(ref node.complexNode, ignoreSizeModulo: true)) + node.complexNode.size = Math.Max(node.complexNode.size, unsafeSize); } +#if NET8_0_OR_GREATER + var inlineArrayLength = node.complexNode.info?.DeclaringType?.InlineArrayLength() ?? 0; + if (inlineArrayLength > 0) + { + var fieldsNodes = new FieldNode[node.complexNode.children.Length * inlineArrayLength]; + for (int i = 0, l = node.complexNode.children.Length; i < l; i++) + { + ref var child = ref node.complexNode.children[i]; + for (int j = 0; j < inlineArrayLength; j++) + { + var k = j * l + i; + ref var clone = ref fieldsNodes[k]; + clone = child; + clone.totalOffset += node.complexNode.size * j; + } + } + node.complexNode.size *= inlineArrayLength; + node.complexNode.children = fieldsNodes; + } +#endif + if (!Detectors.IsNullable(node.info.DeclaringType)) { getterHierarchy.RemoveAt(getterHierarchy.Count - 1); @@ -344,7 +385,7 @@ private static void GetLayout( private static void FindPrimitiveOffset( ref PrimitiveNode node, List getterHierarchy, - Func emptyComparator, + ValueComparer emptyComparator, List?> twiddlerHierarchy) where T : struct { @@ -389,7 +430,7 @@ private static void FindPrimitiveOffset( throw new NotImplementedException($"Failed to find offset and size of {node.Type.FullName} in {typeof(T).FullName}"); } - private static void FindReferenceOffset(ref ReferenceNode node, List> getterHierarchy) + private static void FindReferenceOffset(ref ReferenceNode node, List getterHierarchy) where T : struct { var fieldInfo = node.info; @@ -403,7 +444,7 @@ private static void FindReferenceOffset(ref ReferenceNode node, List object? value = rootDummy; for (int i = 0; i < getterHierarchy.Count; i++) { - Func field = getterHierarchy[i]; + var field = getterHierarchy[i]; value = field(value); } return value; } + + /// + /// Gets the runtime sizeof... from class structure + /// + /// + /// + /// Will return only base size for arrays?: memory size = base size + length * item size + internal static unsafe int SizeOf(Type type) + { + var typeHandle = type.TypeHandle.Value; + return (*(int*)(typeHandle + 4)) - 2*IntPtr.Size; + } } } diff --git a/src/ObjectLayoutInspector/UnsafeTree.cs b/src/ObjectLayoutInspector/UnsafeTree.cs index cd4746e..07f6622 100644 --- a/src/ObjectLayoutInspector/UnsafeTree.cs +++ b/src/ObjectLayoutInspector/UnsafeTree.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using ObjectLayoutInspector.Helpers; @@ -9,15 +8,10 @@ namespace ObjectLayoutInspector internal enum NodeKind : byte { Primitive, - Complex, - Nullable, - Root, - Fixed, - Reference } @@ -25,22 +19,27 @@ internal enum NodeKind : byte [StructLayout(LayoutKind.Explicit)] internal struct FieldNode { - public static FieldNode[] GetFieldNodes(Type type) + public static (FieldNode[] fields, Type[] types) GetFieldNodes(Type type) { - return ReflectionHelper.GetInstanceFields(type).Select(x => + var (fields, types) = ReflectionHelper.GetInstanceFields(type); + + var fieldNodes = new FieldNode[fields.Length]; + for (int i = 0; i < fields.Length; i++) { - int length = 0; - return - x.FieldType.IsClass - ? new FieldNode { kind = NodeKind.Reference, referenceNode = new ReferenceNode(x) } - : Detectors.IsPrimitive(x) - ? new FieldNode { kind = NodeKind.Primitive, primitiveNode = new PrimitiveNode { info = x } } - : Detectors.IsNullable(x) - ? new FieldNode { kind = NodeKind.Nullable, nullableNode = new NullableNode { info = x } } - : Detectors.IsFixed(x, out length) - ? new FieldNode { kind = NodeKind.Fixed, fixedNode = new FixedNode { info = x, length = length } } - : new FieldNode { kind = NodeKind.Complex, complexNode = new ComplexNode { info = x } }; - }).ToArray(); + var x = fields[i]; + if (x.FieldType.IsClass) + fieldNodes[i] = new FieldNode { kind = NodeKind.Reference, referenceNode = new ReferenceNode(x) }; + else if (Detectors.IsPrimitive(x)) + fieldNodes[i] = new FieldNode { kind = NodeKind.Primitive, primitiveNode = new PrimitiveNode { info = x } }; + else if (Detectors.IsNullable(x)) + fieldNodes[i] = new FieldNode { kind = NodeKind.Nullable, nullableNode = new NullableNode { info = x } }; + else if (Detectors.IsFixed(x, out int length)) + fieldNodes[i] = new FieldNode { kind = NodeKind.Fixed, fixedNode = new FixedNode { info = x, length = length } }; + else + fieldNodes[i] = new FieldNode { kind = NodeKind.Complex, complexNode = new ComplexNode { info = x } }; + } + + return (fieldNodes, types); } [FieldOffset(0)] @@ -49,15 +48,9 @@ public static FieldNode[] GetFieldNodes(Type type) [FieldOffset(8)] public int size; - [FieldOffset(12)] - public int totalOffset; - - [FieldOffset(24)] - public FieldInfo info; - [FieldOffset(8)] public PrimitiveNode primitiveNode; - + [FieldOffset(8)] public ComplexNode complexNode; @@ -73,6 +66,12 @@ public static FieldNode[] GetFieldNodes(Type type) [FieldOffset(8)] public ReferenceNode referenceNode; + [FieldOffset(12)] + public int totalOffset; + + [FieldOffset(24)] + public FieldInfo info; + public Type Type => info.FieldType; } @@ -109,7 +108,7 @@ internal struct NullableNode [FieldOffset(16)] public FieldInfo info; - + // Nullable can be only of Complex or Primitive, so may model FieldNode as remain part only of 2 // validate of same size and field layout it test and Unsafe.As map to reinterpret cast /// @@ -124,7 +123,7 @@ internal struct NullableNode public Type Type => info.FieldType; } - internal class Ref where T: struct + internal class Ref where T : struct { public T value; @@ -165,7 +164,7 @@ internal struct PrimitiveNode public FieldInfo info; public Type Type => info.FieldType; - } + } [StructLayout(LayoutKind.Explicit)] internal struct ComplexNode @@ -200,6 +199,6 @@ internal struct RootNode [FieldOffset(24)] public FieldNode[] children; - public bool IsPrimitive => children == null; + public bool IsPrimitive => children is null; } } \ No newline at end of file diff --git a/src/ObjectLayoutInspector/src/ObjectLayoutInspector/ObjectLayoutInspector.xml b/src/ObjectLayoutInspector/src/ObjectLayoutInspector/ObjectLayoutInspector.xml index 27c3ee0..f1524a3 100644 --- a/src/ObjectLayoutInspector/src/ObjectLayoutInspector/ObjectLayoutInspector.xml +++ b/src/ObjectLayoutInspector/src/ObjectLayoutInspector/ObjectLayoutInspector.xml @@ -19,6 +19,11 @@ An offset of a field from the beginning of a struct. + + + Type which declared this field + + @@ -39,6 +44,9 @@ + + + @@ -53,7 +61,10 @@ Represents a padding between fields. - + + + + @@ -185,7 +196,37 @@ - Size of an empty space in the instance. + Size of an always empty space in the instance. + + + + + Size of an always used space in the instance. + + + + + Size of empty/or used space in the instance. + + + + + Array holding used bytes + + + + + Array holding padding bytes + + + + + Array holding mixed bytes + + + + + Is the inspected type unsafe, if so the is unknown @@ -271,6 +312,32 @@ If true the resulting list will have fields for all nested types as well. Not implemented yet if specified. + + + Gets the runtime sizeof... from class structure + + + + Will return only base size for arrays?: memory size = base size + length * item size + + + + Boolean. + + + + +m> + Not implemented yet if specified. + + + + Gets the runtime sizeof... from class structure + + + + Will return only base size for arrays?: memory size = base size + length * item size + Boolean.