borrow: route the application’s own bytes (L0/L1 substrate)

The L0/L1 substrate is a binding layer, and the far end of the spectrum is the transparent byte router (ADR-0012, CONTEXT.md §Memory-binding spectrum): point a segment at an MMIO register, a program variable, or a const ROM table, and libtracer routes those bytes — no copy, no snapshot, no CRC imposed. tr::view::borrow allocates only the small segment_t control block; the payload pointer is the caller’s, and so is the lifetime promise.

The contrast that makes the choice visible is over_bytes, which copies into a fresh segment. Both hand back a view_t; only one keeps the pointer.

What to notice

  • Pointer identity is the whole distinction, and it is asserted both ways. The borrowed view’s bytes().data() is the application array’s address; over_bytes’s is not.

  • A borrow is live, not a snapshot. The example writes through the application buffer after taking the view and reads the new value back out of it — then writes again and shows the earlier over_bytes copy unchanged. Safety by snapshotting is recommended, never mandated.

  • The lifetime is the application’s promise. mem_borrowed’s destroy frees only the control block; the bytes are never touched. Its owns_bytes trait is false, which is the seam’s way of saying such a segment must not be durably stored.

  • borrow_const is for ROM and other read-only bytes. libtracer never writes through one; the const_cast inside merely restores the segment’s uniform writable-at-the-type-level base.

  • 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 — `borrow`: route the application's OWN bytes, without owning them.
 9 *
10 * The L0/L1 substrate is a binding layer, and the extreme of the spectrum is the
11 * **transparent byte router** (ADR-0012, `CONTEXT.md` §Memory-binding spectrum): point a
12 * segment at an MMIO register, a program variable, or a const ROM table and libtracer routes
13 * those bytes — no copy, no CRC imposed. `tr::view::borrow` allocates only the ~32-byte
14 * `segment_t` control block; the payload pointer is the caller's, and the lifetime is the
15 * caller's promise.
16 *
17 * The contrast that makes the choice visible is `over_bytes`, which COPIES into a fresh
18 * segment. Both give a `view_t`; only one keeps the pointer. The example asserts pointer
19 * identity for the borrow and non-identity for the copy, then writes through the application
20 * buffer and reads the new value back out of the borrowed view — live, not a snapshot.
21 *
22 * Runs under ctest as `example_view_borrow`; returns non-zero on any failed check.
23 */
24
25#include <array>
26#include <cstddef>
27#include <cstdio>
28#include <optional>
29#include <span>
30
31#include "libtracer/tracer.hpp"
32
33namespace {
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/** @brief A const table standing in for ROM — the `borrow_const` case. */
42constexpr std::array<std::byte, 4> kRomTable{std::byte{0xDE}, std::byte{0xAD}, std::byte{0xBE},
43                                             std::byte{0xEF}};
44
45}  // namespace
46
47int main() {
48    bool ok = true;
49    std::array<std::byte, 8> app{};  // the application's buffer — libtracer never owns it
50    app[0] = std::byte{0x01};
51
52    const tr::view::view_t borrowed = tr::view::view_t::over(tr::view::borrow(app));
53    std::printf("borrowed view: %zu bytes at the app's own address\n", borrowed.length);
54    check(ok, borrowed.bytes().data() == app.data(), "borrow keeps the caller's pointer");
55    check(ok, borrowed.length == app.size(), "over the caller's whole buffer");
56
57    // Live, not a snapshot: the application writes and the view already sees it.
58    app[0] = std::byte{0x02};
59    check(ok, borrowed.bytes()[0] == std::byte{0x02}, "a write through the app buffer is visible");
60
61    const auto copied = tr::view::over_bytes(app);
62    check(ok, copied.has_value(), "over_bytes produced a view");
63    if (!copied) return 1;
64    check(ok, copied->bytes().data() != app.data(), "over_bytes COPIES into a fresh segment");
65    app[0] = std::byte{0x03};
66    check(ok, copied->bytes()[0] == std::byte{0x02}, "so the copy froze the value it was given");
67
68    const tr::view::view_t rom = tr::view::view_t::over(tr::view::borrow_const(kRomTable));
69    check(ok, rom.bytes().data() == kRomTable.data(), "borrow_const borrows read-only bytes too");
70    check(ok, rom.is_host(), "and reports HOST space — the CPU may dereference it");
71    return ok ? 0 : 1;
72}

See also: backends · segment module · memory substrate reference.