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