1use crate::error::{Error, Result};
33use dvb_common::{Parse, Serialize};
34
35pub const TABLE_ID: u8 = 0x3E;
38
39pub const PID: u16 = 0x0000;
41
42const HEADER_LEN: usize = 3;
44
45const EXTENSION_LEN: usize = 9;
48
49const CRC_LEN: usize = 4;
51
52const MIN_SECTION_LEN: usize = HEADER_LEN + EXTENSION_LEN + CRC_LEN;
54
55#[derive(Debug, Clone, PartialEq, Eq)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize))]
72#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
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 =
145 super::check_section_length(bytes.len(), HEADER_LEN, section_length, MIN_SECTION_LEN)?;
146
147 let mac_6 = bytes[3];
150 let mac_5 = bytes[4];
151
152 let payload_scrambling_control = (bytes[5] >> 4) & 0x03;
154 let address_scrambling_control = (bytes[5] >> 2) & 0x03;
155 let llc_snap_flag = (bytes[5] & 0x02) != 0;
156 let current_next_indicator = (bytes[5] & 0x01) != 0;
157
158 let section_number = bytes[6];
159 let last_section_number = bytes[7];
160
161 let mac_4 = bytes[8];
162 let mac_3 = bytes[9];
163 let mac_2 = bytes[10];
164 let mac_1 = bytes[11];
165 let mac_address = [mac_1, mac_2, mac_3, mac_4, mac_5, mac_6];
166
167 let payload_start = HEADER_LEN + EXTENSION_LEN;
168 let trailer_start = total - CRC_LEN;
169 let payload = &bytes[payload_start..trailer_start];
170 let checksum = [
171 bytes[trailer_start],
172 bytes[trailer_start + 1],
173 bytes[trailer_start + 2],
174 bytes[trailer_start + 3],
175 ];
176
177 Ok(MpeDatagramSection {
178 section_syntax_indicator,
179 private_indicator,
180 mac_address,
181 payload_scrambling_control,
182 address_scrambling_control,
183 llc_snap_flag,
184 current_next_indicator,
185 section_number,
186 last_section_number,
187 payload,
188 checksum,
189 })
190 }
191}
192
193impl Serialize for MpeDatagramSection<'_> {
194 type Error = crate::error::Error;
195
196 fn serialized_len(&self) -> usize {
197 HEADER_LEN + EXTENSION_LEN + self.payload.len() + CRC_LEN
198 }
199
200 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
201 let len = self.serialized_len();
202 if buf.len() < len {
203 return Err(Error::OutputBufferTooSmall {
204 need: len,
205 have: buf.len(),
206 });
207 }
208
209 if self.payload_scrambling_control > 0x03 {
212 return Err(Error::ReservedBitsViolation {
213 field: "payload_scrambling_control",
214 reason: "value exceeds 2-bit field",
215 });
216 }
217 if self.address_scrambling_control > 0x03 {
218 return Err(Error::ReservedBitsViolation {
219 field: "address_scrambling_control",
220 reason: "value exceeds 2-bit field",
221 });
222 }
223
224 let section_length = (len - HEADER_LEN) as u16;
225 if section_length > 0x0FFF {
226 return Err(Error::SectionLengthOverflow {
227 declared: section_length as usize,
228 available: 0x0FFF,
229 });
230 }
231
232 buf[0] = TABLE_ID;
233 buf[1] = (u8::from(self.section_syntax_indicator) << 7)
235 | (u8::from(self.private_indicator) << 6)
236 | 0x30 | ((section_length >> 8) as u8 & 0x0F);
238 buf[2] = (section_length & 0xFF) as u8;
239
240 buf[3] = self.mac_address[5];
242 buf[4] = self.mac_address[4];
243
244 buf[5] = 0xC0
246 | ((self.payload_scrambling_control & 0x03) << 4)
247 | ((self.address_scrambling_control & 0x03) << 2)
248 | (u8::from(self.llc_snap_flag) << 1)
249 | u8::from(self.current_next_indicator);
250
251 buf[6] = self.section_number;
252 buf[7] = self.last_section_number;
253
254 buf[8] = self.mac_address[3];
256 buf[9] = self.mac_address[2];
257 buf[10] = self.mac_address[1];
258 buf[11] = self.mac_address[0];
259
260 let payload_start = HEADER_LEN + EXTENSION_LEN;
261 let trailer_start = payload_start + self.payload.len();
262 buf[payload_start..trailer_start].copy_from_slice(self.payload);
263
264 if self.section_syntax_indicator {
265 let crc = dvb_common::crc32_mpeg2::compute(&buf[..trailer_start]);
267 buf[trailer_start..len].copy_from_slice(&crc.to_be_bytes());
268 } else {
269 buf[trailer_start..len].copy_from_slice(&self.checksum);
272 }
273
274 Ok(len)
275 }
276}
277impl<'a> crate::traits::TableDef<'a> for MpeDatagramSection<'a> {
278 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
283 const NAME: &'static str = "MPE_DATAGRAM_SECTION";
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[allow(clippy::too_many_arguments)]
297 fn build_mpe(
298 ssi: bool,
299 private_indicator: bool,
300 mac_address: [u8; 6],
301 payload_sc: u8,
302 address_sc: u8,
303 llc_snap: bool,
304 section_number: u8,
305 last_section_number: u8,
306 payload: &[u8],
307 trailer: [u8; 4],
308 ) -> Vec<u8> {
309 let section_length = (EXTENSION_LEN + payload.len() + CRC_LEN) as u16;
310 let flags = 0xC0
311 | ((payload_sc & 0x03) << 4)
312 | ((address_sc & 0x03) << 2)
313 | (u8::from(llc_snap) << 1)
314 | 0x01; let mut v = vec![
316 TABLE_ID,
317 (u8::from(ssi) << 7)
318 | (u8::from(private_indicator) << 6)
319 | 0x30
320 | ((section_length >> 8) as u8 & 0x0F),
321 (section_length & 0xFF) as u8,
322 mac_address[5], mac_address[4], flags,
325 section_number,
326 last_section_number,
327 mac_address[3], mac_address[2], mac_address[1], mac_address[0], ];
332 v.extend_from_slice(payload);
333 v.extend_from_slice(&trailer);
334 v
335 }
336
337 #[test]
338 fn parse_happy_path() {
339 let mac = [0x01, 0x00, 0x5E, 0x12, 0x34, 0x56];
340 let payload = [0xDE, 0xAD, 0xBE, 0xEF];
341 let bytes = build_mpe(
342 false,
343 true,
344 mac,
345 0b10,
346 0b01,
347 true,
348 2,
349 3,
350 &payload,
351 [0xAA, 0xBB, 0xCC, 0xDD],
352 );
353 let sec = MpeDatagramSection::parse(&bytes).unwrap();
354 assert!(!sec.section_syntax_indicator);
355 assert!(sec.private_indicator);
356 assert_eq!(sec.mac_address, mac);
357 assert_eq!(sec.payload_scrambling_control, 0b10);
358 assert_eq!(sec.address_scrambling_control, 0b01);
359 assert!(sec.llc_snap_flag);
360 assert!(sec.current_next_indicator);
361 assert_eq!(sec.section_number, 2);
362 assert_eq!(sec.last_section_number, 3);
363 assert_eq!(sec.payload, &payload);
364 assert_eq!(sec.checksum, [0xAA, 0xBB, 0xCC, 0xDD]);
365 }
366
367 #[test]
368 fn mac_scatter_decoded_in_network_order() {
369 let mac = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
371 let bytes = build_mpe(true, false, mac, 0, 0, false, 0, 0, &[], [0, 0, 0, 0]);
372 assert_eq!(bytes[3], 0x66, "byte 3 = MAC_6 (LSB)");
374 assert_eq!(bytes[4], 0x55, "byte 4 = MAC_5");
375 assert_eq!(bytes[8], 0x44, "byte 8 = MAC_4");
376 assert_eq!(bytes[9], 0x33, "byte 9 = MAC_3");
377 assert_eq!(bytes[10], 0x22, "byte 10 = MAC_2");
378 assert_eq!(bytes[11], 0x11, "byte 11 = MAC_1 (MSB)");
379 let sec = MpeDatagramSection::parse(&bytes).unwrap();
380 assert_eq!(sec.mac_address, mac);
381 }
382
383 #[test]
384 fn parse_empty_payload() {
385 let bytes = build_mpe(
386 true,
387 false,
388 [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
389 0,
390 0,
391 false,
392 0,
393 0,
394 &[],
395 [0, 0, 0, 0],
396 );
397 let sec = MpeDatagramSection::parse(&bytes).unwrap();
398 assert!(sec.payload.is_empty());
399 assert_eq!(sec.mac_address, [0xFF; 6]);
400 }
401
402 #[test]
403 fn parse_rejects_wrong_table_id() {
404 let mut bytes = build_mpe(
405 true,
406 false,
407 [0; 6],
408 0,
409 0,
410 false,
411 0,
412 0,
413 &[0x01],
414 [0, 0, 0, 0],
415 );
416 bytes[0] = 0x3F; assert!(matches!(
418 MpeDatagramSection::parse(&bytes).unwrap_err(),
419 Error::UnexpectedTableId { table_id: 0x3F, .. }
420 ));
421 }
422
423 #[test]
424 fn parse_rejects_short_buffer() {
425 let err = MpeDatagramSection::parse(&[TABLE_ID, 0x00]).unwrap_err();
426 assert!(matches!(err, Error::BufferTooShort { .. }));
427 }
428
429 #[test]
430 fn parse_rejects_section_length_overflow() {
431 let mut bytes = build_mpe(
432 true,
433 false,
434 [0; 6],
435 0,
436 0,
437 false,
438 0,
439 0,
440 &[0xAA],
441 [0, 0, 0, 0],
442 );
443 let fake_sl: u16 = (bytes.len() as u16) + 100 - HEADER_LEN as u16;
445 bytes[1] = (bytes[1] & 0xF0) | ((fake_sl >> 8) as u8 & 0x0F);
446 bytes[2] = (fake_sl & 0xFF) as u8;
447 assert!(matches!(
448 MpeDatagramSection::parse(&bytes).unwrap_err(),
449 Error::SectionLengthOverflow { .. }
450 ));
451 }
452
453 #[test]
454 fn round_trip_identity_ssi_set_crc() {
455 let mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
459 let payload = [0x45, 0x00, 0x00, 0x1C, 0x00, 0x01];
460 let original = MpeDatagramSection {
461 section_syntax_indicator: true,
462 private_indicator: false,
463 mac_address: mac,
464 payload_scrambling_control: 0,
465 address_scrambling_control: 0,
466 llc_snap_flag: false,
467 current_next_indicator: true,
468 section_number: 0,
469 last_section_number: 0,
470 payload: &payload,
471 checksum: [0, 0, 0, 0],
472 };
473 let mut buf = vec![0u8; original.serialized_len()];
474 original.serialize_into(&mut buf).unwrap();
475 let parsed = MpeDatagramSection::parse(&buf).unwrap();
476 assert!(parsed.section_syntax_indicator);
478 assert_eq!(parsed.mac_address, mac);
479 assert_eq!(parsed.payload, &payload);
480 let mut buf2 = vec![0u8; parsed.serialized_len()];
482 parsed.serialize_into(&mut buf2).unwrap();
483 assert_eq!(buf, buf2);
484 }
485
486 #[test]
487 fn round_trip_identity_ssi_clear_checksum_preserved() {
488 let mac = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
490 let payload = [0x11, 0x22, 0x33];
491 let trailer = [0x12, 0x34, 0x56, 0x78];
492 let bytes = build_mpe(false, true, mac, 0b11, 0b10, true, 1, 5, &payload, trailer);
493 let parsed = MpeDatagramSection::parse(&bytes).unwrap();
494 assert_eq!(parsed.checksum, trailer);
495 let mut buf = vec![0u8; parsed.serialized_len()];
496 parsed.serialize_into(&mut buf).unwrap();
497 assert_eq!(buf, bytes);
499 assert_eq!(MpeDatagramSection::parse(&buf).unwrap(), parsed);
500 }
501
502 #[test]
503 fn serialize_rejects_output_buffer_too_small() {
504 let sec = MpeDatagramSection {
505 section_syntax_indicator: true,
506 private_indicator: false,
507 mac_address: [0; 6],
508 payload_scrambling_control: 0,
509 address_scrambling_control: 0,
510 llc_snap_flag: false,
511 current_next_indicator: true,
512 section_number: 0,
513 last_section_number: 0,
514 payload: &[],
515 checksum: [0; 4],
516 };
517 let mut buf = [0u8; 2];
518 assert!(matches!(
519 sec.serialize_into(&mut buf).unwrap_err(),
520 Error::OutputBufferTooSmall { .. }
521 ));
522 }
523
524 #[test]
525 fn serialize_rejects_over_range_scrambling_control() {
526 let sec = MpeDatagramSection {
527 section_syntax_indicator: true,
528 private_indicator: false,
529 mac_address: [0; 6],
530 payload_scrambling_control: 0x04, address_scrambling_control: 0,
532 llc_snap_flag: false,
533 current_next_indicator: true,
534 section_number: 0,
535 last_section_number: 0,
536 payload: &[],
537 checksum: [0; 4],
538 };
539 let mut buf = vec![0u8; sec.serialized_len()];
540 assert!(matches!(
541 sec.serialize_into(&mut buf).unwrap_err(),
542 Error::ReservedBitsViolation {
543 field: "payload_scrambling_control",
544 ..
545 }
546 ));
547 }
548
549 #[test]
550 fn table_trait_constants() {
551 assert_eq!(TABLE_ID, 0x3E);
552 assert_eq!(PID, 0x0000);
553 }
554
555 #[cfg(feature = "serde")]
556 #[test]
557 fn serde_json_round_trip() {
558 let payload = [0xAB, 0xCD];
559 let bytes = build_mpe(
560 false,
561 true,
562 [0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F],
563 0b01,
564 0b11,
565 true,
566 3,
567 7,
568 &payload,
569 [0xDE, 0xAD, 0xBE, 0xEF],
570 );
571 let sec = MpeDatagramSection::parse(&bytes).unwrap();
572 let j = serde_json::to_string(&sec).unwrap();
573
574 let reparsed = MpeDatagramSection::parse(&bytes).unwrap();
582 assert_eq!(serde_json::to_string(&reparsed).unwrap(), j);
583
584 assert!(j.contains("\"mac_address\":[10,11,12,13,14,15]"));
587 assert!(j.contains("\"payload_scrambling_control\":1"));
588 assert!(j.contains("\"address_scrambling_control\":3"));
589 assert!(j.contains("\"checksum\":[222,173,190,239]"));
590 }
591
592 #[test]
593 fn parse_rejects_zero_section_length() {
594 let mut buf = vec![0u8; 64];
595 buf[0] = TABLE_ID;
596 buf[1] = 0xF0;
597 buf[2] = 0x00;
598 for b in &mut buf[3..] {
599 *b = 0xFF;
600 }
601 assert!(matches!(
602 MpeDatagramSection::parse(&buf).unwrap_err(),
603 Error::SectionLengthOverflow { .. }
604 ));
605 }
606}