Skip to main content

dvb_si/tables/
rst.rs

1//! Running Status Table — ETSI EN 300 468 §5.2.8.
2//!
3//! Carried on PID `0x0013` with `table_id = 0x71`. A SHORT-FORM section — there
4//! is no version/section header and no CRC. The body is a flat loop of 9-byte
5//! entries, each giving the running status of one event:
6//!
7//! ```text
8//! transport_stream_id(16) original_network_id(16) service_id(16)
9//! event_id(16) reserved_future_use(5) running_status(3)
10//! ```
11
12use crate::error::{Error, Result};
13use dvb_common::{Parse, Serialize};
14
15/// table_id for the Running Status Table.
16pub const TABLE_ID: u8 = 0x71;
17/// Well-known PID on which the RST is carried.
18pub const PID: u16 = 0x0013;
19
20const HEADER_LEN: usize = 3;
21/// Each entry is 9 bytes: tsid(2) + onid(2) + sid(2) + evid(2) + status(1).
22const ENTRY_LEN: usize = 9;
23
24/// One RST entry — the running status of a single event.
25///
26/// `running_status` is the 3-bit code from EN 300 468 Table 6: 0 undefined,
27/// 1 not running, 2 starts in a few seconds, 3 pausing, 4 running,
28/// 5 service off-air, 6–7 reserved.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub struct RstEntry {
32    /// Transport stream carrying the event.
33    pub transport_stream_id: u16,
34    /// Originating network.
35    pub original_network_id: u16,
36    /// Service (matches `program_number` in the PAT).
37    pub service_id: u16,
38    /// Event identifier.
39    pub event_id: u16,
40    /// 3-bit running_status code (EN 300 468 Table 6).
41    pub running_status: u8,
42}
43
44/// Running Status Table (§5.2.8, Table 10).
45#[derive(Debug, Clone, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize))]
47pub struct RstSection {
48    /// Entries in wire order.
49    pub entries: Vec<RstEntry>,
50}
51
52impl<'a> Parse<'a> for RstSection {
53    type Error = crate::error::Error;
54
55    fn parse(bytes: &'a [u8]) -> Result<Self> {
56        if bytes.len() < HEADER_LEN {
57            return Err(Error::BufferTooShort {
58                need: HEADER_LEN,
59                have: bytes.len(),
60                what: "RstSection",
61            });
62        }
63        if bytes[0] != TABLE_ID {
64            return Err(Error::UnexpectedTableId {
65                table_id: bytes[0],
66                what: "RstSection",
67                expected: &[TABLE_ID],
68            });
69        }
70        let section_length = ((bytes[1] & 0x0F) as usize) << 8 | bytes[2] as usize;
71        let total = HEADER_LEN + section_length;
72        if bytes.len() < total {
73            return Err(Error::SectionLengthOverflow {
74                declared: section_length,
75                available: bytes.len() - HEADER_LEN,
76            });
77        }
78        if section_length % ENTRY_LEN != 0 {
79            return Err(Error::SectionLengthOverflow {
80                declared: section_length,
81                available: (section_length / ENTRY_LEN) * ENTRY_LEN,
82            });
83        }
84        let mut entries = Vec::with_capacity(section_length / ENTRY_LEN);
85        let mut off = HEADER_LEN;
86        while off + ENTRY_LEN <= total {
87            entries.push(RstEntry {
88                transport_stream_id: u16::from_be_bytes([bytes[off], bytes[off + 1]]),
89                original_network_id: u16::from_be_bytes([bytes[off + 2], bytes[off + 3]]),
90                service_id: u16::from_be_bytes([bytes[off + 4], bytes[off + 5]]),
91                event_id: u16::from_be_bytes([bytes[off + 6], bytes[off + 7]]),
92                running_status: bytes[off + 8] & 0x07,
93            });
94            off += ENTRY_LEN;
95        }
96        Ok(RstSection { entries })
97    }
98}
99
100impl Serialize for RstSection {
101    type Error = crate::error::Error;
102
103    fn serialized_len(&self) -> usize {
104        HEADER_LEN + self.entries.len() * ENTRY_LEN
105    }
106
107    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
108        let len = self.serialized_len();
109        if buf.len() < len {
110            return Err(Error::OutputBufferTooSmall {
111                need: len,
112                have: buf.len(),
113            });
114        }
115        let section_length = (len - HEADER_LEN) as u16;
116        buf[0] = TABLE_ID;
117        // section_syntax_indicator=0 (short form), reserved_future_use=1,
118        // reserved=11, section_length high nibble.
119        buf[1] = 0x70 | ((section_length >> 8) as u8 & 0x0F);
120        buf[2] = (section_length & 0xFF) as u8;
121        let mut off = HEADER_LEN;
122        for e in &self.entries {
123            buf[off..off + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
124            buf[off + 2..off + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
125            buf[off + 4..off + 6].copy_from_slice(&e.service_id.to_be_bytes());
126            buf[off + 6..off + 8].copy_from_slice(&e.event_id.to_be_bytes());
127            // reserved_future_use(5)=1, running_status(3).
128            buf[off + 8] = 0xF8 | (e.running_status & 0x07);
129            off += ENTRY_LEN;
130        }
131        Ok(len)
132    }
133}
134impl<'a> crate::traits::TableDef<'a> for RstSection {
135    const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
136    const NAME: &'static str = "RUNNING_STATUS";
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    fn build_rst(entries: &[RstEntry]) -> Vec<u8> {
144        let section_length = (entries.len() * ENTRY_LEN) as u16;
145        let mut v = vec![
146            TABLE_ID,
147            0x70 | ((section_length >> 8) as u8 & 0x0F),
148            (section_length & 0xFF) as u8,
149        ];
150        for e in entries {
151            v.extend_from_slice(&e.transport_stream_id.to_be_bytes());
152            v.extend_from_slice(&e.original_network_id.to_be_bytes());
153            v.extend_from_slice(&e.service_id.to_be_bytes());
154            v.extend_from_slice(&e.event_id.to_be_bytes());
155            v.push(0xF8 | (e.running_status & 0x07));
156        }
157        v
158    }
159
160    fn entry(tsid: u16, onid: u16, sid: u16, evid: u16, rs: u8) -> RstEntry {
161        RstEntry {
162            transport_stream_id: tsid,
163            original_network_id: onid,
164            service_id: sid,
165            event_id: evid,
166            running_status: rs,
167        }
168    }
169
170    #[test]
171    fn parse_empty() {
172        let rst = RstSection::parse(&build_rst(&[])).unwrap();
173        assert!(rst.entries.is_empty());
174    }
175
176    #[test]
177    fn parse_single_entry() {
178        let e = entry(0x1234, 0x0001, 0xABCD, 0x4000, 4);
179        let rst = RstSection::parse(&build_rst(&[e])).unwrap();
180        assert_eq!(rst.entries.len(), 1);
181        assert_eq!(rst.entries[0], e);
182        assert_eq!(rst.entries[0].running_status, 4); // running
183    }
184
185    #[test]
186    fn parse_multiple_entries() {
187        let es = [
188            entry(0x0001, 0x1000, 0x0010, 0x0100, 1),
189            entry(0x0002, 0x2000, 0x0020, 0x0200, 4),
190            entry(0x0003, 0x3000, 0x0030, 0x0300, 5),
191        ];
192        let rst = RstSection::parse(&build_rst(&es)).unwrap();
193        assert_eq!(rst.entries, es);
194    }
195
196    #[test]
197    fn parse_rejects_wrong_tag() {
198        let mut bytes = build_rst(&[]);
199        bytes[0] = 0x72;
200        assert!(matches!(
201            RstSection::parse(&bytes).unwrap_err(),
202            Error::UnexpectedTableId { table_id: 0x72, .. }
203        ));
204    }
205
206    #[test]
207    fn parse_rejects_short_buffer() {
208        assert!(matches!(
209            RstSection::parse(&[0x71, 0x70]).unwrap_err(),
210            Error::BufferTooShort { .. }
211        ));
212    }
213
214    #[test]
215    fn parse_rejects_non_multiple_loop() {
216        // section_length = 4 (not a multiple of 9)
217        let bytes = [TABLE_ID, 0x70, 0x04, 0x00, 0x00, 0x00, 0x00];
218        assert!(matches!(
219            RstSection::parse(&bytes).unwrap_err(),
220            Error::SectionLengthOverflow { .. }
221        ));
222    }
223
224    #[test]
225    fn serialize_round_trip() {
226        let es = [
227            entry(0xCAFE, 0xBEEF, 0x1234, 0x5678, 4),
228            entry(0x0001, 0x0002, 0x0003, 0x0004, 5),
229        ];
230        let rst = RstSection::parse(&build_rst(&es)).unwrap();
231        let mut buf = vec![0u8; rst.serialized_len()];
232        rst.serialize_into(&mut buf).unwrap();
233        assert_eq!(buf, build_rst(&es));
234        assert_eq!(RstSection::parse(&buf).unwrap(), rst);
235    }
236
237    #[test]
238    fn serialize_empty_round_trip() {
239        let rst = RstSection { entries: vec![] };
240        let mut buf = vec![0u8; rst.serialized_len()];
241        rst.serialize_into(&mut buf).unwrap();
242        assert_eq!(RstSection::parse(&buf).unwrap(), rst);
243    }
244
245    #[test]
246    fn table_trait_constants() {
247        assert_eq!(TABLE_ID, 0x71);
248        assert_eq!(PID, 0x0013);
249    }
250
251    #[cfg(feature = "serde")]
252    #[test]
253    fn serde_json_serializes_fields() {
254        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
255        let rst = RstSection::parse(&build_rst(&[entry(1, 2, 3, 4, 4)])).unwrap();
256        let j = serde_json::to_string(&rst).unwrap();
257        let v: serde_json::Value = serde_json::from_str(&j).unwrap();
258        assert_eq!(v["entries"][0]["service_id"], 3);
259    }
260}