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