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