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, tlv_node_t::over 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_ts sets the bit and the value together. That is not a convenience; it is what keeps a stamped TLV away from encode’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.ts is set by hand with no trailer value behind it makes encode return 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 TIME child 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 <optional>
25#include <span>
26#include <vector>
27
28#include "libtracer/tracer.hpp"
29
30namespace {
31
32using tr::wire::opt_t;
33using tr::wire::tlv_t;
34using tr::wire::type_t;
35
36/** @brief Report expectation @p what and record a failure on @p ok. */
37void check(bool& ok, bool cond, const char* what) {
38    std::printf("  [%s] %s\n", cond ? "ok" : "FAIL", what);
39    ok = ok && cond;
40}
41
42}  // namespace
43
44int main() {
45    bool ok = true;
46    const std::vector<std::byte> body(4, std::byte{0x5A});
47
48    tlv_t v;
49    v.type = type_t::VALUE;
50    v.opt.cr = true;  // ask for the CRC trailer; encode computes it
51    v.payload = std::span(body);
52
53    std::vector<std::byte> frame = tr::wire::encode(v);
54    std::printf("VALUE with a CRC trailer: %zu bytes (4 header + 4 body + 4 CRC-32C)\n",
55                frame.size());
56    const auto good = tr::wire::tlv_node_t::over(frame);
57    check(ok, good.has_value(), "the frame decodes");
58    check(ok, good && good->trailer() && good->trailer()->crc.has_value(), "and carries a CRC");
59
60    // One flipped payload byte. The CRC is what makes that a verdict rather than a guess.
61    std::vector<std::byte> corrupt = frame;
62    corrupt[4] ^= std::byte{0x01};
63    const auto bad = tr::wire::tlv_node_t::over(corrupt);
64    check(ok, !bad && bad.error() == tr::wire::err_t::FRAME_CRC_FAIL,
65          "a single flipped body byte is FRAME_CRC_FAIL");
66
67    // The timestamp half. stamp_ts sets opt.ts, clears opt.tf and writes the value at once.
68    tlv_t stamped;
69    stamped.type = type_t::VALUE;
70    stamped.payload = std::span(body);
71    tr::wire::stamp_ts(stamped, 1'700'000'000'000'000'000);
72    const std::vector<std::byte> stamped_frame = tr::wire::encode(stamped);
73    const auto rt = tr::wire::tlv_node_t::over(stamped_frame);
74    const auto rt_trailer = rt ? rt->trailer() : std::nullopt;
75    check(ok, rt_trailer && rt_trailer->ts, "a stamped TLV round-trips its timestamp");
76    check(ok, rt_trailer && rt_trailer->ts && rt_trailer->ts->value == 1'700'000'000'000'000'000,
77          "with the value intact");
78
79    // The loud refusal: the bit without the value is never emitted as a silent zero.
80    tlv_t claims_ts;
81    claims_ts.type = type_t::VALUE;
82    claims_ts.opt.ts = true;  // set by hand, with no trailer value behind it
83    claims_ts.payload = std::span(body);
84    check(ok, tr::wire::encode(claims_ts).empty(),
85          "opt.ts with no value refuses to encode at all (empty vector)");
86    return ok ? 0 : 1;
87}

See also: frame codec · wire-format bits · data-format reference · wire codec round-trip.