1use crate::error::{Error, Result};
11use crate::traits::Table;
12use dvb_common::{Parse, Serialize};
13
14pub const TABLE_ID: u8 = 0x79;
16pub const PID: u16 = 0x0016;
18
19const HEADER_LEN: usize = 3;
21const EXTENSION_HEADER_LEN: usize = 6;
24const COMMON_DESC_LEN_FIELD: usize = 2;
26const CRC_LEN: usize = 4;
28const MIN_LEN: usize = HEADER_LEN + EXTENSION_HEADER_LEN + COMMON_DESC_LEN_FIELD + CRC_LEN;
30
31#[derive(Debug, Clone, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40pub struct Rnt<'a> {
41 pub context_id: u16,
43 pub version_number: u8,
45 pub current_next_indicator: bool,
47 pub section_number: u8,
49 pub last_section_number: u8,
51 pub context_id_type: u8,
54 #[cfg_attr(feature = "serde", serde(borrow))]
56 pub common_descriptors: &'a [u8],
57 #[cfg_attr(feature = "serde", serde(borrow))]
60 pub resolution_providers: &'a [u8],
61}
62
63impl<'a> Parse<'a> for Rnt<'a> {
64 type Error = crate::error::Error;
65
66 fn parse(bytes: &'a [u8]) -> Result<Self> {
67 if bytes.len() < MIN_LEN {
68 return Err(Error::BufferTooShort {
69 need: MIN_LEN,
70 have: bytes.len(),
71 what: "Rnt",
72 });
73 }
74
75 if bytes[0] != TABLE_ID {
76 return Err(Error::UnexpectedTableId {
77 table_id: bytes[0],
78 what: "Rnt",
79 expected: &[TABLE_ID],
80 });
81 }
82
83 let section_length = ((bytes[1] & 0x0F) as u16) << 8 | bytes[2] as u16;
86 let total = HEADER_LEN + section_length as usize;
87 if bytes.len() < total {
88 return Err(Error::SectionLengthOverflow {
89 declared: section_length as usize,
90 available: bytes.len() - HEADER_LEN,
91 });
92 }
93
94 let context_id = u16::from_be_bytes([bytes[3], bytes[4]]);
101 let version_number = (bytes[5] >> 1) & 0x1F;
102 let current_next_indicator = (bytes[5] & 0x01) != 0;
103 let section_number = bytes[6];
104 let last_section_number = bytes[7];
105 let context_id_type = bytes[8];
106
107 let common_desc_len_pos = HEADER_LEN + EXTENSION_HEADER_LEN;
109 let common_descriptors_length = (((bytes[common_desc_len_pos] & 0x0F) as usize) << 8)
110 | bytes[common_desc_len_pos + 1] as usize;
111
112 let common_desc_start = common_desc_len_pos + COMMON_DESC_LEN_FIELD;
113 let common_desc_end = common_desc_start + common_descriptors_length;
114
115 if common_desc_end > total - CRC_LEN {
116 return Err(Error::SectionLengthOverflow {
117 declared: common_descriptors_length,
118 available: (total - CRC_LEN).saturating_sub(common_desc_start),
119 });
120 }
121
122 let common_descriptors = &bytes[common_desc_start..common_desc_end];
123
124 let rp_start = common_desc_end;
127 let rp_end = total - CRC_LEN;
128 let resolution_providers = &bytes[rp_start..rp_end];
129
130 Ok(Rnt {
131 context_id,
132 version_number,
133 current_next_indicator,
134 section_number,
135 last_section_number,
136 context_id_type,
137 common_descriptors,
138 resolution_providers,
139 })
140 }
141}
142
143impl Serialize for Rnt<'_> {
144 type Error = crate::error::Error;
145
146 fn serialized_len(&self) -> usize {
147 HEADER_LEN
148 + EXTENSION_HEADER_LEN
149 + COMMON_DESC_LEN_FIELD
150 + self.common_descriptors.len()
151 + self.resolution_providers.len()
152 + CRC_LEN
153 }
154
155 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
156 let len = self.serialized_len();
157 if buf.len() < len {
158 return Err(Error::OutputBufferTooSmall {
159 need: len,
160 have: buf.len(),
161 });
162 }
163
164 let section_length = (len - HEADER_LEN) as u16;
166 buf[0] = TABLE_ID;
167 buf[1] = 0xB0 | ((section_length >> 8) as u8 & 0x0F);
169 buf[2] = (section_length & 0xFF) as u8;
170
171 buf[3..5].copy_from_slice(&self.context_id.to_be_bytes());
173 buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
174 buf[6] = self.section_number;
175 buf[7] = self.last_section_number;
176 buf[8] = self.context_id_type;
177
178 let cdl = self.common_descriptors.len() as u16;
180 let cdl_pos = HEADER_LEN + EXTENSION_HEADER_LEN;
181 buf[cdl_pos] = 0xF0 | ((cdl >> 8) as u8 & 0x0F);
182 buf[cdl_pos + 1] = (cdl & 0xFF) as u8;
183
184 let cd_start = cdl_pos + COMMON_DESC_LEN_FIELD;
186 let cd_end = cd_start + self.common_descriptors.len();
187 buf[cd_start..cd_end].copy_from_slice(self.common_descriptors);
188
189 let rp_end = cd_end + self.resolution_providers.len();
191 buf[cd_end..rp_end].copy_from_slice(self.resolution_providers);
192
193 let crc_pos = len - CRC_LEN;
195 let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
196 buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
197
198 Ok(len)
199 }
200}
201
202impl<'a> Table<'a> for Rnt<'a> {
203 const TABLE_ID: u8 = TABLE_ID;
204 const PID: u16 = PID;
205}
206
207impl<'a> crate::traits::TableDef<'a> for Rnt<'a> {
208 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
209 const NAME: &'static str = "RELATED_AND_NEIGHBOURING";
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[allow(clippy::too_many_arguments)]
220 fn build_rnt(
221 context_id: u16,
222 version: u8,
223 current_next: bool,
224 section_number: u8,
225 last_section_number: u8,
226 context_id_type: u8,
227 common_desc: &[u8],
228 resolution_providers: &[u8],
229 ) -> Vec<u8> {
230 let rnt = Rnt {
231 context_id,
232 version_number: version,
233 current_next_indicator: current_next,
234 section_number,
235 last_section_number,
236 context_id_type,
237 common_descriptors: common_desc,
238 resolution_providers,
239 };
240 let mut buf = vec![0u8; rnt.serialized_len()];
241 rnt.serialize_into(&mut buf).unwrap();
242 buf
243 }
244
245 #[test]
246 fn parse_happy_path() {
247 let common_desc = [0x83u8, 0x02, 0xAB, 0xCD];
252 let rp_bytes = [0xF0u8, 0x00, 0x02, b'b', b'b', 0xF0, 0x00];
253 let bytes = build_rnt(0x0042, 3, true, 0, 0, 0x01, &common_desc, &rp_bytes);
254
255 let rnt = Rnt::parse(&bytes).unwrap();
256 assert_eq!(rnt.context_id, 0x0042);
257 assert_eq!(rnt.version_number, 3);
258 assert!(rnt.current_next_indicator);
259 assert_eq!(rnt.section_number, 0);
260 assert_eq!(rnt.last_section_number, 0);
261 assert_eq!(rnt.context_id_type, 0x01);
262 assert_eq!(rnt.common_descriptors, &common_desc[..]);
263 assert_eq!(rnt.resolution_providers, &rp_bytes[..]);
264 }
265
266 #[test]
267 fn parse_no_descriptors_no_providers() {
268 let bytes = build_rnt(0x0000, 0, false, 0, 0, 0x00, &[], &[]);
269 let rnt = Rnt::parse(&bytes).unwrap();
270 assert_eq!(rnt.common_descriptors.len(), 0);
271 assert_eq!(rnt.resolution_providers.len(), 0);
272 }
273
274 #[test]
275 fn parse_rejects_wrong_table_id() {
276 let mut bytes = build_rnt(0x0001, 0, true, 0, 0, 0x00, &[], &[]);
277 bytes[0] = 0x70; let err = Rnt::parse(&bytes).unwrap_err();
279 assert!(matches!(
280 err,
281 Error::UnexpectedTableId { table_id: 0x70, .. }
282 ));
283 }
284
285 #[test]
286 fn parse_rejects_short_buffer() {
287 let err = Rnt::parse(&[0x79, 0x00]).unwrap_err();
288 assert!(matches!(err, Error::BufferTooShort { .. }));
289 }
290
291 #[test]
292 fn serialize_round_trip() {
293 let common_desc = [0x40u8, 0x03, b'R', b'N', b'T'];
294 let rp_bytes = [0xF0u8, 0x03, 0x00, 0xF0, 0x00];
297 let rnt = Rnt {
298 context_id: 0xABCD,
299 version_number: 15,
300 current_next_indicator: true,
301 section_number: 1,
302 last_section_number: 2,
303 context_id_type: 0x02,
304 common_descriptors: &common_desc,
305 resolution_providers: &rp_bytes,
306 };
307
308 let mut buf = vec![0u8; rnt.serialized_len()];
309 rnt.serialize_into(&mut buf).unwrap();
310 let parsed = Rnt::parse(&buf).unwrap();
311 assert_eq!(rnt, parsed);
312 }
313
314 #[test]
315 fn serialize_rejects_too_small_buffer() {
316 let rnt = Rnt {
317 context_id: 0x0001,
318 version_number: 0,
319 current_next_indicator: true,
320 section_number: 0,
321 last_section_number: 0,
322 context_id_type: 0x00,
323 common_descriptors: &[],
324 resolution_providers: &[],
325 };
326 let mut buf = vec![0u8; 2];
327 let err = rnt.serialize_into(&mut buf).unwrap_err();
328 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
329 }
330}