1use crate::error::{Error, Result};
33use crate::traits::Table;
34use dvb_common::{Parse, Serialize};
35
36pub const TABLE_ID: u8 = 0x3E;
39
40pub const PID: u16 = 0x0000;
42
43const HEADER_LEN: usize = 3;
45
46const EXTENSION_LEN: usize = 9;
49
50const CRC_LEN: usize = 4;
52
53const MIN_SECTION_LEN: usize = HEADER_LEN + EXTENSION_LEN + CRC_LEN;
55
56#[derive(Debug, Clone, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize))]
73pub struct MpeDatagramSection<'a> {
74 pub section_syntax_indicator: bool,
78
79 pub private_indicator: bool,
81
82 pub mac_address: [u8; 6],
85
86 pub payload_scrambling_control: u8,
89
90 pub address_scrambling_control: u8,
93
94 pub llc_snap_flag: bool,
98
99 pub current_next_indicator: bool,
101
102 pub section_number: u8,
104
105 pub last_section_number: u8,
107
108 pub payload: &'a [u8],
113
114 pub checksum: [u8; 4],
118}
119
120impl<'a> Parse<'a> for MpeDatagramSection<'a> {
121 type Error = crate::error::Error;
122
123 fn parse(bytes: &'a [u8]) -> Result<Self> {
124 if bytes.len() < MIN_SECTION_LEN {
125 return Err(Error::BufferTooShort {
126 need: MIN_SECTION_LEN,
127 have: bytes.len(),
128 what: "MpeDatagramSection",
129 });
130 }
131
132 if bytes[0] != TABLE_ID {
133 return Err(Error::UnexpectedTableId {
134 table_id: bytes[0],
135 what: "MpeDatagramSection",
136 expected: &[TABLE_ID],
137 });
138 }
139
140 let section_syntax_indicator = (bytes[1] & 0x80) != 0;
142 let private_indicator = (bytes[1] & 0x40) != 0;
143 let section_length = (((bytes[1] & 0x0F) as usize) << 8) | bytes[2] as usize;
144 let total = HEADER_LEN + section_length;
145 if bytes.len() < total {
146 return Err(Error::SectionLengthOverflow {
147 declared: section_length,
148 available: bytes.len() - HEADER_LEN,
149 });
150 }
151 if section_length < EXTENSION_LEN + CRC_LEN {
153 return Err(Error::SectionLengthOverflow {
154 declared: section_length,
155 available: bytes.len() - HEADER_LEN,
156 });
157 }
158
159 let mac_6 = bytes[3];
162 let mac_5 = bytes[4];
163
164 let payload_scrambling_control = (bytes[5] >> 4) & 0x03;
166 let address_scrambling_control = (bytes[5] >> 2) & 0x03;
167 let llc_snap_flag = (bytes[5] & 0x02) != 0;
168 let current_next_indicator = (bytes[5] & 0x01) != 0;
169
170 let section_number = bytes[6];
171 let last_section_number = bytes[7];
172
173 let mac_4 = bytes[8];
174 let mac_3 = bytes[9];
175 let mac_2 = bytes[10];
176 let mac_1 = bytes[11];
177 let mac_address = [mac_1, mac_2, mac_3, mac_4, mac_5, mac_6];
178
179 let payload_start = HEADER_LEN + EXTENSION_LEN;
180 let trailer_start = total - CRC_LEN;
181 let payload = &bytes[payload_start..trailer_start];
182 let checksum = [
183 bytes[trailer_start],
184 bytes[trailer_start + 1],
185 bytes[trailer_start + 2],
186 bytes[trailer_start + 3],
187 ];
188
189 Ok(MpeDatagramSection {
190 section_syntax_indicator,
191 private_indicator,
192 mac_address,
193 payload_scrambling_control,
194 address_scrambling_control,
195 llc_snap_flag,
196 current_next_indicator,
197 section_number,
198 last_section_number,
199 payload,
200 checksum,
201 })
202 }
203}
204
205impl Serialize for MpeDatagramSection<'_> {
206 type Error = crate::error::Error;
207
208 fn serialized_len(&self) -> usize {
209 HEADER_LEN + EXTENSION_LEN + self.payload.len() + CRC_LEN
210 }
211
212 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
213 let len = self.serialized_len();
214 if buf.len() < len {
215 return Err(Error::OutputBufferTooSmall {
216 need: len,
217 have: buf.len(),
218 });
219 }
220
221 if self.payload_scrambling_control > 0x03 {
224 return Err(Error::ReservedBitsViolation {
225 field: "payload_scrambling_control",
226 reason: "value exceeds 2-bit field",
227 });
228 }
229 if self.address_scrambling_control > 0x03 {
230 return Err(Error::ReservedBitsViolation {
231 field: "address_scrambling_control",
232 reason: "value exceeds 2-bit field",
233 });
234 }
235
236 let section_length = (len - HEADER_LEN) as u16;
237
238 buf[0] = TABLE_ID;
239 buf[1] = (u8::from(self.section_syntax_indicator) << 7)
241 | (u8::from(self.private_indicator) << 6)
242 | 0x30 | ((section_length >> 8) as u8 & 0x0F);
244 buf[2] = (section_length & 0xFF) as u8;
245
246 buf[3] = self.mac_address[5];
248 buf[4] = self.mac_address[4];
249
250 buf[5] = 0xC0
252 | ((self.payload_scrambling_control & 0x03) << 4)
253 | ((self.address_scrambling_control & 0x03) << 2)
254 | (u8::from(self.llc_snap_flag) << 1)
255 | u8::from(self.current_next_indicator);
256
257 buf[6] = self.section_number;
258 buf[7] = self.last_section_number;
259
260 buf[8] = self.mac_address[3];
262 buf[9] = self.mac_address[2];
263 buf[10] = self.mac_address[1];
264 buf[11] = self.mac_address[0];
265
266 let payload_start = HEADER_LEN + EXTENSION_LEN;
267 let trailer_start = payload_start + self.payload.len();
268 buf[payload_start..trailer_start].copy_from_slice(self.payload);
269
270 if self.section_syntax_indicator {
271 let crc = dvb_common::crc32_mpeg2::compute(&buf[..trailer_start]);
273 buf[trailer_start..len].copy_from_slice(&crc.to_be_bytes());
274 } else {
275 buf[trailer_start..len].copy_from_slice(&self.checksum);
278 }
279
280 Ok(len)
281 }
282}
283
284impl<'a> Table<'a> for MpeDatagramSection<'a> {
285 const TABLE_ID: u8 = TABLE_ID;
286 const PID: u16 = PID;
287}
288
289impl<'a> crate::traits::TableDef<'a> for MpeDatagramSection<'a> {
290 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
295 const NAME: &'static str = "MPE_DATAGRAM_SECTION";
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 #[allow(clippy::too_many_arguments)]
309 fn build_mpe(
310 ssi: bool,
311 private_indicator: bool,
312 mac_address: [u8; 6],
313 payload_sc: u8,
314 address_sc: u8,
315 llc_snap: bool,
316 section_number: u8,
317 last_section_number: u8,
318 payload: &[u8],
319 trailer: [u8; 4],
320 ) -> Vec<u8> {
321 let section_length = (EXTENSION_LEN + payload.len() + CRC_LEN) as u16;
322 let flags = 0xC0
323 | ((payload_sc & 0x03) << 4)
324 | ((address_sc & 0x03) << 2)
325 | (u8::from(llc_snap) << 1)
326 | 0x01; let mut v = vec![
328 TABLE_ID,
329 (u8::from(ssi) << 7)
330 | (u8::from(private_indicator) << 6)
331 | 0x30
332 | ((section_length >> 8) as u8 & 0x0F),
333 (section_length & 0xFF) as u8,
334 mac_address[5], mac_address[4], flags,
337 section_number,
338 last_section_number,
339 mac_address[3], mac_address[2], mac_address[1], mac_address[0], ];
344 v.extend_from_slice(payload);
345 v.extend_from_slice(&trailer);
346 v
347 }
348
349 #[test]
350 fn parse_happy_path() {
351 let mac = [0x01, 0x00, 0x5E, 0x12, 0x34, 0x56];
352 let payload = [0xDE, 0xAD, 0xBE, 0xEF];
353 let bytes = build_mpe(
354 false,
355 true,
356 mac,
357 0b10,
358 0b01,
359 true,
360 2,
361 3,
362 &payload,
363 [0xAA, 0xBB, 0xCC, 0xDD],
364 );
365 let sec = MpeDatagramSection::parse(&bytes).unwrap();
366 assert!(!sec.section_syntax_indicator);
367 assert!(sec.private_indicator);
368 assert_eq!(sec.mac_address, mac);
369 assert_eq!(sec.payload_scrambling_control, 0b10);
370 assert_eq!(sec.address_scrambling_control, 0b01);
371 assert!(sec.llc_snap_flag);
372 assert!(sec.current_next_indicator);
373 assert_eq!(sec.section_number, 2);
374 assert_eq!(sec.last_section_number, 3);
375 assert_eq!(sec.payload, &payload);
376 assert_eq!(sec.checksum, [0xAA, 0xBB, 0xCC, 0xDD]);
377 }
378
379 #[test]
380 fn mac_scatter_decoded_in_network_order() {
381 let mac = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
383 let bytes = build_mpe(true, false, mac, 0, 0, false, 0, 0, &[], [0, 0, 0, 0]);
384 assert_eq!(bytes[3], 0x66, "byte 3 = MAC_6 (LSB)");
386 assert_eq!(bytes[4], 0x55, "byte 4 = MAC_5");
387 assert_eq!(bytes[8], 0x44, "byte 8 = MAC_4");
388 assert_eq!(bytes[9], 0x33, "byte 9 = MAC_3");
389 assert_eq!(bytes[10], 0x22, "byte 10 = MAC_2");
390 assert_eq!(bytes[11], 0x11, "byte 11 = MAC_1 (MSB)");
391 let sec = MpeDatagramSection::parse(&bytes).unwrap();
392 assert_eq!(sec.mac_address, mac);
393 }
394
395 #[test]
396 fn parse_empty_payload() {
397 let bytes = build_mpe(
398 true,
399 false,
400 [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
401 0,
402 0,
403 false,
404 0,
405 0,
406 &[],
407 [0, 0, 0, 0],
408 );
409 let sec = MpeDatagramSection::parse(&bytes).unwrap();
410 assert!(sec.payload.is_empty());
411 assert_eq!(sec.mac_address, [0xFF; 6]);
412 }
413
414 #[test]
415 fn parse_rejects_wrong_table_id() {
416 let mut bytes = build_mpe(
417 true,
418 false,
419 [0; 6],
420 0,
421 0,
422 false,
423 0,
424 0,
425 &[0x01],
426 [0, 0, 0, 0],
427 );
428 bytes[0] = 0x3F; assert!(matches!(
430 MpeDatagramSection::parse(&bytes).unwrap_err(),
431 Error::UnexpectedTableId { table_id: 0x3F, .. }
432 ));
433 }
434
435 #[test]
436 fn parse_rejects_short_buffer() {
437 let err = MpeDatagramSection::parse(&[TABLE_ID, 0x00]).unwrap_err();
438 assert!(matches!(err, Error::BufferTooShort { .. }));
439 }
440
441 #[test]
442 fn parse_rejects_section_length_overflow() {
443 let mut bytes = build_mpe(
444 true,
445 false,
446 [0; 6],
447 0,
448 0,
449 false,
450 0,
451 0,
452 &[0xAA],
453 [0, 0, 0, 0],
454 );
455 let fake_sl: u16 = (bytes.len() as u16) + 100 - HEADER_LEN as u16;
457 bytes[1] = (bytes[1] & 0xF0) | ((fake_sl >> 8) as u8 & 0x0F);
458 bytes[2] = (fake_sl & 0xFF) as u8;
459 assert!(matches!(
460 MpeDatagramSection::parse(&bytes).unwrap_err(),
461 Error::SectionLengthOverflow { .. }
462 ));
463 }
464
465 #[test]
466 fn round_trip_identity_ssi_set_crc() {
467 let mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
471 let payload = [0x45, 0x00, 0x00, 0x1C, 0x00, 0x01];
472 let original = MpeDatagramSection {
473 section_syntax_indicator: true,
474 private_indicator: false,
475 mac_address: mac,
476 payload_scrambling_control: 0,
477 address_scrambling_control: 0,
478 llc_snap_flag: false,
479 current_next_indicator: true,
480 section_number: 0,
481 last_section_number: 0,
482 payload: &payload,
483 checksum: [0, 0, 0, 0],
484 };
485 let mut buf = vec![0u8; original.serialized_len()];
486 original.serialize_into(&mut buf).unwrap();
487 let parsed = MpeDatagramSection::parse(&buf).unwrap();
488 assert!(parsed.section_syntax_indicator);
490 assert_eq!(parsed.mac_address, mac);
491 assert_eq!(parsed.payload, &payload);
492 let mut buf2 = vec![0u8; parsed.serialized_len()];
494 parsed.serialize_into(&mut buf2).unwrap();
495 assert_eq!(buf, buf2);
496 }
497
498 #[test]
499 fn round_trip_identity_ssi_clear_checksum_preserved() {
500 let mac = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
502 let payload = [0x11, 0x22, 0x33];
503 let trailer = [0x12, 0x34, 0x56, 0x78];
504 let bytes = build_mpe(false, true, mac, 0b11, 0b10, true, 1, 5, &payload, trailer);
505 let parsed = MpeDatagramSection::parse(&bytes).unwrap();
506 assert_eq!(parsed.checksum, trailer);
507 let mut buf = vec![0u8; parsed.serialized_len()];
508 parsed.serialize_into(&mut buf).unwrap();
509 assert_eq!(buf, bytes);
511 assert_eq!(MpeDatagramSection::parse(&buf).unwrap(), parsed);
512 }
513
514 #[test]
515 fn serialize_rejects_output_buffer_too_small() {
516 let sec = MpeDatagramSection {
517 section_syntax_indicator: true,
518 private_indicator: false,
519 mac_address: [0; 6],
520 payload_scrambling_control: 0,
521 address_scrambling_control: 0,
522 llc_snap_flag: false,
523 current_next_indicator: true,
524 section_number: 0,
525 last_section_number: 0,
526 payload: &[],
527 checksum: [0; 4],
528 };
529 let mut buf = [0u8; 2];
530 assert!(matches!(
531 sec.serialize_into(&mut buf).unwrap_err(),
532 Error::OutputBufferTooSmall { .. }
533 ));
534 }
535
536 #[test]
537 fn serialize_rejects_over_range_scrambling_control() {
538 let sec = MpeDatagramSection {
539 section_syntax_indicator: true,
540 private_indicator: false,
541 mac_address: [0; 6],
542 payload_scrambling_control: 0x04, address_scrambling_control: 0,
544 llc_snap_flag: false,
545 current_next_indicator: true,
546 section_number: 0,
547 last_section_number: 0,
548 payload: &[],
549 checksum: [0; 4],
550 };
551 let mut buf = vec![0u8; sec.serialized_len()];
552 assert!(matches!(
553 sec.serialize_into(&mut buf).unwrap_err(),
554 Error::ReservedBitsViolation {
555 field: "payload_scrambling_control",
556 ..
557 }
558 ));
559 }
560
561 #[test]
562 fn table_trait_constants() {
563 assert_eq!(<MpeDatagramSection as Table>::TABLE_ID, 0x3E);
564 assert_eq!(<MpeDatagramSection as Table>::PID, 0x0000);
565 }
566
567 #[cfg(feature = "serde")]
568 #[test]
569 fn serde_json_round_trip() {
570 let payload = [0xAB, 0xCD];
571 let bytes = build_mpe(
572 false,
573 true,
574 [0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F],
575 0b01,
576 0b11,
577 true,
578 3,
579 7,
580 &payload,
581 [0xDE, 0xAD, 0xBE, 0xEF],
582 );
583 let sec = MpeDatagramSection::parse(&bytes).unwrap();
584 let j = serde_json::to_string(&sec).unwrap();
585
586 let reparsed = MpeDatagramSection::parse(&bytes).unwrap();
594 assert_eq!(serde_json::to_string(&reparsed).unwrap(), j);
595
596 assert!(j.contains("\"mac_address\":[10,11,12,13,14,15]"));
599 assert!(j.contains("\"payload_scrambling_control\":1"));
600 assert!(j.contains("\"address_scrambling_control\":3"));
601 assert!(j.contains("\"checksum\":[222,173,190,239]"));
602 }
603}