Skip to main content

commonware_runtime/iobuf/
mod.rs

1//! Buffer types for I/O operations.
2//!
3//! `IoBuf` and `IoBufMut` store readable/writable cursor state directly in the
4//! public handle, so the [`Buf`](bytes::Buf) and [`BufMut`](bytes::BufMut) hot
5//! paths are simple pointer/length arithmetic. Allocation ownership lives in a
6//! compact owner word, and `owner.rs` documents the owner model.
7//!
8//! Runtime-owned heap buffers allocate exactly the requested size and alignment
9//! ([`IoBufMut::with_capacity`] requests byte alignment). While one handle owns
10//! such a buffer, its owner word describes the allocation and nothing else is
11//! allocated, unless the layout or the spare capacity left at freeze is too
12//! large for the word. The first clone, slice, or partial split allocates one
13//! small shared owner record. Caller-supplied `Vec<u8>` values are adopted
14//! into the same native form without copying. Caller-supplied [`Bytes`] values
15//! are held zero-copy by an owner record that keeps the `Bytes` alive.
16//!
17//! Throughout this module, "native" means runtime-owned storage whose owner
18//! supports zero-copy mutable recovery through [`IoBuf::try_into_mut`]: heap
19//! allocations and adopted vecs, as opposed to external `Bytes` and
20//! `'static` views.
21//!
22//! # Conversions
23//!
24//! Every `From` conversion into [`IoBuf`] or [`IoBufs`] is zero-copy. A
25//! conversion from a byte source (`Vec<u8>`, `Bytes`, `BytesMut`,
26//! [`IoBufMut`], or a `'static` slice) allocates at most one small owner
27//! record. When a `BytesMut` still owns its vector directly and has spare
28//! capacity, freezing it also allocates a shared-ownership record. Collecting
29//! existing chunks into [`IoBufs`], from [`IoBuf`] collections or an
30//! [`IoBufsMut`], allocates chunk storage only for four or more chunks. It
31//! allocates no owner record, except where freezing an [`IoBufsMut`] chunk
32//! does (see [`IoBufMut::freeze`]).
33//!
34//! Conversions into [`IoBufMut`] or [`IoBufsMut`] reuse the source allocation
35//! where it can back a mutable handle and copy into a new one otherwise.
36//! Reusing a `Bytes` or `BytesMut` keeps its whole allocation as the capacity
37//! and moves readable bytes that start past its beginning to the front. Each
38//! mutable conversion, and each conversion out of the handles, documents its
39//! cost on its impl.
40//!
41//! Public types:
42//! - [`IoBuf`]: Immutable byte buffer
43//! - [`IoBufMut`]: Mutable byte buffer
44//! - [`IoBufs`]: Container for one or more immutable buffers
45//! - [`IoBufsMut`]: Container for one or more mutable buffers
46//! - [`Builder`]: Assembles [`IoBufs`] from inline writes and zero-copy pieces
47//!
48//! # Examples
49//!
50//! The core lifecycle: fill a fixed-capacity mutable buffer, freeze it into
51//! cheaply cloneable immutable views, and recover the mutable handle (with
52//! its spare capacity) once the views are gone:
53//!
54//! ```
55//! use commonware_runtime::{Buf, BufMut, IoBuf, IoBufMut};
56//!
57//! let mut buf = IoBufMut::with_capacity(8);
58//! buf.put_slice(b"abcdef");
59//!
60//! let frozen: IoBuf = buf.freeze();
61//! let head = frozen.slice(..3);
62//! assert_eq!(head, b"abc"[..]);
63//!
64//! // A live view shares the owner, so recovery declines.
65//! let frozen = frozen.try_into_mut().unwrap_err();
66//! drop(head);
67//!
68//! // Once the view is gone, recovery returns the mutable handle with its
69//! // spare capacity.
70//! let mut recovered = frozen.try_into_mut().unwrap();
71//! assert_eq!(recovered.as_ref(), b"abcdef");
72//! assert_eq!(recovered.capacity(), 8);
73//! recovered.put_slice(b"gh");
74//! ```
75//!
76//! [`Bytes`]: bytes::Bytes
77
78mod buf;
79mod bufs;
80mod owner;
81
82pub use buf::{IoBuf, IoBufMut};
83pub use bufs::{Builder, EncodeExt, IoBufs, IoBufsMut};
84
85/// Panics for cursor or write operations that run past the available region.
86///
87/// Outlined so the `Buf`/`BufMut` fast paths inline as a compare, a branch,
88/// and a memcpy, mirroring the panic helpers in `bytes`.
89#[cold]
90#[inline(never)]
91fn panic_advance(requested: usize, available: usize) -> ! {
92    panic!("cannot advance past end of buffer: requested {requested}, available {available}");
93}