Table of Contents

Tutorial 1 — your first fixed layout

Suppose a binary message begins with a two-byte kind and a four-byte length:

02 00 06 00 00 00

The layout names those two values:

struct header {
    uint16 kind;
    uint32 length;
};

uint16 is always two bytes, and uint32 is always four. The fields have no initial values in the layout; the declarations describe how to interpret bytes supplied later.

Step 1: choose placement and byte order

CStructSharp's defaults are:

  • packed placement (aligned: false);
  • little-endian neutral numeric fields (isLittleEndian: true); and
  • eight-byte pointer storage (pointerSize: 8).

This layout has no pointer, so pointer width does not affect its size.

Packed placement puts length immediately after kind:

Offset Bytes Field Decoded value
0 02 00 header.kind 2
2 06 00 00 00 header.length 6

The complete size is six bytes. In aligned placement, the four-byte length would start at offset 4, leaving two padding bytes after kind, and the struct would occupy eight bytes.

Step 2: construct and reuse the layout

The documentation runner compiles and executes this complete scenario:

private static void DecodeHeader()
{
    var layout = new CStruct("struct header { uint16 kind; uint32 length; };");
    ReadOnlySpan<byte> bytes = [0x02, 0x00, 0x06, 0x00, 0x00, 0x00];
    StructValue header = layout.Parse(bytes, "header");
    Equal((ushort)2, header.Get<ushort>("kind"));
    Equal(6U, header.Get<uint>("length"));

    bool read = layout.TryReadValue<Header>(bytes, "header", out Header? typed);
    True(read && typed is { Kind: 2, Length: 6 }, "Typed header result differed.");
    True(!layout.TryReadValue<Header>(bytes[..1], "header", out _), "Truncated TryReadValue should fail.");
}

new CStruct(...) reads and prepares the layout once. Parse(bytes, "header") returns a StructValue whose member names come from the layout; Get<ushort>("kind") reads one member with a checked conversion. TryReadValue<Header> maps the same bytes to a C# class and returns false for the deliberately truncated one-byte input.

Expected results:

kind   = 2
length = 6
typed read succeeds = true
truncated read succeeds = false

Step 3: verify a byte-order change

Change only the field declaration to:

uint32> length;

The > suffix forces big-endian storage for that field. The length bytes must then be 00 00 00 06. Its width and offset do not change; only the order of its four bytes changes.

If the original bytes produce a very large length, check whether the format is actually little-endian. If reading fails, confirm that the source contains all six packed bytes and that the root name is exactly header.

The primitive table lists all fixed widths and suffixes. Continue with Tutorial 2 — composites and overlapping storage.