Skip to main content

moqtap_client/draft23/
fill.rs

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