pub struct ChunkData { /* private fields */ }Expand description
Node → payer chunk of blob bytes. The payload carries at least one byte; its
length is the sender’s choice, bounded above by the framing layer’s
MAX_MESSAGE_SIZE; ADR 005
§cdn/client/v1 additionally requires a sender not to cross a CHUNK_BYTES
payment boundary, so in practice a frame runs to at most one interval. Frames need not be uniform, and the frame before
ClientMessage::StreamEnd is commonly shorter than the ones before it.
Frame size is independent of both verification and payment. The bao codec is
the sole verifier and resolves chunk-group boundaries itself (ADR 038), and
the payment meter ticks on cumulative bytes crossing CHUNK_BYTES rather
than on frames. A sender therefore picks whatever size suits it; nothing is
negotiated and no message carries the choice.
The lower bound is load-bearing, not cosmetic (#1088). A shorter final frame is
permitted, never an empty one: an empty frame carries no
payload, so it advances neither the receiver’s cumulative byte count nor its
voucher accounting. An unbounded run of them therefore drives the receive
loops without making application-level progress, and the cumulative > expected_wire overrun guard — which only ever trips on bytes — never fires.
An empty frame cannot be obtained at all — ChunkData::new, the try_from decode
gate, and every encode_chunk_* helper (encode_chunk_frame,
encode_chunk_frame_headers, and the crate-private one the latter builds on)
each reject one, and between them they are every route to a frame body. That is the invariant the pull
paths’ inactivity deadline rests on: with empty frames banned, “a frame arrived” and
“bytes made progress” are the same statement, so a peer cannot refresh the deadline
with padding.
§The bounds are enforced by construction
The field is private and ChunkData::new is the only constructor, so an invalid
frame cannot be built — and #[serde(try_from)] routes decoding through the same
check, so it cannot be decoded either. A receive loop therefore holds a valid frame
by having one at all, and a serve path cannot emit an empty frame even by accident.
The invariant lives on the type, not in a convention, because a convention here is
silently skippable. Were the floor an advisory validate(), it would ride on every
receive loop remembering to call it and every emitter avoiding an empty frame — and
the serve side avoids one only incidentally, differently on each path:
each serve path cuts its byte stream into frames and emits whatever has
accumulated, so an empty frame is avoided only because an exhausted stream yields
nothing to emit. The empty blob (whose bao encoding is zero bytes — see
decdn_bao_range::align_range) reaches ClientMessage::StreamEnd the same way,
with no frame sent first (#1054).
That is a fact about unrelated code, and it could change without anyone noticing
which invariant they had just removed — while the requester’s receive loop depends on it
(#1797): an empty frame advances neither the cumulative delivered bytes nor the voucher
accounting, so a run of them would drive that loop without delivering any content. The
requester’s throughput floor counts bytes off the wire, envelopes included, so it is this
floor — not that counter — that keeps wire progress equal to content progress.
That is too much weight for a convention, so the type carries the floor instead: the field
is private, both ChunkData doors reject an empty payload, and the
encode_chunk_* helpers — which build no ChunkData, and among them
encode_chunk_frame_headers is the door both serve paths use — restate the same
check on payload_len. Every route is closed.
Implementations§
Source§impl ChunkData
impl ChunkData
Sourcepub fn new(bytes: Vec<u8>) -> Result<Self, MessageValidationError>
pub fn new(bytes: Vec<u8>) -> Result<Self, MessageValidationError>
The only constructor. Enforces the payload floor: non-empty.
There is no ceiling here. An oversized frame is refused by the framing
layer against MAX_MESSAGE_SIZE,
which is the bound that runs before the receiver allocates.
§Errors
MessageValidationError::EmptyChunk for a zero-length payload.
Sourcepub fn into_bytes(self) -> Vec<u8> ⓘ
pub fn into_bytes(self) -> Vec<u8> ⓘ
Consume the frame for its payload, avoiding a copy on the receive path.