Skip to main content

ChunkData

Struct ChunkData 

Source
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

Source

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.

Source

pub fn bytes(&self) -> &[u8] ⓘ

The payload. Non-empty by construction.

Source

pub fn into_bytes(self) -> Vec<u8> ⓘ

Consume the frame for its payload, avoiding a copy on the receive path.

Trait Implementations§

Source§

impl Clone for ChunkData

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ChunkData

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ChunkData

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for ChunkData

Source§

impl PartialEq for ChunkData

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for ChunkData

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for ChunkData

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.