The trailer: an opt-in CRC and an opt-in timestamp (L2/L3 codec)¶
Integrity rides at the end of a TLV, and it is optional
(reference 01 §trailer). opt.cr says a CRC-32C follows the
body — encode computes it, decode checks it. opt.ts says a wire-time stamp follows.
Neither is in the header, and a TLV that wants neither pays for neither, which is what makes
the codec usable as a transparent byte router over live memory.
What to notice¶
A flipped byte is a verdict, not a guess. One XOR into the payload turns the frame into
err_t::FRAME_CRC_FAIL. The example flips a byte it wrote itself, so the failure is about the bytes — a receiver never learns anything about intent from a CRC.stamp_tssets the bit and the value together. That is not a convenience; it is what keeps a stamped TLV away fromencode’s refusal below. It writes the absolute (TF=0) form only, deliberately: the relative form is anchored to the parent’s stamp, and the anchorless-reject rule is a conformance gap the reference codec has not closed.The refusal is loud. A TLV whose
opt.tsis set by hand with no trailer value behind it makesencodereturn an empty vector (#1109) rather than emit a silently zero stamp. Empty is unambiguous: a well-formed TLV always carries at least its four-byte header, so nothing valid encodes to nothing.The trailer timestamp is transport time. Sample-acquisition or control-deadline time is application-domain and rides the payload as a
TIMEchild instead — a different clock with different meaning.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 — the optional trailer: a per-TLV CRC and a per-TLV timestamp.
9 *
10 * Integrity is OPT-IN and it rides at the END, not in the header (`CONTEXT.md` §Wire format;
11 * `docs/reference/01-data-format.md` §trailer). `opt.cr` says a CRC-32C follows the body and
12 * `encode` recomputes it, so a single flipped payload byte turns the frame into
13 * `err_t::FRAME_CRC_FAIL` — a receiver's verdict about the bytes, not about the sender's
14 * intent. `opt.ts` says a wire-time stamp follows; `stamp_ts` sets the bit and the value
15 * together, which matters because a TLV claiming `opt.ts` with no value in hand is REFUSED
16 * loudly by `encode` (an empty vector, #1109) rather than emitted with a silent zero.
17 *
18 * Runs under ctest as `example_wire_trailer`; returns non-zero on any failed check.
19 */
20
21#include <cstddef>
22#include <cstdint>
23#include <cstdio>
24#include <span>
25#include <vector>
26
27#include "libtracer/tracer.hpp"
28
29namespace {
30
31using tr::wire::opt_t;
32using tr::wire::tlv_t;
33using tr::wire::type_t;
34
35/** @brief Report expectation @p what and record a failure on @p ok. */
36void check(bool& ok, bool cond, const char* what) {
37 std::printf(" [%s] %s\n", cond ? "ok" : "FAIL", what);
38 ok = ok && cond;
39}
40
41} // namespace
42
43int main() {
44 bool ok = true;
45 const std::vector<std::byte> body(4, std::byte{0x5A});
46
47 tlv_t v;
48 v.type = type_t::VALUE;
49 v.opt.cr = true; // ask for the CRC trailer; encode computes it
50 v.payload = std::span(body);
51
52 std::vector<std::byte> frame = tr::wire::encode(v);
53 std::printf("VALUE with a CRC trailer: %zu bytes (4 header + 4 body + 4 CRC-32C)\n",
54 frame.size());
55 const auto good = tr::wire::decode(frame);
56 check(ok, good.has_value(), "the frame decodes");
57 check(ok, good && good->trailer && good->trailer->crc.has_value(), "and carries a CRC");
58
59 // One flipped payload byte. The CRC is what makes that a verdict rather than a guess.
60 std::vector<std::byte> corrupt = frame;
61 corrupt[4] ^= std::byte{0x01};
62 const auto bad = tr::wire::decode(corrupt);
63 check(ok, !bad && bad.error() == tr::wire::err_t::FRAME_CRC_FAIL,
64 "a single flipped body byte is FRAME_CRC_FAIL");
65
66 // The timestamp half. stamp_ts sets opt.ts, clears opt.tf and writes the value at once.
67 tlv_t stamped;
68 stamped.type = type_t::VALUE;
69 stamped.payload = std::span(body);
70 tr::wire::stamp_ts(stamped, 1'700'000'000'000'000'000);
71 const auto rt = tr::wire::decode(tr::wire::encode(stamped));
72 check(ok, rt && rt->trailer && rt->trailer->ts, "a stamped TLV round-trips its timestamp");
73 check(ok, rt && rt->trailer->ts->value == 1'700'000'000'000'000'000, "with the value intact");
74
75 // The loud refusal: the bit without the value is never emitted as a silent zero.
76 tlv_t claims_ts;
77 claims_ts.type = type_t::VALUE;
78 claims_ts.opt.ts = true; // set by hand, with no trailer value behind it
79 claims_ts.payload = std::span(body);
80 check(ok, tr::wire::encode(claims_ts).empty(),
81 "opt.ts with no value refuses to encode at all (empty vector)");
82 return ok ? 0 : 1;
83}
See also: frame codec · wire-format bits · data-format reference · wire codec round-trip.