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