Inspect a compiled layout
Intermediate · C#. Layout lists every declaration with sizes, offsets, array kinds, and enum members; ToDefinition renders Portable text back; WithEndianness returns the cached sibling for the other byte order.
Run this example
Prerequisites: the repository's .NET 10 SDK and a checkout of this source. Run from the repository root:
dotnet run --project docs/examples/CStructSharp.Docs.Examples.csproj -c Release -- layout-introspection
The runner checks a 7-byte root with length at offset 1, enum member DATA = 2, a definition rendered by ToDefinition that compiles to the same size, and a big-endian sibling layout. Success includes PASS layout-introspection.
This example uses the C# API. Browser capabilities and result shapes are described in the browser guide.
Complete program
The layout, options, input bytes, helper methods, and required types are all included. To adapt it outside the repository, create a .NET 10 console project, add CStructSharp, and replace Program.cs with this complete file. These examples follow the source version; use a matching package when testing a release.
Download the complete C# source.
// Generated from executable documentation examples. Edit the source region, then regenerate.
using System;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Buffers;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using CStructSharp;
using CStructSharp.Codecs;
using CStructSharp.Diagnostics;
using CStructSharp.Introspection;
using CStructSharp.Values;
internal static partial class Program
{
public static void Main()
{
LayoutIntrospection();
Console.WriteLine("PASS layout-introspection");
}
private static void LayoutIntrospection()
{
// `Layout` describes the compiled declarations with sizes, offsets, and members; `ToDefinition` renders
// them back; `WithEndianness` compiles the sibling layout a byte-order-switching format needs.
var layout = new CStruct("enum kind : uint8 { NONE, DATA = 2 }; struct root { kind type; uint16 length; char name[4]; };");
LayoutDeclarationInfo root = layout.Layout.Declarations.Single(item => item.Name == "root");
Equal(7, root.Size!.Value);
Equal(1, root.Fields[1].Offset!.Value);
Equal("uint16", root.Fields[1].TypeName);
Equal(LayoutArrayKind.Fixed, root.Fields[2].ArrayKind);
LayoutDeclarationInfo kind = layout.Layout.Declarations.Single(item => item.Name == "kind");
Equal(new BigInteger(2), kind.Members[1].Value);
string definition = layout.ToDefinition();
True(definition.Contains("enum kind : uint8 {", StringComparison.Ordinal), "the rendered definition names the enum");
Equal(7, new CStruct(definition).GetStructSizeInBytes("root"));
byte[] bigEndian = [2, 0x01, 0x02, (byte)'a', (byte)'b', 0, 0];
Equal((ushort)0x0102, layout.WithEndianness(isLittleEndian: false).Parse(bigEndian, "root").Get<ushort>("length"));
True(ReferenceEquals(layout, layout.WithEndianness(isLittleEndian: true)), "the same byte order returns the same instance");
}
private static void Equal<T>(T expected, T actual)
{
if (!EqualityComparer<T>.Default.Equals(expected, actual))
{
throw new InvalidOperationException($"Expected '{expected}', received '{actual}'.");
}
}
private static void True(bool condition, string message)
{
if (!condition)
{
throw new InvalidOperationException(message);
}
}
}
Try it and diagnose mistakes
Ask the sibling for pointer size 4 instead.
Answer: WithPointerSize(4) returns another cached layout; the root size stays 7 because no field is a pointer. The program contains assertions for its original inputs. When changing an input intentionally, update the expected assertion too; an unchanged assertion is not evidence that the new value is wrong.
Continue with the related guide or choose another recipe.