1use crate::compatibility::CompatibilityDescriptor;
20use crate::descriptors::carousel::CarouselDescriptorLoop;
21use crate::error::{Error, Result};
22use alloc::vec::Vec;
23use broadcast_common::len::need_out;
24use broadcast_common::{Parse, Serialize};
25
26pub const PROTOCOL_DISCRIMINATOR: u8 = 0x11;
28pub const DSMCC_TYPE_UN_DOWNLOAD: u8 = 0x03;
30pub const MESSAGE_ID_DII: u16 = 0x1002;
32pub const MESSAGE_ID_DDB: u16 = 0x1003;
34pub const MESSAGE_ID_DSI: u16 = 0x1006;
36
37const MESSAGE_HEADER_LEN: usize = 12;
41const SERVER_ID_LEN: usize = 20;
43const PRIVATE_LEN_FIELD: usize = 2;
45const DII_FIXED_LEN: usize = 16;
49const MODULE_HEADER_LEN: usize = 8;
52const DDB_FIXED_LEN: usize = 6;
55
56const GII_NUMBER_OF_GROUPS_LEN: usize = 2;
59const GII_GROUP_ID_LEN: usize = 4;
61const GII_GROUP_SIZE_LEN: usize = 4;
63const GII_GROUP_INFO_LEN_FIELD: usize = 2;
65const GII_PRIVATE_DATA_LEN_FIELD: usize = 2;
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize))]
71pub struct GroupInfo<'a> {
72 pub group_id: u32,
74 pub group_size: u32,
76 pub group_compatibility: CompatibilityDescriptor<'a>,
79 #[cfg_attr(feature = "serde", serde(borrow))]
81 pub group_info: &'a [u8],
82 #[cfg_attr(feature = "serde", serde(borrow))]
84 pub private_data: &'a [u8],
85}
86
87impl<'a> GroupInfo<'a> {
88 #[must_use]
93 pub const fn descriptors(&self) -> CarouselDescriptorLoop<'a> {
94 CarouselDescriptorLoop::new(self.group_info)
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize))]
124pub struct GroupInfoIndication<'a> {
125 pub groups: Vec<GroupInfo<'a>>,
127}
128
129impl<'a> Parse<'a> for GroupInfoIndication<'a> {
130 type Error = crate::error::Error;
131
132 fn parse(bytes: &'a [u8]) -> Result<Self> {
133 let (b, _) = bytes.split_first_chunk::<2>().ok_or(Error::BufferTooShort(
134 broadcast_common::error::BufferTooShort {
135 need: GII_NUMBER_OF_GROUPS_LEN,
136 have: bytes.len(),
137 what: "GroupInfoIndication NumberOfGroups",
138 },
139 ))?;
140 let number_of_groups = u16::from_be_bytes(*b) as usize;
141 let mut pos = GII_NUMBER_OF_GROUPS_LEN;
142 let end = bytes.len();
143 let mut groups = Vec::with_capacity(number_of_groups.min(256));
144
145 for _ in 0..number_of_groups {
146 let fixed = GII_GROUP_ID_LEN + GII_GROUP_SIZE_LEN;
148 let (hdr, _) =
149 bytes[pos..end]
150 .split_first_chunk::<8>()
151 .ok_or(Error::BufferTooShort(
152 broadcast_common::error::BufferTooShort {
153 need: pos + fixed,
154 have: end,
155 what: "GroupInfo GroupId/GroupSize",
156 },
157 ))?;
158 let group_id = u32::from_be_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
159 let group_size = u32::from_be_bytes([hdr[4], hdr[5], hdr[6], hdr[7]]);
160 pos += fixed;
161
162 use crate::compatibility::COMPAT_DESC_LEN_FIELD;
166 let (bc, _) = bytes[pos..end]
167 .split_first_chunk::<2>()
168 .ok_or(Error::BufferTooShort(
169 broadcast_common::error::BufferTooShort {
170 need: pos + COMPAT_DESC_LEN_FIELD,
171 have: end,
172 what: "GroupCompatibility length field",
173 },
174 ))?;
175 let compat_len = u16::from_be_bytes(*bc) as usize;
176 let compat_total = COMPAT_DESC_LEN_FIELD + compat_len;
177 if pos + compat_total > end {
178 return Err(Error::SectionLengthOverflow {
179 declared: compat_len,
180 available: end - pos - COMPAT_DESC_LEN_FIELD,
181 });
182 }
183 let group_compatibility =
184 CompatibilityDescriptor::parse(&bytes[pos..pos + compat_total])?;
185 pos += compat_total;
186
187 let (bgi, _) =
189 bytes[pos..end]
190 .split_first_chunk::<2>()
191 .ok_or(Error::BufferTooShort(
192 broadcast_common::error::BufferTooShort {
193 need: pos + GII_GROUP_INFO_LEN_FIELD,
194 have: end,
195 what: "GroupInfo GroupInfoLength",
196 },
197 ))?;
198 let group_info_len = u16::from_be_bytes(*bgi) as usize;
199 pos += GII_GROUP_INFO_LEN_FIELD;
200 if pos + group_info_len > end {
201 return Err(Error::SectionLengthOverflow {
202 declared: group_info_len,
203 available: end - pos,
204 });
205 }
206 let group_info = &bytes[pos..pos + group_info_len];
207 pos += group_info_len;
208
209 let (bpd, _) =
211 bytes[pos..end]
212 .split_first_chunk::<2>()
213 .ok_or(Error::BufferTooShort(
214 broadcast_common::error::BufferTooShort {
215 need: pos + GII_PRIVATE_DATA_LEN_FIELD,
216 have: end,
217 what: "GroupInfo PrivateDataLength",
218 },
219 ))?;
220 let private_data_len = u16::from_be_bytes(*bpd) as usize;
221 pos += GII_PRIVATE_DATA_LEN_FIELD;
222 if pos + private_data_len > end {
223 return Err(Error::SectionLengthOverflow {
224 declared: private_data_len,
225 available: end - pos,
226 });
227 }
228 let private_data = &bytes[pos..pos + private_data_len];
229 pos += private_data_len;
230
231 groups.push(GroupInfo {
232 group_id,
233 group_size,
234 group_compatibility,
235 group_info,
236 private_data,
237 });
238 }
239
240 Ok(GroupInfoIndication { groups })
241 }
242}
243
244impl Serialize for GroupInfoIndication<'_> {
245 type Error = crate::error::Error;
246
247 fn serialized_len(&self) -> usize {
248 GII_NUMBER_OF_GROUPS_LEN
249 + self
250 .groups
251 .iter()
252 .map(|g| {
253 GII_GROUP_ID_LEN
254 + GII_GROUP_SIZE_LEN
255 + g.group_compatibility.serialized_len()
256 + GII_GROUP_INFO_LEN_FIELD
257 + g.group_info.len()
258 + GII_PRIVATE_DATA_LEN_FIELD
259 + g.private_data.len()
260 })
261 .sum::<usize>()
262 }
263
264 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
265 let len = self.serialized_len();
266 need_out(buf, len)?;
267 if self.groups.len() > u16::MAX as usize {
268 return Err(Error::SectionLengthOverflow {
269 declared: self.groups.len(),
270 available: u16::MAX as usize,
271 });
272 }
273 buf[0..2].copy_from_slice(
274 &broadcast_common::len::fit_u16(self.groups.len(), "self.groups")?.to_be_bytes(),
275 );
276 let mut pos = GII_NUMBER_OF_GROUPS_LEN;
277
278 for g in &self.groups {
279 buf[pos..pos + 4].copy_from_slice(&g.group_id.to_be_bytes());
280 buf[pos + 4..pos + 8].copy_from_slice(&g.group_size.to_be_bytes());
281 pos += GII_GROUP_ID_LEN + GII_GROUP_SIZE_LEN;
282
283 let written = g.group_compatibility.serialize_into(&mut buf[pos..])?;
284 pos += written;
285
286 if g.group_info.len() > u16::MAX as usize {
287 return Err(Error::SectionLengthOverflow {
288 declared: g.group_info.len(),
289 available: u16::MAX as usize,
290 });
291 }
292 buf[pos..pos + 2].copy_from_slice(
293 &broadcast_common::len::fit_u16(g.group_info.len(), "g.group_info")?.to_be_bytes(),
294 );
295 pos += GII_GROUP_INFO_LEN_FIELD;
296 buf[pos..pos + g.group_info.len()].copy_from_slice(g.group_info);
297 pos += g.group_info.len();
298
299 if g.private_data.len() > u16::MAX as usize {
300 return Err(Error::SectionLengthOverflow {
301 declared: g.private_data.len(),
302 available: u16::MAX as usize,
303 });
304 }
305 buf[pos..pos + 2].copy_from_slice(
306 &broadcast_common::len::fit_u16(g.private_data.len(), "g.private_data")?
307 .to_be_bytes(),
308 );
309 pos += GII_PRIVATE_DATA_LEN_FIELD;
310 buf[pos..pos + g.private_data.len()].copy_from_slice(g.private_data);
311 pos += g.private_data.len();
312 }
313
314 Ok(len)
315 }
316}
317
318#[derive(Debug, Clone, PartialEq, Eq)]
320#[cfg_attr(feature = "serde", derive(serde::Serialize))]
321pub struct Dsi<'a> {
322 pub transaction_id: u32,
325 pub adaptation: &'a [u8],
327 pub server_id: [u8; SERVER_ID_LEN],
329 pub compatibility_descriptor: CompatibilityDescriptor<'a>,
331 pub private_data: &'a [u8],
334}
335
336#[derive(Debug, Clone, PartialEq, Eq)]
338#[cfg_attr(feature = "serde", derive(serde::Serialize))]
339pub struct DiiModule<'a> {
340 pub module_id: u16,
342 pub module_size: u32,
344 pub module_version: u8,
346 pub module_info: &'a [u8],
349}
350
351impl<'a> DiiModule<'a> {
352 #[must_use]
360 pub const fn descriptors(&self) -> CarouselDescriptorLoop<'a> {
361 CarouselDescriptorLoop::new(self.module_info)
362 }
363}
364
365#[derive(Debug, Clone, PartialEq, Eq)]
367#[cfg_attr(feature = "serde", derive(serde::Serialize))]
368pub struct Dii<'a> {
369 pub transaction_id: u32,
371 pub adaptation: &'a [u8],
373 pub download_id: u32,
375 pub block_size: u16,
377 pub window_size: u8,
379 pub ack_period: u8,
381 pub t_c_download_window: u32,
383 pub t_c_download_scenario: u32,
385 pub compatibility_descriptor: CompatibilityDescriptor<'a>,
387 pub modules: Vec<DiiModule<'a>>,
389 pub private_data: &'a [u8],
391}
392
393#[derive(Debug, Clone, PartialEq, Eq)]
396#[cfg_attr(feature = "serde", derive(serde::Serialize))]
397#[non_exhaustive]
398pub enum UnMessage<'a> {
399 Dsi(Dsi<'a>),
401 Dii(Dii<'a>),
403}
404
405#[derive(Debug, Clone, PartialEq, Eq)]
408#[cfg_attr(feature = "serde", derive(serde::Serialize))]
409pub struct DownloadDataBlock<'a> {
410 pub download_id: u32,
412 pub adaptation: &'a [u8],
414 pub module_id: u16,
416 pub module_version: u8,
418 pub block_number: u16,
420 pub block_data: &'a [u8],
422}
423
424fn parse_header<'a>(bytes: &'a [u8], what: &'static str) -> Result<(u16, u32, &'a [u8], &'a [u8])> {
428 let (hdr, _) = bytes
429 .split_first_chunk::<MESSAGE_HEADER_LEN>()
430 .ok_or(Error::BufferTooShort(
431 broadcast_common::error::BufferTooShort {
432 need: MESSAGE_HEADER_LEN,
433 have: bytes.len(),
434 what,
435 },
436 ))?;
437 if hdr[0] != PROTOCOL_DISCRIMINATOR {
438 return Err(Error::InvalidValue(broadcast_common::error::InvalidValue {
439 field: "protocolDiscriminator",
440 value: u64::from(hdr[0]),
441 reason: "must be 0x11",
442 }));
443 }
444 if hdr[1] != DSMCC_TYPE_UN_DOWNLOAD {
445 return Err(Error::InvalidValue(broadcast_common::error::InvalidValue {
446 field: "dsmccType",
447 value: u64::from(hdr[1]),
448 reason: "must be 0x03 (U-N download)",
449 }));
450 }
451 let message_id = u16::from_be_bytes([hdr[2], hdr[3]]);
452 let id = u32::from_be_bytes([hdr[4], hdr[5], hdr[6], hdr[7]]);
453 let adaptation_length = hdr[9] as usize;
454 let message_length = u16::from_be_bytes([hdr[10], hdr[11]]) as usize;
455 let total = MESSAGE_HEADER_LEN + message_length;
456 if bytes.len() < total {
457 return Err(Error::SectionLengthOverflow {
458 declared: message_length,
459 available: bytes.len() - MESSAGE_HEADER_LEN,
460 });
461 }
462 if adaptation_length > message_length {
463 return Err(Error::SectionLengthOverflow {
464 declared: adaptation_length,
465 available: message_length,
466 });
467 }
468 let adaptation = &bytes[MESSAGE_HEADER_LEN..MESSAGE_HEADER_LEN + adaptation_length];
469 let payload = &bytes[MESSAGE_HEADER_LEN + adaptation_length..total];
470 Ok((message_id, id, adaptation, payload))
471}
472
473fn serialize_header(
476 buf: &mut [u8],
477 message_id: u16,
478 id: u32,
479 adaptation: &[u8],
480 payload_len: usize,
481) -> Result<usize> {
482 let message_length = adaptation.len() + payload_len;
483 if adaptation.len() > u8::MAX as usize {
484 return Err(Error::SectionLengthOverflow {
485 declared: adaptation.len(),
486 available: u8::MAX as usize,
487 });
488 }
489 if message_length > u16::MAX as usize {
490 return Err(Error::SectionLengthOverflow {
491 declared: message_length,
492 available: u16::MAX as usize,
493 });
494 }
495 buf[0] = PROTOCOL_DISCRIMINATOR;
496 buf[1] = DSMCC_TYPE_UN_DOWNLOAD;
497 buf[2..4].copy_from_slice(&message_id.to_be_bytes());
498 buf[4..8].copy_from_slice(&id.to_be_bytes());
499 buf[8] = 0xFF; buf[9] = broadcast_common::len::fit_u8(adaptation.len(), "adaptation")?;
501 buf[10..12].copy_from_slice(&(message_length as u16).to_be_bytes());
502 buf[MESSAGE_HEADER_LEN..MESSAGE_HEADER_LEN + adaptation.len()].copy_from_slice(adaptation);
503 Ok(MESSAGE_HEADER_LEN + adaptation.len())
504}
505
506fn length_prefixed(bytes: &[u8], pos: usize, end: usize) -> Result<(&[u8], usize)> {
508 let (b, _) = bytes[pos..end]
509 .split_first_chunk::<2>()
510 .ok_or(Error::BufferTooShort(
511 broadcast_common::error::BufferTooShort {
512 need: pos + 2,
513 have: end,
514 what: "DSM-CC 16-bit length field",
515 },
516 ))?;
517 let len = u16::from_be_bytes(*b) as usize;
518 let start = pos + 2;
519 if start + len > end {
520 return Err(Error::SectionLengthOverflow {
521 declared: len,
522 available: end - start,
523 });
524 }
525 Ok((&bytes[start..start + len], start + len))
526}
527
528fn parse_compat_block(
531 payload: &[u8],
532 offset: usize,
533 end: usize,
534) -> Result<(CompatibilityDescriptor<'_>, usize)> {
535 use crate::compatibility::COMPAT_DESC_LEN_FIELD;
536 let (b, _) = payload[offset..]
537 .split_first_chunk::<2>()
538 .ok_or(Error::BufferTooShort(
539 broadcast_common::error::BufferTooShort {
540 need: offset + COMPAT_DESC_LEN_FIELD,
541 have: end,
542 what: "compatibilityDescriptor in DSM-CC message",
543 },
544 ))?;
545 let compat_desc_len = u16::from_be_bytes(*b) as usize;
546 let compat_end = offset + COMPAT_DESC_LEN_FIELD + compat_desc_len;
547 if compat_end > end {
548 return Err(Error::SectionLengthOverflow {
549 declared: compat_desc_len,
550 available: end - offset - COMPAT_DESC_LEN_FIELD,
551 });
552 }
553 let cd = CompatibilityDescriptor::parse(&payload[offset..compat_end])?;
554 Ok((cd, compat_end))
555}
556
557impl<'a> Parse<'a> for UnMessage<'a> {
558 type Error = crate::error::Error;
559
560 fn parse(bytes: &'a [u8]) -> Result<Self> {
561 let (message_id, transaction_id, adaptation, payload) =
562 parse_header(bytes, "UnMessage header")?;
563 let end = payload.len();
564 match message_id {
565 MESSAGE_ID_DSI => {
566 if end < SERVER_ID_LEN {
567 return Err(Error::BufferTooShort(
568 broadcast_common::error::BufferTooShort {
569 need: SERVER_ID_LEN,
570 have: end,
571 what: "Dsi body",
572 },
573 ));
574 }
575 let mut server_id = [0u8; SERVER_ID_LEN];
576 server_id.copy_from_slice(&payload[..SERVER_ID_LEN]);
577 let (compatibility_descriptor, pos) =
578 parse_compat_block(payload, SERVER_ID_LEN, end)?;
579 let (private_data, _pos) = length_prefixed(payload, pos, end)?;
580 Ok(UnMessage::Dsi(Dsi {
581 transaction_id,
582 adaptation,
583 server_id,
584 compatibility_descriptor,
585 private_data,
586 }))
587 }
588 MESSAGE_ID_DII => {
589 let (dii_hdr, _) =
590 payload
591 .split_first_chunk::<DII_FIXED_LEN>()
592 .ok_or(Error::BufferTooShort(
593 broadcast_common::error::BufferTooShort {
594 need: DII_FIXED_LEN,
595 have: end,
596 what: "Dii body",
597 },
598 ))?;
599 let download_id =
600 u32::from_be_bytes([dii_hdr[0], dii_hdr[1], dii_hdr[2], dii_hdr[3]]);
601 let block_size = u16::from_be_bytes([dii_hdr[4], dii_hdr[5]]);
602 let window_size = dii_hdr[6];
603 let ack_period = dii_hdr[7];
604 let t_c_download_window =
605 u32::from_be_bytes([dii_hdr[8], dii_hdr[9], dii_hdr[10], dii_hdr[11]]);
606 let t_c_download_scenario =
607 u32::from_be_bytes([dii_hdr[12], dii_hdr[13], dii_hdr[14], dii_hdr[15]]);
608 let (compatibility_descriptor, mut pos) =
609 parse_compat_block(payload, DII_FIXED_LEN, end)?;
610 let (bnm, _) = payload
611 .get(pos..)
612 .and_then(|s| s.split_first_chunk::<2>())
613 .ok_or(Error::BufferTooShort(
614 broadcast_common::error::BufferTooShort {
615 need: pos + 2,
616 have: end,
617 what: "Dii numberOfModules",
618 },
619 ))?;
620 let number_of_modules = u16::from_be_bytes(*bnm) as usize;
621 pos += 2;
622 let mut modules = Vec::with_capacity(number_of_modules.min(256));
623 for _ in 0..number_of_modules {
624 let (mhdr, _) = payload
625 .get(pos..)
626 .and_then(|s| s.split_first_chunk::<MODULE_HEADER_LEN>())
627 .ok_or(Error::BufferTooShort(
628 broadcast_common::error::BufferTooShort {
629 need: pos + MODULE_HEADER_LEN,
630 have: end,
631 what: "Dii module entry",
632 },
633 ))?;
634 let module_id = u16::from_be_bytes([mhdr[0], mhdr[1]]);
635 let module_size = u32::from_be_bytes([mhdr[2], mhdr[3], mhdr[4], mhdr[5]]);
636 let module_version = mhdr[6];
637 let module_info_length = mhdr[7] as usize;
638 let info_start = pos + MODULE_HEADER_LEN;
639 if info_start + module_info_length > end {
640 return Err(Error::SectionLengthOverflow {
641 declared: module_info_length,
642 available: end - info_start,
643 });
644 }
645 modules.push(DiiModule {
646 module_id,
647 module_size,
648 module_version,
649 module_info: &payload[info_start..info_start + module_info_length],
650 });
651 pos = info_start + module_info_length;
652 }
653 let (private_data, _pos) = length_prefixed(payload, pos, end)?;
654 Ok(UnMessage::Dii(Dii {
655 transaction_id,
656 adaptation,
657 download_id,
658 block_size,
659 window_size,
660 ack_period,
661 t_c_download_window,
662 t_c_download_scenario,
663 compatibility_descriptor,
664 modules,
665 private_data,
666 }))
667 }
668 _ => Err(Error::InvalidValue(broadcast_common::error::InvalidValue {
669 field: "messageId",
670 value: u64::from(message_id),
671 reason: "expected 0x1002 (DII) or 0x1006 (DSI) on table_id 0x3B",
672 })),
673 }
674 }
675}
676
677impl Serialize for UnMessage<'_> {
678 type Error = crate::error::Error;
679
680 fn serialized_len(&self) -> usize {
681 match self {
682 UnMessage::Dsi(dsi) => {
683 MESSAGE_HEADER_LEN
684 + dsi.adaptation.len()
685 + SERVER_ID_LEN
686 + dsi.compatibility_descriptor.serialized_len()
687 + PRIVATE_LEN_FIELD
688 + dsi.private_data.len()
689 }
690 UnMessage::Dii(dii) => {
691 MESSAGE_HEADER_LEN
692 + dii.adaptation.len()
693 + DII_FIXED_LEN
694 + dii.compatibility_descriptor.serialized_len()
695 + 2 + dii
697 .modules
698 .iter()
699 .map(|m| MODULE_HEADER_LEN + m.module_info.len())
700 .sum::<usize>()
701 + PRIVATE_LEN_FIELD
702 + dii.private_data.len()
703 }
704 }
705 }
706
707 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
708 let len = self.serialized_len();
709 need_out(buf, len)?;
710 match self {
711 UnMessage::Dsi(dsi) => {
712 let payload_len = len - MESSAGE_HEADER_LEN - dsi.adaptation.len();
713 let mut pos = serialize_header(
714 buf,
715 MESSAGE_ID_DSI,
716 dsi.transaction_id,
717 dsi.adaptation,
718 payload_len,
719 )?;
720 buf[pos..pos + SERVER_ID_LEN].copy_from_slice(&dsi.server_id);
721 pos += SERVER_ID_LEN;
722 let written = dsi
723 .compatibility_descriptor
724 .serialize_into(&mut buf[pos..])?;
725 pos += written;
726 put_length_prefixed(buf, pos, dsi.private_data)?;
727 }
728 UnMessage::Dii(dii) => {
729 let payload_len = len - MESSAGE_HEADER_LEN - dii.adaptation.len();
730 let mut pos = serialize_header(
731 buf,
732 MESSAGE_ID_DII,
733 dii.transaction_id,
734 dii.adaptation,
735 payload_len,
736 )?;
737 buf[pos..pos + 4].copy_from_slice(&dii.download_id.to_be_bytes());
738 buf[pos + 4..pos + 6].copy_from_slice(&dii.block_size.to_be_bytes());
739 buf[pos + 6] = dii.window_size;
740 buf[pos + 7] = dii.ack_period;
741 buf[pos + 8..pos + 12].copy_from_slice(&dii.t_c_download_window.to_be_bytes());
742 buf[pos + 12..pos + 16].copy_from_slice(&dii.t_c_download_scenario.to_be_bytes());
743 pos += DII_FIXED_LEN;
744 let written = dii
745 .compatibility_descriptor
746 .serialize_into(&mut buf[pos..])?;
747 pos += written;
748 if dii.modules.len() > u16::MAX as usize {
749 return Err(Error::SectionLengthOverflow {
750 declared: dii.modules.len(),
751 available: u16::MAX as usize,
752 });
753 }
754 buf[pos..pos + 2].copy_from_slice(
755 &broadcast_common::len::fit_u16(dii.modules.len(), "dii.modules")?
756 .to_be_bytes(),
757 );
758 pos += 2;
759 for m in &dii.modules {
760 if m.module_info.len() > u8::MAX as usize {
761 return Err(Error::SectionLengthOverflow {
762 declared: m.module_info.len(),
763 available: u8::MAX as usize,
764 });
765 }
766 buf[pos..pos + 2].copy_from_slice(&m.module_id.to_be_bytes());
767 buf[pos + 2..pos + 6].copy_from_slice(&m.module_size.to_be_bytes());
768 buf[pos + 6] = m.module_version;
769 buf[pos + 7] =
770 broadcast_common::len::fit_u8(m.module_info.len(), "m.module_info")?;
771 pos += MODULE_HEADER_LEN;
772 buf[pos..pos + m.module_info.len()].copy_from_slice(m.module_info);
773 pos += m.module_info.len();
774 }
775 put_length_prefixed(buf, pos, dii.private_data)?;
776 }
777 }
778 Ok(len)
779 }
780}
781
782fn put_length_prefixed(buf: &mut [u8], pos: usize, data: &[u8]) -> Result<usize> {
784 if data.len() > u16::MAX as usize {
785 return Err(Error::SectionLengthOverflow {
786 declared: data.len(),
787 available: u16::MAX as usize,
788 });
789 }
790 buf[pos..pos + 2]
791 .copy_from_slice(&broadcast_common::len::fit_u16(data.len(), "data")?.to_be_bytes());
792 buf[pos + 2..pos + 2 + data.len()].copy_from_slice(data);
793 Ok(pos + 2 + data.len())
794}
795
796impl<'a> Parse<'a> for DownloadDataBlock<'a> {
797 type Error = crate::error::Error;
798
799 fn parse(bytes: &'a [u8]) -> Result<Self> {
800 let (message_id, download_id, adaptation, payload) =
801 parse_header(bytes, "DownloadDataBlock header")?;
802 if message_id != MESSAGE_ID_DDB {
803 return Err(Error::InvalidValue(broadcast_common::error::InvalidValue {
804 field: "messageId",
805 value: u64::from(message_id),
806 reason: "expected 0x1003 (DDB) on table_id 0x3C",
807 }));
808 }
809 let (ddb_hdr, _) =
810 payload
811 .split_first_chunk::<DDB_FIXED_LEN>()
812 .ok_or(Error::BufferTooShort(
813 broadcast_common::error::BufferTooShort {
814 need: DDB_FIXED_LEN,
815 have: payload.len(),
816 what: "DownloadDataBlock body",
817 },
818 ))?;
819 Ok(DownloadDataBlock {
820 download_id,
821 adaptation,
822 module_id: u16::from_be_bytes([ddb_hdr[0], ddb_hdr[1]]),
823 module_version: ddb_hdr[2],
824 block_number: u16::from_be_bytes([ddb_hdr[4], ddb_hdr[5]]),
825 block_data: &payload[DDB_FIXED_LEN..],
826 })
827 }
828}
829
830impl Serialize for DownloadDataBlock<'_> {
831 type Error = crate::error::Error;
832
833 fn serialized_len(&self) -> usize {
834 MESSAGE_HEADER_LEN + self.adaptation.len() + DDB_FIXED_LEN + self.block_data.len()
835 }
836
837 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
838 let len = self.serialized_len();
839 need_out(buf, len)?;
840 let payload_len = DDB_FIXED_LEN + self.block_data.len();
841 let pos = serialize_header(
842 buf,
843 MESSAGE_ID_DDB,
844 self.download_id,
845 self.adaptation,
846 payload_len,
847 )?;
848 buf[pos..pos + 2].copy_from_slice(&self.module_id.to_be_bytes());
849 buf[pos + 2] = self.module_version;
850 buf[pos + 3] = 0xFF; buf[pos + 4..pos + 6].copy_from_slice(&self.block_number.to_be_bytes());
852 buf[pos + DDB_FIXED_LEN..pos + DDB_FIXED_LEN + self.block_data.len()]
853 .copy_from_slice(self.block_data);
854 Ok(len)
855 }
856}
857
858#[cfg(test)]
859mod tests {
860 use super::*;
861
862 fn sample_dsi() -> UnMessage<'static> {
863 UnMessage::Dsi(Dsi {
864 transaction_id: 0x8000_0000,
865 adaptation: &[],
866 server_id: [0xFF; 20],
867 compatibility_descriptor: CompatibilityDescriptor {
868 empty_count_present: false,
869 descriptors: vec![],
870 },
871 private_data: &[0x0A, 0x0B],
872 })
873 }
874
875 fn sample_dii() -> UnMessage<'static> {
876 UnMessage::Dii(Dii {
877 transaction_id: 0x8002_0002,
878 adaptation: &[],
879 download_id: 0x0000_00AB,
880 block_size: 4066,
881 window_size: 0,
882 ack_period: 0,
883 t_c_download_window: 0,
884 t_c_download_scenario: 0,
885 compatibility_descriptor: CompatibilityDescriptor {
886 empty_count_present: false,
887 descriptors: vec![],
888 },
889 modules: vec![
890 DiiModule {
891 module_id: 1,
892 module_size: 8000,
893 module_version: 3,
894 module_info: &[0xDE, 0xAD],
895 },
896 DiiModule {
897 module_id: 2,
898 module_size: 100,
899 module_version: 1,
900 module_info: &[],
901 },
902 ],
903 private_data: &[],
904 })
905 }
906
907 #[test]
908 fn dsi_round_trip() {
909 let msg = sample_dsi();
910 let mut buf = vec![0u8; msg.serialized_len()];
911 msg.serialize_into(&mut buf).unwrap();
912 assert_eq!(UnMessage::parse(&buf).unwrap(), msg);
913 }
914
915 #[test]
916 fn dii_round_trip() {
917 let msg = sample_dii();
918 let mut buf = vec![0u8; msg.serialized_len()];
919 msg.serialize_into(&mut buf).unwrap();
920 assert_eq!(UnMessage::parse(&buf).unwrap(), msg);
921 }
922
923 fn nonempty_compat() -> CompatibilityDescriptor<'static> {
927 CompatibilityDescriptor {
928 empty_count_present: false,
929 descriptors: vec![crate::compatibility::CompatibilityDescriptorEntry {
930 descriptor_type: crate::compatibility::DescriptorType::SystemHardware,
931 specifier_type: crate::compatibility::SpecifierType::IeeeOui,
932 specifier_data: [0x00, 0x15, 0x0A],
933 model: 0x1234,
934 version: 0x0001,
935 sub_descriptors: vec![crate::compatibility::SubDescriptor {
936 sub_descriptor_type: crate::compatibility::SubDescriptorType::Unallocated(0x05),
937 data: &[0xAA, 0xBB],
938 }],
939 }],
940 }
941 }
942
943 #[test]
944 fn dsi_with_compat_round_trip() {
945 let msg = UnMessage::Dsi(Dsi {
946 transaction_id: 0x8000_0000,
947 adaptation: &[],
948 server_id: [0xFF; 20],
949 compatibility_descriptor: nonempty_compat(),
950 private_data: &[0x0A, 0x0B],
951 });
952 let mut buf = vec![0u8; msg.serialized_len()];
953 msg.serialize_into(&mut buf).unwrap();
954 let re = UnMessage::parse(&buf).unwrap();
955 assert_eq!(re, msg);
956 let mut buf2 = vec![0u8; re.serialized_len()];
957 re.serialize_into(&mut buf2).unwrap();
958 assert_eq!(buf, buf2, "byte-exact re-serialize");
959 }
960
961 #[test]
962 fn dii_with_compat_round_trip() {
963 let msg = UnMessage::Dii(Dii {
964 transaction_id: 0x8002_0002,
965 adaptation: &[],
966 download_id: 0x0000_00AB,
967 block_size: 4066,
968 window_size: 0,
969 ack_period: 0,
970 t_c_download_window: 0,
971 t_c_download_scenario: 0,
972 compatibility_descriptor: nonempty_compat(),
973 modules: vec![DiiModule {
974 module_id: 1,
975 module_size: 8000,
976 module_version: 3,
977 module_info: &[0xDE, 0xAD],
978 }],
979 private_data: &[],
980 });
981 let mut buf = vec![0u8; msg.serialized_len()];
982 msg.serialize_into(&mut buf).unwrap();
983 let re = UnMessage::parse(&buf).unwrap();
984 assert_eq!(re, msg);
985 let mut buf2 = vec![0u8; re.serialized_len()];
986 re.serialize_into(&mut buf2).unwrap();
987 assert_eq!(buf, buf2, "byte-exact re-serialize");
988 }
989
990 #[test]
991 fn ddb_round_trip() {
992 let ddb = DownloadDataBlock {
993 download_id: 0xAB,
994 adaptation: &[],
995 module_id: 1,
996 module_version: 3,
997 block_number: 2,
998 block_data: &[0x55; 64],
999 };
1000 let mut buf = vec![0u8; ddb.serialized_len()];
1001 ddb.serialize_into(&mut buf).unwrap();
1002 assert_eq!(DownloadDataBlock::parse(&buf).unwrap(), ddb);
1003 }
1004
1005 #[test]
1006 fn header_fields_on_wire() {
1007 let msg = sample_dsi();
1008 let mut buf = vec![0u8; msg.serialized_len()];
1009 msg.serialize_into(&mut buf).unwrap();
1010 assert_eq!(buf[0], 0x11); assert_eq!(buf[1], 0x03); assert_eq!(u16::from_be_bytes([buf[2], buf[3]]), MESSAGE_ID_DSI);
1013 assert_eq!(buf[8], 0xFF); let ml = u16::from_be_bytes([buf[10], buf[11]]) as usize;
1016 assert_eq!(ml, buf.len() - 12);
1017 }
1018
1019 #[test]
1020 fn parse_rejects_wrong_protocol_discriminator() {
1021 let msg = sample_dsi();
1022 let mut buf = vec![0u8; msg.serialized_len()];
1023 msg.serialize_into(&mut buf).unwrap();
1024 buf[0] = 0x12;
1025 assert_eq!(
1026 UnMessage::parse(&buf).unwrap_err(),
1027 Error::InvalidValue(broadcast_common::error::InvalidValue {
1028 field: "protocolDiscriminator",
1029 value: 0x12,
1030 reason: "must be 0x11",
1031 })
1032 );
1033 }
1034
1035 #[test]
1036 fn parse_rejects_unknown_message_id() {
1037 let msg = sample_dsi();
1038 let mut buf = vec![0u8; msg.serialized_len()];
1039 msg.serialize_into(&mut buf).unwrap();
1040 buf[2] = 0x10;
1041 buf[3] = 0x01; assert_eq!(
1043 UnMessage::parse(&buf).unwrap_err(),
1044 Error::InvalidValue(broadcast_common::error::InvalidValue {
1045 field: "messageId",
1046 value: 0x1001,
1047 reason: "expected 0x1002 (DII) or 0x1006 (DSI) on table_id 0x3B",
1048 })
1049 );
1050 }
1051
1052 #[test]
1053 fn parse_rejects_short_buffer() {
1054 assert!(matches!(
1055 UnMessage::parse(&[0x11, 0x03]).unwrap_err(),
1056 Error::BufferTooShort(..)
1057 ));
1058 }
1059
1060 #[test]
1061 fn parse_rejects_message_length_overflow() {
1062 let msg = sample_dsi();
1063 let mut buf = vec![0u8; msg.serialized_len()];
1064 msg.serialize_into(&mut buf).unwrap();
1065 buf[10] = 0xFF;
1066 buf[11] = 0xFF; assert!(matches!(
1068 UnMessage::parse(&buf).unwrap_err(),
1069 Error::SectionLengthOverflow { .. }
1070 ));
1071 }
1072
1073 #[test]
1074 fn dii_module_info_overflow_rejected() {
1075 let msg = sample_dii();
1076 let mut buf = vec![0u8; msg.serialized_len()];
1077 msg.serialize_into(&mut buf).unwrap();
1078 buf[39] = 0xFF;
1081 assert!(matches!(
1082 UnMessage::parse(&buf).unwrap_err(),
1083 Error::SectionLengthOverflow { .. }
1084 ));
1085 }
1086
1087 #[test]
1088 fn ddb_rejects_un_message_id() {
1089 let msg = sample_dsi();
1090 let mut buf = vec![0u8; msg.serialized_len()];
1091 msg.serialize_into(&mut buf).unwrap();
1092 assert_eq!(
1093 DownloadDataBlock::parse(&buf).unwrap_err(),
1094 Error::InvalidValue(broadcast_common::error::InvalidValue {
1095 field: "messageId",
1096 value: 0x1006,
1097 reason: "expected 0x1003 (DDB) on table_id 0x3C",
1098 })
1099 );
1100 }
1101
1102 #[test]
1103 fn adaptation_bytes_round_trip() {
1104 let ddb = DownloadDataBlock {
1105 download_id: 1,
1106 adaptation: &[0x01, 0x02, 0x03],
1107 module_id: 9,
1108 module_version: 0,
1109 block_number: 0,
1110 block_data: &[0xAA],
1111 };
1112 let mut buf = vec![0u8; ddb.serialized_len()];
1113 ddb.serialize_into(&mut buf).unwrap();
1114 assert_eq!(buf[9], 3); assert_eq!(DownloadDataBlock::parse(&buf).unwrap(), ddb);
1116 }
1117
1118 #[cfg(feature = "serde")]
1119 #[test]
1120 fn dii_serializes_to_valid_json() {
1121 let msg = sample_dii();
1122 let j = serde_json::to_string(&msg).unwrap();
1123 assert!(j.contains("\"download_id\":171"));
1124 assert!(j.contains("\"block_size\":4066"));
1125 }
1126
1127 fn sample_gii() -> GroupInfoIndication<'static> {
1146 GroupInfoIndication {
1147 groups: vec![GroupInfo {
1148 group_id: 0x0000_0001,
1149 group_size: 500_000,
1150 group_compatibility: CompatibilityDescriptor {
1151 empty_count_present: false,
1152 descriptors: vec![],
1153 },
1154 group_info: &[0xCA, 0xFE],
1155 private_data: &[0xBB],
1156 }],
1157 }
1158 }
1159
1160 #[test]
1161 fn gii_round_trip() {
1162 let gii = sample_gii();
1163 let mut buf = vec![0u8; gii.serialized_len()];
1164 gii.serialize_into(&mut buf).unwrap();
1165 let re = GroupInfoIndication::parse(&buf).unwrap();
1166 assert_eq!(re, gii);
1167 let mut buf2 = vec![0u8; re.serialized_len()];
1169 re.serialize_into(&mut buf2).unwrap();
1170 assert_eq!(buf, buf2, "GII byte-exact re-serialize");
1171 }
1172
1173 #[test]
1177 fn gii_hand_built_byte_anchor() {
1178 #[rustfmt::skip]
1182 let expected: &[u8] = &[
1183 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0xA1, 0x20, 0x00, 0x00, 0x00, 0x02, 0xCA, 0xFE, 0x00, 0x01, 0xBB, ];
1192 let gii = sample_gii();
1193 let mut buf = vec![0u8; gii.serialized_len()];
1194 gii.serialize_into(&mut buf).unwrap();
1195 assert_eq!(buf.as_slice(), expected);
1196 let re = GroupInfoIndication::parse(expected).unwrap();
1197 assert_eq!(re, gii);
1198 }
1199
1200 #[test]
1201 fn gii_empty_groups() {
1202 let gii = GroupInfoIndication { groups: vec![] };
1203 let mut buf = vec![0u8; gii.serialized_len()];
1204 gii.serialize_into(&mut buf).unwrap();
1205 assert_eq!(buf, &[0x00, 0x00]); let re = GroupInfoIndication::parse(&buf).unwrap();
1207 assert!(re.groups.is_empty());
1208 }
1209
1210 #[test]
1211 fn gii_with_compat_round_trip() {
1212 let gii = GroupInfoIndication {
1213 groups: vec![GroupInfo {
1214 group_id: 0xDEAD_BEEF,
1215 group_size: 0x0001_0000,
1216 group_compatibility: nonempty_compat(),
1217 group_info: &[0x01, 0x02, 0x03],
1218 private_data: &[],
1219 }],
1220 };
1221 let mut buf = vec![0u8; gii.serialized_len()];
1222 gii.serialize_into(&mut buf).unwrap();
1223 let re = GroupInfoIndication::parse(&buf).unwrap();
1224 assert_eq!(re, gii);
1225 let mut buf2 = vec![0u8; re.serialized_len()];
1226 re.serialize_into(&mut buf2).unwrap();
1227 assert_eq!(buf, buf2, "GII with compat byte-exact re-serialize");
1228 }
1229
1230 #[test]
1231 fn gii_parse_rejects_short_buffer() {
1232 assert!(matches!(
1233 GroupInfoIndication::parse(&[0x00]).unwrap_err(),
1234 Error::BufferTooShort(..)
1235 ));
1236 }
1237
1238 #[test]
1239 fn gii_parse_rejects_truncated_group() {
1240 let bytes = &[0x00, 0x01, 0x00, 0x00, 0x00];
1242 assert!(matches!(
1243 GroupInfoIndication::parse(bytes).unwrap_err(),
1244 Error::BufferTooShort(..)
1245 ));
1246 }
1247
1248 #[cfg(feature = "serde")]
1249 #[test]
1250 fn gii_serde_round_trip() {
1251 let gii = sample_gii();
1252 let json = serde_json::to_string(&gii).unwrap();
1253 assert!(json.contains("\"group_id\":1"));
1254 assert!(json.contains("\"group_size\":500000"));
1255 assert!(json.contains("\"group_info\""));
1256 }
1257}