1use crate::error::{Error, Result};
33use dvb_common::{Parse, Serialize};
34
35pub const TABLE_ID: u8 = 0x3E;
38
39pub const PID: u16 = 0x0000;
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize))]
45#[cfg_attr(feature = "serde", serde(transparent))]
46pub struct MacAddress(pub [u8; 6]);
47
48impl MacAddress {
49 #[must_use]
51 pub fn as_bytes(&self) -> &[u8; 6] {
52 &self.0
53 }
54}
55
56impl core::fmt::Display for MacAddress {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 write!(
59 f,
60 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
61 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
62 )
63 }
64}
65
66impl AsRef<[u8; 6]> for MacAddress {
67 fn as_ref(&self) -> &[u8; 6] {
68 &self.0
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize))]
75#[cfg_attr(feature = "serde", serde(transparent))]
76pub struct Checksum(pub [u8; 4]);
77
78impl Checksum {
79 #[must_use]
81 pub fn as_bytes(&self) -> &[u8; 4] {
82 &self.0
83 }
84}
85
86impl AsRef<[u8; 4]> for Checksum {
87 fn as_ref(&self) -> &[u8; 4] {
88 &self.0
89 }
90}
91
92const HEADER_LEN: usize = 3;
94
95const EXTENSION_LEN: usize = 9;
98
99const CRC_LEN: usize = 4;
101
102const MIN_SECTION_LEN: usize = HEADER_LEN + EXTENSION_LEN + CRC_LEN;
104
105#[derive(Debug, Clone, PartialEq, Eq)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize))]
122#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
123pub struct MpeDatagramSection<'a> {
124 pub section_syntax_indicator: bool,
128
129 pub private_indicator: bool,
131
132 pub mac_address: MacAddress,
135
136 pub payload_scrambling_control: u8,
139
140 pub address_scrambling_control: u8,
143
144 pub llc_snap_flag: bool,
148
149 pub current_next_indicator: bool,
151
152 pub section_number: u8,
154
155 pub last_section_number: u8,
157
158 pub payload: &'a [u8],
163
164 pub checksum: Checksum,
168}
169
170impl<'a> Parse<'a> for MpeDatagramSection<'a> {
171 type Error = crate::error::Error;
172
173 fn parse(bytes: &'a [u8]) -> Result<Self> {
174 if bytes.len() < MIN_SECTION_LEN {
175 return Err(Error::BufferTooShort {
176 need: MIN_SECTION_LEN,
177 have: bytes.len(),
178 what: "MpeDatagramSection",
179 });
180 }
181
182 if bytes[0] != TABLE_ID {
183 return Err(Error::UnexpectedTableId {
184 table_id: bytes[0],
185 what: "MpeDatagramSection",
186 expected: &[TABLE_ID],
187 });
188 }
189
190 let section_syntax_indicator = (bytes[1] & 0x80) != 0;
192 let private_indicator = (bytes[1] & 0x40) != 0;
193 let section_length = (((bytes[1] & 0x0F) as usize) << 8) | bytes[2] as usize;
194 let total =
195 super::check_section_length(bytes.len(), HEADER_LEN, section_length, MIN_SECTION_LEN)?;
196
197 let mac_6 = bytes[3];
200 let mac_5 = bytes[4];
201
202 let payload_scrambling_control = (bytes[5] >> 4) & 0x03;
204 let address_scrambling_control = (bytes[5] >> 2) & 0x03;
205 let llc_snap_flag = (bytes[5] & 0x02) != 0;
206 let current_next_indicator = (bytes[5] & 0x01) != 0;
207
208 let section_number = bytes[6];
209 let last_section_number = bytes[7];
210
211 let mac_4 = bytes[8];
212 let mac_3 = bytes[9];
213 let mac_2 = bytes[10];
214 let mac_1 = bytes[11];
215 let mac_address = MacAddress([mac_1, mac_2, mac_3, mac_4, mac_5, mac_6]);
216
217 let payload_start = HEADER_LEN + EXTENSION_LEN;
218 let trailer_start = total - CRC_LEN;
219 let payload = &bytes[payload_start..trailer_start];
220 let checksum = Checksum([
221 bytes[trailer_start],
222 bytes[trailer_start + 1],
223 bytes[trailer_start + 2],
224 bytes[trailer_start + 3],
225 ]);
226
227 Ok(MpeDatagramSection {
228 section_syntax_indicator,
229 private_indicator,
230 mac_address,
231 payload_scrambling_control,
232 address_scrambling_control,
233 llc_snap_flag,
234 current_next_indicator,
235 section_number,
236 last_section_number,
237 payload,
238 checksum,
239 })
240 }
241}
242
243impl Serialize for MpeDatagramSection<'_> {
244 type Error = crate::error::Error;
245
246 fn serialized_len(&self) -> usize {
247 HEADER_LEN + EXTENSION_LEN + self.payload.len() + CRC_LEN
248 }
249
250 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
251 let len = self.serialized_len();
252 if buf.len() < len {
253 return Err(Error::OutputBufferTooSmall {
254 need: len,
255 have: buf.len(),
256 });
257 }
258
259 if self.payload_scrambling_control > 0x03 {
262 return Err(Error::ReservedBitsViolation {
263 field: "payload_scrambling_control",
264 reason: "value exceeds 2-bit field",
265 });
266 }
267 if self.address_scrambling_control > 0x03 {
268 return Err(Error::ReservedBitsViolation {
269 field: "address_scrambling_control",
270 reason: "value exceeds 2-bit field",
271 });
272 }
273
274 let section_length = (len - HEADER_LEN) as u16;
275 if section_length > 0x0FFF {
276 return Err(Error::SectionLengthOverflow {
277 declared: section_length as usize,
278 available: 0x0FFF,
279 });
280 }
281
282 buf[0] = TABLE_ID;
283 buf[1] = (u8::from(self.section_syntax_indicator) << 7)
285 | (u8::from(self.private_indicator) << 6)
286 | 0x30 | ((section_length >> 8) as u8 & 0x0F);
288 buf[2] = (section_length & 0xFF) as u8;
289
290 buf[3] = self.mac_address.0[5];
292 buf[4] = self.mac_address.0[4];
293
294 buf[5] = 0xC0
296 | ((self.payload_scrambling_control & 0x03) << 4)
297 | ((self.address_scrambling_control & 0x03) << 2)
298 | (u8::from(self.llc_snap_flag) << 1)
299 | u8::from(self.current_next_indicator);
300
301 buf[6] = self.section_number;
302 buf[7] = self.last_section_number;
303
304 buf[8] = self.mac_address.0[3];
306 buf[9] = self.mac_address.0[2];
307 buf[10] = self.mac_address.0[1];
308 buf[11] = self.mac_address.0[0];
309
310 let payload_start = HEADER_LEN + EXTENSION_LEN;
311 let trailer_start = payload_start + self.payload.len();
312 buf[payload_start..trailer_start].copy_from_slice(self.payload);
313
314 if self.section_syntax_indicator {
315 let crc = dvb_common::crc32_mpeg2::compute(&buf[..trailer_start]);
317 buf[trailer_start..len].copy_from_slice(&crc.to_be_bytes());
318 } else {
319 buf[trailer_start..len].copy_from_slice(&self.checksum.0);
322 }
323
324 Ok(len)
325 }
326}
327impl<'a> crate::traits::TableDef<'a> for MpeDatagramSection<'a> {
328 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
333 const NAME: &'static str = "MPE_DATAGRAM_SECTION";
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[allow(clippy::too_many_arguments)]
347 fn build_mpe(
348 ssi: bool,
349 private_indicator: bool,
350 mac_address: MacAddress,
351 payload_sc: u8,
352 address_sc: u8,
353 llc_snap: bool,
354 section_number: u8,
355 last_section_number: u8,
356 payload: &[u8],
357 trailer: Checksum,
358 ) -> Vec<u8> {
359 let section_length = (EXTENSION_LEN + payload.len() + CRC_LEN) as u16;
360 let flags = 0xC0
361 | ((payload_sc & 0x03) << 4)
362 | ((address_sc & 0x03) << 2)
363 | (u8::from(llc_snap) << 1)
364 | 0x01; let mut v = vec![
366 TABLE_ID,
367 (u8::from(ssi) << 7)
368 | (u8::from(private_indicator) << 6)
369 | 0x30
370 | ((section_length >> 8) as u8 & 0x0F),
371 (section_length & 0xFF) as u8,
372 mac_address.0[5], mac_address.0[4], flags,
375 section_number,
376 last_section_number,
377 mac_address.0[3], mac_address.0[2], mac_address.0[1], mac_address.0[0], ];
382 v.extend_from_slice(payload);
383 v.extend_from_slice(&trailer.0);
384 v
385 }
386
387 #[test]
388 fn parse_happy_path() {
389 let mac = [0x01, 0x00, 0x5E, 0x12, 0x34, 0x56];
390 let payload = [0xDE, 0xAD, 0xBE, 0xEF];
391 let bytes = build_mpe(
392 false,
393 true,
394 MacAddress(mac),
395 0b10,
396 0b01,
397 true,
398 2,
399 3,
400 &payload,
401 Checksum([0xAA, 0xBB, 0xCC, 0xDD]),
402 );
403 let sec = MpeDatagramSection::parse(&bytes).unwrap();
404 assert!(!sec.section_syntax_indicator);
405 assert!(sec.private_indicator);
406 assert_eq!(sec.mac_address, MacAddress(mac));
407 assert_eq!(sec.payload_scrambling_control, 0b10);
408 assert_eq!(sec.address_scrambling_control, 0b01);
409 assert!(sec.llc_snap_flag);
410 assert!(sec.current_next_indicator);
411 assert_eq!(sec.section_number, 2);
412 assert_eq!(sec.last_section_number, 3);
413 assert_eq!(sec.payload, &payload);
414 assert_eq!(sec.checksum, Checksum([0xAA, 0xBB, 0xCC, 0xDD]));
415 }
416
417 #[test]
418 fn mac_scatter_decoded_in_network_order() {
419 let mac = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
421 let bytes = build_mpe(
422 true,
423 false,
424 MacAddress(mac),
425 0,
426 0,
427 false,
428 0,
429 0,
430 &[],
431 Checksum([0; 4]),
432 );
433 assert_eq!(bytes[3], 0x66, "byte 3 = MAC_6 (LSB)");
435 assert_eq!(bytes[4], 0x55, "byte 4 = MAC_5");
436 assert_eq!(bytes[8], 0x44, "byte 8 = MAC_4");
437 assert_eq!(bytes[9], 0x33, "byte 9 = MAC_3");
438 assert_eq!(bytes[10], 0x22, "byte 10 = MAC_2");
439 assert_eq!(bytes[11], 0x11, "byte 11 = MAC_1 (MSB)");
440 let sec = MpeDatagramSection::parse(&bytes).unwrap();
441 assert_eq!(sec.mac_address, MacAddress(mac));
442 }
443
444 #[test]
445 fn parse_empty_payload() {
446 let bytes = build_mpe(
447 true,
448 false,
449 MacAddress([0xFF; 6]),
450 0,
451 0,
452 false,
453 0,
454 0,
455 &[],
456 Checksum([0; 4]),
457 );
458 let sec = MpeDatagramSection::parse(&bytes).unwrap();
459 assert!(sec.payload.is_empty());
460 assert_eq!(sec.mac_address, MacAddress([0xFF; 6]));
461 }
462
463 #[test]
464 fn parse_rejects_wrong_table_id() {
465 let mut bytes = build_mpe(
466 true,
467 false,
468 MacAddress([0; 6]),
469 0,
470 0,
471 false,
472 0,
473 0,
474 &[0x01],
475 Checksum([0; 4]),
476 );
477 bytes[0] = 0x3F; assert!(matches!(
479 MpeDatagramSection::parse(&bytes).unwrap_err(),
480 Error::UnexpectedTableId { table_id: 0x3F, .. }
481 ));
482 }
483
484 #[test]
485 fn parse_rejects_short_buffer() {
486 let err = MpeDatagramSection::parse(&[TABLE_ID, 0x00]).unwrap_err();
487 assert!(matches!(err, Error::BufferTooShort { .. }));
488 }
489
490 #[test]
491 fn parse_rejects_section_length_overflow() {
492 let mut bytes = build_mpe(
493 true,
494 false,
495 MacAddress([0; 6]),
496 0,
497 0,
498 false,
499 0,
500 0,
501 &[0xAA],
502 Checksum([0; 4]),
503 );
504 let fake_sl: u16 = (bytes.len() as u16) + 100 - HEADER_LEN as u16;
506 bytes[1] = (bytes[1] & 0xF0) | ((fake_sl >> 8) as u8 & 0x0F);
507 bytes[2] = (fake_sl & 0xFF) as u8;
508 assert!(matches!(
509 MpeDatagramSection::parse(&bytes).unwrap_err(),
510 Error::SectionLengthOverflow { .. }
511 ));
512 }
513
514 #[test]
515 fn round_trip_identity_ssi_set_crc() {
516 let mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
520 let payload = [0x45, 0x00, 0x00, 0x1C, 0x00, 0x01];
521 let original = MpeDatagramSection {
522 section_syntax_indicator: true,
523 private_indicator: false,
524 mac_address: MacAddress(mac),
525 payload_scrambling_control: 0,
526 address_scrambling_control: 0,
527 llc_snap_flag: false,
528 current_next_indicator: true,
529 section_number: 0,
530 last_section_number: 0,
531 payload: &payload,
532 checksum: Checksum([0; 4]),
533 };
534 let mut buf = vec![0u8; original.serialized_len()];
535 original.serialize_into(&mut buf).unwrap();
536 let parsed = MpeDatagramSection::parse(&buf).unwrap();
537 assert!(parsed.section_syntax_indicator);
539 assert_eq!(parsed.mac_address, MacAddress(mac));
540 assert_eq!(parsed.payload, &payload);
541 let mut buf2 = vec![0u8; parsed.serialized_len()];
543 parsed.serialize_into(&mut buf2).unwrap();
544 assert_eq!(buf, buf2);
545 }
546
547 #[test]
548 fn round_trip_identity_ssi_clear_checksum_preserved() {
549 let mac = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
551 let payload = [0x11, 0x22, 0x33];
552 let trailer = Checksum([0x12, 0x34, 0x56, 0x78]);
553 let bytes = build_mpe(
554 false,
555 true,
556 MacAddress(mac),
557 0b11,
558 0b10,
559 true,
560 1,
561 5,
562 &payload,
563 trailer,
564 );
565 let parsed = MpeDatagramSection::parse(&bytes).unwrap();
566 assert_eq!(parsed.checksum, trailer);
567 let mut buf = vec![0u8; parsed.serialized_len()];
568 parsed.serialize_into(&mut buf).unwrap();
569 assert_eq!(buf, bytes);
571 assert_eq!(MpeDatagramSection::parse(&buf).unwrap(), parsed);
572 }
573
574 #[test]
575 fn serialize_rejects_output_buffer_too_small() {
576 let sec = MpeDatagramSection {
577 section_syntax_indicator: true,
578 private_indicator: false,
579 mac_address: MacAddress([0; 6]),
580 payload_scrambling_control: 0,
581 address_scrambling_control: 0,
582 llc_snap_flag: false,
583 current_next_indicator: true,
584 section_number: 0,
585 last_section_number: 0,
586 payload: &[],
587 checksum: Checksum([0; 4]),
588 };
589 let mut buf = [0u8; 2];
590 assert!(matches!(
591 sec.serialize_into(&mut buf).unwrap_err(),
592 Error::OutputBufferTooSmall { .. }
593 ));
594 }
595
596 #[test]
597 fn serialize_rejects_over_range_scrambling_control() {
598 let sec = MpeDatagramSection {
599 section_syntax_indicator: true,
600 private_indicator: false,
601 mac_address: MacAddress([0; 6]),
602 payload_scrambling_control: 0x04, address_scrambling_control: 0,
604 llc_snap_flag: false,
605 current_next_indicator: true,
606 section_number: 0,
607 last_section_number: 0,
608 payload: &[],
609 checksum: Checksum([0; 4]),
610 };
611 let mut buf = vec![0u8; sec.serialized_len()];
612 assert!(matches!(
613 sec.serialize_into(&mut buf).unwrap_err(),
614 Error::ReservedBitsViolation {
615 field: "payload_scrambling_control",
616 ..
617 }
618 ));
619 }
620
621 #[test]
622 fn table_trait_constants() {
623 assert_eq!(TABLE_ID, 0x3E);
624 assert_eq!(PID, 0x0000);
625 }
626
627 #[cfg(feature = "serde")]
628 #[test]
629 fn serde_json_round_trip() {
630 let payload = [0xAB, 0xCD];
631 let bytes = build_mpe(
632 false,
633 true,
634 MacAddress([0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]),
635 0b01,
636 0b11,
637 true,
638 3,
639 7,
640 &payload,
641 Checksum([0xDE, 0xAD, 0xBE, 0xEF]),
642 );
643 let sec = MpeDatagramSection::parse(&bytes).unwrap();
644 let j = serde_json::to_string(&sec).unwrap();
645
646 let reparsed = MpeDatagramSection::parse(&bytes).unwrap();
654 assert_eq!(serde_json::to_string(&reparsed).unwrap(), j);
655
656 assert!(j.contains("\"mac_address\":[10,11,12,13,14,15]"));
659 assert!(j.contains("\"payload_scrambling_control\":1"));
660 assert!(j.contains("\"address_scrambling_control\":3"));
661 assert!(j.contains("\"checksum\":[222,173,190,239]"));
662 }
663
664 #[test]
665 fn parse_rejects_zero_section_length() {
666 let mut buf = vec![0u8; 64];
667 buf[0] = TABLE_ID;
668 buf[1] = 0xF0;
669 buf[2] = 0x00;
670 for b in &mut buf[3..] {
671 *b = 0xFF;
672 }
673 assert!(matches!(
674 MpeDatagramSection::parse(&buf).unwrap_err(),
675 Error::SectionLengthOverflow { .. }
676 ));
677 }
678}