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 let entry = &bytes[off..off + ENTRY_LEN];
91 entries.push(RstEntry {
92 transport_stream_id: u16::from_be_bytes(*entry[0..].first_chunk::<2>().unwrap()),
93 original_network_id: u16::from_be_bytes(*entry[2..].first_chunk::<2>().unwrap()),
94 service_id: u16::from_be_bytes(*entry[4..].first_chunk::<2>().unwrap()),
95 event_id: u16::from_be_bytes(*entry[6..].first_chunk::<2>().unwrap()),
96 running_status: RunningStatus::from_u8(entry[8] & 0x07),
97 });
98 off += ENTRY_LEN;
99 }
100 Ok(RstSection { entries })
101 }
102}
103
104impl Serialize for RstSection {
105 type Error = crate::error::Error;
106
107 fn serialized_len(&self) -> usize {
108 HEADER_LEN + self.entries.len() * ENTRY_LEN
109 }
110
111 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
112 let len = self.serialized_len();
113 if buf.len() < len {
114 return Err(Error::OutputBufferTooSmall {
115 need: len,
116 have: buf.len(),
117 });
118 }
119 let section_length = (len - HEADER_LEN) as u16;
120 buf[0] = TABLE_ID;
121 buf[1] = super::SECTION_B1_FLAGS_SHORT | ((section_length >> 8) as u8 & 0x0F);
124 buf[2] = (section_length & 0xFF) as u8;
125 let mut off = HEADER_LEN;
126 for e in &self.entries {
127 buf[off..off + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
128 buf[off + 2..off + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
129 buf[off + 4..off + 6].copy_from_slice(&e.service_id.to_be_bytes());
130 buf[off + 6..off + 8].copy_from_slice(&e.event_id.to_be_bytes());
131 buf[off + 8] = 0xF8 | (e.running_status.to_u8() & 0x07);
133 off += ENTRY_LEN;
134 }
135 Ok(len)
136 }
137}
138impl<'a> crate::traits::TableDef<'a> for RstSection {
139 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
140 const NAME: &'static str = "RUNNING_STATUS";
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 fn build_rst(entries: &[RstEntry]) -> Vec<u8> {
148 let section_length = (entries.len() * ENTRY_LEN) as u16;
149 let mut v = vec![
150 TABLE_ID,
151 0x70 | ((section_length >> 8) as u8 & 0x0F),
152 (section_length & 0xFF) as u8,
153 ];
154 for e in entries {
155 v.extend_from_slice(&e.transport_stream_id.to_be_bytes());
156 v.extend_from_slice(&e.original_network_id.to_be_bytes());
157 v.extend_from_slice(&e.service_id.to_be_bytes());
158 v.extend_from_slice(&e.event_id.to_be_bytes());
159 v.push(0xF8 | (e.running_status.to_u8() & 0x07));
160 }
161 v
162 }
163
164 fn entry(tsid: u16, onid: u16, sid: u16, evid: u16, rs: RunningStatus) -> RstEntry {
165 RstEntry {
166 transport_stream_id: tsid,
167 original_network_id: onid,
168 service_id: sid,
169 event_id: evid,
170 running_status: rs,
171 }
172 }
173
174 #[test]
175 fn parse_empty() {
176 let rst = RstSection::parse(&build_rst(&[])).unwrap();
177 assert!(rst.entries.is_empty());
178 }
179
180 #[test]
181 fn parse_single_entry() {
182 let e = entry(0x1234, 0x0001, 0xABCD, 0x4000, RunningStatus::Running);
183 let rst = RstSection::parse(&build_rst(&[e])).unwrap();
184 assert_eq!(rst.entries.len(), 1);
185 assert_eq!(rst.entries[0], e);
186 assert_eq!(rst.entries[0].running_status, RunningStatus::Running); }
188
189 #[test]
190 fn parse_multiple_entries() {
191 let es = [
192 entry(0x0001, 0x1000, 0x0010, 0x0100, RunningStatus::NotRunning),
193 entry(0x0002, 0x2000, 0x0020, 0x0200, RunningStatus::Running),
194 entry(0x0003, 0x3000, 0x0030, 0x0300, RunningStatus::ServiceOffAir),
195 ];
196 let rst = RstSection::parse(&build_rst(&es)).unwrap();
197 assert_eq!(rst.entries, es);
198 }
199
200 #[test]
201 fn parse_rejects_wrong_tag() {
202 let mut bytes = build_rst(&[]);
203 bytes[0] = 0x72;
204 assert!(matches!(
205 RstSection::parse(&bytes).unwrap_err(),
206 Error::UnexpectedTableId { table_id: 0x72, .. }
207 ));
208 }
209
210 #[test]
211 fn parse_rejects_short_buffer() {
212 assert!(matches!(
213 RstSection::parse(&[0x71, 0x70]).unwrap_err(),
214 Error::BufferTooShort { .. }
215 ));
216 }
217
218 #[test]
219 fn parse_rejects_non_multiple_loop() {
220 let bytes = [TABLE_ID, 0x70, 0x04, 0x00, 0x00, 0x00, 0x00];
221 assert!(matches!(
222 RstSection::parse(&bytes).unwrap_err(),
223 Error::BufferTooShort {
224 what: "RstSection entry alignment",
225 ..
226 }
227 ));
228 }
229
230 #[test]
231 fn serialize_round_trip() {
232 let es = [
233 entry(0xCAFE, 0xBEEF, 0x1234, 0x5678, RunningStatus::Running),
234 entry(0x0001, 0x0002, 0x0003, 0x0004, RunningStatus::ServiceOffAir),
235 ];
236 let rst = RstSection::parse(&build_rst(&es)).unwrap();
237 let mut buf = vec![0u8; rst.serialized_len()];
238 rst.serialize_into(&mut buf).unwrap();
239 assert_eq!(buf, build_rst(&es));
240 assert_eq!(RstSection::parse(&buf).unwrap(), rst);
241 }
242
243 #[test]
244 fn serialize_empty_round_trip() {
245 let rst = RstSection { entries: vec![] };
246 let mut buf = vec![0u8; rst.serialized_len()];
247 rst.serialize_into(&mut buf).unwrap();
248 assert_eq!(RstSection::parse(&buf).unwrap(), rst);
249 }
250
251 #[test]
252 fn table_trait_constants() {
253 assert_eq!(TABLE_ID, 0x71);
254 assert_eq!(PID, 0x0013);
255 }
256
257 #[cfg(feature = "serde")]
258 #[test]
259 fn serde_json_serializes_fields() {
260 let rst =
262 RstSection::parse(&build_rst(&[entry(1, 2, 3, 4, RunningStatus::Running)])).unwrap();
263 let j = serde_json::to_string(&rst).unwrap();
264 let v: serde_json::Value = serde_json::from_str(&j).unwrap();
265 assert_eq!(v["entries"][0]["service_id"], 3);
266 }
267}