Skip to main content

moqtap_client/draft22/
fill.rs

1//! Location filters and fill fetch streams, the two things draft-22 asks a
2//! subscriber to build byte by byte.
3//!
4//! The range of a FETCH travels in the `LOCATION_FILTER` message parameter
5//! (Section 9.20.9), and `FILL_PARAMETERS` (Section 9.20.15) is a parameter
6//! whose *presence* on a SUBSCRIBE or a REQUEST_UPDATE asks the publisher to
7//! open a fill fetch stream, and whose value is a nested parameter block that
8//! overrides the subscription's own settings for that fill alone.
9//!
10//! Both are parameter values a caller would otherwise have to construct by
11//! hand, which is why they are here: neither carries anything a receiver could
12//! resynchronise on, so getting either wrong desynchronises the whole parameter
13//! list rather than producing a recognisable error. A `LOCATION_FILTER` has no
14//! length at all — its `Location Filter Type` is the only thing that says
15//! where it ends — and `FILL_PARAMETERS` is the place draft-22 is least
16//! explicit.
17//!
18//! # What this module decides, and where the draft is silent
19//!
20//! * A `FILL_PARAMETERS` value **begins with a `Number of Parameters`
21//!   count**, and its `Type Delta` chain restarts at 0. See
22//!   [`FillParameters`].
23//! * A fill fetch stream with no `GROUP_ORDER` anywhere is read **Ascending**.
24//!   See [`group_order`].
25//!
26//! Every value this module builds is handed to the codec's own decoder before
27//! it is returned, so a disagreement between the two is an error here rather
28//! than a frame on the wire.
29//!
30//! # Ranges are inclusive
31//!
32//! Draft-19's fetch end was "the last Object, plus 1; or 0 to indicate the
33//! entire Group". Draft-22 Section 3.3.1 says a Location filter "specifies an
34//! inclusive range of Locations", Section 3.2 that a FETCH requests Objects
35//! "between a Start Location and an End Location, inclusive", and neither
36//! draft-19 convention survives. **Nothing in this module adds or subtracts one from
37//! an end location**, and a caller porting draft-19 arithmetic forward fetches
38//! one object too many.
39
40use moqtap_codec::draft22::data_stream::GroupOrder;
41use moqtap_codec::draft22::message::{
42    decode_fill_parameters, decode_location_filter, location_filter_types, FILL_PARAMETERS,
43    LOCATION_FILTER,
44};
45use moqtap_codec::kvp::{KeyValuePair, KvpValue};
46use moqtap_codec::varint::{Moqt18 as Wire, VarInt};
47
48/// `GROUP_ORDER`, Parameter Type 0x22 (Section 9.20.8).
49///
50/// Spelled here rather than imported because the codec's draft-22 message
51/// module does not export it, and because this module needs the same number in
52/// three places: Table 7's allow-list, the uint8 shape test, and [`group_order`].
53pub const GROUP_ORDER: u64 = 0x22;
54
55/// The `GROUP_ORDER` value Section 9.20.8 assigns to Ascending.
56const GROUP_ORDER_ASCENDING: u64 = 0x1;
57
58/// The `GROUP_ORDER` value Section 9.20.8 assigns to Descending.
59const GROUP_ORDER_DESCENDING: u64 = 0x2;
60
61/// Errors from building a `LOCATION_FILTER` or a `FILL_PARAMETERS` value.
62#[derive(Debug, thiserror::Error, PartialEq, Eq)]
63pub enum FillError {
64    /// `StartGroup + EndGroupDelta` left the number space.
65    ///
66    /// Section 9.20.9: "If StartGroup + EndGroupDelta exceeds 2^64 - 1, the
67    /// endpoint MUST close the session with a PROTOCOL_VIOLATION." A value the
68    /// peer must close the session over is not a value this endpoint has a way
69    /// of sending, so it is refused here instead.
70    #[error("start group {start_group} plus end group delta {delta} exceeds 2^64 - 1")]
71    EndGroupOverflow {
72        /// The start group the filter named.
73        start_group: u64,
74        /// The delta added to it.
75        delta: u64,
76    },
77    /// A parameter type Table 7 does not list was put inside `FILL_PARAMETERS`.
78    ///
79    /// Section 9.20.15: "An endpoint that receives a parameter inside
80    /// FILL_PARAMETERS that is not listed above MUST close the session with
81    /// PROTOCOL_VIOLATION."
82    #[error("parameter type {0:#x} may not appear inside FILL_PARAMETERS")]
83    NotAFillParameter(u64),
84    /// The same parameter type was put inside `FILL_PARAMETERS` twice where its
85    /// own definition does not allow repeats.
86    #[error("parameter type {0:#x} appears twice inside FILL_PARAMETERS")]
87    RepeatedFillParameter(u64),
88    /// A value whose shape does not match the type it was filed under.
89    #[error(
90        "the value under parameter type {key:#x} is not the shape that type defines: {detail}"
91    )]
92    MalformedValue {
93        /// The parameter type.
94        key: u64,
95        /// What is wrong with the value.
96        detail: &'static str,
97    },
98    /// The value this module built is one the codec's own decoder refuses.
99    ///
100    /// Unreachable unless the encoder here and the decoder in
101    /// `moqtap-codec` have drifted apart, which is what the check exists to
102    /// catch: a value that goes out and comes back as a session close is worse
103    /// than one that never goes out.
104    #[error("the encoded value does not decode: {0}")]
105    NotRoundTrippable(String),
106}
107
108/// The parameter types draft-22 Section 9.20.15, Table 7 permits inside a
109/// `FILL_PARAMETERS` value, in ascending order.
110///
111/// `TRACK_PROPERTY_FILTER` (0x29) is deliberately absent: it selects tracks,
112/// and a fill applies to one already-selected track. The same list is enforced
113/// on the decode side by `moqtap_codec::draft22::message::decode_fill_parameters`,
114/// which is what the round-trip check at the end of
115/// [`FillParameters::parameter`] leans on.
116const FILL_PARAMETERS_ALLOWED: &[u64] = &[0x0A, 0x20, 0x21, 0x22, 0x25, 0x26, 0x27, 0x28];
117
118/// The five Range Filters, of which four may be nested. Only these may repeat.
119///
120/// Section 3.3.2 lets the Range Filters "appear multiple times", and Section
121/// 9.20 forbids a repeat of anything else. A repeat is encoded as a `Type
122/// Delta` of 0, which is the only encoding a second instance of an ascending
123/// chain has.
124fn may_repeat(key: u64) -> bool {
125    (0x25..=0x29).contains(&key)
126}
127
128/// Whether a Table 7 parameter's value is a single raw byte rather than a
129/// varint or a length-prefixed block.
130///
131/// `SUBSCRIBER_PRIORITY` (0x20, Section 9.20.7) and `GROUP_ORDER` (0x22,
132/// Section 9.20.8) are the two uint8s that may be nested.
133fn is_uint8(key: u64) -> bool {
134    key == 0x20 || key == GROUP_ORDER
135}
136
137/// Whether a Table 7 parameter's value is a bare varint.
138///
139/// `FILL_TIMEOUT` (0x0A, Section 9.20.5) is the only one. `LOCATION_FILTER`
140/// (0x21) also opens with a varint, but a varint alone is only its type, and
141/// accepting one here would write a bare `0x05` as Next Object and a bare `0x00`
142/// as no filter: the codec's own encoder refuses that value, so this one does
143/// too.
144fn is_bare_varint(key: u64) -> bool {
145    key == 0x0A
146}
147
148/// Whether a Table 7 parameter's value carries its own length prefix.
149///
150/// The four nestable Range Filters (0x25 through 0x28). `FILL_TIMEOUT` (0x0A)
151/// is the only bare varint in the table, and `LOCATION_FILTER` (0x21) is
152/// neither: its value is a `Location Filter Type` and the fields that type
153/// names, with no length ahead of them, nested or not.
154fn is_length_prefixed(key: u64) -> bool {
155    (0x25..=0x28).contains(&key)
156}
157
158/// A draft-22 `LOCATION_FILTER` (Parameter Type 0x21), Section 9.20.9.
159///
160/// ```text
161/// LOCATION_FILTER Parameter {
162///   Parameter Type (vi64) = 0x21,
163///   Location Filter Type (vi64),
164///   [StartGroup (vi64),]
165///   [StartObject (vi64),]
166///   [EndGroupDelta (vi64),]
167///   [EndObject (vi64),]
168/// }
169/// ```
170///
171/// # The shape is the type
172///
173/// Section 9.20.9: "The Location Filter Type dictates which optional
174/// variable-length integer fields follow, and how they are interpreted." Each
175/// constructor here is one of the six types Table 6 lists, and the type is
176/// written first, so a caller cannot build a type the draft does not define,
177/// cannot build a three-field filter by leaving a field out of a four-field
178/// one, and cannot confuse the two types that carry no fields at all — None
179/// and Next Object, which mean opposite things.
180///
181/// There is no length. The type is the only thing that says where the value
182/// ends, so a receiver that reads the type wrong reads every parameter after
183/// it wrong too; this is why the value is built here and handed back through
184/// the codec's own decoder before it is returned.
185///
186/// # Migrating a draft-19 filter
187///
188/// Draft-19's filter also opened with a type, and its numbers are **not**
189/// these. Draft-19's Filter Type 0x1 (Next Group Start) is
190/// [`LocationFilter::relative`] with `0`, type 0x01 here. Its 0x2 (Largest
191/// Object) is [`LocationFilter::next_object`], type 0x05 here — not 0x02,
192/// which is an absolute start. Its 0x3 and 0x4 (AbsoluteStart and
193/// AbsoluteRange) are [`LocationFilter::absolute_start`] and
194/// [`LocationFilter::range`], types 0x02 and 0x03 here. A draft-19 type number
195/// copied across as a number asks a different question.
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct LocationFilter {
198    filter_type: u64,
199    fields: Vec<u64>,
200}
201
202impl LocationFilter {
203    /// Type 0x00: no filter at all, and no fields.
204    ///
205    /// Section 9.20.9: "If Location Filter Type is 0x00, no fields follow and
206    /// there is no Location Filter." In a REQUEST_UPDATE this is how a filter
207    /// already in force is taken away: Section 9.5 has "no generic mechanism to
208    /// remove a parameter from a request", and omitting `LOCATION_FILTER` from
209    /// a REQUEST_UPDATE leaves the value unchanged rather than clearing it.
210    pub fn none() -> Self {
211        Self { filter_type: location_filter_types::NONE, fields: Vec::new() }
212    }
213
214    /// Type 0x01: a start relative to the live edge, open-ended.
215    ///
216    /// Section 9.20.9: the start Location is `{Largest Object.Group + 1 -
217    /// StartGroup, 0}`, so `0` is the next group, `1` the current group, and
218    /// `N` is `N - 1` groups before the current one. Clamped at both ends of
219    /// the number space by the publisher rather than here, because resolving it
220    /// needs Largest Object.
221    pub fn relative(start_group: u64) -> Self {
222        Self { filter_type: location_filter_types::RELATIVE_START, fields: vec![start_group] }
223    }
224
225    /// Type 0x02: an absolute start, open-ended.
226    ///
227    /// Every start is absolute here, `{0, 0}` included: it is the first
228    /// Location of the track. The live edge is a type of its own,
229    /// [`LocationFilter::next_object`].
230    pub fn absolute_start(start_group: u64, start_object: u64) -> Self {
231        Self {
232            filter_type: location_filter_types::ABSOLUTE_START,
233            fields: vec![start_group, start_object],
234        }
235    }
236
237    /// Type 0x05: start at the Next Object, open-ended, with no fields.
238    ///
239    /// Section 9.20.9: "If Location Filter Type is 0x05, no fields follow and
240    /// it specifies the Next Object." Section 3.4 names this filter as half of
241    /// the recipe for exactly-once delivery on a subscription with a fill: a
242    /// Next Object subscription filter paired with an open-ended fill range,
243    /// which the publisher ends at Largest Object.
244    pub fn next_object() -> Self {
245        Self { filter_type: location_filter_types::NEXT_OBJECT, fields: Vec::new() }
246    }
247
248    /// Type 0x03: an absolute start and an end group, covering **all** objects
249    /// in the end group.
250    ///
251    /// Section 9.20.9: "EndGroupDelta is delta encoded from StartGroup, but both
252    /// the start and end groups are absolute, not relative to Largest Object."
253    /// So the end group is `start_group + end_group_delta`, and a delta of 0
254    /// ends in the group it started in.
255    ///
256    /// # Errors
257    ///
258    /// [`FillError::EndGroupOverflow`] when the sum leaves the number space,
259    /// which Section 9.20.9 makes a PROTOCOL_VIOLATION at the receiver.
260    pub fn range(
261        start_group: u64,
262        start_object: u64,
263        end_group_delta: u64,
264    ) -> Result<Self, FillError> {
265        check_end_group(start_group, end_group_delta)?;
266        Ok(Self {
267            filter_type: location_filter_types::ABSOLUTE_START_GROUP_END,
268            fields: vec![start_group, start_object, end_group_delta],
269        })
270    }
271
272    /// Type 0x04: a fully specified **inclusive** range,
273    /// `{start_group, start_object}` through
274    /// `{start_group + end_group_delta, end_object}`.
275    ///
276    /// `end_object` is the Object ID of the last object the range covers. It is
277    /// not that Object ID plus one, and an `end_object` of 0 means object 0
278    /// rather than the whole group — both draft-19 conventions are absent from
279    /// this draft, and this is the constructor where a ported `+ 1` does its
280    /// damage.
281    ///
282    /// # Errors
283    ///
284    /// [`FillError::EndGroupOverflow`], as [`LocationFilter::range`].
285    pub fn range_to(
286        start_group: u64,
287        start_object: u64,
288        end_group_delta: u64,
289        end_object: u64,
290    ) -> Result<Self, FillError> {
291        check_end_group(start_group, end_group_delta)?;
292        Ok(Self {
293            filter_type: location_filter_types::ABSOLUTE_RANGE,
294            fields: vec![start_group, start_object, end_group_delta, end_object],
295        })
296    }
297
298    /// The `Location Filter Type`, one of
299    /// [`location_filter_types`].
300    pub fn filter_type(&self) -> u64 {
301        self.filter_type
302    }
303
304    /// The `vi64` fields that follow the type, in wire order. Empty for both
305    /// None and Next Object, which only [`LocationFilter::filter_type`] tells
306    /// apart.
307    pub fn fields(&self) -> &[u64] {
308        &self.fields
309    }
310
311    /// The encoded value, without the parameter type ahead of it: the
312    /// `Location Filter Type`, then the fields it names.
313    pub fn encode_value(&self) -> Vec<u8> {
314        let mut out = Vec::with_capacity(1 + self.fields.len() * 2);
315        VarInt::from_u64_moqt(self.filter_type).encode_moqt::<Wire>(&mut out);
316        for field in &self.fields {
317            VarInt::from_u64_moqt(*field).encode_moqt::<Wire>(&mut out);
318        }
319        out
320    }
321
322    /// This filter as the Message Parameter that carries it.
323    ///
324    /// # Errors
325    ///
326    /// [`FillError::NotRoundTrippable`] if the codec's own
327    /// `decode_location_filter` does not read back the type and fields that
328    /// went in.
329    pub fn parameter(&self) -> Result<KeyValuePair, FillError> {
330        let value = self.encode_value();
331        let (filter_type, fields) = decode_location_filter(&value)
332            .map_err(|e| FillError::NotRoundTrippable(e.to_string()))?;
333        if filter_type != self.filter_type || fields != self.fields {
334            return Err(FillError::NotRoundTrippable(
335                "the codec read back a different Location Filter Type or field list".to_string(),
336            ));
337        }
338        Ok(KeyValuePair {
339            key: VarInt::from_u64_moqt(LOCATION_FILTER),
340            value: KvpValue::Bytes(value),
341        })
342    }
343}
344
345fn check_end_group(start_group: u64, delta: u64) -> Result<(), FillError> {
346    start_group
347        .checked_add(delta)
348        .map(|_| ())
349        .ok_or(FillError::EndGroupOverflow { start_group, delta })
350}
351
352/// A draft-22 `FILL_PARAMETERS` (Parameter Type 0x23), Section 9.20.15.
353///
354/// Putting one of these on a SUBSCRIBE or on a REQUEST_UPDATE for a
355/// subscription is what asks the publisher to open a **fill fetch stream**: a
356/// unidirectional stream beginning with a FETCH_HEADER, delivered exactly as a
357/// FETCH response, carrying the Objects behind the live edge that the
358/// subscription itself will not deliver. Its mere presence is the request;
359/// [`FillParameters::inherited`] asks for a fill with every setting taken from
360/// the subscription.
361///
362/// The nested parameters override the subscription's for the fill alone. A
363/// `LOCATION_FILTER` nested here selects the **fill range** and is evaluated
364/// with Fetch rules, so it never reaches past Largest Object; it is independent
365/// of the subscription's own filter.
366///
367/// # This value begins with a `Number of Parameters` count
368///
369/// Section 9.20.15 says the value is "a sequence of Parameters that apply to
370/// the fill fetch stream" and is "encoded as if they were Parameters for a
371/// separate message", and stops there. **This crate reads that as the
372/// whole block, count included.** The draft does not state it either way. The
373/// grounds are Section 9.20's own definition of what a parameter block is —
374/// "Because unknown parameters cannot be skipped, the block is bounded by a
375/// parameter count rather than a length" — and the fact that every message
376/// figure in Section 9 pairs `Number of Parameters` with `Parameters`. The
377/// outer length prefix is the generic length-prefixed value encoding, and says
378/// nothing about the value's internal structure.
379///
380/// So an empty `FILL_PARAMETERS` is `Length = 1` carrying the single byte
381/// `0x00`, **not** `Length = 0`. Getting this wrong desynchronises the whole
382/// enclosing parameter list rather than producing a recognisable error, which
383/// is why it is named here as well as at the decoder.
384///
385/// # The `Type Delta` chain restarts here
386///
387/// Section 9.20.15: "The value of FILL_PARAMETERS is a separate parameter
388/// scope. Parameters inside it are not considered to appear in the enclosing
389/// message for the purposes of Section 9.20, so a Parameter Type MAY appear
390/// both in the message and inside FILL_PARAMETERS." Section 9.20 defines `Type
391/// Delta` against "the previous Parameter Type in the message", and a
392/// separate scope is not the message — so the inner chain starts from 0, and
393/// the outer parameter after `FILL_PARAMETERS` deltas from `0x23` rather than
394/// from the last inner type. **The draft states neither half**; both are
395/// decided here and in the codec's decoder, together.
396#[derive(Debug, Clone, Default, PartialEq, Eq)]
397pub struct FillParameters {
398    inner: Vec<KeyValuePair>,
399}
400
401impl FillParameters {
402    /// A fill with every setting inherited from the subscription.
403    ///
404    /// The common case, and the one whose encoding is worth knowing: one byte,
405    /// `0x00`, under a `Length` of 1.
406    pub fn inherited() -> Self {
407        Self::default()
408    }
409
410    /// Add a nested parameter. Types must be added in ascending order.
411    ///
412    /// # Errors
413    ///
414    /// [`FillError::NotAFillParameter`] for a type outside Table 7, and
415    /// [`FillError::RepeatedFillParameter`] for a second instance of a type
416    /// whose own definition does not allow repeats. Ordering is not checked
417    /// here — [`FillParameters::parameter`] sorts before encoding, because
418    /// Section 9.20 requires ascending order on the wire and a caller should
419    /// not have to know Table 7's numbering to satisfy it.
420    pub fn with(mut self, parameter: KeyValuePair) -> Result<Self, FillError> {
421        let key = parameter.key.into_inner();
422        if !FILL_PARAMETERS_ALLOWED.contains(&key) {
423            return Err(FillError::NotAFillParameter(key));
424        }
425        if !may_repeat(key) && self.inner.iter().any(|p| p.key.into_inner() == key) {
426            return Err(FillError::RepeatedFillParameter(key));
427        }
428        self.inner.push(parameter);
429        Ok(self)
430    }
431
432    /// Add the `LOCATION_FILTER` that selects the fill range.
433    ///
434    /// Shorthand for `with(filter.parameter()?)`, and the parameter most fills
435    /// carry: the fill range is what a fill is for.
436    pub fn with_range(self, filter: &LocationFilter) -> Result<Self, FillError> {
437        let parameter = filter.parameter()?;
438        self.with(parameter)
439    }
440
441    /// Add the `GROUP_ORDER` (0x22) that the fill fetch stream's Groups arrive
442    /// in.
443    ///
444    /// Section 9.20.8: "When it appears inside FILL_PARAMETERS, it governs the
445    /// fill fetch stream and its ordering relative to subscription-delivered
446    /// Objects". It is the one nested parameter a **reader** cannot ignore:
447    /// Section 11.4.1.1 makes a Group ID Delta count upward under Ascending and
448    /// downward under Descending, nothing on the data stream says which, and a
449    /// reader started in the wrong order decodes every Object after the first
450    /// into a Group that walks the wrong way — without failing to parse.
451    ///
452    /// A shorthand rather than a raw [`KeyValuePair`] because the value is a
453    /// uint8 on the wire while the crate's `KvpValue` has no uint8 arm: it
454    /// travels as a `Varint` whose value must fit one byte, and
455    /// `encode_nested_value` writes the byte. Getting that pair wrong is a
456    /// `MalformedValue` at best and a desynchronised block at worst.
457    ///
458    /// # Errors
459    ///
460    /// As [`FillParameters::with`]: [`FillError::RepeatedFillParameter`] for a
461    /// second `GROUP_ORDER`, which Section 9.20 does not allow.
462    pub fn with_group_order(self, order: GroupOrder) -> Result<Self, FillError> {
463        self.with(KeyValuePair {
464            key: VarInt::from_u64_moqt(GROUP_ORDER),
465            value: KvpValue::Varint(VarInt::from_u64_moqt(encode_group_order(order))),
466        })
467    }
468
469    /// The nested parameters, in the order they were added.
470    pub fn inner(&self) -> &[KeyValuePair] {
471        &self.inner
472    }
473
474    /// This block as the Message Parameter that carries it.
475    ///
476    /// The value is the count, then the parameters in ascending type order with
477    /// their types delta-encoded from 0, each value in the shape its own type
478    /// names. It is decoded back through the codec before it is returned.
479    ///
480    /// # Errors
481    ///
482    /// [`FillError::MalformedValue`] for a value whose shape does not match its
483    /// type, and [`FillError::NotRoundTrippable`] if the codec's
484    /// `decode_fill_parameters` refuses what this built or reads it back
485    /// differently.
486    pub fn parameter(&self) -> Result<KeyValuePair, FillError> {
487        let mut sorted = self.inner.clone();
488        // Section 9.20: "Parameters MUST be serialized in ascending order by
489        // Type." A stable sort keeps two instances of a repeatable filter in
490        // the order the caller added them, which is the order their SetIDs are
491        // meant to be read in.
492        sorted.sort_by_key(|p| p.key.into_inner());
493
494        let mut value = Vec::new();
495        VarInt::from_usize(sorted.len()).encode_moqt::<Wire>(&mut value);
496        let mut prev_key: u64 = 0;
497        for pair in &sorted {
498            let key = pair.key.into_inner();
499            // The chain starts at 0 because this is a separate scope, not
500            // because the block is at the start of a message.
501            let delta = key - prev_key;
502            prev_key = key;
503            VarInt::from_u64_moqt(delta).encode_moqt::<Wire>(&mut value);
504            encode_nested_value(key, &pair.value, &mut value)?;
505        }
506
507        let read = decode_fill_parameters(&value)
508            .map_err(|e| FillError::NotRoundTrippable(e.to_string()))?;
509        if read.len() != sorted.len() {
510            return Err(FillError::NotRoundTrippable(
511                "the codec read back a different number of parameters".to_string(),
512            ));
513        }
514        Ok(KeyValuePair {
515            key: VarInt::from_u64_moqt(FILL_PARAMETERS),
516            value: KvpValue::Bytes(value),
517        })
518    }
519}
520
521/// Write one nested parameter's value in the shape its type names.
522///
523/// The shapes are Table 7's eight types only, which is the whole of what may be
524/// nested; anything else was refused by [`FillParameters::with`] before it got
525/// here.
526fn encode_nested_value(key: u64, value: &KvpValue, out: &mut Vec<u8>) -> Result<(), FillError> {
527    match value {
528        KvpValue::Varint(v) if is_uint8(key) => {
529            let raw = v.into_inner();
530            let byte = u8::try_from(raw).map_err(|_| FillError::MalformedValue {
531                key,
532                detail: "a uint8 parameter's value does not fit one byte",
533            })?;
534            out.push(byte);
535            Ok(())
536        }
537        KvpValue::Varint(v) if is_bare_varint(key) => {
538            v.encode_moqt::<Wire>(out);
539            Ok(())
540        }
541        KvpValue::Bytes(bytes) if is_length_prefixed(key) => {
542            VarInt::from_usize(bytes.len()).encode_moqt::<Wire>(out);
543            out.extend_from_slice(bytes);
544            Ok(())
545        }
546        // Written as it stands: the reader finds the end of these bytes by
547        // reading the type, so a value whose fields disagree with its type is
548        // refused here rather than shifting every nested parameter after it.
549        KvpValue::Bytes(bytes) if key == LOCATION_FILTER => {
550            decode_location_filter(bytes).map_err(|_| FillError::MalformedValue {
551                key,
552                detail: "bytes that are not a Location Filter Type and the fields it names",
553            })?;
554            out.extend_from_slice(bytes);
555            Ok(())
556        }
557        KvpValue::Varint(_) => Err(FillError::MalformedValue {
558            key,
559            detail: "a bare varint where the type defines a structure",
560        }),
561        KvpValue::Bytes(_) => Err(FillError::MalformedValue {
562            key,
563            detail: "bytes where the type defines a bare varint or a uint8",
564        }),
565    }
566}
567
568/// The Section 9.20.8 value for a Group Order.
569fn encode_group_order(order: GroupOrder) -> u64 {
570    match order {
571        GroupOrder::Ascending => GROUP_ORDER_ASCENDING,
572        GroupOrder::Descending => GROUP_ORDER_DESCENDING,
573    }
574}
575
576/// The `GROUP_ORDER` in one parameter list, or `None` if it holds none.
577///
578/// One list, one level: the caller decides which scope this is being asked
579/// about, because the two scopes answer the question in a fixed order and only
580/// [`group_order`] knows that order.
581fn read_group_order(parameters: &[KeyValuePair]) -> Result<Option<GroupOrder>, FillError> {
582    let Some(parameter) = parameters.iter().find(|p| p.key.into_inner() == GROUP_ORDER) else {
583        return Ok(None);
584    };
585    let KvpValue::Varint(value) = &parameter.value else {
586        return Err(FillError::MalformedValue {
587            key: GROUP_ORDER,
588            detail: "bytes where Section 9.20.8 defines a uint8",
589        });
590    };
591    match value.into_inner() {
592        GROUP_ORDER_ASCENDING => Ok(Some(GroupOrder::Ascending)),
593        GROUP_ORDER_DESCENDING => Ok(Some(GroupOrder::Descending)),
594        // The codec's decoder holds a received 0x22 to this same range before
595        // it ever reaches here (`uint8_value_in_range`), so this arm is for a
596        // pair a caller built in memory. Section 9.20.8: "If an endpoint
597        // receives a value outside this range, it MUST close the session with
598        // PROTOCOL_VIOLATION", so it is refused rather than rounded.
599        _ => Err(FillError::MalformedValue {
600            key: GROUP_ORDER,
601            detail: "Section 9.20.8 allows only Ascending (0x1) and Descending (0x2)",
602        }),
603    }
604}
605
606/// The Group Order a fill fetch stream's Objects arrive in, given the
607/// parameters of the SUBSCRIBE (or REQUEST_UPDATE) that asked for the fill.
608///
609/// A fill fetch stream is "delivered as a FETCH response" (Section 3.4), and
610/// a FETCH response's Group ID Deltas are read against a Group Order that is
611/// nowhere on the data stream — Section 11.4.1.1 makes a delta count upward
612/// under Ascending and downward under Descending. So a subscriber has to
613/// resolve the order from the control exchange before it reads the first
614/// Object, and this is that resolution. Hand the answer to
615/// [`begin_fetch_objects`](crate::draft22::connection::FramedRecvStream::begin_fetch_objects);
616/// [`accept_fill_stream`](crate::draft22::connection::Connection::accept_fill_stream)
617/// already does.
618///
619/// Three steps, in the order the two sections put them:
620///
621/// 1. A `GROUP_ORDER` **inside** `FILL_PARAMETERS`. Section 9.20.8: "When it
622///    appears inside FILL_PARAMETERS, it governs the fill fetch stream and its
623///    ordering relative to subscription-delivered Objects".
624/// 2. Otherwise the `GROUP_ORDER` on the request itself. Section 3.4: "The
625///    fill fetch stream inherits the subscription's parameters" and
626///    "parameters carried inside FILL_PARAMETERS override them for the fill
627///    fetch stream".
628/// 3. Otherwise **Ascending**.
629///
630/// # Step 3 is a choice the draft does not make
631///
632/// Section 9.20.8 gives two different defaults and a fill sits between them:
633/// "If omitted from SUBSCRIBE or SUBSCRIBE_TRACKS, the publisher's preference
634/// from the Track is used. If omitted from FETCH, the receiver uses Ascending
635/// (0x1)." A fill is asked for by a SUBSCRIBE and delivered as a FETCH
636/// response, so both sentences reach it. **This crate takes the FETCH
637/// default**, because the publisher's preference is not a thing a subscriber
638/// holds when the first Object arrives — it is a Track Property that arrives in
639/// SUBSCRIBE_OK at the earliest, and on this path may not arrive at all — while
640/// Ascending is a value the reader can be started with before the stream opens.
641/// A subscriber that does learn the publisher's preference and finds it
642/// Descending can restart the reader with `begin_fetch_objects` before reading
643/// the first Object.
644///
645/// # Errors
646///
647/// [`FillError::MalformedValue`] for a `GROUP_ORDER` whose value is not a uint8
648/// in `{1, 2}` or whose shape is not a bare number, and
649/// [`FillError::NotRoundTrippable`] for a `FILL_PARAMETERS` value the codec's
650/// own decoder refuses. Neither is reachable from a block this module built.
651pub fn group_order(parameters: &[KeyValuePair]) -> Result<GroupOrder, FillError> {
652    if let Some(fill) = parameters.iter().find(|p| p.key.into_inner() == FILL_PARAMETERS) {
653        let KvpValue::Bytes(value) = &fill.value else {
654            return Err(FillError::MalformedValue {
655                key: FILL_PARAMETERS,
656                detail: "a bare varint where Section 9.20.15 defines a parameter block",
657            });
658        };
659        let nested = decode_fill_parameters(value)
660            .map_err(|e| FillError::NotRoundTrippable(e.to_string()))?;
661        if let Some(order) = read_group_order(&nested)? {
662            return Ok(order);
663        }
664    }
665    if let Some(order) = read_group_order(parameters)? {
666        return Ok(order);
667    }
668    Ok(GroupOrder::Ascending)
669}
670
671/// Put `parameter` into `parameters` at the position ascending type order
672/// requires.
673///
674/// Section 9.20 makes the order a wire rule — "Parameters MUST be serialized in
675/// ascending order by Type" — and the codec's encoder refuses a descending
676/// pair rather than emitting one, so a caller appending a `LOCATION_FILTER` to
677/// a list that already holds a higher type would otherwise get an error from
678/// the encoder instead of a message.
679///
680/// A parameter of a type already present is inserted after the ones already
681/// there, which is the only placement that keeps a repeatable filter's
682/// instances in the order they were added.
683pub fn insert_parameter(parameters: &mut Vec<KeyValuePair>, parameter: KeyValuePair) {
684    let key = parameter.key.into_inner();
685    let at = parameters.iter().position(|p| p.key.into_inner() > key).unwrap_or(parameters.len());
686    parameters.insert(at, parameter);
687}
688
689#[cfg(test)]
690mod tests {
691    use super::*;
692
693    /// The empty fill is one byte, and that byte is the count: `Length = 1`
694    /// carrying `0x00`, not `Length = 0`.
695    ///
696    /// Encoding it as `Length = 0` instead fails with a decode error from the
697    /// codec, because a block with no count is a block whose first parameter
698    /// count is missing.
699    #[test]
700    fn an_empty_fill_is_a_single_zero_byte() {
701        let p = FillParameters::inherited().parameter().unwrap();
702        assert_eq!(p.key.into_inner(), FILL_PARAMETERS);
703        match p.value {
704            KvpValue::Bytes(bytes) => assert_eq!(bytes, vec![0x00]),
705            KvpValue::Varint(_) => panic!("FILL_PARAMETERS is length-prefixed"),
706        }
707    }
708
709    /// The inner chain starts at 0, so the first nested type is written as its
710    /// own number and not as a delta from the enclosing `0x23`.
711    #[test]
712    fn the_nested_type_delta_chain_restarts_at_zero() {
713        let fill = FillParameters::inherited()
714            .with_range(&LocationFilter::range_to(4, 0, 2, 7).unwrap())
715            .unwrap();
716        let p = fill.parameter().unwrap();
717        let KvpValue::Bytes(bytes) = p.value else { panic!("length-prefixed") };
718        // count = 1, then Type Delta = 0x21 (not 0x21 - 0x23, which would wrap),
719        // then Location Filter Type 0x04, then the four minimal one-byte
720        // fields. No length: the type says where the value ends.
721        assert_eq!(bytes, vec![0x01, 0x21, 0x04, 0x04, 0x00, 0x02, 0x07]);
722    }
723
724    /// A nested filter carries no length, which only a filter whose type and
725    /// field count differ can show: Next Object is the type alone, and a
726    /// parameter after it starts on the very next byte.
727    #[test]
728    fn a_nested_filter_is_written_without_a_length() {
729        let fill = FillParameters::inherited()
730            .with_range(&LocationFilter::next_object())
731            .unwrap()
732            .with_group_order(GroupOrder::Descending)
733            .unwrap()
734            .parameter()
735            .unwrap();
736        let KvpValue::Bytes(bytes) = fill.value else { panic!("length-prefixed") };
737        // count = 2; Type Delta 0x21, type 0x05; Type Delta 1 to 0x22, value 2.
738        assert_eq!(bytes, vec![0x02, 0x21, 0x05, 0x01, 0x02]);
739        let nested = decode_fill_parameters(&bytes).unwrap();
740        assert_eq!(nested.len(), 2);
741    }
742
743    /// Every type Section 9.20.9 defines, each with the fields its sentence
744    /// names. The type is what the decoder switches on, so the type is
745    /// asserted alongside the fields.
746    #[test]
747    fn each_constructor_is_one_location_filter_type() {
748        use location_filter_types as T;
749        let cases = [
750            (LocationFilter::none(), T::NONE, vec![]),
751            (LocationFilter::relative(3), T::RELATIVE_START, vec![3]),
752            (LocationFilter::absolute_start(1, 2), T::ABSOLUTE_START, vec![1, 2]),
753            (LocationFilter::range(1, 2, 3).unwrap(), T::ABSOLUTE_START_GROUP_END, vec![1, 2, 3]),
754            (LocationFilter::range_to(1, 2, 3, 4).unwrap(), T::ABSOLUTE_RANGE, vec![1, 2, 3, 4]),
755            (LocationFilter::next_object(), T::NEXT_OBJECT, vec![]),
756        ];
757        for (filter, filter_type, fields) in cases {
758            assert_eq!(filter.filter_type(), filter_type);
759            assert_eq!(filter.fields(), fields.as_slice());
760            let mut expected = vec![filter_type as u8];
761            expected.extend(fields.iter().map(|f| *f as u8));
762            assert_eq!(filter.encode_value(), expected);
763            assert!(filter.parameter().is_ok());
764        }
765    }
766
767    /// `{0, 0}` is the beginning of the track and Next Object is a type of its
768    /// own, so the two are different bytes. Section 9.20.9: "If Location
769    /// Filter Type is 0x05, no fields follow and it specifies the Next Object."
770    #[test]
771    fn an_absolute_start_at_zero_is_not_the_next_object() {
772        assert_eq!(LocationFilter::absolute_start(0, 0).encode_value(), vec![0x02, 0x00, 0x00]);
773        assert_eq!(LocationFilter::next_object().encode_value(), vec![0x05]);
774        assert_ne!(LocationFilter::absolute_start(0, 0), LocationFilter::next_object());
775    }
776
777    /// A field too large for one byte is where a type-led value and a
778    /// length-led one part company: the type stays 0x02 for two fields however
779    /// many bytes they take.
780    #[test]
781    fn the_type_counts_fields_not_bytes() {
782        let filter = LocationFilter::absolute_start(1000, 3);
783        let value = filter.encode_value();
784        assert_eq!(value[0], 0x02);
785        assert_eq!(value.len(), 4);
786        assert_eq!(decode_location_filter(&value).unwrap(), (0x02, vec![1000, 3]));
787    }
788
789    /// None and Next Object both carry no fields and mean opposite things, so
790    /// they are told apart by the type and never by the field list.
791    #[test]
792    fn none_and_next_object_differ_only_in_their_type() {
793        assert_eq!(LocationFilter::none().fields(), LocationFilter::next_object().fields());
794        assert_ne!(
795            LocationFilter::none().filter_type(),
796            LocationFilter::next_object().filter_type()
797        );
798        assert_eq!(LocationFilter::none().encode_value(), vec![0x00]);
799    }
800
801    /// The end is the last object, and nothing here adds one to it. Draft-19's
802    /// encoder wrote `last + 1`; a port that kept the arithmetic fetches one
803    /// object too many, and nothing on the wire distinguishes the two.
804    #[test]
805    fn an_end_object_is_the_last_object_and_not_one_past_it() {
806        let filter = LocationFilter::range_to(10, 0, 0, 5).unwrap();
807        assert_eq!(filter.fields(), &[10, 0, 0, 5]);
808        let KvpValue::Bytes(bytes) = filter.parameter().unwrap().value else { panic!() };
809        assert_eq!(bytes, vec![0x04, 10, 0, 0, 5]);
810    }
811
812    /// Section 9.20.9 answers the overflow with a session close, so it is
813    /// refused rather than emitted.
814    #[test]
815    fn an_end_group_past_the_number_space_is_refused() {
816        assert_eq!(
817            LocationFilter::range(u64::MAX, 0, 1),
818            Err(FillError::EndGroupOverflow { start_group: u64::MAX, delta: 1 })
819        );
820        assert!(LocationFilter::range_to(u64::MAX - 1, 0, 1, 0).is_ok());
821    }
822
823    /// Table 7 is a closed list, and the one Range Filter it leaves out is the
824    /// one that selects tracks rather than objects.
825    #[test]
826    fn a_type_outside_table_7_may_not_be_nested() {
827        let track_property_filter =
828            KeyValuePair { key: VarInt::from_u64_moqt(0x29), value: KvpValue::Bytes(vec![]) };
829        assert_eq!(
830            FillParameters::inherited().with(track_property_filter).unwrap_err(),
831            FillError::NotAFillParameter(0x29)
832        );
833        let expires = KeyValuePair {
834            key: VarInt::from_u64_moqt(0x08),
835            value: KvpValue::Varint(VarInt::from_u64_moqt(1)),
836        };
837        assert_eq!(
838            FillParameters::inherited().with(expires).unwrap_err(),
839            FillError::NotAFillParameter(0x08)
840        );
841    }
842
843    /// The order on the wire is ascending whatever order the caller used, so a
844    /// caller does not have to know Table 7's numbering.
845    #[test]
846    fn nested_parameters_go_out_in_ascending_type_order() {
847        let group_order = KeyValuePair {
848            key: VarInt::from_u64_moqt(0x22),
849            value: KvpValue::Varint(VarInt::from_u64_moqt(1)),
850        };
851        let priority = KeyValuePair {
852            key: VarInt::from_u64_moqt(0x20),
853            value: KvpValue::Varint(VarInt::from_u64_moqt(128)),
854        };
855        let fill = FillParameters::inherited()
856            .with(group_order)
857            .unwrap()
858            .with(priority)
859            .unwrap()
860            .parameter()
861            .unwrap();
862        let KvpValue::Bytes(bytes) = fill.value else { panic!() };
863        // count = 2, then 0x20 with value 128, then a delta of 2 to 0x22 with
864        // value 1 (Ascending).
865        assert_eq!(bytes, vec![0x02, 0x20, 128, 0x02, 0x01]);
866    }
867
868    /// A repeat of a type whose definition does not allow one is the frame the
869    /// codec refuses, so it is refused here first.
870    #[test]
871    fn a_non_repeatable_nested_type_may_not_appear_twice() {
872        let filter = LocationFilter::relative(0);
873        let err = FillParameters::inherited()
874            .with_range(&filter)
875            .unwrap()
876            .with_range(&filter)
877            .unwrap_err();
878        assert_eq!(err, FillError::RepeatedFillParameter(LOCATION_FILTER));
879    }
880
881    /// `INCLUDE_PROPERTIES` (0x35) is not a fill parameter, and the reason is
882    /// worth having on the record: Section 9.20.21 makes it govern the **Track
883    /// Properties** in an OK message, and a fill fetch stream has no OK message
884    /// at all (Section 3.4.1 — no FETCH_OK, no REQUEST_ERROR). It is absent
885    /// from Table 7, so nesting it is refused.
886    ///
887    /// The consequence for the data stream is the one to hold on to: the Object
888    /// Properties on a fill fetch object are governed by Serialization Flags bit
889    /// 0x20 (Section 11.4.1.1) and by nothing else. `INCLUDE_PROPERTIES` does
890    /// not gate them, on a fill or anywhere else.
891    #[test]
892    fn include_properties_is_not_a_fill_parameter() {
893        let include_properties = KeyValuePair {
894            key: VarInt::from_u64_moqt(0x35),
895            value: KvpValue::Varint(VarInt::from_u64_moqt(0)),
896        };
897        assert_eq!(
898            FillParameters::inherited().with(include_properties).unwrap_err(),
899            FillError::NotAFillParameter(0x35)
900        );
901    }
902
903    /// A `GROUP_ORDER` inside `FILL_PARAMETERS` is one byte under a `Type
904    /// Delta` counted from 0, and it is what the fill stream is read in.
905    #[test]
906    fn a_nested_group_order_is_the_order_the_fill_is_read_in() {
907        let fill = FillParameters::inherited()
908            .with_group_order(GroupOrder::Descending)
909            .unwrap()
910            .parameter()
911            .unwrap();
912        let KvpValue::Bytes(bytes) = &fill.value else { panic!("length-prefixed") };
913        // count = 1, Type Delta = 0x22 from the restarted chain, value = 0x02.
914        assert_eq!(bytes, &vec![0x01, 0x22, 0x02]);
915        assert_eq!(group_order(&[fill]).unwrap(), GroupOrder::Descending);
916    }
917
918    /// The nested order overrides the subscription's, which is what Section
919    /// 3.4 means by "parameters carried inside FILL_PARAMETERS override them
920    /// for the fill fetch stream".
921    #[test]
922    fn the_nested_group_order_beats_the_subscriptions_own() {
923        let subscription_order = KeyValuePair {
924            key: VarInt::from_u64_moqt(GROUP_ORDER),
925            value: KvpValue::Varint(VarInt::from_u64_moqt(1)),
926        };
927        let fill = FillParameters::inherited()
928            .with_group_order(GroupOrder::Descending)
929            .unwrap()
930            .parameter()
931            .unwrap();
932        let mut parameters = vec![fill];
933        insert_parameter(&mut parameters, subscription_order);
934        assert_eq!(group_order(&parameters).unwrap(), GroupOrder::Descending);
935    }
936
937    /// With nothing nested, the subscription's own `GROUP_ORDER` is inherited.
938    #[test]
939    fn a_fill_with_no_order_of_its_own_inherits_the_subscriptions() {
940        let mut parameters = vec![FillParameters::inherited().parameter().unwrap()];
941        insert_parameter(
942            &mut parameters,
943            KeyValuePair {
944                key: VarInt::from_u64_moqt(GROUP_ORDER),
945                value: KvpValue::Varint(VarInt::from_u64_moqt(2)),
946            },
947        );
948        assert_eq!(group_order(&parameters).unwrap(), GroupOrder::Descending);
949    }
950
951    /// With no `GROUP_ORDER` in either scope, Ascending. Section 9.20.8 gives a
952    /// fill two candidate defaults and this crate takes the FETCH one; see
953    /// [`group_order`].
954    #[test]
955    fn a_fill_that_names_no_order_anywhere_is_ascending() {
956        let parameters = vec![FillParameters::inherited().parameter().unwrap()];
957        assert_eq!(group_order(&parameters).unwrap(), GroupOrder::Ascending);
958        assert_eq!(group_order(&[]).unwrap(), GroupOrder::Ascending);
959    }
960
961    /// A `GROUP_ORDER` outside `{1, 2}` is refused rather than rounded to a
962    /// direction, because reading a stream in the wrong direction does not fail
963    /// to parse — it mis-locates every Object after the first.
964    #[test]
965    fn a_group_order_outside_the_two_values_is_refused() {
966        let parameters = vec![KeyValuePair {
967            key: VarInt::from_u64_moqt(GROUP_ORDER),
968            value: KvpValue::Varint(VarInt::from_u64_moqt(3)),
969        }];
970        assert!(matches!(
971            group_order(&parameters),
972            Err(FillError::MalformedValue { key: GROUP_ORDER, .. })
973        ));
974    }
975
976    /// A bare varint under `LOCATION_FILTER` is refused rather than written as
977    /// a Location Filter Type with no fields after it, which is what the codec's
978    /// encoder does with the same value outside a fill.
979    #[test]
980    fn a_bare_varint_location_filter_is_refused() {
981        for v in [0, 5] {
982            let result = FillParameters::inherited()
983                .with(KeyValuePair {
984                    key: VarInt::from_u64_moqt(LOCATION_FILTER),
985                    value: KvpValue::Varint(VarInt::from_u64_moqt(v)),
986                })
987                .and_then(|fill| fill.parameter());
988            assert!(
989                matches!(result, Err(FillError::MalformedValue { key: LOCATION_FILTER, .. })),
990                "a bare varint {v} under 0x21 gave {result:?}"
991            );
992        }
993    }
994
995    /// Ascending order is a wire rule the codec's encoder enforces, so the
996    /// helper that puts a parameter in a list has to respect it.
997    #[test]
998    fn insert_parameter_keeps_the_list_ascending() {
999        let mut params = vec![
1000            KeyValuePair { key: VarInt::from_u64_moqt(0x03), value: KvpValue::Bytes(vec![]) },
1001            KeyValuePair {
1002                key: VarInt::from_u64_moqt(0x35),
1003                value: KvpValue::Varint(VarInt::from_u64_moqt(1)),
1004            },
1005        ];
1006        insert_parameter(&mut params, LocationFilter::relative(0).parameter().unwrap());
1007        let keys: Vec<u64> = params.iter().map(|p| p.key.into_inner()).collect();
1008        assert_eq!(keys, vec![0x03, 0x21, 0x35]);
1009    }
1010}