The rope: assembly is chaining, never memcpy (L1 views)¶
A rope is the L1 composite — an ordered chain of views over possibly different segments,
forming one logical byte sequence (CONTEXT.md §Rope / assembly). A static
header segment plus a live DMA payload segment become one payload by chaining them.
append links; total_length sums what the links already hold. Nothing is copied, and
“assembly” and “reassembly” both mean exactly this.
Rope scatter-gather shows the same type at the egress end; this page is the composition itself.
What to notice¶
A view converts to a one-link rope implicitly. The trivial case is meant to be free, and the single-link value is the hot path — a rope-valued vertex slot costs what a view-valued slot cost.
only()is the consumer’s explicit “this is one segment”. Zero copy, and debug-asserted. A consumer that cannot promise contiguity asks formaterialize()instead, which is the visible choice between a refcount bump and one flatten copy.The first two links live in inline storage. A one- or two-link chain allocates nothing at all; the third link spills the chain to the heap. That threshold is a cost tuning knob, not a limit — nothing refuses a longer rope, and a caller that knows its final count can
try_reserveit up front.operator+takes its left operand by value. The example checks that the original rope still has its three links afterwards: concatenation chains, it does not consume.A rope is not a list of TLVs. It composes storage;
opt.PLcomposes meaning (structured vs opaque). The two axes are independent, which is why a link boundary may fall mid-header — see the lazy view.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 — a rope is an ordered chain of views; assembly is chaining, never copy.
9 *
10 * A **rope** is the L1 composite: an ordered chain of views over possibly different segments,
11 * forming one logical byte sequence (`CONTEXT.md` §Rope / assembly). A static header segment
12 * plus a live DMA payload segment become one payload by CHAINING them — `append` links, it
13 * does not `memcpy`, and `total_length` sums what the links already hold.
14 *
15 * Two spellings matter to a first-contact reader. `only()` is the consumer's explicit "this
16 * value is one segment" — the single-link hot path, zero copy. And a rope keeps its first two
17 * links in inline storage, so a one- or two-link chain allocates nothing at all; the third
18 * link spills the chain to the heap. That threshold is a cost tuning knob, not a limit —
19 * nothing refuses a longer rope.
20 *
21 * Runs under ctest as `example_view_rope_compose`; returns non-zero on any failed check.
22 */
23
24#include <cstddef>
25#include <cstdio>
26#include <optional>
27
28#include "libtracer/tracer.hpp"
29
30namespace {
31
32/** @brief Report expectation @p what and record a failure on @p ok. */
33void check(bool& ok, bool cond, const char* what) {
34 std::printf(" [%s] %s\n", cond ? "ok" : "FAIL", what);
35 ok = ok && cond;
36}
37
38/** @brief A fresh 4-byte heap segment filled with @p fill, as a whole-segment view. */
39tr::view::view_t block(std::byte fill) {
40 tr::view::segment_ptr_t seg = tr::view::heap_alloc(4);
41 for (std::byte& b : seg->bytes) b = fill;
42 return tr::view::view_t::over(std::move(seg));
43}
44
45} // namespace
46
47int main() {
48 bool ok = true;
49
50 tr::view::rope_t one{block(std::byte{0xAA})}; // a view converts to a one-link rope
51 check(ok, one.link_count() == 1, "one link");
52 check(ok, one.total_length() == 4, "four logical bytes");
53 check(ok, one.only().bytes()[0] == std::byte{0xAA},
54 "only() is the single-segment fast path — zero copy, no flatten");
55
56 one.append(block(std::byte{0xBB}));
57 check(ok, one.link_count() == 2 && one.total_length() == 8,
58 "appending chains a second segment into the same logical sequence");
59 check(ok, one.all_host(), "both links are CPU-addressable");
60
61 // The third link spills the chain to the heap. An optimization threshold, not a bound.
62 one.append(block(std::byte{0xCC}));
63 std::printf("rope: %zu links, %zu logical bytes, still zero byte copies\n", one.link_count(),
64 one.total_length());
65 check(ok, one.link_count() == 3 && one.total_length() == 12,
66 "a third link chains just as well");
67
68 tr::view::rope_t tail{block(std::byte{0xDD})};
69 const tr::view::rope_t joined = one + tail; // concat = chaining, both operands intact
70 check(ok, joined.link_count() == 4 && joined.total_length() == 16,
71 "operator+ chains two ropes");
72 check(ok, one.link_count() == 3, "leaving the left operand's own chain untouched");
73 return ok ? 0 : 1;
74}
See also: views module · views & ownership reference · composition axes.