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]
386 fn system_header_round_trip_no_streams() {
387 let bytes = vec![
388 0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF, ];
395 let h = SystemHeader::parse(&bytes).unwrap();
396 assert_eq!(h.rate_bound, 0);
397 assert_eq!(h.audio_bound, 1);
398 assert!(!h.fixed_flag);
399 assert!(!h.csps_flag);
400 assert!(!h.system_audio_lock_flag);
401 assert!(!h.system_video_lock_flag);
402 assert_eq!(h.video_bound, 0);
403 assert!(h.packet_rate_restriction_flag);
404 assert!(h.std_buffer_bounds.is_empty());
405
406 let mut out = vec![0u8; h.serialized_len()];
407 h.serialize_into(&mut out).unwrap();
408 assert_eq!(&out[..], &bytes[..]);
409
410 let h2 = SystemHeader::parse(&out).unwrap();
411 assert_eq!(h, h2);
412 }
413
414 #[test]
419 fn video_bound_bit_4_round_trips() {
420 let bytes = vec![
421 0x00,
422 0x00,
423 0x01,
424 0xBB, 0x00,
426 0x06, 0x80,
428 0x00,
429 0x01, 0x04, 0x20 | 0x1F, 0xFF,
433 ];
434 let h = SystemHeader::parse(&bytes).unwrap();
435 assert_eq!(
436 h.video_bound, 31,
437 "bit 4 of video_bound must not be dropped"
438 );
439
440 let mut out = vec![0u8; h.serialized_len()];
441 h.serialize_into(&mut out).unwrap();
442 assert_eq!(&out[..], &bytes[..]);
443 }
444
445 #[test]
446 fn system_header_round_trip_with_streams() {
447 let bytes = vec![
448 0x00, 0x00, 0x01, 0xBB, 0x00,
449 0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
451 0xE0, 0xFF, 0xFF, 0xC0, 0xC1, 0x00,
454 ];
455 let h = SystemHeader::parse(&bytes).unwrap();
456 assert_eq!(h.std_buffer_bounds.len(), 2);
457 assert_eq!(h.std_buffer_bounds[0].stream_id, 0xE0);
458 assert!(h.std_buffer_bounds[0].stream_id_extension.is_none());
459 assert!(h.std_buffer_bounds[0].buffer_bound_scale);
460 assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x1FFF);
461
462 assert_eq!(h.std_buffer_bounds[1].stream_id, 0xC0);
463 assert!(h.std_buffer_bounds[1].stream_id_extension.is_none());
464 assert!(!h.std_buffer_bounds[1].buffer_bound_scale);
465 assert_eq!(h.std_buffer_bounds[1].buffer_size_bound, 0x0100);
466
467 let mut out = vec![0u8; h.serialized_len()];
468 h.serialize_into(&mut out).unwrap();
469 assert_eq!(&out[..], &bytes[..]);
470
471 let h2 = SystemHeader::parse(&out).unwrap();
472 assert_eq!(h, h2);
473
474 let h_mut = SystemHeader {
476 rate_bound: 12345,
477 ..h.clone()
478 };
479 let mut out2 = vec![0u8; h_mut.serialized_len()];
480 h_mut.serialize_into(&mut out2).unwrap();
481 assert_ne!(&out[..], &out2[..]);
482 }
483
484 #[test]
488 fn trailing_bytes_after_stream_loop_round_trip() {
489 let bytes = vec![
490 0x00,
491 0x00,
492 0x01,
493 0xBB, 0x00,
495 0x0C, 0x80,
497 0x00,
498 0x01,
499 0x04,
500 0x20 | 0x01,
501 0xFF, 0xE0,
503 0xE0,
504 0x80, 0x00,
506 0x7F,
507 0xAB, ];
509 let h = SystemHeader::parse(&bytes).unwrap();
510 assert_eq!(h.std_buffer_bounds.len(), 1);
511 assert_eq!(h.trailing_reserved, vec![0x00, 0x7F, 0xAB]);
512 let mut out = vec![0u8; h.serialized_len()];
513 h.serialize_into(&mut out).unwrap();
514 assert_eq!(out, bytes);
515 }
516
517 #[test]
520 fn non_conventional_reserved_bits_round_trip() {
521 let bytes = vec![
522 0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0x2A, ];
529 let h = SystemHeader::parse(&bytes).unwrap();
530 assert_eq!(h.reserved_bits, 0x2A);
531 assert!(!h.packet_rate_restriction_flag);
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]
549 fn many_bounds_past_old_u16_overflow_point_errs_not_panics() {
550 let std_buffer_bounds = (0..11_000)
551 .map(|_| StdBufferBound {
552 stream_id: EXT_STREAM_ID,
553 stream_id_extension: Some(0x05),
554 buffer_bound_scale: false,
555 buffer_size_bound: 0,
556 })
557 .collect::<Vec<_>>();
558 let h = SystemHeader {
559 rate_bound: 0,
560 audio_bound: 0,
561 fixed_flag: false,
562 csps_flag: false,
563 system_audio_lock_flag: false,
564 system_video_lock_flag: false,
565 video_bound: 0,
566 packet_rate_restriction_flag: false,
567 reserved_bits: RESERVED_BITS_MASK,
568 std_buffer_bounds,
569 trailing_reserved: Vec::new(),
570 };
571 let mut buf = vec![0u8; h.serialized_len()];
575 assert!(matches!(
576 h.serialize_into(&mut buf),
577 Err(Error::FieldOverflow(_))
578 ));
579 }
580
581 #[test]
584 fn header_length_over_u16_is_rejected_not_wrapped() {
585 let std_buffer_bounds = (0..25_000)
586 .map(|_| StdBufferBound {
587 stream_id: 0xC0,
588 stream_id_extension: None,
589 buffer_bound_scale: false,
590 buffer_size_bound: 0,
591 })
592 .collect::<Vec<_>>();
593 let h = SystemHeader {
594 rate_bound: 0,
595 audio_bound: 0,
596 fixed_flag: false,
597 csps_flag: false,
598 system_audio_lock_flag: false,
599 system_video_lock_flag: false,
600 video_bound: 0,
601 packet_rate_restriction_flag: false,
602 reserved_bits: RESERVED_BITS_MASK,
603 std_buffer_bounds,
604 trailing_reserved: Vec::new(),
605 };
606 let mut buf = vec![0u8; h.serialized_len()];
607 assert!(matches!(
608 h.serialize_into(&mut buf),
609 Err(Error::FieldOverflow(_))
610 ));
611 }
612
613 #[test]
616 fn many_bounds_within_u16_header_length_round_trips() {
617 let std_buffer_bounds = (0..1000)
618 .map(|_| StdBufferBound {
619 stream_id: 0xC0,
620 stream_id_extension: None,
621 buffer_bound_scale: false,
622 buffer_size_bound: 0x0100,
623 })
624 .collect::<Vec<_>>();
625 let h = SystemHeader {
626 rate_bound: 0,
627 audio_bound: 0,
628 fixed_flag: false,
629 csps_flag: false,
630 system_audio_lock_flag: false,
631 system_video_lock_flag: false,
632 video_bound: 0,
633 packet_rate_restriction_flag: false,
634 reserved_bits: RESERVED_BITS_MASK,
635 std_buffer_bounds,
636 trailing_reserved: Vec::new(),
637 };
638 let mut buf = vec![0u8; h.serialized_len()];
639 h.serialize_into(&mut buf).unwrap();
640 let back = SystemHeader::parse(&buf).unwrap();
641 assert_eq!(back.std_buffer_bounds.len(), 1000);
642 }
643
644 #[test]
645 fn system_header_round_trip_extended_stream_id() {
646 let bytes = vec![
647 0x00, 0x00, 0x01, 0xBB, 0x00, 0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
649 0xB7, 0xC0, 0x05, 0xB6, 0xE1, 0x00,
651 ];
652 let h = SystemHeader::parse(&bytes).unwrap();
653 assert_eq!(h.std_buffer_bounds.len(), 1);
654 assert_eq!(h.std_buffer_bounds[0].stream_id, 0xB7);
655 assert_eq!(h.std_buffer_bounds[0].stream_id_extension, Some(0x05));
656 assert!(h.std_buffer_bounds[0].buffer_bound_scale);
657 assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x100);
658
659 let mut out = vec![0u8; h.serialized_len()];
660 h.serialize_into(&mut out).unwrap();
661 assert_eq!(&out[..], &bytes[..]);
662 }
663
664 fn ext_entry_header(entry: [u8; 6]) -> Vec<u8> {
665 let mut bytes = vec![
666 0x00, 0x00, 0x01, 0xBB, 0x00, 0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
667 ];
668 bytes.extend_from_slice(&entry);
669 bytes
670 }
671
672 #[test]
673 fn a_bad_stream_id_extension_prefix_is_reported_with_the_offending_byte() {
674 assert_eq!(
675 SystemHeader::parse(&ext_entry_header([0xB7, 0x00, 0x00, 0xB6, 0xC0, 0x00])),
676 Err(Error::BadStreamIdExtensionPrefix(0x00))
677 );
678 assert_eq!(
679 SystemHeader::parse(&ext_entry_header([0xB7, 0xC0, 0x00, 0x11, 0xC0, 0x00])),
680 Err(Error::BadStreamIdExtensionPrefix(0x11))
681 );
682 }
683
684 fn bare_header() -> SystemHeader {
685 let bytes = vec![
686 0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
687 ];
688 SystemHeader::parse(&bytes).unwrap()
689 }
690
691 fn serialize_error(h: &SystemHeader) -> Error {
692 let mut out = vec![0u8; h.serialized_len()];
693 h.serialize_into(&mut out).unwrap_err()
694 }
695
696 fn invalid(field: &'static str, reason: &'static str) -> Error {
697 Error::InvalidField(broadcast_common::error::InvalidField { field, reason })
698 }
699
700 #[test]
701 fn a_stream_loop_that_would_not_read_back_is_an_invalid_field() {
702 let bound = |stream_id, stream_id_extension| StdBufferBound {
703 stream_id,
704 stream_id_extension,
705 buffer_bound_scale: false,
706 buffer_size_bound: 1,
707 };
708 let mut h = bare_header();
709 h.std_buffer_bounds = vec![bound(0x7F, None)];
710 assert_eq!(
711 serialize_error(&h),
712 invalid(
713 "system_header.stream_id",
714 "stream_id MSB is 0, so the entry would end the stream loop on parse"
715 )
716 );
717 h.std_buffer_bounds = vec![bound(0xE0, Some(1))];
718 assert_eq!(
719 serialize_error(&h),
720 invalid(
721 "system_header.stream_id_extension",
722 "stream_id_extension requires stream_id 0xB7"
723 )
724 );
725 h.std_buffer_bounds = vec![bound(0xB7, None)];
726 assert_eq!(
727 serialize_error(&h),
728 invalid(
729 "system_header.stream_id",
730 "stream_id 0xB7 requires stream_id_extension"
731 )
732 );
733 h.std_buffer_bounds = Vec::new();
734 h.trailing_reserved = vec![0x80];
735 assert_eq!(
736 serialize_error(&h),
737 invalid(
738 "system_header.trailing_reserved",
739 "first byte has its MSB set, so it would be re-read as a stream-bound entry"
740 )
741 );
742 }
743}