Skip to main content

dvb_subtitle/segments/
stuffing.rs

1//! Stuffing segment — ETSI EN 300 743 Table 7 (segment_type 0xFF).
2//!
3//! Stuffing bytes have no semantic meaning; their segment_type is 0xFF.
4//! The body is opaque raw bytes.
5
6use crate::error::{Error, Result};
7use broadcast_common::len::{need_in, need_out};
8use broadcast_common::{Parse, Serialize};
9
10/// The stuffing segment_type.
11pub const SEGMENT_TYPE: u8 = 0xFF;
12/// Header: sync_byte(1) + segment_type(1) + page_id(2) + segment_length(2) = 6 bytes.
13pub const HEADER_LEN: usize = 6;
14
15/// A stuffing segment — opaque data bytes following the header.
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18pub struct StuffingSegment<'a> {
19    /// The page_id from the segment header.
20    pub page_id: u16,
21    /// Opaque stuffing data bytes.
22    #[cfg_attr(feature = "serde", serde(skip))]
23    pub data: &'a [u8],
24}
25
26impl<'a> Parse<'a> for StuffingSegment<'a> {
27    type Error = Error;
28
29    fn parse(bytes: &'a [u8]) -> Result<Self> {
30        need_in(bytes, HEADER_LEN, "stuffing_segment")?;
31        if bytes[0] != crate::pes_data_field::SYNC_BYTE {
32            return Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
33                what: "sync_byte",
34                expected: u32::from(crate::pes_data_field::SYNC_BYTE),
35                found: u32::from(bytes[0]),
36            }));
37        }
38        if bytes[1] != SEGMENT_TYPE {
39            return Err(Error::UnknownSegmentType(bytes[1]));
40        }
41        let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
42        let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
43        let total = HEADER_LEN + segment_length;
44        need_in(bytes, total, "stuffing_segment data")?;
45        Ok(StuffingSegment {
46            page_id,
47            data: &bytes[HEADER_LEN..total],
48        })
49    }
50}
51
52impl Serialize for StuffingSegment<'_> {
53    type Error = Error;
54
55    fn serialized_len(&self) -> usize {
56        HEADER_LEN + self.data.len()
57    }
58
59    fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
60        let len = self.serialized_len();
61        need_out(buf, len)?;
62        buf[0] = 0x0F;
63        buf[1] = SEGMENT_TYPE;
64        buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
65        let seg_len = crate::segments::check_segment_length(self.data.len())?;
66        buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
67        buf[6..len].copy_from_slice(self.data);
68        Ok(len)
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use broadcast_common::{Parse, Serialize};
76
77    #[test]
78    fn round_trip() {
79        let bytes = [0x0F, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xAA, 0xBB, 0xCC];
80        let seg = StuffingSegment::parse(&bytes).unwrap();
81        assert_eq!(seg.data, &[0xAA, 0xBB, 0xCC]);
82        let out = seg.try_to_bytes().unwrap();
83        assert_eq!(out, bytes);
84
85        // Biting test: page_id changes output
86        let mut seg2 = seg.clone();
87        seg2.page_id = 9;
88        let out2 = seg2.try_to_bytes().unwrap();
89        assert_ne!(out2, bytes);
90        let reparse = StuffingSegment::parse(&out2).unwrap();
91        assert_eq!(reparse.page_id, 9);
92    }
93
94    /// DS-W2 (#1108): sync_byte wasn't checked by any typed segment parser.
95    #[test]
96    fn rejects_bad_sync_byte() {
97        let bytes = [0x00, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xAA, 0xBB, 0xCC];
98        assert!(matches!(
99            StuffingSegment::parse(&bytes),
100            Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
101                found: 0x00,
102                ..
103            }))
104        ));
105    }
106}