1use crate::error::{Error, Result};
13use dvb_common::{Parse, Serialize};
14
15pub const TABLE_ID: u8 = 0x71;
17pub const PID: u16 = 0x0013;
19
20const HEADER_LEN: usize = 3;
21const ENTRY_LEN: usize = 9;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub struct RstEntry {
32 pub transport_stream_id: u16,
34 pub original_network_id: u16,
36 pub service_id: u16,
38 pub event_id: u16,
40 pub running_status: u8,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize))]
47pub struct RstSection {
48 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 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 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); }
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 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 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}