Skip to main content

dvb_si/tables/
st.rs

1//! Stuffing Table — ETSI EN 300 468 §5.2.8.
2//!
3//! Short-form section on PID 0x0014 with table_id 0x72. Payload is stuffing
4//! bytes used to invalidate/replace sections; per §5.2.8 each `data_byte`
5//! "may take any value and has no meaning" (0xFF fill is common on real
6//! transponders). No CRC.
7
8use crate::error::{Error, Result};
9use crate::traits::Table;
10use dvb_common::{Parse, Serialize};
11
12/// table_id for Stuffing Table.
13pub const TABLE_ID: u8 = 0x72;
14/// Well-known PID on which ST is carried (shared with TDT/TOT).
15pub const PID: u16 = 0x0014;
16
17const HEADER_LEN: usize = 3;
18
19/// Stuffing Table.
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct St {
23    /// Raw stuffing bytes — any value, no meaning (§5.2.8).
24    pub payload: Vec<u8>,
25}
26
27impl St {
28    /// Construct a new ST table with the given stuffing bytes.
29    #[inline]
30    #[must_use]
31    pub fn new(payload: Vec<u8>) -> Self {
32        Self { payload }
33    }
34
35    /// Number of stuffing bytes in the payload.
36    #[inline]
37    #[must_use]
38    pub fn len(&self) -> usize {
39        self.payload.len()
40    }
41
42    /// Returns `true` if the payload contains no stuffing bytes.
43    #[inline]
44    #[must_use]
45    pub fn is_empty(&self) -> bool {
46        self.payload.is_empty()
47    }
48}
49
50impl<'a> Parse<'a> for St {
51    type Error = crate::error::Error;
52
53    fn parse(bytes: &'a [u8]) -> Result<Self> {
54        if bytes.len() < HEADER_LEN {
55            return Err(Error::BufferTooShort {
56                need: HEADER_LEN,
57                have: bytes.len(),
58                what: "St",
59            });
60        }
61
62        if bytes[0] != TABLE_ID {
63            return Err(Error::UnexpectedTableId {
64                table_id: bytes[0],
65                what: "St",
66                expected: &[TABLE_ID],
67            });
68        }
69
70        let section_length = ((bytes[1] & 0x0F) as u16) << 8 | bytes[2] as u16;
71        let payload_len = section_length as usize;
72
73        if bytes.len() < HEADER_LEN + payload_len {
74            return Err(Error::BufferTooShort {
75                need: HEADER_LEN + payload_len,
76                have: bytes.len(),
77                what: "St payload",
78            });
79        }
80
81        // §5.2.8: data_byte "may take any value and has no meaning" — no
82        // value constraint; preserve verbatim.
83        let payload = &bytes[HEADER_LEN..HEADER_LEN + payload_len];
84        Ok(Self {
85            payload: payload.to_vec(),
86        })
87    }
88}
89
90impl Serialize for St {
91    type Error = crate::error::Error;
92
93    fn serialized_len(&self) -> usize {
94        HEADER_LEN + self.payload.len()
95    }
96
97    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
98        let len = self.serialized_len();
99        if buf.len() < len {
100            return Err(Error::OutputBufferTooSmall {
101                need: len,
102                have: buf.len(),
103            });
104        }
105
106        // Byte 0: table_id = 0x72
107        buf[0] = TABLE_ID;
108
109        // Byte 1: SSI=0, reserved_future_use='1', reserved='11', upper nibble of
110        // section_length. Top nibble 0b0111 = 0x70, matching DIT/RST/TDT/TOT.
111        buf[1] = 0x70 | ((self.payload.len() >> 8) as u8 & 0x0F);
112
113        // Byte 2: section_length low byte
114        buf[2] = (self.payload.len() & 0xFF) as u8;
115
116        // Payload: stuffing bytes
117        buf[HEADER_LEN..len].copy_from_slice(&self.payload);
118
119        Ok(len)
120    }
121}
122
123impl<'a> Table<'a> for St {
124    const TABLE_ID: u8 = TABLE_ID;
125    const PID: u16 = PID;
126}
127
128impl<'a> crate::traits::TableDef<'a> for St {
129    const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
130    const NAME: &'static str = "STUFFING";
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    /// Build a minimal ST section byte vector.
138    fn make_st_section(payload: &[u8]) -> Vec<u8> {
139        let mut buf = vec![TABLE_ID, 0x70, payload.len() as u8];
140        buf.extend_from_slice(payload);
141        buf
142    }
143
144    #[test]
145    fn parse_rejects_wrong_tag() {
146        let bytes = [0x71, 0x70, 0x02, 0x00, 0x00];
147        assert!(matches!(
148            St::parse(&bytes).unwrap_err(),
149            Error::UnexpectedTableId { table_id: 0x71, .. }
150        ));
151    }
152
153    #[test]
154    fn parse_empty_payload() {
155        let bytes = make_st_section(&[]);
156        let st = St::parse(&bytes).unwrap();
157        assert!(st.is_empty());
158    }
159
160    /// §5.2.8: data_byte "may take any value and has no meaning" — 0xFF fill
161    /// (common on real transponders) must parse and round-trip.
162    #[test]
163    fn parse_accepts_any_data_byte_value() {
164        let bytes = make_st_section(&[0x00, 0xFF, 0xAA]);
165        let st = St::parse(&bytes).unwrap();
166        assert_eq!(st.payload, vec![0x00, 0xFF, 0xAA]);
167        let mut buf = vec![0u8; st.serialized_len()];
168        st.serialize_into(&mut buf).unwrap();
169        assert_eq!(buf, bytes);
170    }
171
172    #[test]
173    fn serialize_writes_correct_header() {
174        let st = St::new(vec![0x00, 0x00]);
175        let mut buf = vec![0u8; st.serialized_len()];
176        st.serialize_into(&mut buf).unwrap();
177        assert_eq!(buf[0], TABLE_ID);
178        assert_eq!(buf[1], 0x70 | ((2 >> 8) as u8 & 0x0F));
179        assert_eq!(buf[2], 2);
180    }
181
182    #[test]
183    fn serialize_empty_payload() {
184        let st = St::new(vec![]);
185        let mut buf = vec![0u8; st.serialized_len()];
186        st.serialize_into(&mut buf).unwrap();
187        assert_eq!(buf, [TABLE_ID, 0x70, 0x00]);
188    }
189
190    #[test]
191    fn serialize_rejects_too_small_buffer() {
192        let st = St::new(vec![0x00]);
193        let mut too_small = vec![0u8; st.serialized_len() - 1];
194        let err = st.serialize_into(&mut too_small).unwrap_err();
195        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
196    }
197
198    #[test]
199    fn round_trip_preserves_all_fields() {
200        let st = St::new(vec![0x00, 0x00, 0x00]);
201        let mut buf = vec![0u8; st.serialized_len()];
202        st.serialize_into(&mut buf).unwrap();
203        let re = St::parse(&buf).unwrap();
204        assert_eq!(st, re);
205    }
206
207    #[test]
208    fn round_trip_empty() {
209        let st = St::new(vec![]);
210        let mut buf = vec![0u8; st.serialized_len()];
211        st.serialize_into(&mut buf).unwrap();
212        let re = St::parse(&buf).unwrap();
213        assert_eq!(st, re);
214    }
215
216    #[test]
217    fn round_trip_many_stuffs() {
218        let st = St::new(vec![0x00; 185]);
219        let mut buf = vec![0u8; st.serialized_len()];
220        st.serialize_into(&mut buf).unwrap();
221        let re = St::parse(&buf).unwrap();
222        assert_eq!(st, re);
223    }
224
225    #[test]
226    fn parse_rejects_buffer_too_short() {
227        let bytes = [0x72, 0x70]; // only 2 bytes
228        assert!(matches!(
229            St::parse(&bytes).unwrap_err(),
230            Error::BufferTooShort { need: 3, .. }
231        ));
232    }
233
234    #[test]
235    fn parse_rejects_section_length_exceeds_buffer() {
236        // section_length = 10 but only 2 payload bytes available after header
237        let bytes = [0x72, 0x70, 10, 0x00, 0x00];
238        assert!(matches!(
239            St::parse(&bytes).unwrap_err(),
240            Error::BufferTooShort {
241                what: "St payload",
242                ..
243            }
244        ));
245    }
246
247    #[test]
248    fn table_trait_constants() {
249        assert_eq!(<St as Table>::TABLE_ID, 0x72);
250        assert_eq!(<St as Table>::PID, 0x0014);
251    }
252
253    #[test]
254    fn serialized_len_matches_wire_size() {
255        let st = St::new(vec![0x00; 50]);
256        assert_eq!(st.serialized_len(), HEADER_LEN + 50);
257    }
258
259    #[test]
260    fn to_bytes_produces_valid_section() {
261        let st = St::new(vec![0x00, 0x00]);
262        let bytes = st.to_bytes();
263        assert_eq!(bytes[0], TABLE_ID);
264        assert_eq!(bytes.len(), st.serialized_len());
265    }
266
267    #[test]
268    fn len_and_is_empty() {
269        let empty = St::new(vec![]);
270        assert!(empty.is_empty());
271        assert_eq!(empty.len(), 0);
272
273        let filled = St::new(vec![0x00; 10]);
274        assert!(!filled.is_empty());
275        assert_eq!(filled.len(), 10);
276    }
277
278    #[test]
279    fn new_constructor() {
280        let st = St::new(vec![0x00]);
281        assert_eq!(st.len(), 1);
282    }
283}