Skip to content

Annotations

Annotations customize compiler behavior or attach metadata to definitions. They are prefixed with @ and appear before the definition they apply to.

@endian

Syntax: @endian big or @endian little

Sets the default endianness for all multi-byte fields in the module. Applies at module scope — typically placed at the top of the file.

ValueDescription
bigBig-endian (network byte order). Default for QUIC, IP, TCP.
littleLittle-endian. Common for BLE, USB, PCIe.

Per-field suffix always overrides the module default. Use u16be, u16le, u32be, u32le, etc. to force a specific endianness on individual fields regardless of the module setting.

Example — BLE ATT (examples/ble/att.wspec):

wire
@endian little
module ble.att

type AttHandle = u16le
type Uuid16    = u16le

frame AttPdu = match opcode: u8 {
    0x02 => ExchangeMtuReq { client_rx_mtu: u16le },
    0x0a => ReadReq  { handle: AttHandle },
    0x0b => ReadRsp  { value: bytes[remaining] },
    # ...
}

Here @endian little sets the module default. u16le fields are explicit per-field overrides (consistent with the module default, but explicit for clarity).

Constraints:

  • One @endian declaration per module.
  • Valid values are big and little only.
  • Has no effect on single-byte fields (u8, bit, bits[N] where N ≤ 8).
  • The bit primitive and bits[N] fields are always packed according to endianness rules for bit groups (see Language Reference — Bit Fields).

@strict

Syntax: @strict on a type definition

Rejects non-canonical encodings at parse time. When @strict is present, the parser checks that the wire encoding is the shortest possible representation. Non-canonical encodings cause the parse function to return WIRESPEC_ERR_NONCANONICAL.

This applies to variable-length integer types where a value may be legally encoded in multiple byte widths (e.g., encoding 5 as a 2-byte QUIC VarInt instead of the canonical 1-byte form).

Example — QUIC VarInt (examples/quic/varint.wspec):

wire
module quic.varint

@strict
type VarInt = {
    prefix: bits[2],
    value: match prefix {
        0b00 => bits[6],   # 1-byte encoding: values 0..=63
        0b01 => bits[14],  # 2-byte encoding: values 0..=16383
        0b10 => bits[30],  # 4-byte encoding: values 0..=1073741823
        0b11 => bits[62],  # 8-byte encoding: values 0..=4611686018427387903
    },
}

With @strict, encoding 5 using the 2-byte form (prefix = 0b01) is rejected even though it is a valid encoding per the format spec. RFC 9000 requires canonical (minimum-length) encoding; @strict enforces this.

Without @strict, all encodings that decode to a valid value are accepted.

Constraints:

  • Applies only to type definitions with variable-length structure (computed types using match).
  • Applying @strict to a fixed-width type has no effect.
  • One @strict per type definition.

@checksum(algorithm)

Syntax: @checksum(algorithm) on a field within a packet, frame branch, or capsule branch

Field-level annotation that enables automatic checksum verification on parse and automatic checksum computation on serialize.

Supported algorithms:

AlgorithmField TypeMethod
internetu16RFC 1071 one's complement sum
crc32u32IEEE 802.3 CRC-32
crc32cu32Castagnoli CRC-32C
fletcher16u16RFC 1146 Fletcher-16

Behavior on parse:

The checksum is computed over the entire parsed struct (all bytes consumed by the packet/frame branch). If verification fails, the parse function returns WIRESPEC_ERR_CHECKSUM.

Behavior on serialize:

  1. The checksum field is zeroed.
  2. The checksum is computed over the entire serialized struct.
  3. The computed value is written back into the checksum field position.

The serialized output always contains a valid checksum.

Example — IPv4 header checksum (examples/ip/ipv4.wspec):

wire
module ip.v4
@endian big

packet IPv4Header {
    version:           bits[4],
    ihl:               bits[4],
    dscp:              bits[6],
    ecn:               bits[2],
    total_length:      u16,
    identification:    u16,
    flags:             bits[3],
    fragment_offset:   bits[13],
    ttl:               u8,
    protocol:          u8,
    @checksum(internet)
    header_checksum:   u16,
    src_addr:          u32,
    dst_addr:          u32,
}

Constraints:

  • At most one @checksum annotation per packet or frame/capsule branch. Multiple checksum fields in the same struct is a compile error.
  • The field type must match the algorithm requirement (u16 for internet/fletcher16, u32 for crc32/crc32c). Mismatched type is a compile error.
  • The checksum covers the entire struct (all bytes of the packet/frame branch). Cross-layer checksums such as TCP/UDP pseudo-headers are not supported.
  • The @checksum annotation cannot appear on bytes[...] fields or array fields — only on scalar integer fields.

@max_len(N)

Syntax: @max_len(N) on an array field, where N is a positive integer literal

Overrides the global default array capacity (WIRESPEC_MAX_ARRAY_ELEMENTS, default 64) for the annotated field. All other array fields in the struct continue to use the global default.

When the parsed element count exceeds the field's capacity, the parse function returns WIRESPEC_ERR_CAPACITY.

Example:

wire
packet Foo {
    count: u16,
    items:       [Item; count],         # capacity = WIRESPEC_MAX_ARRAY_ELEMENTS (64)
    @max_len(1024)
    large_items: [Item; count],         # capacity = 1024
}

Setting the global default:

Pass -DWIRESPEC_MAX_ARRAY_ELEMENTS=128 to the C compiler to change the default for all fields without @max_len.

Constraints:

  • N must be a positive integer literal. Zero and negative values are compile errors.
  • Applies only to array fields ([T; expr]). Applying to a non-array field is a compile error.
  • One @max_len per field.

@doc(string)

Syntax: @doc("text") on any definition

Attaches a documentation string to a type, packet, frame, capsule, field, or constant definition. The string is preserved in the IR but currently has no effect on generated C code.

Example:

wire
@doc("RFC 9000 Section 17 — Long Header Packet")
packet QuicLongHeader {
    header_form: bits[1],
    fixed_bit:   bits[1],
    # ...
}

Note: Future tooling (documentation generators, language server protocol) will use @doc strings for hover documentation and generated API docs. The annotation is parsed and validated today; integration with output is planned for a later milestone.

Constraints:

  • The argument must be a string literal (double-quoted).
  • One @doc per definition.