Structured or opaque — one bit decides (L2/L3 codec)¶
opt.PL is the whole of TLV composition. Clear, and the body is payload bytes — an
opaque TLV, the leaf. Set, and the body is a packed run of sub-TLVs — a structured
TLV, the composite, whose type code says what the children mean
(CONTEXT.md §Two compositions). There is no LIST type and no array flag;
nesting is this bit plus a purpose type byte.
The example makes the point by encoding POINT{VALUE, VALUE} once, clearing PL on a copy,
and decoding the same bytes as one opaque payload whose length is exactly the children
region’s.
What to notice¶
Meaning composes at L3; storage composes at L1, and they do not constrain each other. A structured TLV’s children are a meaning tree. The rope that physically holds those bytes is a storage tree, and a link boundary may fall anywhere, including mid-header — see the lazy view. That decoupling is the zero-copy story.
A decoded TLV is one or the other, never both.
childrenis populated andpayloadempty for a structured TLV; the reverse for an opaque one. Reading the wrong field gets you a legitimately empty container, not an error.The children region is contiguous bytes with no framing of its own. That is why the same body reads as one opaque payload of
frame.size() - 4: sub-TLVs are self-describing, so no count field and no separators are needed.Nothing here is conditional — the target builds and runs under every CI leg.
Source¶
1/*
2 * SPDX-License-Identifier: Apache-2.0
3 * SPDX-FileCopyrightText: Copyright 2026 avatarsd LLC
4 */
5
6/**
7 * @file
8 * @brief ONE CONCEPT — `opt.PL`: an opaque TLV holds bytes, a structured TLV holds children.
9 *
10 * TLV composition (L3) composes MEANING: the leaf is an opaque TLV (`opt.PL = 0`, its body
11 * is payload bytes), the composite is a structured TLV (`opt.PL = 1`, its body is a packed
12 * run of sub-TLVs) whose type code says what the children mean (`CONTEXT.md` §Two
13 * compositions). One bit decides which, and it decides it for the SAME body bytes: this
14 * example encodes `POINT{VALUE, VALUE}`, clears `PL` on the copy, and decodes the very same
15 * bytes as one opaque payload of exactly the children's length.
16 *
17 * That is the decoupling from memory composition (L1): meaning is `opt.PL`, storage is the
18 * view/rope chain, and neither constrains the other.
19 *
20 * Runs under ctest as `example_wire_structured_vs_opaque`; returns non-zero on any failure.
21 */
22
23#include <cstddef>
24#include <cstdint>
25#include <cstdio>
26#include <span>
27#include <vector>
28
29#include "libtracer/tracer.hpp"
30
31namespace {
32
33using tr::wire::opt_t;
34using tr::wire::tlv_t;
35using tr::wire::type_t;
36
37/** @brief Report expectation @p what and record a failure on @p ok. */
38void check(bool& ok, bool cond, const char* what) {
39 std::printf(" [%s] %s\n", cond ? "ok" : "FAIL", what);
40 ok = ok && cond;
41}
42
43} // namespace
44
45int main() {
46 bool ok = true;
47 const std::vector<std::byte> x{std::byte{0x11}, std::byte{0x22}};
48 const std::vector<std::byte> y{std::byte{0x33}, std::byte{0x44}};
49
50 tlv_t point;
51 point.type = type_t::POINT;
52 point.opt.pl = true; // structured: the body is children, not bytes
53 point.children.push_back(tlv_t{.type = type_t::VALUE, .payload = std::span(x)});
54 point.children.push_back(tlv_t{.type = type_t::VALUE, .payload = std::span(y)});
55
56 std::vector<std::byte> frame = tr::wire::encode(point);
57 std::printf("POINT{VALUE,VALUE} is %zu bytes: 4 header + 2 x (4 header + 2 body)\n",
58 frame.size());
59
60 const auto structured = tr::wire::decode(frame);
61 check(ok, structured.has_value(), "the structured frame decodes");
62 check(ok, structured && structured->children.size() == 2, "PL = 1 gives two children");
63 check(ok, structured && structured->payload.empty(), "and no opaque payload at all");
64
65 // The SAME body bytes, read with PL cleared. Byte 1 is the root's opt (see
66 // wire_tlv_header): clearing bit 6 tells the decoder "this body is payload".
67 std::vector<std::byte> as_opaque = frame;
68 as_opaque[1] = static_cast<std::byte>(static_cast<std::uint8_t>(as_opaque[1]) & 0xBFu);
69
70 const auto opaque = tr::wire::decode(as_opaque);
71 check(ok, opaque.has_value(), "the same bytes with PL = 0 also decode");
72 check(ok, opaque && opaque->children.empty(), "PL = 0 gives no children");
73 check(ok, opaque && opaque->payload.size() == frame.size() - 4,
74 "the payload IS the children region, byte for byte");
75 return ok ? 0 : 1;
76}
See also: frame codec · data-format reference · protocol TLVs.