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 if v < 0x40 => Self::IsoReserved(v),
73 v if v < 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 compat_desc_len = u16::from_be_bytes([bytes[0], bytes[1]]) as usize;
250 let body_end = COMPAT_DESC_LEN_FIELD + compat_desc_len;
251 if body_end > bytes.len() {
252 return Err(Error::SectionLengthOverflow {
253 declared: compat_desc_len,
254 available: bytes.len() - COMPAT_DESC_LEN_FIELD,
255 });
256 }
257 if compat_desc_len == 0 {
258 return Ok(CompatibilityDescriptor {
259 descriptors: Vec::new(),
260 });
261 }
262 if compat_desc_len < DESC_COUNT_FIELD {
263 return Err(Error::BufferTooShort {
264 need: COMPAT_DESC_LEN_FIELD + DESC_COUNT_FIELD,
265 have: bytes.len(),
266 what: "CompatibilityDescriptor descriptorCount",
267 });
268 }
269 let body = &bytes[COMPAT_DESC_LEN_FIELD..body_end];
270 let descriptor_count = u16::from_be_bytes([body[0], body[1]]) as usize;
271 let mut pos = DESC_COUNT_FIELD;
272 let max_entries = (body.len() - DESC_COUNT_FIELD) / (DESC_HEADER_LEN + DESC_FIXED_LEN);
273 let mut descriptors = Vec::with_capacity(descriptor_count.min(max_entries));
274 for _ in 0..descriptor_count {
275 if pos + DESC_HEADER_LEN > body.len() {
276 return Err(Error::BufferTooShort {
277 need: COMPAT_DESC_LEN_FIELD + pos + DESC_HEADER_LEN,
278 have: COMPAT_DESC_LEN_FIELD + body.len(),
279 what: "CompatibilityDescriptor entry header",
280 });
281 }
282 let descriptor_type = DescriptorType::from_u8(body[pos]);
283 let descriptor_length = body[pos + 1] as usize;
284 let entry_end = pos + DESC_HEADER_LEN + descriptor_length;
285 if entry_end > body.len() {
286 return Err(Error::SectionLengthOverflow {
287 declared: descriptor_length,
288 available: body.len() - pos - DESC_HEADER_LEN,
289 });
290 }
291 if descriptor_length < DESC_FIXED_LEN {
292 return Err(Error::InvalidDescriptor {
293 tag: descriptor_type.to_u8(),
294 reason: "descriptorLength shorter than fixed fields",
295 });
296 }
297 let specifier_type = SpecifierType::from_u8(body[pos + DESC_HEADER_LEN]);
298 let specifier_data = [
299 body[pos + DESC_HEADER_LEN + 1],
300 body[pos + DESC_HEADER_LEN + 2],
301 body[pos + DESC_HEADER_LEN + 3],
302 ];
303 let model = u16::from_be_bytes([
304 body[pos + DESC_HEADER_LEN + 4],
305 body[pos + DESC_HEADER_LEN + 5],
306 ]);
307 let version = u16::from_be_bytes([
308 body[pos + DESC_HEADER_LEN + 6],
309 body[pos + DESC_HEADER_LEN + 7],
310 ]);
311 let sub_descriptor_count = body[pos + DESC_HEADER_LEN + 8] as usize;
312 let sub_desc_start = pos + DESC_HEADER_LEN + DESC_FIXED_LEN;
313 let sub_desc_end = entry_end;
314 let sub_desc_region_len = sub_desc_end.saturating_sub(sub_desc_start);
315 let mut sub_descriptors = Vec::with_capacity(
316 sub_descriptor_count.min(sub_desc_region_len / SUB_DESC_HEADER_LEN),
317 );
318 let mut sub_pos = sub_desc_start;
319 for _ in 0..sub_descriptor_count {
320 if sub_pos + SUB_DESC_HEADER_LEN > sub_desc_end {
321 return Err(Error::BufferTooShort {
322 need: COMPAT_DESC_LEN_FIELD + sub_pos + SUB_DESC_HEADER_LEN,
323 have: COMPAT_DESC_LEN_FIELD + sub_desc_end,
324 what: "CompatibilityDescriptor subDescriptor header",
325 });
326 }
327 let sub_descriptor_type = SubDescriptorType::from_u8(body[sub_pos]);
328 let sub_descriptor_length = body[sub_pos + 1] as usize;
329 sub_pos += SUB_DESC_HEADER_LEN;
330 if sub_pos + sub_descriptor_length > sub_desc_end {
331 return Err(Error::SectionLengthOverflow {
332 declared: sub_descriptor_length,
333 available: sub_desc_end - sub_pos,
334 });
335 }
336 sub_descriptors.push(SubDescriptor {
337 sub_descriptor_type,
338 data: &body[sub_pos..sub_pos + sub_descriptor_length],
339 });
340 sub_pos += sub_descriptor_length;
341 }
342 pos = entry_end;
343 descriptors.push(CompatibilityDescriptorEntry {
344 descriptor_type,
345 specifier_type,
346 specifier_data,
347 model,
348 version,
349 sub_descriptors,
350 });
351 }
352 if pos != body.len() {
355 return Err(Error::InvalidDescriptor {
356 tag: 0,
357 reason: "trailing bytes after compatibility descriptor entries",
358 });
359 }
360 Ok(CompatibilityDescriptor { descriptors })
361 }
362}
363
364impl Serialize for CompatibilityDescriptor<'_> {
365 type Error = Error;
366
367 fn serialized_len(&self) -> usize {
368 if self.descriptors.is_empty() {
369 return COMPAT_DESC_LEN_FIELD;
370 }
371 COMPAT_DESC_LEN_FIELD
372 + DESC_COUNT_FIELD
373 + self
374 .descriptors
375 .iter()
376 .map(entry_serialized_len)
377 .sum::<usize>()
378 }
379
380 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
381 let len = self.serialized_len();
382 if buf.len() < len {
383 return Err(Error::OutputBufferTooSmall {
384 need: len,
385 have: buf.len(),
386 });
387 }
388 if self.descriptors.is_empty() {
389 buf[0] = 0x00;
390 buf[1] = 0x00;
391 return Ok(COMPAT_DESC_LEN_FIELD);
392 }
393 let body_len = len - COMPAT_DESC_LEN_FIELD;
394 if body_len > u16::MAX as usize {
395 return Err(Error::SectionLengthOverflow {
396 declared: body_len,
397 available: u16::MAX as usize,
398 });
399 }
400 if self.descriptors.len() > u16::MAX as usize {
401 return Err(Error::SectionLengthOverflow {
402 declared: self.descriptors.len(),
403 available: u16::MAX as usize,
404 });
405 }
406 buf[..COMPAT_DESC_LEN_FIELD].copy_from_slice(&(body_len as u16).to_be_bytes());
407 buf[COMPAT_DESC_LEN_FIELD..COMPAT_DESC_LEN_FIELD + DESC_COUNT_FIELD]
408 .copy_from_slice(&(self.descriptors.len() as u16).to_be_bytes());
409 let mut pos = COMPAT_DESC_LEN_FIELD + DESC_COUNT_FIELD;
410 for entry in &self.descriptors {
411 let entry_body_len = entry_serialized_len(entry) - DESC_HEADER_LEN;
412 if entry_body_len > u8::MAX as usize {
413 return Err(Error::SectionLengthOverflow {
414 declared: entry_body_len,
415 available: u8::MAX as usize,
416 });
417 }
418 buf[pos] = entry.descriptor_type.to_u8();
419 buf[pos + 1] = entry_body_len as u8;
420 buf[pos + DESC_HEADER_LEN] = entry.specifier_type.to_u8();
421 buf[pos + DESC_HEADER_LEN + 1..pos + DESC_HEADER_LEN + 4]
422 .copy_from_slice(&entry.specifier_data);
423 buf[pos + DESC_HEADER_LEN + 4..pos + DESC_HEADER_LEN + 6]
424 .copy_from_slice(&entry.model.to_be_bytes());
425 buf[pos + DESC_HEADER_LEN + 6..pos + DESC_HEADER_LEN + 8]
426 .copy_from_slice(&entry.version.to_be_bytes());
427 if entry.sub_descriptors.len() > u8::MAX as usize {
428 return Err(Error::SectionLengthOverflow {
429 declared: entry.sub_descriptors.len(),
430 available: u8::MAX as usize,
431 });
432 }
433 buf[pos + DESC_HEADER_LEN + 8] = entry.sub_descriptors.len() as u8;
434 pos += DESC_HEADER_LEN + DESC_FIXED_LEN;
435 for sd in &entry.sub_descriptors {
436 buf[pos] = sd.sub_descriptor_type.to_u8();
437 if sd.data.len() > u8::MAX as usize {
438 return Err(Error::SectionLengthOverflow {
439 declared: sd.data.len(),
440 available: u8::MAX as usize,
441 });
442 }
443 buf[pos + 1] = sd.data.len() as u8;
444 pos += SUB_DESC_HEADER_LEN;
445 buf[pos..pos + sd.data.len()].copy_from_slice(sd.data);
446 pos += sd.data.len();
447 }
448 }
449 Ok(len)
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use super::*;
456
457 #[test]
458 fn empty_round_trip() {
459 let cd = CompatibilityDescriptor {
460 descriptors: vec![],
461 };
462 let mut buf = vec![0u8; cd.serialized_len()];
463 cd.serialize_into(&mut buf).unwrap();
464 assert_eq!(buf, &[0x00, 0x00]);
465 let re = CompatibilityDescriptor::parse(&buf).unwrap();
466 assert!(re.descriptors.is_empty());
467 }
468
469 #[test]
470 fn empty_with_count_parses_to_empty() {
471 let bytes: &[u8] = &[0x00, 0x02, 0x00, 0x00];
472 let cd = CompatibilityDescriptor::parse(bytes).unwrap();
473 assert!(cd.descriptors.is_empty());
474 let mut buf = vec![0u8; cd.serialized_len()];
475 cd.serialize_into(&mut buf).unwrap();
476 assert_eq!(buf, &[0x00, 0x00]);
477 }
478
479 #[test]
483 fn hand_built_byte_anchor() {
484 let bytes: &[u8] = &[
488 0x00, 0x11, 0x00, 0x01, 0x01, 0x0D, 0x01, 0x00, 0x15, 0x0A, 0x12, 0x34, 0x00, 0x01,
489 0x01, 0x05, 0x02, 0xAA, 0xBB,
490 ];
491 let cd = CompatibilityDescriptor::parse(bytes).unwrap();
492 assert_eq!(cd.descriptors.len(), 1);
493 let e = &cd.descriptors[0];
494 assert_eq!(e.descriptor_type, DescriptorType::SystemHardware);
495 assert_eq!(e.specifier_type, SpecifierType::IeeeOui);
496 assert_eq!(e.specifier_data, [0x00, 0x15, 0x0A]);
497 assert_eq!(e.model, 0x1234);
498 assert_eq!(e.version, 0x0001);
499 assert_eq!(e.sub_descriptors.len(), 1);
500 assert_eq!(
501 e.sub_descriptors[0].sub_descriptor_type,
502 SubDescriptorType::Unallocated(0x05)
503 );
504 assert_eq!(e.sub_descriptors[0].data, &[0xAA, 0xBB]);
505 let mut buf = vec![0u8; cd.serialized_len()];
507 cd.serialize_into(&mut buf).unwrap();
508 assert_eq!(buf, bytes);
509 }
510
511 #[test]
512 fn rejects_trailing_bytes() {
513 let bytes: &[u8] = &[0x00, 0x03, 0x00, 0x00, 0xFF];
515 assert!(matches!(
516 CompatibilityDescriptor::parse(bytes).unwrap_err(),
517 Error::InvalidDescriptor { .. }
518 ));
519 }
520
521 #[test]
522 fn rejects_truncated_entry_header() {
523 let bytes: &[u8] = &[0x00, 0x03, 0x00, 0x01, 0x01];
525 assert!(CompatibilityDescriptor::parse(bytes).is_err());
526 }
527
528 #[test]
529 fn rejects_truncated_sub_descriptor() {
530 let bytes: &[u8] = &[
533 0x00, 0x0D, 0x00, 0x01, 0x01, 0x09, 0x01, 0x00, 0x15, 0x0A, 0x12, 0x34, 0x00, 0x01,
534 0x01,
535 ];
536 assert!(CompatibilityDescriptor::parse(bytes).is_err());
537 }
538
539 #[test]
540 fn one_descriptor_with_sub_round_trip() {
541 let cd = CompatibilityDescriptor {
542 descriptors: vec![CompatibilityDescriptorEntry {
543 descriptor_type: DescriptorType::SystemHardware,
544 specifier_type: SpecifierType::IeeeOui,
545 specifier_data: [0x00, 0x15, 0x0A],
546 model: 0x1234,
547 version: 0x0001,
548 sub_descriptors: vec![
549 SubDescriptor {
550 sub_descriptor_type: SubDescriptorType::Unallocated(0x01),
551 data: &[0xAA, 0xBB],
552 },
553 SubDescriptor {
554 sub_descriptor_type: SubDescriptorType::Unallocated(0x02),
555 data: &[0xCC],
556 },
557 ],
558 }],
559 };
560 let mut buf = vec![0u8; cd.serialized_len()];
561 cd.serialize_into(&mut buf).unwrap();
562 let re = CompatibilityDescriptor::parse(&buf).unwrap();
563 assert_eq!(re.descriptors.len(), 1);
564 let e = &re.descriptors[0];
565 assert_eq!(e.descriptor_type, DescriptorType::SystemHardware);
566 assert_eq!(e.specifier_type, SpecifierType::IeeeOui);
567 assert_eq!(e.specifier_data, [0x00, 0x15, 0x0A]);
568 assert_eq!(e.model, 0x1234);
569 assert_eq!(e.version, 0x0001);
570 assert_eq!(e.sub_descriptors.len(), 2);
571 assert_eq!(
572 e.sub_descriptors[0].sub_descriptor_type,
573 SubDescriptorType::Unallocated(0x01)
574 );
575 assert_eq!(e.sub_descriptors[0].data, &[0xAA, 0xBB]);
576 assert_eq!(
577 e.sub_descriptors[1].sub_descriptor_type,
578 SubDescriptorType::Unallocated(0x02)
579 );
580 assert_eq!(e.sub_descriptors[1].data, &[0xCC]);
581 let mut buf2 = vec![0u8; cd.serialized_len()];
582 cd.serialize_into(&mut buf2).unwrap();
583 assert_eq!(buf, buf2, "byte-exact re-serialize");
584 assert_eq!(re, cd);
585 }
586
587 #[test]
588 fn two_descriptors_round_trip() {
589 let cd = CompatibilityDescriptor {
590 descriptors: vec![
591 CompatibilityDescriptorEntry {
592 descriptor_type: DescriptorType::SystemHardware,
593 specifier_type: SpecifierType::IeeeOui,
594 specifier_data: [0x00, 0x00, 0x00],
595 model: 0x0000,
596 version: 0x0000,
597 sub_descriptors: vec![],
598 },
599 CompatibilityDescriptorEntry {
600 descriptor_type: DescriptorType::SystemSoftware,
601 specifier_type: SpecifierType::IeeeOui,
602 specifier_data: [0x00, 0x15, 0x5A],
603 model: 0x0100,
604 version: 0x0002,
605 sub_descriptors: vec![SubDescriptor {
606 sub_descriptor_type: SubDescriptorType::Unallocated(0x80),
607 data: &[0xDE, 0xAD, 0xBE, 0xEF],
608 }],
609 },
610 ],
611 };
612 let mut buf = vec![0u8; cd.serialized_len()];
613 cd.serialize_into(&mut buf).unwrap();
614 let re = CompatibilityDescriptor::parse(&buf).unwrap();
615 assert_eq!(re, cd);
616 }
617
618 #[test]
619 fn parse_rejects_short_buffer() {
620 assert!(matches!(
621 CompatibilityDescriptor::parse(&[0x00]).unwrap_err(),
622 Error::BufferTooShort { .. }
623 ));
624 }
625
626 #[test]
627 fn parse_rejects_truncated_body() {
628 assert!(matches!(
629 CompatibilityDescriptor::parse(&[0x00, 0x05, 0x00, 0x01]).unwrap_err(),
630 Error::SectionLengthOverflow { .. }
631 ));
632 }
633
634 #[test]
635 fn parse_rejects_descriptor_length_too_short() {
636 let bytes: &[u8] = &[
637 0x00, 0x06, 0x00, 0x01, 0x01, 0x02, 0xAA, 0xBB, ];
642 assert!(matches!(
643 CompatibilityDescriptor::parse(bytes).unwrap_err(),
644 Error::InvalidDescriptor { .. }
645 ));
646 }
647
648 #[test]
649 fn serialize_rejects_small_buffer() {
650 let cd = CompatibilityDescriptor {
651 descriptors: vec![],
652 };
653 assert!(matches!(
654 cd.serialize_into(&mut [0u8; 1]).unwrap_err(),
655 Error::OutputBufferTooSmall { .. }
656 ));
657 }
658}