1use super::descriptor_body;
24use crate::error::{Error, Result};
25use dvb_common::{Parse, Serialize};
26
27pub const TAG: u8 = 0x13;
29
30const HEADER_LEN: usize = 2;
33const CAROUSEL_ID_LEN: usize = 4;
35const FORMAT_ID_LEN: usize = 1;
37const BODY_PREFIX_LEN: usize = CAROUSEL_ID_LEN + FORMAT_ID_LEN;
39
40const FS1_MODULE_VERSION_LEN: usize = 1;
43const FS1_MODULE_ID_LEN: usize = 2;
45const FS1_BLOCK_SIZE_LEN: usize = 2;
47const FS1_MODULE_SIZE_LEN: usize = 4;
49const FS1_COMPRESSION_METHOD_LEN: usize = 1;
51const FS1_ORIGINAL_SIZE_LEN: usize = 4;
53const FS1_TIMEOUT_LEN: usize = 1;
56const FS1_OBJECT_KEY_LENGTH_LEN: usize = 1;
58const FS1_FIXED_LEN: usize = FS1_MODULE_VERSION_LEN
60 + FS1_MODULE_ID_LEN
61 + FS1_BLOCK_SIZE_LEN
62 + FS1_MODULE_SIZE_LEN
63 + FS1_COMPRESSION_METHOD_LEN
64 + FS1_ORIGINAL_SIZE_LEN
65 + FS1_TIMEOUT_LEN
66 + FS1_OBJECT_KEY_LENGTH_LEN; const FORMAT_ID_NONE: u8 = 0x00;
71const FORMAT_ID_AGGREGATED: u8 = 0x01;
73
74#[derive(Debug, Clone, PartialEq, Eq)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize))]
89#[non_exhaustive]
90pub enum FormatSpecifier<'a> {
91 Absent,
93 Aggregated {
109 module_version: u8,
111 module_id: u16,
113 block_size: u16,
115 module_size: u32,
117 compression_method: u8,
119 original_size: u32,
122 timeout: u8,
124 #[cfg_attr(feature = "serde", serde(borrow))]
126 object_key: &'a [u8],
127 },
128 Other {
130 format_id: u8,
132 #[cfg_attr(feature = "serde", serde(borrow))]
139 bytes: &'a [u8],
140 },
141}
142
143impl<'a> FormatSpecifier<'a> {
144 #[must_use]
146 pub fn format_id(&self) -> u8 {
147 match self {
148 FormatSpecifier::Absent => FORMAT_ID_NONE,
149 FormatSpecifier::Aggregated { .. } => FORMAT_ID_AGGREGATED,
150 FormatSpecifier::Other { format_id, .. } => *format_id,
151 }
152 }
153
154 fn serialized_len(&self) -> usize {
157 match self {
158 FormatSpecifier::Absent => 0,
159 FormatSpecifier::Aggregated { object_key, .. } => FS1_FIXED_LEN + object_key.len(),
160 FormatSpecifier::Other { bytes, .. } => bytes.len(),
161 }
162 }
163}
164
165#[derive(Debug, Clone, PartialEq, Eq)]
172#[cfg_attr(feature = "serde", derive(serde::Serialize))]
173#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
174pub struct CarouselIdentifierDescriptor<'a> {
175 pub carousel_id: u32,
177 #[cfg_attr(feature = "serde", serde(borrow))]
179 pub format: FormatSpecifier<'a>,
180 #[cfg_attr(feature = "serde", serde(borrow))]
185 pub private_data: &'a [u8],
186}
187
188impl<'a> Parse<'a> for CarouselIdentifierDescriptor<'a> {
189 type Error = crate::error::Error;
190
191 fn parse(bytes: &'a [u8]) -> Result<Self> {
192 let body = descriptor_body(
193 bytes,
194 TAG,
195 "CarouselIdentifierDescriptor",
196 "unexpected tag for carousel_identifier_descriptor",
197 )?;
198 let (prefix, after_prefix) =
199 body.split_first_chunk::<BODY_PREFIX_LEN>()
200 .ok_or(Error::InvalidDescriptor {
201 tag: TAG,
202 reason: "carousel_identifier_descriptor body shorter than 5 bytes",
203 })?;
204 let carousel_id = u32::from_be_bytes([prefix[0], prefix[1], prefix[2], prefix[3]]);
205 let format_id = prefix[CAROUSEL_ID_LEN];
206
207 let (format, private_data) = match format_id {
208 FORMAT_ID_NONE => (FormatSpecifier::Absent, after_prefix),
209 FORMAT_ID_AGGREGATED => {
210 let (fs1, after_fixed) = after_prefix.split_first_chunk::<FS1_FIXED_LEN>().ok_or(
211 Error::InvalidDescriptor {
212 tag: TAG,
213 reason: "FormatId=0x01 specifier truncated (insufficient fixed fields)",
214 },
215 )?;
216 let module_version = fs1[0];
220 let module_id = u16::from_be_bytes([fs1[1], fs1[2]]);
221 let block_size = u16::from_be_bytes([fs1[3], fs1[4]]);
222 let module_size = u32::from_be_bytes([fs1[5], fs1[6], fs1[7], fs1[8]]);
223 let compression_method = fs1[9];
224 let original_size = u32::from_be_bytes([fs1[10], fs1[11], fs1[12], fs1[13]]);
225 let timeout = fs1[14];
226 let object_key_length = fs1[15] as usize;
227 if object_key_length > after_fixed.len() {
228 return Err(Error::InvalidDescriptor {
229 tag: TAG,
230 reason: "FormatId=0x01 ObjectKeyData exceeds descriptor body",
231 });
232 }
233 let object_key = &after_fixed[..object_key_length];
234 let private_data = &after_fixed[object_key_length..];
235 (
236 FormatSpecifier::Aggregated {
237 module_version,
238 module_id,
239 block_size,
240 module_size,
241 compression_method,
242 original_size,
243 timeout,
244 object_key,
245 },
246 private_data,
247 )
248 }
249 other => {
250 (
254 FormatSpecifier::Other {
255 format_id: other,
256 bytes: after_prefix,
257 },
258 &[][..],
259 )
260 }
261 };
262 Ok(Self {
263 carousel_id,
264 format,
265 private_data,
266 })
267 }
268}
269
270impl Serialize for CarouselIdentifierDescriptor<'_> {
271 type Error = crate::error::Error;
272
273 fn serialized_len(&self) -> usize {
274 let body = BODY_PREFIX_LEN + self.format.serialized_len() + self.private_data.len();
275 HEADER_LEN + body
276 }
277
278 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
279 let total = self.serialized_len();
280 let body_len = BODY_PREFIX_LEN + self.format.serialized_len() + self.private_data.len();
281 if body_len > u8::MAX as usize {
282 return Err(Error::InvalidDescriptor {
283 tag: TAG,
284 reason: "carousel_identifier_descriptor body exceeds 255 bytes",
285 });
286 }
287 if buf.len() < total {
288 return Err(Error::OutputBufferTooSmall {
289 need: total,
290 have: buf.len(),
291 });
292 }
293
294 buf[0] = TAG;
295 buf[1] = body_len as u8;
296 buf[HEADER_LEN..HEADER_LEN + CAROUSEL_ID_LEN]
297 .copy_from_slice(&self.carousel_id.to_be_bytes());
298 buf[HEADER_LEN + CAROUSEL_ID_LEN] = self.format.format_id();
299
300 let mut pos = HEADER_LEN + BODY_PREFIX_LEN;
301 match &self.format {
302 FormatSpecifier::Absent => {}
303 FormatSpecifier::Aggregated {
304 module_version,
305 module_id,
306 block_size,
307 module_size,
308 compression_method,
309 original_size,
310 timeout,
311 object_key,
312 } => {
313 if object_key.len() > u8::MAX as usize {
314 return Err(Error::InvalidDescriptor {
315 tag: TAG,
316 reason: "FormatId=0x01 ObjectKeyData exceeds 255 bytes",
317 });
318 }
319 buf[pos] = *module_version;
320 pos += FS1_MODULE_VERSION_LEN;
321 buf[pos..pos + FS1_MODULE_ID_LEN].copy_from_slice(&module_id.to_be_bytes());
322 pos += FS1_MODULE_ID_LEN;
323 buf[pos..pos + FS1_BLOCK_SIZE_LEN].copy_from_slice(&block_size.to_be_bytes());
324 pos += FS1_BLOCK_SIZE_LEN;
325 buf[pos..pos + FS1_MODULE_SIZE_LEN].copy_from_slice(&module_size.to_be_bytes());
326 pos += FS1_MODULE_SIZE_LEN;
327 buf[pos] = *compression_method;
328 pos += FS1_COMPRESSION_METHOD_LEN;
329 buf[pos..pos + FS1_ORIGINAL_SIZE_LEN].copy_from_slice(&original_size.to_be_bytes());
330 pos += FS1_ORIGINAL_SIZE_LEN;
331 buf[pos] = *timeout;
332 pos += FS1_TIMEOUT_LEN;
333 buf[pos] = object_key.len() as u8;
334 pos += FS1_OBJECT_KEY_LENGTH_LEN;
335 buf[pos..pos + object_key.len()].copy_from_slice(object_key);
336 pos += object_key.len();
337 }
338 FormatSpecifier::Other { bytes, .. } => {
339 buf[pos..pos + bytes.len()].copy_from_slice(bytes);
340 pos += bytes.len();
341 }
342 }
343 buf[pos..pos + self.private_data.len()].copy_from_slice(self.private_data);
344 Ok(total)
345 }
346}
347
348impl<'a> crate::traits::DescriptorDef<'a> for CarouselIdentifierDescriptor<'a> {
349 const TAG: u8 = TAG;
350 const NAME: &'static str = "CAROUSEL_IDENTIFIER";
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[rustfmt::skip]
392 const ANCHOR_BYTES: &[u8] = &[
393 0x13, 0x18, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1E, 0x01, 0xAB, 0xDE, 0xAD, ];
407
408 fn anchor_descriptor() -> CarouselIdentifierDescriptor<'static> {
409 CarouselIdentifierDescriptor {
410 carousel_id: 1,
411 format: FormatSpecifier::Aggregated {
412 module_version: 3,
413 module_id: 5,
414 block_size: 1024,
415 module_size: 4096,
416 compression_method: 0,
417 original_size: 4096,
418 timeout: 30,
419 object_key: &[0xAB],
420 },
421 private_data: &[0xDE, 0xAD],
422 }
423 }
424
425 #[test]
428 fn byte_anchor_serialize_matches_hand_built() {
429 let d = anchor_descriptor();
430 let mut buf = vec![0u8; d.serialized_len()];
431 d.serialize_into(&mut buf).unwrap();
432 assert_eq!(
433 buf.as_slice(),
434 ANCHOR_BYTES,
435 "serialized bytes differ from anchor"
436 );
437 }
438
439 #[test]
440 fn byte_anchor_parse_extracts_correct_fields() {
441 let d = CarouselIdentifierDescriptor::parse(ANCHOR_BYTES).unwrap();
442 assert_eq!(d.carousel_id, 1);
443 assert_eq!(d.private_data, &[0xDE, 0xAD]);
444 match &d.format {
445 FormatSpecifier::Aggregated {
446 module_version,
447 module_id,
448 block_size,
449 module_size,
450 compression_method,
451 original_size,
452 timeout,
453 object_key,
454 } => {
455 assert_eq!(*module_version, 3);
456 assert_eq!(*module_id, 5);
457 assert_eq!(*block_size, 1024);
458 assert_eq!(*module_size, 4096);
459 assert_eq!(*compression_method, 0);
460 assert_eq!(*original_size, 4096);
461 assert_eq!(*timeout, 30);
462 assert_eq!(*object_key, &[0xAB]);
463 }
464 other => panic!("expected Aggregated, got {other:?}"),
465 }
466 }
467
468 #[test]
469 fn byte_anchor_round_trip_byte_identical() {
470 let d = CarouselIdentifierDescriptor::parse(ANCHOR_BYTES).unwrap();
472 let mut buf = vec![0u8; d.serialized_len()];
473 d.serialize_into(&mut buf).unwrap();
474 assert_eq!(
475 buf.as_slice(),
476 ANCHOR_BYTES,
477 "round-trip not byte-identical"
478 );
479 let d2 = anchor_descriptor();
481 let mut buf2 = vec![0u8; d2.serialized_len()];
482 d2.serialize_into(&mut buf2).unwrap();
483 let d3 = CarouselIdentifierDescriptor::parse(&buf2).unwrap();
484 let mut buf3 = vec![0u8; d3.serialized_len()];
485 d3.serialize_into(&mut buf3).unwrap();
486 assert_eq!(buf2, buf3, "struct literal round-trip not byte-identical");
487 }
488
489 #[test]
492 fn parse_format_absent_extracts_fields() {
493 let bytes = [TAG, 0x08, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xAA, 0xBB, 0xCC];
495 let d = CarouselIdentifierDescriptor::parse(&bytes).unwrap();
496 assert_eq!(d.carousel_id, 0xDEAD_BEEF);
497 assert_eq!(d.format, FormatSpecifier::Absent);
498 assert_eq!(d.format.format_id(), 0x00);
499 assert_eq!(d.private_data, &[0xAA, 0xBB, 0xCC]);
500 }
501
502 #[test]
503 fn round_trip_format_absent() {
504 let d = CarouselIdentifierDescriptor {
505 carousel_id: 0x0000_0042,
506 format: FormatSpecifier::Absent,
507 private_data: &[0x01, 0x02, 0x03],
508 };
509 let mut buf = vec![0u8; d.serialized_len()];
510 d.serialize_into(&mut buf).unwrap();
511 let re = CarouselIdentifierDescriptor::parse(&buf).unwrap();
512 assert_eq!(re, d);
513 }
514
515 #[test]
516 fn format_absent_no_private_data() {
517 let bytes = [TAG, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00];
519 let d = CarouselIdentifierDescriptor::parse(&bytes).unwrap();
520 assert_eq!(d.carousel_id, 0);
521 assert_eq!(d.format, FormatSpecifier::Absent);
522 assert!(d.private_data.is_empty());
523 }
524
525 #[test]
528 fn parse_format_other_carries_raw_bytes() {
529 let bytes = [TAG, 0x08, 0x00, 0x00, 0x00, 0x7F, 0x42, 0xAA, 0xBB, 0xCC];
531 let d = CarouselIdentifierDescriptor::parse(&bytes).unwrap();
532 assert_eq!(d.carousel_id, 0x7F);
533 assert_eq!(d.format.format_id(), 0x42);
534 match &d.format {
535 FormatSpecifier::Other { format_id, bytes } => {
536 assert_eq!(*format_id, 0x42);
537 assert_eq!(*bytes, &[0xAA, 0xBB, 0xCC]);
538 }
539 other => panic!("expected Other, got {other:?}"),
540 }
541 assert!(d.private_data.is_empty());
543 }
544
545 #[test]
546 fn round_trip_format_other() {
547 let d = CarouselIdentifierDescriptor {
548 carousel_id: 0x0000_00FF,
549 format: FormatSpecifier::Other {
550 format_id: 0x05,
551 bytes: &[0x11, 0x22],
552 },
553 private_data: &[],
554 };
555 let mut buf = vec![0u8; d.serialized_len()];
556 d.serialize_into(&mut buf).unwrap();
557 let re = CarouselIdentifierDescriptor::parse(&buf).unwrap();
558 assert_eq!(re, d);
559 }
560
561 #[test]
564 fn parse_rejects_wrong_tag() {
565 let err = CarouselIdentifierDescriptor::parse(&[0x14, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00])
566 .unwrap_err();
567 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x14, .. }));
568 }
569
570 #[test]
571 fn parse_rejects_short_buffer() {
572 let err = CarouselIdentifierDescriptor::parse(&[TAG]).unwrap_err();
574 assert!(matches!(err, Error::BufferTooShort { .. }));
575 }
576
577 #[test]
578 fn parse_rejects_body_too_short() {
579 let err =
581 CarouselIdentifierDescriptor::parse(&[TAG, 0x04, 0x00, 0x00, 0x00, 0x01]).unwrap_err();
582 assert!(matches!(err, Error::InvalidDescriptor { .. }));
583 }
584
585 #[test]
586 fn parse_rejects_format1_fixed_truncated() {
587 let mut bytes = vec![TAG, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0xAA, 0xBB, 0xCC];
589 bytes[1] = (bytes.len() - 2) as u8;
590 let err = CarouselIdentifierDescriptor::parse(&bytes).unwrap_err();
591 assert!(matches!(err, Error::InvalidDescriptor { .. }));
592 }
593
594 #[test]
595 fn parse_rejects_format1_objectkey_overflow() {
596 let body = vec![
599 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x05, 0x0A, ];
610 let mut full = vec![TAG, body.len() as u8];
611 full.extend_from_slice(&body);
612 full.push(0xEE);
614 full[1] = (full.len() - 2) as u8;
615 let _ = body; let err = CarouselIdentifierDescriptor::parse(&full).unwrap_err();
620 assert!(matches!(err, Error::InvalidDescriptor { .. }));
621 }
622
623 #[test]
624 fn parse_rejects_length_overrun() {
625 let err =
627 CarouselIdentifierDescriptor::parse(&[TAG, 0x0A, 0x00, 0x00, 0x00, 0x01]).unwrap_err();
628 assert!(matches!(err, Error::BufferTooShort { .. }));
629 }
630
631 #[test]
632 fn serialize_rejects_too_small_buffer() {
633 let d = anchor_descriptor();
634 let mut tiny = [0u8; 2];
635 let err = d.serialize_into(&mut tiny).unwrap_err();
636 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
637 }
638
639 #[cfg(feature = "serde")]
642 #[test]
643 fn serde_serialize_aggregated_fields_present() {
644 let d = anchor_descriptor();
645 let json = serde_json::to_string(&d).unwrap();
646 assert!(json.contains("\"carousel_id\""));
647 assert!(json.contains("\"format\""));
648 assert!(json.contains("Aggregated") || json.contains("aggregated"));
650 assert!(json.contains("\"module_version\""));
651 assert!(json.contains("\"timeout\""));
652 }
653
654 #[cfg(feature = "serde")]
655 #[test]
656 fn serde_serialize_absent_format() {
657 let d = CarouselIdentifierDescriptor {
658 carousel_id: 0xABCD,
659 format: FormatSpecifier::Absent,
660 private_data: &[],
661 };
662 let json = serde_json::to_string(&d).unwrap();
663 assert!(json.contains("\"carousel_id\""));
664 assert!(json.contains("Absent") || json.contains("absent"));
665 }
666}