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