1use crate::error::{Error, Result};
36use alloc::vec::Vec;
37use dvb_common::{Parse, Serialize};
38
39pub(crate) const COMPAT_DESC_LEN_FIELD: usize = 2;
40const DESC_COUNT_FIELD: usize = 2;
41const DESC_HEADER_LEN: usize = 2;
42const DESC_FIXED_LEN: usize = 9;
43const SUB_DESC_HEADER_LEN: usize = 2;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize))]
48#[non_exhaustive]
49pub enum DescriptorType {
50 Pad,
52 SystemHardware,
54 SystemSoftware,
56 IsoReserved(u8),
58 DvbReserved(u8),
60 UserDefined(u8),
62}
63
64impl DescriptorType {
65 #[must_use]
66 pub fn from_u8(v: u8) -> Self {
68 match v {
69 0x00 => Self::Pad,
70 0x01 => Self::SystemHardware,
71 0x02 => Self::SystemSoftware,
72 v @ 0x03..0x40 => Self::IsoReserved(v),
73 v @ 0x40..0x80 => Self::DvbReserved(v),
74 v => Self::UserDefined(v),
75 }
76 }
77
78 #[must_use]
79 pub fn to_u8(self) -> u8 {
81 match self {
82 Self::Pad => 0x00,
83 Self::SystemHardware => 0x01,
84 Self::SystemSoftware => 0x02,
85 Self::IsoReserved(v) | Self::DvbReserved(v) | Self::UserDefined(v) => v,
86 }
87 }
88
89 #[must_use]
90 pub fn name(self) -> &'static str {
92 match self {
93 Self::Pad => "Pad",
94 Self::SystemHardware => "System Hardware",
95 Self::SystemSoftware => "System Software",
96 Self::IsoReserved(_) => "ISO Reserved",
97 Self::DvbReserved(_) => "DVB Reserved",
98 Self::UserDefined(_) => "User Defined",
99 }
100 }
101}
102dvb_common::impl_spec_display!(DescriptorType, IsoReserved, DvbReserved, UserDefined);
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106#[cfg_attr(feature = "serde", derive(serde::Serialize))]
107#[non_exhaustive]
108pub enum SpecifierType {
109 IeeeOui,
111 Unallocated(u8),
113}
114
115impl SpecifierType {
116 #[must_use]
117 pub fn from_u8(v: u8) -> Self {
119 match v {
120 0x01 => Self::IeeeOui,
121 v => Self::Unallocated(v),
122 }
123 }
124
125 #[must_use]
126 pub fn to_u8(self) -> u8 {
128 match self {
129 Self::IeeeOui => 0x01,
130 Self::Unallocated(v) => v,
131 }
132 }
133
134 #[must_use]
135 pub fn name(self) -> &'static str {
137 match self {
138 Self::IeeeOui => "IEEE OUI",
139 Self::Unallocated(_) => "Unallocated",
140 }
141 }
142}
143dvb_common::impl_spec_display!(SpecifierType, Unallocated);
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147#[cfg_attr(feature = "serde", derive(serde::Serialize))]
148#[non_exhaustive]
149pub enum SubDescriptorType {
150 Unallocated(u8),
152}
153
154impl SubDescriptorType {
155 #[must_use]
156 pub fn from_u8(v: u8) -> Self {
158 Self::Unallocated(v)
159 }
160
161 #[must_use]
162 pub fn to_u8(self) -> u8 {
164 match self {
165 Self::Unallocated(v) => v,
166 }
167 }
168
169 #[must_use]
170 pub fn name(self) -> &'static str {
172 match self {
173 Self::Unallocated(_) => "Unallocated",
174 }
175 }
176}
177dvb_common::impl_spec_display!(SubDescriptorType, Unallocated);
178
179#[derive(Debug, Clone, PartialEq, Eq)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize))]
189pub struct CompatibilityDescriptor<'a> {
190 pub descriptors: Vec<CompatibilityDescriptorEntry<'a>>,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize))]
202pub struct CompatibilityDescriptorEntry<'a> {
203 pub descriptor_type: DescriptorType,
205 pub specifier_type: SpecifierType,
207 pub specifier_data: [u8; 3],
209 pub model: u16,
211 pub version: u16,
213 pub sub_descriptors: Vec<SubDescriptor<'a>>,
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize))]
220pub struct SubDescriptor<'a> {
221 pub sub_descriptor_type: SubDescriptorType,
223 #[cfg_attr(feature = "serde", serde(borrow))]
225 pub data: &'a [u8],
226}
227
228fn entry_serialized_len(entry: &CompatibilityDescriptorEntry) -> usize {
229 DESC_HEADER_LEN
230 + DESC_FIXED_LEN
231 + entry
232 .sub_descriptors
233 .iter()
234 .map(|sd| SUB_DESC_HEADER_LEN + sd.data.len())
235 .sum::<usize>()
236}
237
238impl<'a> Parse<'a> for CompatibilityDescriptor<'a> {
239 type Error = Error;
240
241 fn parse(bytes: &'a [u8]) -> Result<Self> {
242 if bytes.len() < COMPAT_DESC_LEN_FIELD {
243 return Err(Error::BufferTooShort {
244 need: COMPAT_DESC_LEN_FIELD,
245 have: bytes.len(),
246 what: "CompatibilityDescriptor length field",
247 });
248 }
249 let (b2, _) = bytes
250 .split_first_chunk::<2>()
251 .ok_or(Error::BufferTooShort {
252 need: COMPAT_DESC_LEN_FIELD,
253 have: bytes.len(),
254 what: "CompatibilityDescriptor length field",
255 })?;
256 let compat_desc_len = u16::from_be_bytes(*b2) as usize;
257 let body_end = COMPAT_DESC_LEN_FIELD + compat_desc_len;
258 if body_end > bytes.len() {
259 return Err(Error::SectionLengthOverflow {
260 declared: compat_desc_len,
261 available: bytes.len() - COMPAT_DESC_LEN_FIELD,
262 });
263 }
264 if compat_desc_len == 0 {
265 return Ok(CompatibilityDescriptor {
266 descriptors: Vec::new(),
267 });
268 }
269 if compat_desc_len < DESC_COUNT_FIELD {
270 return Err(Error::BufferTooShort {
271 need: COMPAT_DESC_LEN_FIELD + DESC_COUNT_FIELD,
272 have: bytes.len(),
273 what: "CompatibilityDescriptor descriptorCount",
274 });
275 }
276 let body = &bytes[COMPAT_DESC_LEN_FIELD..body_end];
277 let descriptor_count = u16::from_be_bytes(*body.first_chunk::<2>().unwrap()) as usize;
278 let mut pos = DESC_COUNT_FIELD;
279 let max_entries = (body.len() - DESC_COUNT_FIELD) / (DESC_HEADER_LEN + DESC_FIXED_LEN);
280 let mut descriptors = Vec::with_capacity(descriptor_count.min(max_entries));
281 for _ in 0..descriptor_count {
282 if pos + DESC_HEADER_LEN > body.len() {
283 return Err(Error::BufferTooShort {
284 need: COMPAT_DESC_LEN_FIELD + pos + DESC_HEADER_LEN,
285 have: COMPAT_DESC_LEN_FIELD + body.len(),
286 what: "CompatibilityDescriptor entry header",
287 });
288 }
289 let descriptor_type = DescriptorType::from_u8(body[pos]);
290 let descriptor_length = body[pos + 1] as usize;
291 let entry_end = pos + DESC_HEADER_LEN + descriptor_length;
292 if entry_end > body.len() {
293 return Err(Error::SectionLengthOverflow {
294 declared: descriptor_length,
295 available: body.len() - pos - DESC_HEADER_LEN,
296 });
297 }
298 if descriptor_length < DESC_FIXED_LEN {
299 return Err(Error::InvalidDescriptor {
300 tag: descriptor_type.to_u8(),
301 reason: "descriptorLength shorter than fixed fields",
302 });
303 }
304 let specifier_type = SpecifierType::from_u8(body[pos + DESC_HEADER_LEN]);
305 let specifier_data = [
306 body[pos + DESC_HEADER_LEN + 1],
307 body[pos + DESC_HEADER_LEN + 2],
308 body[pos + DESC_HEADER_LEN + 3],
309 ];
310 let model = u16::from_be_bytes(
311 *body[pos + DESC_HEADER_LEN + 4..]
312 .first_chunk::<2>()
313 .unwrap(),
314 );
315 let version = u16::from_be_bytes(
316 *body[pos + DESC_HEADER_LEN + 6..]
317 .first_chunk::<2>()
318 .unwrap(),
319 );
320 let sub_descriptor_count = body[pos + DESC_HEADER_LEN + 8] as usize;
321 let sub_desc_start = pos + DESC_HEADER_LEN + DESC_FIXED_LEN;
322 let sub_desc_end = entry_end;
323 let sub_desc_region_len = sub_desc_end.saturating_sub(sub_desc_start);
324 let mut sub_descriptors = Vec::with_capacity(
325 sub_descriptor_count.min(sub_desc_region_len / SUB_DESC_HEADER_LEN),
326 );
327 let mut sub_pos = sub_desc_start;
328 for _ in 0..sub_descriptor_count {
329 if sub_pos + SUB_DESC_HEADER_LEN > sub_desc_end {
330 return Err(Error::BufferTooShort {
331 need: COMPAT_DESC_LEN_FIELD + sub_pos + SUB_DESC_HEADER_LEN,
332 have: COMPAT_DESC_LEN_FIELD + sub_desc_end,
333 what: "CompatibilityDescriptor subDescriptor header",
334 });
335 }
336 let sub_descriptor_type = SubDescriptorType::from_u8(body[sub_pos]);
337 let sub_descriptor_length = body[sub_pos + 1] as usize;
338 sub_pos += SUB_DESC_HEADER_LEN;
339 if sub_pos + sub_descriptor_length > sub_desc_end {
340 return Err(Error::SectionLengthOverflow {
341 declared: sub_descriptor_length,
342 available: sub_desc_end - sub_pos,
343 });
344 }
345 sub_descriptors.push(SubDescriptor {
346 sub_descriptor_type,
347 data: &body[sub_pos..sub_pos + sub_descriptor_length],
348 });
349 sub_pos += sub_descriptor_length;
350 }
351 pos = entry_end;
352 descriptors.push(CompatibilityDescriptorEntry {
353 descriptor_type,
354 specifier_type,
355 specifier_data,
356 model,
357 version,
358 sub_descriptors,
359 });
360 }
361 if pos != body.len() {
364 return Err(Error::InvalidDescriptor {
365 tag: 0,
366 reason: "trailing bytes after compatibility descriptor entries",
367 });
368 }
369 Ok(CompatibilityDescriptor { descriptors })
370 }
371}
372
373impl Serialize for CompatibilityDescriptor<'_> {
374 type Error = Error;
375
376 fn serialized_len(&self) -> usize {
377 if self.descriptors.is_empty() {
378 return COMPAT_DESC_LEN_FIELD;
379 }
380 COMPAT_DESC_LEN_FIELD
381 + DESC_COUNT_FIELD
382 + self
383 .descriptors
384 .iter()
385 .map(entry_serialized_len)
386 .sum::<usize>()
387 }
388
389 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
390 let len = self.serialized_len();
391 if buf.len() < len {
392 return Err(Error::OutputBufferTooSmall {
393 need: len,
394 have: buf.len(),
395 });
396 }
397 if self.descriptors.is_empty() {
398 buf[0] = 0x00;
399 buf[1] = 0x00;
400 return Ok(COMPAT_DESC_LEN_FIELD);
401 }
402 let body_len = len - COMPAT_DESC_LEN_FIELD;
403 if body_len > u16::MAX as usize {
404 return Err(Error::SectionLengthOverflow {
405 declared: body_len,
406 available: u16::MAX as usize,
407 });
408 }
409 if self.descriptors.len() > u16::MAX as usize {
410 return Err(Error::SectionLengthOverflow {
411 declared: self.descriptors.len(),
412 available: u16::MAX as usize,
413 });
414 }
415 buf[..COMPAT_DESC_LEN_FIELD].copy_from_slice(&(body_len as u16).to_be_bytes());
416 buf[COMPAT_DESC_LEN_FIELD..COMPAT_DESC_LEN_FIELD + DESC_COUNT_FIELD]
417 .copy_from_slice(&(self.descriptors.len() as u16).to_be_bytes());
418 let mut pos = COMPAT_DESC_LEN_FIELD + DESC_COUNT_FIELD;
419 for entry in &self.descriptors {
420 let entry_body_len = entry_serialized_len(entry) - DESC_HEADER_LEN;
421 if entry_body_len > u8::MAX as usize {
422 return Err(Error::SectionLengthOverflow {
423 declared: entry_body_len,
424 available: u8::MAX as usize,
425 });
426 }
427 buf[pos] = entry.descriptor_type.to_u8();
428 buf[pos + 1] = entry_body_len as u8;
429 buf[pos + DESC_HEADER_LEN] = entry.specifier_type.to_u8();
430 buf[pos + DESC_HEADER_LEN + 1..pos + DESC_HEADER_LEN + 4]
431 .copy_from_slice(&entry.specifier_data);
432 buf[pos + DESC_HEADER_LEN + 4..pos + DESC_HEADER_LEN + 6]
433 .copy_from_slice(&entry.model.to_be_bytes());
434 buf[pos + DESC_HEADER_LEN + 6..pos + DESC_HEADER_LEN + 8]
435 .copy_from_slice(&entry.version.to_be_bytes());
436 if entry.sub_descriptors.len() > u8::MAX as usize {
437 return Err(Error::SectionLengthOverflow {
438 declared: entry.sub_descriptors.len(),
439 available: u8::MAX as usize,
440 });
441 }
442 buf[pos + DESC_HEADER_LEN + 8] = entry.sub_descriptors.len() as u8;
443 pos += DESC_HEADER_LEN + DESC_FIXED_LEN;
444 for sd in &entry.sub_descriptors {
445 buf[pos] = sd.sub_descriptor_type.to_u8();
446 if sd.data.len() > u8::MAX as usize {
447 return Err(Error::SectionLengthOverflow {
448 declared: sd.data.len(),
449 available: u8::MAX as usize,
450 });
451 }
452 buf[pos + 1] = sd.data.len() as u8;
453 pos += SUB_DESC_HEADER_LEN;
454 buf[pos..pos + sd.data.len()].copy_from_slice(sd.data);
455 pos += sd.data.len();
456 }
457 }
458 Ok(len)
459 }
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465
466 #[test]
467 fn empty_round_trip() {
468 let cd = CompatibilityDescriptor {
469 descriptors: vec![],
470 };
471 let mut buf = vec![0u8; cd.serialized_len()];
472 cd.serialize_into(&mut buf).unwrap();
473 assert_eq!(buf, &[0x00, 0x00]);
474 let re = CompatibilityDescriptor::parse(&buf).unwrap();
475 assert!(re.descriptors.is_empty());
476 }
477
478 #[test]
479 fn empty_with_count_parses_to_empty() {
480 let bytes: &[u8] = &[0x00, 0x02, 0x00, 0x00];
481 let cd = CompatibilityDescriptor::parse(bytes).unwrap();
482 assert!(cd.descriptors.is_empty());
483 let mut buf = vec![0u8; cd.serialized_len()];
484 cd.serialize_into(&mut buf).unwrap();
485 assert_eq!(buf, &[0x00, 0x00]);
486 }
487
488 #[test]
492 fn hand_built_byte_anchor() {
493 let bytes: &[u8] = &[
497 0x00, 0x11, 0x00, 0x01, 0x01, 0x0D, 0x01, 0x00, 0x15, 0x0A, 0x12, 0x34, 0x00, 0x01,
498 0x01, 0x05, 0x02, 0xAA, 0xBB,
499 ];
500 let cd = CompatibilityDescriptor::parse(bytes).unwrap();
501 assert_eq!(cd.descriptors.len(), 1);
502 let e = &cd.descriptors[0];
503 assert_eq!(e.descriptor_type, DescriptorType::SystemHardware);
504 assert_eq!(e.specifier_type, SpecifierType::IeeeOui);
505 assert_eq!(e.specifier_data, [0x00, 0x15, 0x0A]);
506 assert_eq!(e.model, 0x1234);
507 assert_eq!(e.version, 0x0001);
508 assert_eq!(e.sub_descriptors.len(), 1);
509 assert_eq!(
510 e.sub_descriptors[0].sub_descriptor_type,
511 SubDescriptorType::Unallocated(0x05)
512 );
513 assert_eq!(e.sub_descriptors[0].data, &[0xAA, 0xBB]);
514 let mut buf = vec![0u8; cd.serialized_len()];
516 cd.serialize_into(&mut buf).unwrap();
517 assert_eq!(buf, bytes);
518 }
519
520 #[test]
521 fn rejects_trailing_bytes() {
522 let bytes: &[u8] = &[0x00, 0x03, 0x00, 0x00, 0xFF];
524 assert!(matches!(
525 CompatibilityDescriptor::parse(bytes).unwrap_err(),
526 Error::InvalidDescriptor { .. }
527 ));
528 }
529
530 #[test]
531 fn rejects_truncated_entry_header() {
532 let bytes: &[u8] = &[0x00, 0x03, 0x00, 0x01, 0x01];
534 assert!(CompatibilityDescriptor::parse(bytes).is_err());
535 }
536
537 #[test]
538 fn rejects_truncated_sub_descriptor() {
539 let bytes: &[u8] = &[
542 0x00, 0x0D, 0x00, 0x01, 0x01, 0x09, 0x01, 0x00, 0x15, 0x0A, 0x12, 0x34, 0x00, 0x01,
543 0x01,
544 ];
545 assert!(CompatibilityDescriptor::parse(bytes).is_err());
546 }
547
548 #[test]
549 fn one_descriptor_with_sub_round_trip() {
550 let cd = CompatibilityDescriptor {
551 descriptors: vec![CompatibilityDescriptorEntry {
552 descriptor_type: DescriptorType::SystemHardware,
553 specifier_type: SpecifierType::IeeeOui,
554 specifier_data: [0x00, 0x15, 0x0A],
555 model: 0x1234,
556 version: 0x0001,
557 sub_descriptors: vec![
558 SubDescriptor {
559 sub_descriptor_type: SubDescriptorType::Unallocated(0x01),
560 data: &[0xAA, 0xBB],
561 },
562 SubDescriptor {
563 sub_descriptor_type: SubDescriptorType::Unallocated(0x02),
564 data: &[0xCC],
565 },
566 ],
567 }],
568 };
569 let mut buf = vec![0u8; cd.serialized_len()];
570 cd.serialize_into(&mut buf).unwrap();
571 let re = CompatibilityDescriptor::parse(&buf).unwrap();
572 assert_eq!(re.descriptors.len(), 1);
573 let e = &re.descriptors[0];
574 assert_eq!(e.descriptor_type, DescriptorType::SystemHardware);
575 assert_eq!(e.specifier_type, SpecifierType::IeeeOui);
576 assert_eq!(e.specifier_data, [0x00, 0x15, 0x0A]);
577 assert_eq!(e.model, 0x1234);
578 assert_eq!(e.version, 0x0001);
579 assert_eq!(e.sub_descriptors.len(), 2);
580 assert_eq!(
581 e.sub_descriptors[0].sub_descriptor_type,
582 SubDescriptorType::Unallocated(0x01)
583 );
584 assert_eq!(e.sub_descriptors[0].data, &[0xAA, 0xBB]);
585 assert_eq!(
586 e.sub_descriptors[1].sub_descriptor_type,
587 SubDescriptorType::Unallocated(0x02)
588 );
589 assert_eq!(e.sub_descriptors[1].data, &[0xCC]);
590 let mut buf2 = vec![0u8; cd.serialized_len()];
591 cd.serialize_into(&mut buf2).unwrap();
592 assert_eq!(buf, buf2, "byte-exact re-serialize");
593 assert_eq!(re, cd);
594 }
595
596 #[test]
597 fn two_descriptors_round_trip() {
598 let cd = CompatibilityDescriptor {
599 descriptors: vec![
600 CompatibilityDescriptorEntry {
601 descriptor_type: DescriptorType::SystemHardware,
602 specifier_type: SpecifierType::IeeeOui,
603 specifier_data: [0x00, 0x00, 0x00],
604 model: 0x0000,
605 version: 0x0000,
606 sub_descriptors: vec![],
607 },
608 CompatibilityDescriptorEntry {
609 descriptor_type: DescriptorType::SystemSoftware,
610 specifier_type: SpecifierType::IeeeOui,
611 specifier_data: [0x00, 0x15, 0x5A],
612 model: 0x0100,
613 version: 0x0002,
614 sub_descriptors: vec![SubDescriptor {
615 sub_descriptor_type: SubDescriptorType::Unallocated(0x80),
616 data: &[0xDE, 0xAD, 0xBE, 0xEF],
617 }],
618 },
619 ],
620 };
621 let mut buf = vec![0u8; cd.serialized_len()];
622 cd.serialize_into(&mut buf).unwrap();
623 let re = CompatibilityDescriptor::parse(&buf).unwrap();
624 assert_eq!(re, cd);
625 }
626
627 #[test]
628 fn parse_rejects_short_buffer() {
629 assert!(matches!(
630 CompatibilityDescriptor::parse(&[0x00]).unwrap_err(),
631 Error::BufferTooShort { .. }
632 ));
633 }
634
635 #[test]
636 fn parse_rejects_truncated_body() {
637 assert!(matches!(
638 CompatibilityDescriptor::parse(&[0x00, 0x05, 0x00, 0x01]).unwrap_err(),
639 Error::SectionLengthOverflow { .. }
640 ));
641 }
642
643 #[test]
644 fn parse_rejects_descriptor_length_too_short() {
645 let bytes: &[u8] = &[
646 0x00, 0x06, 0x00, 0x01, 0x01, 0x02, 0xAA, 0xBB, ];
651 assert!(matches!(
652 CompatibilityDescriptor::parse(bytes).unwrap_err(),
653 Error::InvalidDescriptor { .. }
654 ));
655 }
656
657 #[test]
658 fn serialize_rejects_small_buffer() {
659 let cd = CompatibilityDescriptor {
660 descriptors: vec![],
661 };
662 assert!(matches!(
663 cd.serialize_into(&mut [0u8; 1]).unwrap_err(),
664 Error::OutputBufferTooSmall { .. }
665 ));
666 }
667}