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