1use alloc::vec::Vec;
9use broadcast_common::len::{need_in, need_out};
10
11use crate::error::{Error, Result};
12use broadcast_common::{Parse, Serialize};
13
14pub const SYSTEM_HEADER_START_CODE: u32 = 0x0000_01BB;
16
17const EXT_STREAM_ID: u8 = 0xB7;
19
20pub(crate) const PREFIX_LEN: usize = 6;
22const RESERVED_BITS_MASK: u8 = 0x7F;
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize))]
28pub struct StdBufferBound {
29 pub stream_id: u8,
31 pub stream_id_extension: Option<u8>,
34 pub buffer_bound_scale: bool,
36 pub buffer_size_bound: u16,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize))]
43pub struct SystemHeader {
44 pub rate_bound: u32,
46 pub audio_bound: u8,
48 pub fixed_flag: bool,
50 pub csps_flag: bool,
52 pub system_audio_lock_flag: bool,
54 pub system_video_lock_flag: bool,
56 pub video_bound: u8,
58 pub packet_rate_restriction_flag: bool,
60 pub reserved_bits: u8,
65 pub std_buffer_bounds: Vec<StdBufferBound>,
67 pub trailing_reserved: Vec<u8>,
72}
73
74fn stream_loop_len(system_header: &SystemHeader) -> usize {
82 let mut len: usize = 0;
83 for b in &system_header.std_buffer_bounds {
84 if b.stream_id_extension.is_some() {
85 len += 6;
86 } else {
87 len += 3;
88 }
89 }
90 len + system_header.trailing_reserved.len()
91}
92
93impl<'a> Parse<'a> for SystemHeader {
94 type Error = Error;
95
96 fn parse(b: &'a [u8]) -> Result<Self> {
97 need_in(b, PREFIX_LEN, "system_header prefix")?;
98
99 if u32::from_be_bytes([b[0], b[1], b[2], b[3]]) != SYSTEM_HEADER_START_CODE {
100 return Err(Error::BadSystemHeaderStartCode(u32::from_be_bytes([
101 b[0], b[1], b[2], b[3],
102 ])));
103 }
104
105 let header_length = u16::from_be_bytes([b[4], b[5]]) as usize;
106 let body_end = PREFIX_LEN + header_length;
107 if b.len() < body_end {
108 return Err(Error::HeaderLengthOverflow {
109 header_length,
110 available: b.len().saturating_sub(PREFIX_LEN),
111 });
112 }
113 let body = &b[PREFIX_LEN..body_end];
114
115 if body.len() < 6 {
124 return Err(Error::BufferTooShort(
125 broadcast_common::error::BufferTooShort {
126 need: 6 + PREFIX_LEN,
127 have: b.len(),
128 what: "system_header fixed body",
129 },
130 ));
131 }
132
133 if body[0] & 0x80 == 0 {
135 return Err(Error::BadMarker("system_header rate_bound marker 1"));
136 }
137 if body[2] & 0x01 == 0 {
138 return Err(Error::BadMarker("system_header rate_bound marker 2"));
139 }
140 if body[4] & 0x20 == 0 {
141 return Err(Error::BadMarker("system_header video_bound marker"));
142 }
143
144 let rate_bound = ((u32::from(body[0] & 0x7F) << 15)
146 | (u32::from(body[1]) << 7)
147 | u32::from(body[2] >> 1))
148 & 0x3F_FFFF;
149
150 let audio_bound = (body[3] >> 2) & 0x3F;
152 let fixed_flag = body[3] & 0x02 != 0;
153 let csps_flag = body[3] & 0x01 != 0;
154
155 let system_audio_lock_flag = body[4] & 0x80 != 0;
157 let system_video_lock_flag = body[4] & 0x40 != 0;
158 let video_bound = body[4] & 0x1F;
163
164 let packet_rate_restriction_flag = body[5] & 0x80 != 0;
166 let reserved_bits = body[5] & RESERVED_BITS_MASK;
167
168 let mut pos = 6;
170 let mut std_buffer_bounds = Vec::new();
171 while pos < body.len() && body[pos] & 0x80 != 0 {
172 let stream_id = body[pos];
173 if stream_id == EXT_STREAM_ID {
174 need_in(body, pos + 6, "system_header extended stream entry")?;
176 if body[pos + 1] & 0xC0 != 0xC0 {
178 return Err(Error::BadStreamIdExtensionPrefix(body[pos + 1]));
179 }
180 let stream_id_extension = body[pos + 2] & 0x7F;
182 if body[pos + 3] != 0xB6 {
184 return Err(Error::BadStreamIdExtensionPrefix(body[pos + 3]));
185 }
186 if body[pos + 4] & 0xC0 != 0xC0 {
188 return Err(Error::BadMarker("P-STD_buffer_bound_scale prefix (ext)"));
189 }
190 let buffer_bound_scale = body[pos + 4] & 0x20 != 0;
191 let buffer_size_bound =
194 (u16::from(body[pos + 4] & 0x1F) << 8) | u16::from(body[pos + 5]);
195 std_buffer_bounds.push(StdBufferBound {
196 stream_id,
197 stream_id_extension: Some(stream_id_extension),
198 buffer_bound_scale,
199 buffer_size_bound,
200 });
201 pos += 6;
202 } else {
203 need_in(body, pos + 3, "system_header stream entry")?;
205 if body[pos + 1] & 0xC0 != 0xC0 {
207 return Err(Error::BadMarker("P-STD_buffer_bound_scale prefix"));
208 }
209 let buffer_bound_scale = body[pos + 1] & 0x20 != 0;
210 let buffer_size_bound =
212 (u16::from(body[pos + 1] & 0x1F) << 8) | u16::from(body[pos + 2]);
213 std_buffer_bounds.push(StdBufferBound {
214 stream_id,
215 stream_id_extension: None,
216 buffer_bound_scale,
217 buffer_size_bound,
218 });
219 pos += 3;
220 }
221 }
222
223 Ok(SystemHeader {
224 rate_bound,
225 audio_bound,
226 fixed_flag,
227 csps_flag,
228 system_audio_lock_flag,
229 system_video_lock_flag,
230 video_bound,
231 packet_rate_restriction_flag,
232 reserved_bits,
233 std_buffer_bounds,
234 trailing_reserved: body[pos..].to_vec(),
235 })
236 }
237}
238
239impl SystemHeader {
240 fn validate_stream_loop(&self) -> Result<()> {
244 for b in &self.std_buffer_bounds {
245 if b.stream_id & 0x80 == 0 {
246 return Err(Error::InvalidField(broadcast_common::error::InvalidField {
247 field: "system_header.stream_id",
248 reason: "stream_id MSB is 0, so the entry would end the stream loop on parse",
249 }));
250 }
251 match b.stream_id_extension {
252 Some(ext) => {
253 if b.stream_id != EXT_STREAM_ID {
254 return Err(Error::InvalidField(broadcast_common::error::InvalidField {
255 field: "system_header.stream_id_extension",
256 reason: "stream_id_extension requires stream_id 0xB7",
257 }));
258 }
259 broadcast_common::len::fit_bits(
260 u64::from(ext),
261 7,
262 "system_header.stream_id_extension",
263 )?;
264 }
265 None => {
266 if b.stream_id == EXT_STREAM_ID {
267 return Err(Error::InvalidField(broadcast_common::error::InvalidField {
268 field: "system_header.stream_id",
269 reason: "stream_id 0xB7 requires stream_id_extension",
270 }));
271 }
272 }
273 }
274 broadcast_common::len::fit_bits(
275 u64::from(b.buffer_size_bound),
276 13,
277 "system_header.P-STD_buffer_size_bound",
278 )?;
279 }
280 if self
281 .trailing_reserved
282 .first()
283 .is_some_and(|f| f & 0x80 != 0)
284 {
285 return Err(Error::InvalidField(broadcast_common::error::InvalidField {
286 field: "system_header.trailing_reserved",
287 reason: "first byte has its MSB set, so it would be re-read as a stream-bound entry",
288 }));
289 }
290 Ok(())
291 }
292}
293
294impl Serialize for SystemHeader {
295 type Error = Error;
296
297 fn serialized_len(&self) -> usize {
298 PREFIX_LEN + 6 + stream_loop_len(self)
299 }
300
301 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
302 let total = self.serialized_len();
303 need_out(buf, total)?;
304
305 buf[0..4].copy_from_slice(&SYSTEM_HEADER_START_CODE.to_be_bytes());
307
308 let header_length = broadcast_common::len::fit_u16(
310 6 + stream_loop_len(self),
311 "system_header.header_length",
312 )?;
313 buf[4..6].copy_from_slice(&header_length.to_be_bytes());
314
315 let rate_bound = broadcast_common::len::fit_bits(
316 u64::from(self.rate_bound),
317 22,
318 "system_header.rate_bound",
319 )? as u32;
320 let audio_bound = broadcast_common::len::fit_bits(
321 u64::from(self.audio_bound),
322 6,
323 "system_header.audio_bound",
324 )? as u8;
325 let video_bound = broadcast_common::len::fit_bits(
326 u64::from(self.video_bound),
327 5,
328 "system_header.video_bound",
329 )? as u8;
330 let reserved_bits = broadcast_common::len::fit_mask(
331 self.reserved_bits,
332 RESERVED_BITS_MASK,
333 "system_header.reserved_bits",
334 )?;
335 self.validate_stream_loop()?;
336 buf[6] = 0x80 | ((rate_bound >> 15) & 0x7F) as u8;
338 buf[7] = ((rate_bound >> 7) & 0xFF) as u8;
340 buf[8] = (((rate_bound & 0x7F) as u8) << 1) | 0x01;
342 buf[9] = audio_bound << 2 | (u8::from(self.fixed_flag) << 1) | u8::from(self.csps_flag);
344 buf[10] = (u8::from(self.system_audio_lock_flag) << 7)
346 | (u8::from(self.system_video_lock_flag) << 6)
347 | 0x20 | video_bound;
349 buf[11] = (u8::from(self.packet_rate_restriction_flag) << 7) | reserved_bits;
351
352 let mut pos = 12;
354 for bound in &self.std_buffer_bounds {
355 if let Some(ext) = bound.stream_id_extension {
356 buf[pos] = bound.stream_id;
357 buf[pos + 1] = 0xC0;
358 buf[pos + 2] = ext;
359 buf[pos + 3] = 0xB6;
360 buf[pos + 4] = 0xC0
361 | (u8::from(bound.buffer_bound_scale) << 5)
362 | (bound.buffer_size_bound >> 8) as u8;
363 buf[pos + 5] = (bound.buffer_size_bound & 0xFF) as u8;
364 pos += 6;
365 } else {
366 buf[pos] = bound.stream_id;
367 buf[pos + 1] = 0xC0
368 | (u8::from(bound.buffer_bound_scale) << 5)
369 | (bound.buffer_size_bound >> 8) as u8;
370 buf[pos + 2] = (bound.buffer_size_bound & 0xFF) as u8;
371 pos += 3;
372 }
373 }
374 buf[pos..pos + self.trailing_reserved.len()].copy_from_slice(&self.trailing_reserved);
375
376 Ok(total)
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383 use alloc::vec;
384
385 #[test]
388 fn serialize_into_too_small_output_reports_output_buffer_error() {
389 let bytes = vec![
390 0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF, ];
397 let h = SystemHeader::parse(&bytes).unwrap();
398 let mut out = vec![0u8; h.serialized_len() - 1];
399 assert!(matches!(
400 h.serialize_into(&mut out),
401 Err(Error::OutputBufferTooSmall(_))
402 ));
403 }
404
405 #[test]
406 fn system_header_round_trip_no_streams() {
407 let bytes = vec![
408 0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF, ];
415 let h = SystemHeader::parse(&bytes).unwrap();
416 assert_eq!(h.rate_bound, 0);
417 assert_eq!(h.audio_bound, 1);
418 assert!(!h.fixed_flag);
419 assert!(!h.csps_flag);
420 assert!(!h.system_audio_lock_flag);
421 assert!(!h.system_video_lock_flag);
422 assert_eq!(h.video_bound, 0);
423 assert!(h.packet_rate_restriction_flag);
424 assert!(h.std_buffer_bounds.is_empty());
425
426 let mut out = vec![0u8; h.serialized_len()];
427 h.serialize_into(&mut out).unwrap();
428 assert_eq!(&out[..], &bytes[..]);
429
430 let h2 = SystemHeader::parse(&out).unwrap();
431 assert_eq!(h, h2);
432 }
433
434 #[test]
439 fn video_bound_bit_4_round_trips() {
440 let bytes = vec![
441 0x00,
442 0x00,
443 0x01,
444 0xBB, 0x00,
446 0x06, 0x80,
448 0x00,
449 0x01, 0x04, 0x20 | 0x1F, 0xFF,
453 ];
454 let h = SystemHeader::parse(&bytes).unwrap();
455 assert_eq!(
456 h.video_bound, 31,
457 "bit 4 of video_bound must not be dropped"
458 );
459
460 let mut out = vec![0u8; h.serialized_len()];
461 h.serialize_into(&mut out).unwrap();
462 assert_eq!(&out[..], &bytes[..]);
463 }
464
465 #[test]
466 fn system_header_round_trip_with_streams() {
467 let bytes = vec![
468 0x00, 0x00, 0x01, 0xBB, 0x00,
469 0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
471 0xE0, 0xFF, 0xFF, 0xC0, 0xC1, 0x00,
474 ];
475 let h = SystemHeader::parse(&bytes).unwrap();
476 assert_eq!(h.std_buffer_bounds.len(), 2);
477 assert_eq!(h.std_buffer_bounds[0].stream_id, 0xE0);
478 assert!(h.std_buffer_bounds[0].stream_id_extension.is_none());
479 assert!(h.std_buffer_bounds[0].buffer_bound_scale);
480 assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x1FFF);
481
482 assert_eq!(h.std_buffer_bounds[1].stream_id, 0xC0);
483 assert!(h.std_buffer_bounds[1].stream_id_extension.is_none());
484 assert!(!h.std_buffer_bounds[1].buffer_bound_scale);
485 assert_eq!(h.std_buffer_bounds[1].buffer_size_bound, 0x0100);
486
487 let mut out = vec![0u8; h.serialized_len()];
488 h.serialize_into(&mut out).unwrap();
489 assert_eq!(&out[..], &bytes[..]);
490
491 let h2 = SystemHeader::parse(&out).unwrap();
492 assert_eq!(h, h2);
493
494 let h_mut = SystemHeader {
496 rate_bound: 12345,
497 ..h.clone()
498 };
499 let mut out2 = vec![0u8; h_mut.serialized_len()];
500 h_mut.serialize_into(&mut out2).unwrap();
501 assert_ne!(&out[..], &out2[..]);
502 }
503
504 #[test]
508 fn trailing_bytes_after_stream_loop_round_trip() {
509 let bytes = vec![
510 0x00,
511 0x00,
512 0x01,
513 0xBB, 0x00,
515 0x0C, 0x80,
517 0x00,
518 0x01,
519 0x04,
520 0x20 | 0x01,
521 0xFF, 0xE0,
523 0xE0,
524 0x80, 0x00,
526 0x7F,
527 0xAB, ];
529 let h = SystemHeader::parse(&bytes).unwrap();
530 assert_eq!(h.std_buffer_bounds.len(), 1);
531 assert_eq!(h.trailing_reserved, vec![0x00, 0x7F, 0xAB]);
532 let mut out = vec![0u8; h.serialized_len()];
533 h.serialize_into(&mut out).unwrap();
534 assert_eq!(out, bytes);
535 }
536
537 #[test]
540 fn non_conventional_reserved_bits_round_trip() {
541 let bytes = vec![
542 0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0x2A, ];
549 let h = SystemHeader::parse(&bytes).unwrap();
550 assert_eq!(h.reserved_bits, 0x2A);
551 assert!(!h.packet_rate_restriction_flag);
552 let mut out = vec![0u8; h.serialized_len()];
553 h.serialize_into(&mut out).unwrap();
554 assert_eq!(out, bytes);
555 }
556
557 #[test]
569 fn many_bounds_past_old_u16_overflow_point_errs_not_panics() {
570 let std_buffer_bounds = (0..11_000)
571 .map(|_| StdBufferBound {
572 stream_id: EXT_STREAM_ID,
573 stream_id_extension: Some(0x05),
574 buffer_bound_scale: false,
575 buffer_size_bound: 0,
576 })
577 .collect::<Vec<_>>();
578 let h = SystemHeader {
579 rate_bound: 0,
580 audio_bound: 0,
581 fixed_flag: false,
582 csps_flag: false,
583 system_audio_lock_flag: false,
584 system_video_lock_flag: false,
585 video_bound: 0,
586 packet_rate_restriction_flag: false,
587 reserved_bits: RESERVED_BITS_MASK,
588 std_buffer_bounds,
589 trailing_reserved: Vec::new(),
590 };
591 let mut buf = vec![0u8; h.serialized_len()];
595 assert!(matches!(
596 h.serialize_into(&mut buf),
597 Err(Error::FieldOverflow(_))
598 ));
599 }
600
601 #[test]
604 fn header_length_over_u16_is_rejected_not_wrapped() {
605 let std_buffer_bounds = (0..25_000)
606 .map(|_| StdBufferBound {
607 stream_id: 0xC0,
608 stream_id_extension: None,
609 buffer_bound_scale: false,
610 buffer_size_bound: 0,
611 })
612 .collect::<Vec<_>>();
613 let h = SystemHeader {
614 rate_bound: 0,
615 audio_bound: 0,
616 fixed_flag: false,
617 csps_flag: false,
618 system_audio_lock_flag: false,
619 system_video_lock_flag: false,
620 video_bound: 0,
621 packet_rate_restriction_flag: false,
622 reserved_bits: RESERVED_BITS_MASK,
623 std_buffer_bounds,
624 trailing_reserved: Vec::new(),
625 };
626 let mut buf = vec![0u8; h.serialized_len()];
627 assert!(matches!(
628 h.serialize_into(&mut buf),
629 Err(Error::FieldOverflow(_))
630 ));
631 }
632
633 #[test]
636 fn many_bounds_within_u16_header_length_round_trips() {
637 let std_buffer_bounds = (0..1000)
638 .map(|_| StdBufferBound {
639 stream_id: 0xC0,
640 stream_id_extension: None,
641 buffer_bound_scale: false,
642 buffer_size_bound: 0x0100,
643 })
644 .collect::<Vec<_>>();
645 let h = SystemHeader {
646 rate_bound: 0,
647 audio_bound: 0,
648 fixed_flag: false,
649 csps_flag: false,
650 system_audio_lock_flag: false,
651 system_video_lock_flag: false,
652 video_bound: 0,
653 packet_rate_restriction_flag: false,
654 reserved_bits: RESERVED_BITS_MASK,
655 std_buffer_bounds,
656 trailing_reserved: Vec::new(),
657 };
658 let mut buf = vec![0u8; h.serialized_len()];
659 h.serialize_into(&mut buf).unwrap();
660 let back = SystemHeader::parse(&buf).unwrap();
661 assert_eq!(back.std_buffer_bounds.len(), 1000);
662 }
663
664 #[test]
665 fn system_header_round_trip_extended_stream_id() {
666 let bytes = vec![
667 0x00, 0x00, 0x01, 0xBB, 0x00, 0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
669 0xB7, 0xC0, 0x05, 0xB6, 0xE1, 0x00,
671 ];
672 let h = SystemHeader::parse(&bytes).unwrap();
673 assert_eq!(h.std_buffer_bounds.len(), 1);
674 assert_eq!(h.std_buffer_bounds[0].stream_id, 0xB7);
675 assert_eq!(h.std_buffer_bounds[0].stream_id_extension, Some(0x05));
676 assert!(h.std_buffer_bounds[0].buffer_bound_scale);
677 assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x100);
678
679 let mut out = vec![0u8; h.serialized_len()];
680 h.serialize_into(&mut out).unwrap();
681 assert_eq!(&out[..], &bytes[..]);
682 }
683
684 fn ext_entry_header(entry: [u8; 6]) -> Vec<u8> {
685 let mut bytes = vec![
686 0x00, 0x00, 0x01, 0xBB, 0x00, 0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
687 ];
688 bytes.extend_from_slice(&entry);
689 bytes
690 }
691
692 #[test]
693 fn a_bad_stream_id_extension_prefix_is_reported_with_the_offending_byte() {
694 assert_eq!(
695 SystemHeader::parse(&ext_entry_header([0xB7, 0x00, 0x00, 0xB6, 0xC0, 0x00])),
696 Err(Error::BadStreamIdExtensionPrefix(0x00))
697 );
698 assert_eq!(
699 SystemHeader::parse(&ext_entry_header([0xB7, 0xC0, 0x00, 0x11, 0xC0, 0x00])),
700 Err(Error::BadStreamIdExtensionPrefix(0x11))
701 );
702 }
703
704 fn bare_header() -> SystemHeader {
705 let bytes = vec![
706 0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
707 ];
708 SystemHeader::parse(&bytes).unwrap()
709 }
710
711 fn serialize_error(h: &SystemHeader) -> Error {
712 let mut out = vec![0u8; h.serialized_len()];
713 h.serialize_into(&mut out).unwrap_err()
714 }
715
716 fn invalid(field: &'static str, reason: &'static str) -> Error {
717 Error::InvalidField(broadcast_common::error::InvalidField { field, reason })
718 }
719
720 #[test]
721 fn a_stream_loop_that_would_not_read_back_is_an_invalid_field() {
722 let bound = |stream_id, stream_id_extension| StdBufferBound {
723 stream_id,
724 stream_id_extension,
725 buffer_bound_scale: false,
726 buffer_size_bound: 1,
727 };
728 let mut h = bare_header();
729 h.std_buffer_bounds = vec![bound(0x7F, None)];
730 assert_eq!(
731 serialize_error(&h),
732 invalid(
733 "system_header.stream_id",
734 "stream_id MSB is 0, so the entry would end the stream loop on parse"
735 )
736 );
737 h.std_buffer_bounds = vec![bound(0xE0, Some(1))];
738 assert_eq!(
739 serialize_error(&h),
740 invalid(
741 "system_header.stream_id_extension",
742 "stream_id_extension requires stream_id 0xB7"
743 )
744 );
745 h.std_buffer_bounds = vec![bound(0xB7, None)];
746 assert_eq!(
747 serialize_error(&h),
748 invalid(
749 "system_header.stream_id",
750 "stream_id 0xB7 requires stream_id_extension"
751 )
752 );
753 h.std_buffer_bounds = Vec::new();
754 h.trailing_reserved = vec![0x80];
755 assert_eq!(
756 serialize_error(&h),
757 invalid(
758 "system_header.trailing_reserved",
759 "first byte has its MSB set, so it would be re-read as a stream-bound entry"
760 )
761 );
762 }
763}