Wire format#

Schema-order encode#

Official capnp encode sets pointer fields in schema order. C encode that allocates the same way memcmp-equals that output.

Allocating nested objects in a different order is still valid Cap’n wire. The bytes differ. Tests that claim CLI identity use schema order.

Empty struct#

A zero-size struct encodes as offset -1: 0xFFFFFFFC (B=-1), not a null pointer. capn_new_struct(seg, 0, 0) writes that word.

Canonical form#

capn_canonicalize rewrites to a single segment, no far pointers, no holes, preorder layout. Trailing zero data and null pointer words are truncated. Empty struct stays B=-1.

struct capn canon;
capn_init_malloc(&canon);
if (capn_canonicalize(&c, &canon) == 0) {
    /* unframed: canon.seglist->data[0 .. len) */
    /* 1-segment stream: capn_write_mem(&canon, buf, sz, 0) */
}
capn_free(&canon);

capn_canonicalize memcmp-equals capnp convert binary:canonical on the AddressBook / TestAllTypes fixtures.

Packed#

Packed of the same unpacked bytes memcmp-equals capnp convert binary:packed.

0xFF extra-word count is one byte (0..255). The C++ heuristic keeps a following word when it has fewer than two zero bytes; a run stops at two or more zeros.

Lists#

List(Struct) is always C=7 plus a tag, including empty lists. C=6 pointer lists upgrade to 0-data / 1-pointer structs on getp when the schema says List(Struct).

Live CLI tests#

tests/capnp-cli-interop-test.cpp runs when Meson finds capnp on PATH. Ubuntu CI installs capnproto so those tests run there.