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