1use crate::descriptors::DescriptorLoop;
10use crate::error::{Error, Result};
11use crate::text::DvbText;
12use dvb_common::{Parse, Serialize};
13
14pub const TABLE_ID: u8 = 0x79;
16pub const PID: u16 = 0x0016;
18
19const HEADER_LEN: usize = 3;
20const EXTENSION_HEADER_LEN: usize = 6;
21const COMMON_DESC_LEN_FIELD: usize = 2;
22const CRC_LEN: usize = 4;
23const MIN_LEN: usize = HEADER_LEN + EXTENSION_HEADER_LEN + COMMON_DESC_LEN_FIELD + CRC_LEN;
24
25const RP_INFO_LEN_FIELD: usize = 2;
26const RP_NAME_LEN_FIELD: usize = 1;
27const RP_DESC_LEN_FIELD: usize = 2;
28const CA_NAME_LEN_FIELD: usize = 1;
29const CA_HEADER_LEN: usize = 2;
30
31const RESERVED_NIBBLE: u8 = 0xF0;
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize))]
36pub struct CridAuthority<'a> {
37 pub name: DvbText<'a>,
39 pub crid_authority_policy: u8,
42 pub descriptors: DescriptorLoop<'a>,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize))]
49pub struct ResolutionProvider<'a> {
50 pub name: DvbText<'a>,
52 pub descriptors: DescriptorLoop<'a>,
54 pub crid_authorities: Vec<CridAuthority<'a>>,
56}
57
58fn crid_authority_serialized_len(ca: &CridAuthority) -> usize {
59 CA_NAME_LEN_FIELD + ca.name.len() + CA_HEADER_LEN + ca.descriptors.len()
60}
61
62fn resolution_provider_serialized_len(rp: &ResolutionProvider) -> usize {
63 RP_NAME_LEN_FIELD
64 + rp.name.len()
65 + RP_DESC_LEN_FIELD
66 + rp.descriptors.len()
67 + rp.crid_authorities
68 .iter()
69 .map(crid_authority_serialized_len)
70 .sum::<usize>()
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize))]
80#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
81pub struct RntSection<'a> {
82 pub context_id: u16,
84 pub version_number: u8,
86 pub current_next_indicator: bool,
88 pub section_number: u8,
90 pub last_section_number: u8,
92 pub context_id_type: u8,
94 pub common_descriptors: DescriptorLoop<'a>,
97 pub resolution_providers: Vec<ResolutionProvider<'a>>,
99}
100
101impl<'a> Parse<'a> for RntSection<'a> {
102 type Error = crate::error::Error;
103
104 fn parse(bytes: &'a [u8]) -> Result<Self> {
105 if bytes.len() < MIN_LEN {
106 return Err(Error::BufferTooShort {
107 need: MIN_LEN,
108 have: bytes.len(),
109 what: "RntSection",
110 });
111 }
112 if bytes[0] != TABLE_ID {
113 return Err(Error::UnexpectedTableId {
114 table_id: bytes[0],
115 what: "RntSection",
116 expected: &[TABLE_ID],
117 });
118 }
119
120 let section_length = ((bytes[1] & 0x0F) as u16) << 8 | bytes[2] as u16;
121 let total =
122 super::check_section_length(bytes.len(), HEADER_LEN, section_length as usize, MIN_LEN)?;
123
124 let context_id = u16::from_be_bytes([bytes[3], bytes[4]]);
125 let version_number = (bytes[5] >> 1) & 0x1F;
126 let current_next_indicator = (bytes[5] & 0x01) != 0;
127 let section_number = bytes[6];
128 let last_section_number = bytes[7];
129 let context_id_type = bytes[8];
130
131 let common_desc_len_pos = HEADER_LEN + EXTENSION_HEADER_LEN;
132 let common_descriptors_length = (((bytes[common_desc_len_pos] & 0x0F) as usize) << 8)
133 | bytes[common_desc_len_pos + 1] as usize;
134 let common_desc_start = common_desc_len_pos + COMMON_DESC_LEN_FIELD;
135 let common_desc_end = common_desc_start + common_descriptors_length;
136 if common_desc_end > total - CRC_LEN {
137 return Err(Error::SectionLengthOverflow {
138 declared: common_descriptors_length,
139 available: (total - CRC_LEN).saturating_sub(common_desc_start),
140 });
141 }
142 let common_descriptors = DescriptorLoop::new(&bytes[common_desc_start..common_desc_end]);
143
144 let payload_end = total - CRC_LEN;
145 let mut pos = common_desc_end;
146 let mut resolution_providers = Vec::new();
147
148 while pos < payload_end {
149 if pos + RP_INFO_LEN_FIELD > payload_end {
150 return Err(Error::BufferTooShort {
151 need: pos + RP_INFO_LEN_FIELD,
152 have: payload_end,
153 what: "RntSection resolution_provider_info_length",
154 });
155 }
156 let rp_info_length = (((bytes[pos] & 0x0F) as usize) << 8) | bytes[pos + 1] as usize;
157 pos += RP_INFO_LEN_FIELD;
158 let rp_end = pos + rp_info_length;
159 if rp_end > payload_end {
160 return Err(Error::SectionLengthOverflow {
161 declared: rp_info_length,
162 available: payload_end.saturating_sub(pos),
163 });
164 }
165
166 if pos + RP_NAME_LEN_FIELD > rp_end {
167 return Err(Error::BufferTooShort {
168 need: pos + RP_NAME_LEN_FIELD,
169 have: rp_end,
170 what: "RntSection resolution_provider_name_length",
171 });
172 }
173 let name_len = bytes[pos] as usize;
174 pos += RP_NAME_LEN_FIELD;
175 if pos + name_len > rp_end {
176 return Err(Error::BufferTooShort {
177 need: pos + name_len,
178 have: rp_end,
179 what: "RntSection resolution_provider_name",
180 });
181 }
182 let name = DvbText::new(&bytes[pos..pos + name_len]);
183 pos += name_len;
184
185 if pos + RP_DESC_LEN_FIELD > rp_end {
186 return Err(Error::BufferTooShort {
187 need: pos + RP_DESC_LEN_FIELD,
188 have: rp_end,
189 what: "RntSection resolution_provider_descriptors_length",
190 });
191 }
192 let rp_desc_len = (((bytes[pos] & 0x0F) as usize) << 8) | bytes[pos + 1] as usize;
193 pos += RP_DESC_LEN_FIELD;
194 let rp_desc_start = pos;
195 let rp_desc_end = rp_desc_start + rp_desc_len;
196 if rp_desc_end > rp_end {
197 return Err(Error::SectionLengthOverflow {
198 declared: rp_desc_len,
199 available: rp_end.saturating_sub(rp_desc_start),
200 });
201 }
202 let descriptors = DescriptorLoop::new(&bytes[rp_desc_start..rp_desc_end]);
203 pos = rp_desc_end;
204
205 let mut crid_authorities = Vec::new();
206 while pos < rp_end {
207 if pos + CA_NAME_LEN_FIELD > rp_end {
208 return Err(Error::BufferTooShort {
209 need: pos + CA_NAME_LEN_FIELD,
210 have: rp_end,
211 what: "RntSection CRID_authority_name_length",
212 });
213 }
214 let ca_name_len = bytes[pos] as usize;
215 pos += CA_NAME_LEN_FIELD;
216 if pos + ca_name_len > rp_end {
217 return Err(Error::BufferTooShort {
218 need: pos + ca_name_len,
219 have: rp_end,
220 what: "RntSection CRID_authority_name",
221 });
222 }
223 let ca_name = DvbText::new(&bytes[pos..pos + ca_name_len]);
224 pos += ca_name_len;
225
226 if pos + CA_HEADER_LEN > rp_end {
227 return Err(Error::BufferTooShort {
228 need: pos + CA_HEADER_LEN,
229 have: rp_end,
230 what: "RntSection CRID_authority header",
231 });
232 }
233 let ca_packed = bytes[pos];
234 let crid_authority_policy = (ca_packed >> 4) & 0x03;
235 let ca_desc_len = (((ca_packed & 0x0F) as usize) << 8) | bytes[pos + 1] as usize;
236 pos += CA_HEADER_LEN;
237 let ca_desc_start = pos;
238 let ca_desc_end = ca_desc_start + ca_desc_len;
239 if ca_desc_end > rp_end {
240 return Err(Error::SectionLengthOverflow {
241 declared: ca_desc_len,
242 available: rp_end.saturating_sub(ca_desc_start),
243 });
244 }
245 let ca_descriptors = DescriptorLoop::new(&bytes[ca_desc_start..ca_desc_end]);
246 pos = ca_desc_end;
247
248 crid_authorities.push(CridAuthority {
249 name: ca_name,
250 crid_authority_policy,
251 descriptors: ca_descriptors,
252 });
253 }
254
255 resolution_providers.push(ResolutionProvider {
256 name,
257 descriptors,
258 crid_authorities,
259 });
260 pos = rp_end;
261 }
262
263 Ok(RntSection {
264 context_id,
265 version_number,
266 current_next_indicator,
267 section_number,
268 last_section_number,
269 context_id_type,
270 common_descriptors,
271 resolution_providers,
272 })
273 }
274}
275
276impl Serialize for RntSection<'_> {
277 type Error = crate::error::Error;
278
279 fn serialized_len(&self) -> usize {
280 HEADER_LEN
281 + EXTENSION_HEADER_LEN
282 + COMMON_DESC_LEN_FIELD
283 + self.common_descriptors.len()
284 + self
285 .resolution_providers
286 .iter()
287 .map(|rp| RP_INFO_LEN_FIELD + resolution_provider_serialized_len(rp))
288 .sum::<usize>()
289 + CRC_LEN
290 }
291
292 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
293 let len = self.serialized_len();
294 if buf.len() < len {
295 return Err(Error::OutputBufferTooSmall {
296 need: len,
297 have: buf.len(),
298 });
299 }
300
301 let section_length = (len - HEADER_LEN) as u16;
302 if section_length > 0x0FFF {
303 return Err(Error::SectionLengthOverflow {
304 declared: section_length as usize,
305 available: 0x0FFF,
306 });
307 }
308 buf[0] = TABLE_ID;
309 buf[1] = super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F);
310 buf[2] = (section_length & 0xFF) as u8;
311
312 buf[3..5].copy_from_slice(&self.context_id.to_be_bytes());
313 buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
314 buf[6] = self.section_number;
315 buf[7] = self.last_section_number;
316 buf[8] = self.context_id_type;
317
318 let cdl = self.common_descriptors.len() as u16;
319 let cdl_pos = HEADER_LEN + EXTENSION_HEADER_LEN;
320 buf[cdl_pos] = RESERVED_NIBBLE | ((cdl >> 8) as u8 & 0x0F);
321 buf[cdl_pos + 1] = (cdl & 0xFF) as u8;
322
323 let cd_start = cdl_pos + COMMON_DESC_LEN_FIELD;
324 let cd_end = cd_start + self.common_descriptors.len();
325 buf[cd_start..cd_end].copy_from_slice(self.common_descriptors.raw());
326
327 let mut pos = cd_end;
328 for rp in &self.resolution_providers {
329 let rp_body_len = resolution_provider_serialized_len(rp);
330 let rp_info_length = rp_body_len as u16;
331 buf[pos] = RESERVED_NIBBLE | ((rp_info_length >> 8) as u8 & 0x0F);
332 buf[pos + 1] = (rp_info_length & 0xFF) as u8;
333 pos += RP_INFO_LEN_FIELD;
334
335 if rp.name.len() > u8::MAX as usize {
336 return Err(Error::ValueOutOfRange {
337 field: "resolution_provider_name_length",
338 reason: "exceeds 255 bytes",
339 });
340 }
341 buf[pos] = rp.name.len() as u8;
342 pos += RP_NAME_LEN_FIELD;
343 buf[pos..pos + rp.name.len()].copy_from_slice(rp.name.raw());
344 pos += rp.name.len();
345
346 let rdl = rp.descriptors.len() as u16;
347 buf[pos] = RESERVED_NIBBLE | ((rdl >> 8) as u8 & 0x0F);
348 buf[pos + 1] = (rdl & 0xFF) as u8;
349 pos += RP_DESC_LEN_FIELD;
350 buf[pos..pos + rp.descriptors.len()].copy_from_slice(rp.descriptors.raw());
351 pos += rp.descriptors.len();
352
353 for ca in &rp.crid_authorities {
354 if ca.name.len() > u8::MAX as usize {
355 return Err(Error::ValueOutOfRange {
356 field: "crid_authority_name_length",
357 reason: "exceeds 255 bytes",
358 });
359 }
360 buf[pos] = ca.name.len() as u8;
361 pos += CA_NAME_LEN_FIELD;
362 buf[pos..pos + ca.name.len()].copy_from_slice(ca.name.raw());
363 pos += ca.name.len();
364
365 let adl = ca.descriptors.len() as u16;
366 buf[pos] =
367 0xC0 | ((ca.crid_authority_policy & 0x03) << 4) | ((adl >> 8) as u8 & 0x0F);
368 buf[pos + 1] = (adl & 0xFF) as u8;
369 pos += CA_HEADER_LEN;
370 buf[pos..pos + ca.descriptors.len()].copy_from_slice(ca.descriptors.raw());
371 pos += ca.descriptors.len();
372 }
373 }
374
375 let crc_pos = len - CRC_LEN;
376 let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
377 buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
378 Ok(len)
379 }
380}
381impl<'a> crate::traits::TableDef<'a> for RntSection<'a> {
382 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
383 const NAME: &'static str = "RESOLUTION_PROVIDER_NOTIFICATION";
384}
385
386#[cfg(test)]
387mod tests {
388 use super::*;
389
390 #[test]
391 fn parse_happy_path() {
392 let common_desc = [0x83u8, 0x02, 0xAB, 0xCD];
393 let rp = ResolutionProvider {
394 name: DvbText::new(b"bb"),
395 descriptors: DescriptorLoop::new(&[]),
396 crid_authorities: vec![CridAuthority {
397 name: DvbText::new(b"au"),
398 crid_authority_policy: 1,
399 descriptors: DescriptorLoop::new(&[]),
400 }],
401 };
402 let rnt = RntSection {
403 context_id: 0x0042,
404 version_number: 3,
405 current_next_indicator: true,
406 section_number: 0,
407 last_section_number: 0,
408 context_id_type: 0x01,
409 common_descriptors: DescriptorLoop::new(&common_desc),
410 resolution_providers: vec![rp],
411 };
412 let mut buf = vec![0u8; rnt.serialized_len()];
413 rnt.serialize_into(&mut buf).unwrap();
414 let parsed = RntSection::parse(&buf).unwrap();
415 assert_eq!(parsed.context_id, 0x0042);
416 assert_eq!(parsed.version_number, 3);
417 assert!(parsed.current_next_indicator);
418 assert_eq!(parsed.context_id_type, 0x01);
419 assert_eq!(parsed.resolution_providers.len(), 1);
420 assert_eq!(parsed.resolution_providers[0].name.raw(), b"bb");
421 assert_eq!(parsed.resolution_providers[0].crid_authorities.len(), 1);
422 assert_eq!(
423 parsed.resolution_providers[0].crid_authorities[0].crid_authority_policy,
424 1
425 );
426 assert_eq!(
427 parsed.resolution_providers[0].crid_authorities[0]
428 .name
429 .raw(),
430 b"au"
431 );
432 }
433
434 #[test]
435 fn parse_no_descriptors_no_providers() {
436 let rnt = RntSection {
437 context_id: 0x0000,
438 version_number: 0,
439 current_next_indicator: false,
440 section_number: 0,
441 last_section_number: 0,
442 context_id_type: 0x00,
443 common_descriptors: DescriptorLoop::new(&[]),
444 resolution_providers: Vec::new(),
445 };
446 let mut buf = vec![0u8; rnt.serialized_len()];
447 rnt.serialize_into(&mut buf).unwrap();
448 let parsed = RntSection::parse(&buf).unwrap();
449 assert_eq!(parsed.common_descriptors.len(), 0);
450 assert!(parsed.resolution_providers.is_empty());
451 }
452
453 #[test]
454 fn byte_exact_round_trip() {
455 let rp = ResolutionProvider {
456 name: DvbText::new(b"provider"),
457 descriptors: DescriptorLoop::new(&[0x40, 0x03, b'R', b'N', b'T']),
458 crid_authorities: vec![
459 CridAuthority {
460 name: DvbText::new(b"auth1"),
461 crid_authority_policy: 0,
462 descriptors: DescriptorLoop::new(&[]),
463 },
464 CridAuthority {
465 name: DvbText::new(b"auth2"),
466 crid_authority_policy: 2,
467 descriptors: DescriptorLoop::new(&[0x42, 0x00]),
468 },
469 ],
470 };
471 let rnt = RntSection {
472 context_id: 0xABCD,
473 version_number: 15,
474 current_next_indicator: true,
475 section_number: 1,
476 last_section_number: 2,
477 context_id_type: 0x02,
478 common_descriptors: DescriptorLoop::new(&[0x40, 0x03, b'R', b'N', b'T']),
479 resolution_providers: vec![rp],
480 };
481 let mut buf = vec![0u8; rnt.serialized_len()];
482 rnt.serialize_into(&mut buf).unwrap();
483 let re = RntSection::parse(&buf).unwrap();
484 let mut buf2 = vec![0u8; re.serialized_len()];
485 re.serialize_into(&mut buf2).unwrap();
486 assert_eq!(buf, buf2, "byte-exact re-serialize");
487 let re = RntSection::parse(&buf).unwrap();
488 assert_eq!(re.resolution_providers.len(), 1);
489 assert_eq!(re.resolution_providers[0].name.raw(), b"provider");
490 assert_eq!(re.resolution_providers[0].crid_authorities.len(), 2);
491 assert_eq!(
492 re.resolution_providers[0].crid_authorities[1].crid_authority_policy,
493 2
494 );
495 }
496
497 #[test]
498 fn parse_rejects_wrong_table_id() {
499 let rnt = RntSection {
500 context_id: 0x0001,
501 version_number: 0,
502 current_next_indicator: true,
503 section_number: 0,
504 last_section_number: 0,
505 context_id_type: 0x00,
506 common_descriptors: DescriptorLoop::new(&[]),
507 resolution_providers: Vec::new(),
508 };
509 let mut buf = vec![0u8; rnt.serialized_len()];
510 rnt.serialize_into(&mut buf).unwrap();
511 buf[0] = 0x70;
512 assert!(matches!(
513 RntSection::parse(&buf).unwrap_err(),
514 Error::UnexpectedTableId { table_id: 0x70, .. }
515 ));
516 }
517
518 #[test]
519 fn parse_rejects_short_buffer() {
520 assert!(matches!(
521 RntSection::parse(&[0x79, 0x00]).unwrap_err(),
522 Error::BufferTooShort { .. }
523 ));
524 }
525
526 #[test]
527 fn serialize_rejects_too_small_buffer() {
528 let rnt = RntSection {
529 context_id: 0x0001,
530 version_number: 0,
531 current_next_indicator: true,
532 section_number: 0,
533 last_section_number: 0,
534 context_id_type: 0x00,
535 common_descriptors: DescriptorLoop::new(&[]),
536 resolution_providers: Vec::new(),
537 };
538 let mut buf = vec![0u8; 2];
539 assert!(matches!(
540 rnt.serialize_into(&mut buf).unwrap_err(),
541 Error::OutputBufferTooSmall { .. }
542 ));
543 }
544
545 #[test]
546 fn parse_rejects_zero_section_length() {
547 let mut buf = vec![0u8; 64];
548 buf[0] = TABLE_ID;
549 buf[1] = 0xF0;
550 buf[2] = 0x00;
551 for b in &mut buf[3..] {
552 *b = 0xFF;
553 }
554 assert!(matches!(
555 RntSection::parse(&buf).unwrap_err(),
556 Error::SectionLengthOverflow { .. }
557 ));
558 }
559
560 #[test]
561 fn parse_handwritten_rnt_no_providers() {
562 let mut bytes: Vec<u8> = vec![
563 0x79, 0xF0, 0x0C, 0x00, 0x42, 0xC7, 0x00, 0x00, 0x01, 0xF0, 0x00,
564 ];
565 let crc = dvb_common::crc32_mpeg2::compute(&bytes);
566 bytes.extend_from_slice(&crc.to_be_bytes());
567 let rnt = RntSection::parse(&bytes).unwrap();
568 assert_eq!(rnt.context_id, 0x0042);
569 assert_eq!(rnt.version_number, 3);
570 assert!(rnt.current_next_indicator);
571 assert!(rnt.resolution_providers.is_empty());
572 }
573}