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) = ¶meter.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(¶meters).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(¶meters).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(¶meters).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(¶meters),
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}