Skip to main content

moqtap_client/draft20/
fill.rs

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