1use super::RunningStatus;
13use crate::error::{Error, Result};
14use alloc::vec::Vec;
15use dvb_common::{Parse, Serialize};
16
17pub const TABLE_ID: u8 = 0x71;
19pub const PID: u16 = 0x0013;
21
22const HEADER_LEN: usize = 3;
23const ENTRY_LEN: usize = 9;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct RstEntry {
34 pub transport_stream_id: u16,
36 pub original_network_id: u16,
38 pub service_id: u16,
40 pub event_id: u16,
42 pub running_status: RunningStatus,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize))]
49pub struct RstSection {
50 pub entries: Vec<RstEntry>,
52}
53
54impl<'a> Parse<'a> for RstSection {
55 type Error = crate::error::Error;
56
57 fn parse(bytes: &'a [u8]) -> Result<Self> {
58 if bytes.len() < HEADER_LEN {
59 return Err(Error::BufferTooShort {
60 need: HEADER_LEN,
61 have: bytes.len(),
62 what: "RstSection",
63 });
64 }
65 if bytes[0] != TABLE_ID {
66 return Err(Error::UnexpectedTableId {
67 table_id: bytes[0],
68 what: "RstSection",
69 expected: &[TABLE_ID],
70 });
71 }
72 let section_length = ((bytes[1] & 0x0F) as usize) << 8 | bytes[2] as usize;
73 let total = HEADER_LEN + section_length;
74 if bytes.len() < total {
75 return Err(Error::SectionLengthOverflow {
76 declared: section_length,
77 available: bytes.len() - HEADER_LEN,
78 });
79 }
80 if section_length % ENTRY_LEN != 0 {
81 return Err(Error::BufferTooShort {
82 need: (section_length / ENTRY_LEN + 1) * ENTRY_LEN,
83 have: section_length,
84 what: "RstSection entry alignment",
85 });
86 }
87 let mut entries = Vec::with_capacity(section_length / ENTRY_LEN);
88 let mut off = HEADER_LEN;
89 while off + ENTRY_LEN <= total {
90 entries.push(RstEntry {
91 transport_stream_id: u16::from_be_bytes([bytes[off], bytes[off + 1]]),
92 original_network_id: u16::from_be_bytes([bytes[off + 2], bytes[off + 3]]),
93 service_id: u16::from_be_bytes([bytes[off + 4], bytes[off + 5]]),
94 event_id: u16::from_be_bytes([bytes[off + 6], bytes[off + 7]]),
95 running_status: RunningStatus::from_u8(bytes[off + 8] & 0x07),
96 });
97 off += ENTRY_LEN;
98 }
99 Ok(RstSection { entries })
100 }
101}
102
103impl Serialize for RstSection {
104 type Error = crate::error::Error;
105
106 fn serialized_len(&self) -> usize {
107 HEADER_LEN + self.entries.len() * ENTRY_LEN
108 }
109
110 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
111 let len = self.serialized_len();
112 if buf.len() < len {
113 return Err(Error::OutputBufferTooSmall {
114 need: len,
115 have: buf.len(),
116 });
117 }
118 let section_length = (len - HEADER_LEN) as u16;
119 buf[0] = TABLE_ID;
120 buf[1] = super::SECTION_B1_FLAGS_SHORT | ((section_length >> 8) as u8 & 0x0F);
123 buf[2] = (section_length & 0xFF) as u8;
124 let mut off = HEADER_LEN;
125 for e in &self.entries {
126 buf[off..off + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
127 buf[off + 2..off + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
128 buf[off + 4..off + 6].copy_from_slice(&e.service_id.to_be_bytes());
129 buf[off + 6..off + 8].copy_from_slice(&e.event_id.to_be_bytes());
130 buf[off + 8] = 0xF8 | (e.running_status.to_u8() & 0x07);
132 off += ENTRY_LEN;
133 }
134 Ok(len)
135 }
136}
137impl<'a> crate::traits::TableDef<'a> for RstSection {
138 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
139 const NAME: &'static str = "RUNNING_STATUS";
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 fn build_rst(entries: &[RstEntry]) -> Vec<u8> {
147 let section_length = (entries.len() * ENTRY_LEN) as u16;
148 let mut v = vec![
149 TABLE_ID,
150 0x70 | ((section_length >> 8) as u8 & 0x0F),
151 (section_length & 0xFF) as u8,
152 ];
153 for e in entries {
154 v.extend_from_slice(&e.transport_stream_id.to_be_bytes());
155 v.extend_from_slice(&e.original_network_id.to_be_bytes());
156 v.extend_from_slice(&e.service_id.to_be_bytes());
157 v.extend_from_slice(&e.event_id.to_be_bytes());
158 v.push(0xF8 | (e.running_status.to_u8() & 0x07));
159 }
160 v
161 }
162
163 fn entry(tsid: u16, onid: u16, sid: u16, evid: u16, rs: RunningStatus) -> RstEntry {
164 RstEntry {
165 transport_stream_id: tsid,
166 original_network_id: onid,
167 service_id: sid,
168 event_id: evid,
169 running_status: rs,
170 }
171 }
172
173 #[test]
174 fn parse_empty() {
175 let rst = RstSection::parse(&build_rst(&[])).unwrap();
176 assert!(rst.entries.is_empty());
177 }
178
179 #[test]
180 fn parse_single_entry() {
181 let e = entry(0x1234, 0x0001, 0xABCD, 0x4000, RunningStatus::Running);
182 let rst = RstSection::parse(&build_rst(&[e])).unwrap();
183 assert_eq!(rst.entries.len(), 1);
184 assert_eq!(rst.entries[0], e);
185 assert_eq!(rst.entries[0].running_status, RunningStatus::Running); }
187
188 #[test]
189 fn parse_multiple_entries() {
190 let es = [
191 entry(0x0001, 0x1000, 0x0010, 0x0100, RunningStatus::NotRunning),
192 entry(0x0002, 0x2000, 0x0020, 0x0200, RunningStatus::Running),
193 entry(0x0003, 0x3000, 0x0030, 0x0300, RunningStatus::ServiceOffAir),
194 ];
195 let rst = RstSection::parse(&build_rst(&es)).unwrap();
196 assert_eq!(rst.entries, es);
197 }
198
199 #[test]
200 fn parse_rejects_wrong_tag() {
201 let mut bytes = build_rst(&[]);
202 bytes[0] = 0x72;
203 assert!(matches!(
204 RstSection::parse(&bytes).unwrap_err(),
205 Error::UnexpectedTableId { table_id: 0x72, .. }
206 ));
207 }
208
209 #[test]
210 fn parse_rejects_short_buffer() {
211 assert!(matches!(
212 RstSection::parse(&[0x71, 0x70]).unwrap_err(),
213 Error::BufferTooShort { .. }
214 ));
215 }
216
217 #[test]
218 fn parse_rejects_non_multiple_loop() {
219 let bytes = [TABLE_ID, 0x70, 0x04, 0x00, 0x00, 0x00, 0x00];
220 assert!(matches!(
221 RstSection::parse(&bytes).unwrap_err(),
222 Error::BufferTooShort {
223 what: "RstSection entry alignment",
224 ..
225 }
226 ));
227 }
228
229 #[test]
230 fn serialize_round_trip() {
231 let es = [
232 entry(0xCAFE, 0xBEEF, 0x1234, 0x5678, RunningStatus::Running),
233 entry(0x0001, 0x0002, 0x0003, 0x0004, RunningStatus::ServiceOffAir),
234 ];
235 let rst = RstSection::parse(&build_rst(&es)).unwrap();
236 let mut buf = vec![0u8; rst.serialized_len()];
237 rst.serialize_into(&mut buf).unwrap();
238 assert_eq!(buf, build_rst(&es));
239 assert_eq!(RstSection::parse(&buf).unwrap(), rst);
240 }
241
242 #[test]
243 fn serialize_empty_round_trip() {
244 let rst = RstSection { entries: vec![] };
245 let mut buf = vec![0u8; rst.serialized_len()];
246 rst.serialize_into(&mut buf).unwrap();
247 assert_eq!(RstSection::parse(&buf).unwrap(), rst);
248 }
249
250 #[test]
251 fn table_trait_constants() {
252 assert_eq!(TABLE_ID, 0x71);
253 assert_eq!(PID, 0x0013);
254 }
255
256 #[cfg(feature = "serde")]
257 #[test]
258 fn serde_json_serializes_fields() {
259 let rst =
261 RstSection::parse(&build_rst(&[entry(1, 2, 3, 4, RunningStatus::Running)])).unwrap();
262 let j = serde_json::to_string(&rst).unwrap();
263 let v: serde_json::Value = serde_json::from_str(&j).unwrap();
264 assert_eq!(v["entries"][0]["service_id"], 3);
265 }
266}