#[non_exhaustive]pub struct RangeFrame {
pub offset: u64,
pub length: u64,
pub bytes: Vec<u8>,
pub complete: bool,
pub total_length: Option<u64>,
pub chunk_lens: Option<Vec<u64>>,
pub chunk_index: Option<u64>,
pub inclusion_proof: Option<String>,
pub root: Option<String>,
pub chunk_count: Option<u64>,
pub chunk_lens_offset: Option<u64>,
}Expand description
One streamed dig.fetchRange frame (L7 spec §8 framing). Frames arrive in ascending offset
order and tile the requested range exactly; the caller reassembles by offset and stops on
complete.
§The metadata splits into two sets, and which frames carry them differs
Read this before implementing either side — the two halves have different rules, and conforming to the wrong half produces a verification miss on a multi-frame read rather than a clean failure.
- Identity — fixed-size, on EVERY frame:
root,total_length,chunk_count, pluschunk_indexwhere the window is chunk-aligned. These are what let a reader reject a wrong-generation or wrong-layout holder the moment a frame arrives, which is a property the once-per-stream set can never have. They cost a bounded number of bytes, so carrying them everywhere is cheap. Set them withwith_identity+with_chunk_index. - Prologue — resource-scaling, ONCE per range stream:
chunk_lens(paged, located bychunk_lens_offset) andinclusion_proof. Their size is a function of the RESOURCE rather than of the frame, so they ride the first frame or a paged prologue and MUST NOT be repeated on later frames. Set them withwith_chunk_lens_page+with_inclusion_proof, or omit them entirely when the request setRangeRequest::skip_layout.
Before 0.13.0 every one of these was “first frame only”, because the whole layout had to fit one
frame or the range was unservable (#1640). SPEC.md §5.1.1 is normative.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.offset: u64This frame’s start offset within the requested range.
length: u64This frame’s byte length.
bytes: Vec<u8>The raw ciphertext bytes. On the wire they are base64 (base64_bytes) — the canonical
dig.fetchRange frame encoding every producer emits.
complete: boolWhether this is the final frame of the range.
total_length: Option<u64>(identity — every frame) the full resource ciphertext length.
chunk_lens: Option<Vec<u64>>(prologue — once per stream) per-chunk ciphertext lengths of the whole resource, in order,
or ONE PAGE of them beginning at chunk_lens_offset.
Resource-scaling, so it MUST NOT be repeated on later frames: on a later frame it was only a
redundant equality check on an array the client already held. Omitted entirely when the request
set RangeRequest::skip_layout.
chunk_index: Option<u64>(identity — every frame, where the window is chunk-aligned) index into the resource’s
chunk_lens array of the first chunk in THIS frame.
Fixed-size, and about this frame rather than about the resource, so a chunk-aligned continuation
frame states it too — see with_chunk_index, which is deliberately
separate from the proof setter so stating alignment never costs a repeated proof.
inclusion_proof: Option<String>(prologue — once per stream) merkle inclusion proof of the whole resource vs the generation
root (base64, relayed verbatim); null/absent for capsule: true (self-verifying on install).
Resource-scaling and capped at MAX_INCLUSION_PROOF_B64. At 4,096 B it would consume the
entire remaining frame budget if repeated, which is the concrete reason “once per stream” is a
MUST NOT rather than a preference.
root: Option<String>(identity — every frame) the generation root (64-hex) this range is served from, and which the inclusion proof is against.
chunk_count: Option<u64>(identity — every frame) the resource’s TOTAL chunk count — how many entries the
reassembled chunk_lens array has.
Together with root and total_length it is what lets a reader detect a wrong-generation or
wrong-layout holder the moment a frame arrives. It is also how a reader sizes the array it is
paging in, and how it knows the prologue is complete.
chunk_lens_offset: Option<u64>(prologue — once per page) the index into the resource’s chunk_lens array at which THIS
frame’s chunk_lens page begins — how a paged prologue is located and reassembled.
A resource whose layout exceeds MAX_CHUNK_LENS_PER_FRAME cannot state it on one frame, so
the sender pages it: successive frames each carry up to that many entries, stamped with the
offset they start at. A reader places each page at its offset and has the whole array once it
holds chunk_count entries. Absent means “this frame’s chunk_lens, if any, begins at 0” — the
single-frame layout, which is the pre-0.13.0 shape.
Implementations§
Source§impl RangeFrame
impl RangeFrame
Sourcepub fn data(offset: u64, bytes: Vec<u8>) -> Self
pub fn data(offset: u64, bytes: Vec<u8>) -> Self
A data frame: bytes at offset, with no metadata — the shape of every continuation frame.
length is derived from bytes, since a frame that misdeclares its own payload length is never
what a serve path wants. The metadata a FIRST frame or a prologue page carries is layered on with
the with_* setters, so a continuation frame cannot accidentally claim a layout it is not
stating — the two shapes are different call chains, not one call with a pile of Nones.
Sourcepub fn with_complete(self, complete: bool) -> Self
pub fn with_complete(self, complete: bool) -> Self
Mark this as the final frame of the range.
Sourcepub fn with_identity(
self,
root: impl Into<String>,
total_length: u64,
chunk_count: u64,
) -> Self
pub fn with_identity( self, root: impl Into<String>, total_length: u64, chunk_count: u64, ) -> Self
The fixed-size identity metadata EVERY frame of a range should carry: the generation root
(64-hex) the range is served from, the resource’s ciphertext total_length, and its
chunk_count.
These three are what let a reader reject a wrong-generation or wrong-layout holder the moment a frame arrives — which the resource-scaling metadata never could, since it arrives once. They are fixed-size, so carrying them everywhere costs a bounded number of bytes.
Sourcepub fn with_chunk_lens_page(
self,
chunk_lens_offset: u64,
chunk_lens: Vec<u64>,
) -> Self
pub fn with_chunk_lens_page( self, chunk_lens_offset: u64, chunk_lens: Vec<u64>, ) -> Self
A page of the resource’s chunk_lens array, beginning at entry chunk_lens_offset.
Call it once with offset 0 for a layout that fits one frame (at most
MAX_CHUNK_LENS_PER_FRAME entries), or once per page of a paged prologue. chunk_lens is
a DECRYPT input — per-chunk AES-GCM-SIV needs the WHOLE array, and a reader rejects an array
whose sum differs from total_length — so a page is only ever useful as part of a complete set.
Sourcepub fn split_chunk_lens_pages(chunk_lens: &[u64]) -> Vec<(u64, Vec<u64>)>
pub fn split_chunk_lens_pages(chunk_lens: &[u64]) -> Vec<(u64, Vec<u64>)>
Split a whole resource’s chunk_lens into the pages of a paged prologue: (offset, page)
pairs, in order, each ready for with_chunk_lens_page.
This is the encoder half of the paging rule, and it exists so that no serve path re-derives that
rule by hand. #1640 was an encode/decode asymmetry, so the split and its mirror
ChunkLensAssembler are published together from one module: the pages this returns are exactly
the pages the assembler requires — aligned offsets, MAX_CHUNK_LENS_PER_FRAME entries each
except a shorter tail, tiling the array with no gap and no overlap.
A layout at or under the threshold yields a single page at offset 0, which is the single-frame pre-0.13.0 shape; an empty layout yields no pages at all.
Sourcepub fn with_chunk_index(self, chunk_index: u64) -> Self
pub fn with_chunk_index(self, chunk_index: u64) -> Self
Where this frame starts in the resource’s chunk sequence — the index into chunk_lens of its
first chunk, for a chunk-aligned window.
Fixed-size identity, so a chunk-aligned CONTINUATION frame states it too, not only the first
frame. It has its own setter for exactly that reason: it used to be a parameter of
with_inclusion_proof, which meant the one field every reader wants
on every frame could not be stated without repeating a once-per-stream proof — a SPEC.md §5.1.1
MUST NOT, and 4,096 B per frame against a budget with zero slack.
Sourcepub fn with_inclusion_proof(self, inclusion_proof: impl Into<String>) -> Self
pub fn with_inclusion_proof(self, inclusion_proof: impl Into<String>) -> Self
The merkle inclusion proof of the whole resource against the generation root (base64, relayed
verbatim).
Resource-scaling, so it rides the first frame or the prologue — once per range stream, never
repeated. Base64 longer than MAX_INCLUSION_PROOF_B64 is refused by
encode: such a resource has no conforming range stream at all, and the holder
must say so rather than stream frames a reader cannot verify.
Sourcepub fn with_declared_length(self, length: u64) -> Self
pub fn with_declared_length(self, length: u64) -> Self
Override the declared length, which data otherwise derives from bytes.
For budget fixtures that hold every scalar at its widest legal value. A serve path has no reason
to call this: a frame whose length disagrees with its payload is a frame the reader distrusts.
Sourcepub fn encode(&self) -> Result<Vec<u8>>
pub fn encode(&self) -> Result<Vec<u8>>
Serialize as a u32 big-endian length prefix + JSON body (one framed frame on the stream).
Fails with io::ErrorKind::InvalidData if bytes exceeds
MAX_RANGE_FRAME_PAYLOAD, if inclusion_proof exceeds
MAX_INCLUSION_PROOF_B64, or if the serialized body exceeds MAX_FRAMED_BODY for any
other reason (an unusually large chunk_lens page). A serving peer therefore cannot
emit a frame decode is required to reject: it splits its resource on
MAX_RANGE_FRAME_PAYLOAD or it learns about it here, at the send site, with the ceiling
named in the error.
The payload is checked separately from the body because the body check alone is too weak: a
payload well over the ceiling still fits in MAX_FRAMED_BODY once base64’d when the frame
carries no metadata, so it would encode here and then overflow the moment the same span rode a
FIRST frame with a chunk table attached — a size-dependent, intermittent failure. One explicit
ceiling on bytes makes the limit the same for every frame. The proof is checked for the same
reason: it is premise 2 of MAX_FIRST_FRAME_CHUNK_LENS, and a premise only a test believes
is not a bound (#1655).
Trait Implementations§
Source§impl Clone for RangeFrame
impl Clone for RangeFrame
Source§fn clone(&self) -> RangeFrame
fn clone(&self) -> RangeFrame
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for RangeFrame
impl Debug for RangeFrame
Source§impl<'de> Deserialize<'de> for RangeFrame
impl<'de> Deserialize<'de> for RangeFrame
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for RangeFrame
Source§impl PartialEq for RangeFrame
impl PartialEq for RangeFrame
Source§impl Serialize for RangeFrame
impl Serialize for RangeFrame
impl StructuralPartialEq for RangeFrame
Auto Trait Implementations§
impl Freeze for RangeFrame
impl RefUnwindSafe for RangeFrame
impl Send for RangeFrame
impl Sync for RangeFrame
impl Unpin for RangeFrame
impl UnsafeUnpin for RangeFrame
impl UnwindSafe for RangeFrame
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.