moqtap_codec/draft10/data_stream.rs
1//! Draft-10 data stream header encoding and decoding.
2//!
3//! Changes from draft-08:
4//! - `extension_count` → `extension_headers_length` (byte length, not count)
5//! - Datagram (0x01): no `payload_length` or `object_status`; payload is remaining bytes
6//! - DatagramStatus (0x02): gains `extension_headers_length` field
7
8use super::types::ObjectStatus;
9use crate::error::CodecError;
10use crate::types::read_bytes;
11use crate::varint::VarInt;
12use bytes::{Buf, BufMut};
13
14/// Every type ID this draft's data plane assigns, from two separate tables.
15///
16/// The draft keeps unidirectional stream types and datagram types in different
17/// tables and different number spaces: SUBGROUP_HEADER and FETCH_HEADER name
18/// unidirectional streams, OBJECT_DATAGRAM and OBJECT_DATAGRAM_STATUS name
19/// datagrams. The numbers happen not to collide on this draft, which is what
20/// lets one enum hold all four.
21///
22/// [`StreamType::from_id`] therefore answers across both tables and cannot say
23/// which one a value came from, which makes it the wrong question to ask about
24/// a stream or a datagram in hand. `stream_type_error` and
25/// `datagram_type_error` ask it per table, and the difference is not
26/// cosmetic: a value assigned in the other table is unknown in this one, and
27/// this draft answers an unknown type by ending the session.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[repr(u64)]
30pub enum StreamType {
31 /// Datagram with payload (0x01).
32 Datagram = 0x01,
33 /// Datagram with status only, no payload (0x02).
34 DatagramStatus = 0x02,
35 /// Subgroup stream type (0x04).
36 Subgroup = 0x04,
37 /// Fetch stream type (0x05).
38 Fetch = 0x05,
39}
40
41impl StreamType {
42 /// Convert a raw stream type ID to a `StreamType`, if valid.
43 pub fn from_id(id: u64) -> Option<Self> {
44 match id {
45 0x01 => Some(StreamType::Datagram),
46 0x02 => Some(StreamType::DatagramStatus),
47 0x04 => Some(StreamType::Subgroup),
48 0x05 => Some(StreamType::Fetch),
49 _ => None,
50 }
51 }
52}
53
54/// Which failure a leading unidirectional stream type that is not the one a
55/// reader wants is.
56///
57/// Section 9: "An endpoint that receives an unknown stream or datagram type
58/// MUST close the session." One sentence, two tables. The stream table assigns
59/// SUBGROUP_HEADER and FETCH_HEADER; everything outside those two is unknown at
60/// the head of a stream, and the session ends.
61///
62/// The datagram types are among the values outside them. This draft is the
63/// first to give datagrams a table of their own, with numbering independent of
64/// the stream table, so a datagram type says nothing about a stream and is not
65/// a value the stream table assigns. Draft-07, which numbered both in one
66/// table, is the only draft where that is not so.
67///
68/// A stream announcing the *other* assigned stream type is a different matter
69/// and not that rule. The value is one this draft defines, and the disagreement
70/// is with the reader that was called rather than with the draft, so the
71/// session survives it.
72fn stream_type_error(raw: u64) -> CodecError {
73 if raw == StreamType::Subgroup as u64 || raw == StreamType::Fetch as u64 {
74 CodecError::InvalidField
75 } else {
76 CodecError::UnknownStreamType(raw)
77 }
78}
79
80/// Which failure a leading datagram type that is not one a reader wants is.
81///
82/// The datagram half of the sentence quoted on `stream_type_error`, read
83/// against the other table: OBJECT_DATAGRAM and OBJECT_DATAGRAM_STATUS are what
84/// it assigns, and everything else arriving as a datagram is unknown.
85///
86/// Both assigned values are decodable here, so the [`CodecError::InvalidField`]
87/// arm is reached only by the stream types — 0x04 and 0x05 — delivered as
88/// datagrams. Those are values this draft defines, and refusing them without
89/// closing is the same judgement the stream side makes about a datagram type.
90fn datagram_type_error(raw: u64) -> CodecError {
91 if raw == StreamType::Datagram as u64 || raw == StreamType::DatagramStatus as u64 {
92 CodecError::InvalidField
93 } else {
94 CodecError::UnknownDatagramType(raw)
95 }
96}
97
98// ── Extension helpers (length-based, not count-based) ─────────
99
100/// Read `byte_len` bytes of raw extension data from the buffer.
101fn read_extension_bytes(buf: &mut impl Buf, byte_len: u64) -> Result<Vec<u8>, CodecError> {
102 read_bytes(buf, byte_len as usize)
103}
104
105/// Encode extension bytes to the buffer (just writes the raw bytes).
106fn encode_extensions(extensions: &[u8], buf: &mut impl BufMut) {
107 buf.put_slice(extensions);
108}
109
110// ============================================================
111// Subgroup stream (type 0x04)
112// ============================================================
113
114/// Subgroup stream header (follows the stream type varint).
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub struct SubgroupHeader {
117 /// Track alias identifying the subscription.
118 pub track_alias: VarInt,
119 /// Group identifier.
120 pub group_id: VarInt,
121 /// Subgroup identifier within the group.
122 pub subgroup_id: VarInt,
123 /// Publisher priority for delivery ordering.
124 pub publisher_priority: u8,
125}
126
127/// Object within a subgroup stream (draft-10).
128///
129/// Encoding: object_id(vi), extension_headers_length(vi), [extensions...],
130/// payload_length(vi),
131/// if payload_length == 0: object_status(vi)
132/// else: payload bytes
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct ObjectHeader {
135 /// Object identifier within the subgroup.
136 pub object_id: VarInt,
137 /// Total byte length of extension headers, as it arrived.
138 ///
139 /// Advisory on encode. Every encoder here writes
140 /// `VarInt::from_usize(self.extensions.len())` and never reads this field,
141 /// so a hand-built header whose stated length disagrees with the bytes
142 /// beside it goes on the wire with the derived length and is read back
143 /// consistent. Decoding always sets the two together, so a value that came
144 /// off the wire never disagrees.
145 ///
146 /// It is kept because it is what the peer stated, which is not always
147 /// recoverable from the bytes: a non-minimal varint length encodes the same
148 /// number in more bytes, and a relay that must forward the block unchanged
149 /// has to know which it saw.
150 pub extension_headers_length: VarInt,
151 /// Raw extension bytes (opaque).
152 pub extensions: Vec<u8>,
153 /// Length of the object payload in bytes.
154 pub payload_length: VarInt,
155 /// Status of this object.
156 pub object_status: ObjectStatus,
157}
158
159impl SubgroupHeader {
160 /// Encode a subgroup stream header including its leading stream-type
161 /// field, so the bytes form the start of a data stream a peer can read.
162 ///
163 /// [`Self::encode`] writes the body alone, which is what a caller wants
164 /// once the stream is already open and what a caller must not use for its
165 /// first write.
166 pub fn encode_stream(&self, buf: &mut impl BufMut) {
167 VarInt::from_usize(StreamType::Subgroup as usize).encode(buf);
168 self.encode(buf);
169 }
170
171 /// Encode the subgroup header into the buffer.
172 pub fn encode(&self, buf: &mut impl BufMut) {
173 self.track_alias.encode(buf);
174 self.group_id.encode(buf);
175 self.subgroup_id.encode(buf);
176 buf.put_u8(self.publisher_priority);
177 }
178
179 /// Decode a subgroup header from the buffer.
180 pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
181 let track_alias = VarInt::decode(buf)?;
182 let group_id = VarInt::decode(buf)?;
183 let subgroup_id = VarInt::decode(buf)?;
184 if buf.remaining() < 1 {
185 return Err(CodecError::UnexpectedEnd);
186 }
187 let publisher_priority = buf.get_u8();
188 Ok(Self { track_alias, group_id, subgroup_id, publisher_priority })
189 }
190
191 /// Decode a subgroup header from the start of a data stream, consuming
192 /// the leading stream type varint.
193 ///
194 /// Errors with [`CodecError::UnknownStreamType`] when the stream type is
195 /// one the stream table does not assign, which this draft answers with a
196 /// close, and with [`CodecError::InvalidField`] when it is assigned but is
197 /// not [`StreamType::Subgroup`]. `stream_type_error` draws that line.
198 pub fn decode_stream(buf: &mut impl Buf) -> Result<Self, CodecError> {
199 let stream_type = VarInt::decode(buf)?.into_inner();
200 if stream_type != StreamType::Subgroup as u64 {
201 return Err(stream_type_error(stream_type));
202 }
203 Self::decode(buf)
204 }
205}
206
207/// Refuse an end-of-track object that does not end at object zero.
208///
209/// Section 9.1.1.1 describes Object Status 0x5 as "end of Track. GroupID is one
210/// greater than the largest group produced in this track and the ObjectId is
211/// zero", and states the consequence: "An object with this status that has a
212/// Group ID less than or equal to any other Group ID, or an Object ID other
213/// than zero, is a protocol error, and the receiver MUST terminate the
214/// session."
215///
216/// Only the Object ID half is answerable here. The Group ID half compares
217/// against the largest group produced on the track, which no single header
218/// carries and no reader of one header can know.
219///
220/// Applied on both sides. A receiver is required to close the session over
221/// this, so writing one is not a way to send it.
222fn check_end_of_track(object_id: VarInt, status: ObjectStatus) -> Result<(), CodecError> {
223 if status == ObjectStatus::EndOfTrack && object_id.into_inner() != 0 {
224 return Err(CodecError::EndOfTrackObjectId(object_id.into_inner()));
225 }
226 Ok(())
227}
228
229impl ObjectHeader {
230 /// Encode the object header into the buffer.
231 pub fn encode(&self, buf: &mut impl BufMut) {
232 self.object_id.encode(buf);
233 VarInt::from_usize(self.extensions.len()).encode(buf);
234 encode_extensions(&self.extensions, buf);
235 self.payload_length.encode(buf);
236 if self.payload_length.into_inner() == 0 {
237 VarInt::from_usize(self.object_status as usize).encode(buf);
238 }
239 }
240
241 /// Encode the header, refusing a status the framing cannot carry.
242 ///
243 /// Section 9.4.2 puts the Object Status field on the wire only when the
244 /// Object Payload Length is zero, and Section 9.1.1.1 says "Any object
245 /// with a status code other than zero MUST have an empty payload". A
246 /// non-zero status paired with a non-zero payload length therefore has no
247 /// encoding at all: [`Self::encode`] drops the status and the peer reads an
248 /// ordinary object, which is a different object from the one the caller
249 /// described. This refuses instead.
250 ///
251 /// The datagram types on this draft already refuse the same pairing. These
252 /// two did not, and they are the ones a publisher writes on every stream.
253 ///
254 /// # Errors
255 ///
256 /// [`CodecError::InvalidField`] if a non-zero Object Status is paired with
257 /// a non-zero Object Payload Length.
258 pub fn encode_checked(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
259 check_end_of_track(self.object_id, self.object_status)?;
260 if self.payload_length.into_inner() != 0 && self.object_status as usize != 0 {
261 return Err(CodecError::InvalidField);
262 }
263 self.encode(buf);
264 Ok(())
265 }
266
267 /// Decode an object header from the buffer.
268 pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
269 let object_id = VarInt::decode(buf)?;
270 let extension_headers_length = VarInt::decode(buf)?;
271 let extensions = read_extension_bytes(buf, extension_headers_length.into_inner())?;
272 let payload_length = VarInt::decode(buf)?;
273 let object_status = if payload_length.into_inner() == 0 {
274 let status_val = VarInt::decode(buf)?.into_inner();
275 ObjectStatus::from_u64(status_val).ok_or(CodecError::InvalidField)?
276 } else {
277 ObjectStatus::Normal
278 };
279 check_end_of_track(object_id, object_status)?;
280 Ok(Self { object_id, extension_headers_length, extensions, payload_length, object_status })
281 }
282}
283
284// ============================================================
285// Datagram (type 0x01)
286// ============================================================
287
288/// Datagram header with payload (draft-10, type 0x01).
289///
290/// Draft-09 dropped these two fields and draft-10 keeps them dropped: no
291/// `payload_length` and no `object_status`.
292/// Payload is the remaining bytes in the datagram.
293///
294/// Encoding (after type varint):
295/// track_alias(vi), group_id(vi), object_id(vi),
296/// publisher_priority(u8), extension_headers_length(vi), [extensions...],
297/// [remaining bytes = payload]
298#[derive(Debug, Clone, PartialEq, Eq)]
299pub struct DatagramHeader {
300 /// Track alias identifying the subscription.
301 pub track_alias: VarInt,
302 /// Group identifier.
303 pub group_id: VarInt,
304 /// Object identifier within the group.
305 pub object_id: VarInt,
306 /// Publisher priority for delivery ordering.
307 pub publisher_priority: u8,
308 /// Total byte length of extension headers, as it arrived.
309 ///
310 /// Advisory on encode. Every encoder here writes
311 /// `VarInt::from_usize(self.extensions.len())` and never reads this field,
312 /// so a hand-built header whose stated length disagrees with the bytes
313 /// beside it goes on the wire with the derived length and is read back
314 /// consistent. Decoding always sets the two together, so a value that came
315 /// off the wire never disagrees.
316 ///
317 /// It is kept because it is what the peer stated, which is not always
318 /// recoverable from the bytes: a non-minimal varint length encodes the same
319 /// number in more bytes, and a relay that must forward the block unchanged
320 /// has to know which it saw.
321 pub extension_headers_length: VarInt,
322 /// Raw extension bytes (opaque).
323 pub extensions: Vec<u8>,
324}
325
326impl DatagramHeader {
327 /// Encode the datagram header into the buffer.
328 pub fn encode(&self, buf: &mut impl BufMut) {
329 self.track_alias.encode(buf);
330 self.group_id.encode(buf);
331 self.object_id.encode(buf);
332 buf.put_u8(self.publisher_priority);
333 VarInt::from_usize(self.extensions.len()).encode(buf);
334 encode_extensions(&self.extensions, buf);
335 }
336
337 /// Encode the datagram header, refusing a status the framing cannot carry.
338 ///
339 /// Nothing here is ever refused, and that is a fact about draft-10 rather
340 /// than a check left out. This is the OBJECT_DATAGRAM of Section 9.2, whose
341 /// layout carries no Object Status field at all; a datagram that states a
342 /// status is the separate OBJECT_DATAGRAM_STATUS message of Section 9.3,
343 /// modelled here as [`DatagramStatusHeader`]. So there is no status for
344 /// [`Self::encode`] to drop, and nothing for Section 9.1.1.1's "Any object
345 /// with a status code other than zero MUST have an empty payload" to rule
346 /// on: an object framed this way has status zero by construction.
347 ///
348 /// The fallible signature is what lets one entry point span every draft.
349 /// `dispatch::AnyDatagramHeader::encode` calls this on all thirteen, and
350 /// the drafts whose payload-bearing datagram *does* carry a status field
351 /// need somewhere to say no.
352 pub fn encode_checked(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
353 self.encode(buf);
354 Ok(())
355 }
356
357 /// Decode a datagram header from the buffer.
358 pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
359 let track_alias = VarInt::decode(buf)?;
360 let group_id = VarInt::decode(buf)?;
361 let object_id = VarInt::decode(buf)?;
362 if buf.remaining() < 1 {
363 return Err(CodecError::UnexpectedEnd);
364 }
365 let publisher_priority = buf.get_u8();
366 let extension_headers_length = VarInt::decode(buf)?;
367 let extensions = read_extension_bytes(buf, extension_headers_length.into_inner())?;
368 Ok(Self {
369 track_alias,
370 group_id,
371 object_id,
372 publisher_priority,
373 extension_headers_length,
374 extensions,
375 })
376 }
377}
378
379// ============================================================
380// Datagram Status (type 0x02)
381// ============================================================
382
383/// Datagram status header (draft-10, type 0x02).
384///
385/// Draft-09 added `extension_headers_length` here and draft-10 keeps it.
386#[derive(Debug, Clone, PartialEq, Eq)]
387pub struct DatagramStatusHeader {
388 /// Track alias identifying the subscription.
389 pub track_alias: VarInt,
390 /// Group identifier.
391 pub group_id: VarInt,
392 /// Object identifier within the group.
393 pub object_id: VarInt,
394 /// Publisher priority for delivery ordering.
395 pub publisher_priority: u8,
396 /// Total byte length of extension headers, as it arrived.
397 ///
398 /// Advisory on encode. Every encoder here writes
399 /// `VarInt::from_usize(self.extensions.len())` and never reads this field,
400 /// so a hand-built header whose stated length disagrees with the bytes
401 /// beside it goes on the wire with the derived length and is read back
402 /// consistent. Decoding always sets the two together, so a value that came
403 /// off the wire never disagrees.
404 ///
405 /// It is kept because it is what the peer stated, which is not always
406 /// recoverable from the bytes: a non-minimal varint length encodes the same
407 /// number in more bytes, and a relay that must forward the block unchanged
408 /// has to know which it saw.
409 pub extension_headers_length: VarInt,
410 /// Raw extension bytes (opaque).
411 pub extensions: Vec<u8>,
412 /// Object status code.
413 pub object_status: ObjectStatus,
414}
415
416impl DatagramStatusHeader {
417 /// Encode the datagram status header into the buffer.
418 pub fn encode(&self, buf: &mut impl BufMut) {
419 self.track_alias.encode(buf);
420 self.group_id.encode(buf);
421 self.object_id.encode(buf);
422 buf.put_u8(self.publisher_priority);
423 VarInt::from_usize(self.extensions.len()).encode(buf);
424 encode_extensions(&self.extensions, buf);
425 VarInt::from_usize(self.object_status as usize).encode(buf);
426 }
427
428 /// Encode the datagram status header, refusing an end-of-track object that
429 /// does not end at object zero.
430 ///
431 /// # Errors
432 ///
433 /// [`CodecError::EndOfTrackObjectId`] if an end-of-track status is paired
434 /// with a non-zero Object ID.
435 pub fn encode_checked(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
436 check_end_of_track(self.object_id, self.object_status)?;
437 self.encode(buf);
438 Ok(())
439 }
440
441 /// Decode a datagram status header from the buffer.
442 pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
443 let track_alias = VarInt::decode(buf)?;
444 let group_id = VarInt::decode(buf)?;
445 let object_id = VarInt::decode(buf)?;
446 if buf.remaining() < 1 {
447 return Err(CodecError::UnexpectedEnd);
448 }
449 let publisher_priority = buf.get_u8();
450 let extension_headers_length = VarInt::decode(buf)?;
451 let extensions = read_extension_bytes(buf, extension_headers_length.into_inner())?;
452 let status_val = VarInt::decode(buf)?.into_inner();
453 let object_status = ObjectStatus::from_u64(status_val).ok_or(CodecError::InvalidField)?;
454 check_end_of_track(object_id, object_status)?;
455 Ok(Self {
456 track_alias,
457 group_id,
458 object_id,
459 publisher_priority,
460 extension_headers_length,
461 extensions,
462 object_status,
463 })
464 }
465}
466
467// ============================================================
468// Datagram framing
469// ============================================================
470
471/// One datagram, of whichever shape its type field names.
472///
473/// A MoQT datagram opens with a variable-length integer naming its type, and
474/// that integer is what says which of the layouts above follows it.
475/// Neither [`DatagramHeader`] nor [`DatagramStatusHeader`] reads or writes it, so neither can be handed
476/// the first byte of a datagram a peer sent, and neither produces bytes a peer
477/// can read. This is the entry point that does both.
478///
479/// The payload of a payload-bearing datagram runs to the end of the QUIC
480/// datagram, so it is not part of this value: [`Self::decode`] stops at the end
481/// of the header and leaves the payload in the buffer, and a caller appends the
482/// payload after [`Self::encode`].
483#[derive(Debug, Clone, PartialEq, Eq)]
484pub enum Datagram {
485 /// An object carrying a payload.
486 Payload(DatagramHeader),
487 /// An object stating a status, with no payload.
488 Status(DatagramStatusHeader),
489}
490
491impl Datagram {
492 /// Whether this datagram states an Object Status instead of carrying a
493 /// payload.
494 pub fn is_status(&self) -> bool {
495 matches!(self, Self::Status(_))
496 }
497
498 /// The type field this value writes.
499 pub fn datagram_type(&self) -> StreamType {
500 match self {
501 Self::Payload(_) => StreamType::Datagram,
502 Self::Status(_) => StreamType::DatagramStatus,
503 }
504 }
505
506 /// Decode a datagram from its first byte, type field included.
507 ///
508 /// Errors with [`CodecError::UnknownDatagramType`] when the leading type is
509 /// one the datagram table does not assign, which this draft answers with a
510 /// close, and with [`CodecError::InvalidField`] for the stream types, which
511 /// it does assign but not to a datagram. `datagram_type_error` draws that
512 /// line.
513 pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
514 let raw = VarInt::decode(buf)?.into_inner();
515 match StreamType::from_id(raw) {
516 Some(StreamType::Datagram) => Ok(Self::Payload(DatagramHeader::decode(buf)?)),
517 Some(StreamType::DatagramStatus) => {
518 Ok(Self::Status(DatagramStatusHeader::decode(buf)?))
519 }
520 _ => Err(datagram_type_error(raw)),
521 }
522 }
523
524 /// Encode the datagram, type field included.
525 pub fn encode(&self, buf: &mut impl BufMut) {
526 VarInt::from_usize(self.datagram_type() as usize).encode(buf);
527 match self {
528 Self::Payload(header) => header.encode(buf),
529 Self::Status(header) => header.encode(buf),
530 }
531 }
532
533 /// Encode the datagram, refusing a header the framing it names cannot
534 /// carry.
535 ///
536 /// The body is built before anything reaches `buf`, so a refused datagram
537 /// leaves `buf` untouched rather than a type field with no body under it.
538 pub fn encode_checked(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
539 let mut body = Vec::with_capacity(64);
540 match self {
541 Self::Payload(header) => header.encode_checked(&mut body)?,
542 Self::Status(header) => header.encode_checked(&mut body)?,
543 }
544 VarInt::from_usize(self.datagram_type() as usize).encode(buf);
545 buf.put_slice(&body);
546 Ok(())
547 }
548}
549
550// ============================================================
551// Fetch stream (type 0x05)
552// ============================================================
553
554/// Fetch stream header (follows the stream type varint).
555#[derive(Debug, Clone, PartialEq, Eq)]
556pub struct FetchHeader {
557 /// Subscribe ID this fetch responds to.
558 pub subscribe_id: VarInt,
559}
560
561/// Object within a fetch stream (draft-10).
562///
563/// Uses `extension_headers_length` instead of `extension_count`.
564#[derive(Debug, Clone, PartialEq, Eq)]
565pub struct FetchObjectHeader {
566 /// Group identifier.
567 pub group_id: VarInt,
568 /// Subgroup identifier within the group.
569 pub subgroup_id: VarInt,
570 /// Object identifier within the subgroup.
571 pub object_id: VarInt,
572 /// Publisher priority for delivery ordering.
573 pub publisher_priority: u8,
574 /// Total byte length of extension headers, as it arrived.
575 ///
576 /// Advisory on encode. Every encoder here writes
577 /// `VarInt::from_usize(self.extensions.len())` and never reads this field,
578 /// so a hand-built header whose stated length disagrees with the bytes
579 /// beside it goes on the wire with the derived length and is read back
580 /// consistent. Decoding always sets the two together, so a value that came
581 /// off the wire never disagrees.
582 ///
583 /// It is kept because it is what the peer stated, which is not always
584 /// recoverable from the bytes: a non-minimal varint length encodes the same
585 /// number in more bytes, and a relay that must forward the block unchanged
586 /// has to know which it saw.
587 pub extension_headers_length: VarInt,
588 /// Raw extension bytes (opaque).
589 pub extensions: Vec<u8>,
590 /// Status of this object.
591 pub object_status: ObjectStatus,
592 /// Length of the object payload in bytes.
593 pub payload_length: VarInt,
594}
595
596impl FetchHeader {
597 /// Encode a fetch stream header including its leading stream-type field,
598 /// so the bytes form the start of a data stream a peer can read.
599 ///
600 /// [`Self::encode`] writes the body alone, which is what a caller wants
601 /// once the stream is already open and what a caller must not use for its
602 /// first write. The read side has had [`Self::decode_stream`] all along,
603 /// so without this the codec could not round-trip its own fetch stream
604 /// through its own reader.
605 pub fn encode_stream(&self, buf: &mut impl BufMut) {
606 VarInt::from_usize(StreamType::Fetch as usize).encode(buf);
607 self.encode(buf);
608 }
609
610 /// Encode the fetch header into the buffer.
611 pub fn encode(&self, buf: &mut impl BufMut) {
612 self.subscribe_id.encode(buf);
613 }
614
615 /// Decode a fetch header from the buffer.
616 pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
617 let subscribe_id = VarInt::decode(buf)?;
618 Ok(Self { subscribe_id })
619 }
620
621 /// Decode a fetch header from the start of a data stream, consuming the
622 /// leading stream type varint.
623 ///
624 /// Errors with [`CodecError::UnknownStreamType`] when the stream type is
625 /// one the stream table does not assign, which this draft answers with a
626 /// close, and with [`CodecError::InvalidField`] when it is assigned but is
627 /// not [`StreamType::Fetch`]. `stream_type_error` draws that line.
628 pub fn decode_stream(buf: &mut impl Buf) -> Result<Self, CodecError> {
629 let stream_type = VarInt::decode(buf)?.into_inner();
630 if stream_type != StreamType::Fetch as u64 {
631 return Err(stream_type_error(stream_type));
632 }
633 Self::decode(buf)
634 }
635}
636
637impl FetchObjectHeader {
638 /// Encode the fetch object header into the buffer.
639 pub fn encode(&self, buf: &mut impl BufMut) {
640 self.group_id.encode(buf);
641 self.subgroup_id.encode(buf);
642 self.object_id.encode(buf);
643 buf.put_u8(self.publisher_priority);
644 VarInt::from_usize(self.extensions.len()).encode(buf);
645 encode_extensions(&self.extensions, buf);
646 self.payload_length.encode(buf);
647 if self.payload_length.into_inner() == 0 {
648 VarInt::from_usize(self.object_status as usize).encode(buf);
649 }
650 }
651
652 /// Encode the header, refusing a status the framing cannot carry.
653 ///
654 /// Section 9.4.4 puts the Object Status field on the wire only when the
655 /// Object Payload Length is zero, and Section 9.1.1.1 says "Any object
656 /// with a status code other than zero MUST have an empty payload". A
657 /// non-zero status paired with a non-zero payload length therefore has no
658 /// encoding at all: [`Self::encode`] drops the status and the peer reads an
659 /// ordinary object, which is a different object from the one the caller
660 /// described. This refuses instead.
661 ///
662 /// The datagram types on this draft already refuse the same pairing. These
663 /// two did not, and they are the ones a publisher writes on every stream.
664 ///
665 /// # Errors
666 ///
667 /// [`CodecError::InvalidField`] if a non-zero Object Status is paired with
668 /// a non-zero Object Payload Length.
669 pub fn encode_checked(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
670 check_end_of_track(self.object_id, self.object_status)?;
671 if self.payload_length.into_inner() != 0 && self.object_status as usize != 0 {
672 return Err(CodecError::InvalidField);
673 }
674 self.encode(buf);
675 Ok(())
676 }
677
678 /// Decode a fetch object header from the buffer.
679 pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
680 let group_id = VarInt::decode(buf)?;
681 let subgroup_id = VarInt::decode(buf)?;
682 let object_id = VarInt::decode(buf)?;
683 if buf.remaining() < 1 {
684 return Err(CodecError::UnexpectedEnd);
685 }
686 let publisher_priority = buf.get_u8();
687 let extension_headers_length = VarInt::decode(buf)?;
688 let extensions = read_extension_bytes(buf, extension_headers_length.into_inner())?;
689 let payload_length = VarInt::decode(buf)?;
690 let object_status = if payload_length.into_inner() == 0 {
691 let status_val = VarInt::decode(buf)?.into_inner();
692 ObjectStatus::from_u64(status_val).ok_or(CodecError::InvalidField)?
693 } else {
694 ObjectStatus::Normal
695 };
696 check_end_of_track(object_id, object_status)?;
697 Ok(Self {
698 group_id,
699 subgroup_id,
700 object_id,
701 publisher_priority,
702 extension_headers_length,
703 extensions,
704 object_status,
705 payload_length,
706 })
707 }
708}