1use crate::descriptors::DescriptorLoop;
11use crate::error::{Error, Result};
12use dvb_common::{Parse, Serialize};
13
14pub const TABLE_ID_PF_ACTUAL: u8 = 0x4E;
16pub const TABLE_ID_PF_OTHER: u8 = 0x4F;
18pub const TABLE_ID_SCHEDULE_ACTUAL_FIRST: u8 = 0x50;
20pub const TABLE_ID_SCHEDULE_ACTUAL_LAST: u8 = 0x5F;
22pub const TABLE_ID_SCHEDULE_OTHER_FIRST: u8 = 0x60;
24pub const TABLE_ID_SCHEDULE_OTHER_LAST: u8 = 0x6F;
26pub const PID: u16 = 0x0012;
28
29const MIN_HEADER_LEN: usize = 3;
30const EXTENSION_HEADER_LEN: usize = 5;
31const POST_EXTENSION_LEN: usize = 6;
34const CRC_LEN: usize = 4;
35const MIN_SECTION_LEN: usize = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + CRC_LEN;
36const EVENT_HEADER_LEN: usize = 12;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize))]
41pub enum EitKind {
42 PresentFollowingActual,
44 PresentFollowingOther,
46 ScheduleActual,
48 ScheduleOther,
50}
51
52impl EitKind {
53 #[must_use]
55 pub fn from_table_id(table_id: u8) -> Option<Self> {
56 match table_id {
57 TABLE_ID_PF_ACTUAL => Some(Self::PresentFollowingActual),
58 TABLE_ID_PF_OTHER => Some(Self::PresentFollowingOther),
59 TABLE_ID_SCHEDULE_ACTUAL_FIRST..=TABLE_ID_SCHEDULE_ACTUAL_LAST => {
60 Some(Self::ScheduleActual)
61 }
62 TABLE_ID_SCHEDULE_OTHER_FIRST..=TABLE_ID_SCHEDULE_OTHER_LAST => {
63 Some(Self::ScheduleOther)
64 }
65 _ => None,
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize))]
73#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
74pub struct EitEvent<'a> {
75 pub event_id: u16,
77 pub start_time_raw: [u8; 5],
79 pub duration_raw: [u8; 3],
81 pub running_status: u8,
83 pub free_ca_mode: bool,
85 pub descriptors: DescriptorLoop<'a>,
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize))]
93#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
94pub struct EitSection<'a> {
95 pub kind: EitKind,
97 pub table_id: u8,
99 pub service_id: u16,
101 pub version_number: u8,
103 pub current_next_indicator: bool,
105 pub section_number: u8,
107 pub last_section_number: u8,
109 pub transport_stream_id: u16,
111 pub original_network_id: u16,
113 pub segment_last_section_number: u8,
115 pub last_table_id: u8,
117 pub events: Vec<EitEvent<'a>>,
119}
120
121impl<'a> Parse<'a> for EitSection<'a> {
122 type Error = crate::error::Error;
123 fn parse(bytes: &'a [u8]) -> Result<Self> {
124 let min_len = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + CRC_LEN;
125 if bytes.len() < min_len {
126 return Err(Error::BufferTooShort {
127 need: min_len,
128 have: bytes.len(),
129 what: "EitSection",
130 });
131 }
132
133 let table_id = bytes[0];
134 let kind = EitKind::from_table_id(table_id).ok_or(Error::UnexpectedTableId {
135 table_id,
136 what: "EitSection",
137 expected: &[
138 TABLE_ID_PF_ACTUAL,
139 TABLE_ID_PF_OTHER,
140 TABLE_ID_SCHEDULE_ACTUAL_FIRST,
141 TABLE_ID_SCHEDULE_OTHER_FIRST,
142 ],
143 })?;
144
145 let section_length = ((bytes[1] & 0x0F) as u16) << 8 | bytes[2] as u16;
146 let total = super::check_section_length(
147 bytes.len(),
148 MIN_HEADER_LEN,
149 section_length as usize,
150 MIN_SECTION_LEN,
151 )?;
152
153 let service_id = u16::from_be_bytes([bytes[3], bytes[4]]);
154 let version_number = (bytes[5] >> 1) & 0x1F;
155 let current_next_indicator = (bytes[5] & 0x01) != 0;
156 let section_number = bytes[6];
157 let last_section_number = bytes[7];
158
159 let transport_stream_id = u16::from_be_bytes([bytes[8], bytes[9]]);
160 let original_network_id = u16::from_be_bytes([bytes[10], bytes[11]]);
161 let segment_last_section_number = bytes[12];
162 let last_table_id = bytes[13];
163
164 let events_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN;
165 let events_end = total - CRC_LEN;
166 let mut events = Vec::new();
167 let mut pos = events_start;
168 while pos + EVENT_HEADER_LEN <= events_end {
169 let event_id = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
170 let start_time_raw = [
171 bytes[pos + 2],
172 bytes[pos + 3],
173 bytes[pos + 4],
174 bytes[pos + 5],
175 bytes[pos + 6],
176 ];
177 let duration_raw = [bytes[pos + 7], bytes[pos + 8], bytes[pos + 9]];
178 let status_and_len_hi = bytes[pos + 10];
179 let running_status = (status_and_len_hi >> 5) & 0x07;
180 let free_ca_mode = (status_and_len_hi & 0x10) != 0;
181 let descriptors_loop_length =
182 (((status_and_len_hi & 0x0F) as usize) << 8) | bytes[pos + 11] as usize;
183 let desc_start = pos + EVENT_HEADER_LEN;
184 let desc_end = desc_start + descriptors_loop_length;
185 if desc_end > events_end {
186 return Err(Error::SectionLengthOverflow {
187 declared: descriptors_loop_length,
188 available: events_end.saturating_sub(desc_start),
189 });
190 }
191 events.push(EitEvent {
192 event_id,
193 start_time_raw,
194 duration_raw,
195 running_status,
196 free_ca_mode,
197 descriptors: DescriptorLoop::new(&bytes[desc_start..desc_end]),
198 });
199 pos = desc_end;
200 }
201
202 if pos != events_end {
203 return Err(Error::BufferTooShort {
204 need: events_end - pos,
205 have: 0,
206 what: "EitSection trailing event bytes",
207 });
208 }
209
210 Ok(EitSection {
211 kind,
212 table_id,
213 service_id,
214 version_number,
215 current_next_indicator,
216 section_number,
217 last_section_number,
218 transport_stream_id,
219 original_network_id,
220 segment_last_section_number,
221 last_table_id,
222 events,
223 })
224 }
225}
226
227impl Serialize for EitSection<'_> {
228 type Error = crate::error::Error;
229 fn serialized_len(&self) -> usize {
230 let ev_bytes: usize = self
231 .events
232 .iter()
233 .map(|e| EVENT_HEADER_LEN + e.descriptors.len())
234 .sum();
235 MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + ev_bytes + CRC_LEN
236 }
237
238 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
239 let len = self.serialized_len();
240 if buf.len() < len {
241 return Err(Error::OutputBufferTooSmall {
242 need: len,
243 have: buf.len(),
244 });
245 }
246 let section_length_usize = len - MIN_HEADER_LEN;
247 if section_length_usize > 0x0FFF {
248 return Err(Error::SectionLengthOverflow {
249 declared: section_length_usize,
250 available: 0x0FFF,
251 });
252 }
253 let section_length: u16 = section_length_usize as u16;
254 buf[0] = self.table_id;
255 buf[1] = super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F);
256 buf[2] = (section_length & 0xFF) as u8;
257 buf[3..5].copy_from_slice(&self.service_id.to_be_bytes());
258 buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
259 buf[6] = self.section_number;
260 buf[7] = self.last_section_number;
261 buf[8..10].copy_from_slice(&self.transport_stream_id.to_be_bytes());
262 buf[10..12].copy_from_slice(&self.original_network_id.to_be_bytes());
263 buf[12] = self.segment_last_section_number;
264 buf[13] = self.last_table_id;
265
266 let mut pos = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + POST_EXTENSION_LEN;
267 for ev in &self.events {
268 buf[pos..pos + 2].copy_from_slice(&ev.event_id.to_be_bytes());
269 buf[pos + 2..pos + 7].copy_from_slice(&ev.start_time_raw);
270 buf[pos + 7..pos + 10].copy_from_slice(&ev.duration_raw);
271 let dll = ev.descriptors.len() as u16;
272 buf[pos + 10] = ((ev.running_status & 0x07) << 5)
273 | (u8::from(ev.free_ca_mode) << 4)
274 | ((dll >> 8) as u8 & 0x0F);
275 buf[pos + 11] = (dll & 0xFF) as u8;
276 let desc_start = pos + EVENT_HEADER_LEN;
277 buf[desc_start..desc_start + ev.descriptors.len()]
278 .copy_from_slice(ev.descriptors.raw());
279 pos = desc_start + ev.descriptors.len();
280 }
281
282 let crc_pos = len - CRC_LEN;
283 let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
284 buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
285 Ok(len)
286 }
287}
288impl<'a> crate::traits::TableDef<'a> for EitSection<'a> {
289 const TABLE_ID_RANGES: &'static [(u8, u8)] =
290 &[(TABLE_ID_PF_ACTUAL, TABLE_ID_SCHEDULE_OTHER_LAST)];
291 const NAME: &'static str = "EVENT_INFORMATION";
292}
293
294impl EitEvent<'_> {
295 #[must_use]
300 pub fn duration(&self) -> Option<core::time::Duration> {
301 dvb_common::time::decode_bcd_duration(self.duration_raw)
302 }
303
304 pub fn set_duration(&mut self, duration: core::time::Duration) -> crate::Result<()> {
310 self.duration_raw = dvb_common::time::encode_bcd_duration(duration).ok_or(
311 crate::Error::ValueOutOfRange {
312 field: "EitEvent::duration",
313 reason: "duration must be < 100 hours",
314 },
315 )?;
316 Ok(())
317 }
318}
319
320#[cfg(feature = "chrono")]
321impl EitEvent<'_> {
322 #[must_use]
327 pub fn start_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
328 dvb_common::time::decode_mjd_bcd_utc(self.start_time_raw)
329 }
330
331 pub fn set_start_time(
337 &mut self,
338 start_time: chrono::DateTime<chrono::Utc>,
339 ) -> crate::Result<()> {
340 self.start_time_raw = dvb_common::time::encode_mjd_bcd_utc(start_time).ok_or(
341 crate::Error::ValueOutOfRange {
342 field: "EitEvent::start_time",
343 reason: "date not representable in 16-bit MJD",
344 },
345 )?;
346 Ok(())
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353
354 type TestEvent = (u16, [u8; 5], [u8; 3], u8, bool, Vec<u8>);
355
356 fn build_eit(
357 table_id: u8,
358 service_id: u16,
359 version: u8,
360 tsid: u16,
361 onid: u16,
362 events: &[TestEvent],
363 ) -> Vec<u8> {
364 let ev_bytes: usize = events
365 .iter()
366 .map(|(_, _, _, _, _, d)| EVENT_HEADER_LEN + d.len())
367 .sum();
368 let section_length: u16 =
369 (EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + ev_bytes + CRC_LEN) as u16;
370 let mut v = Vec::new();
371 v.push(table_id);
372 v.push(super::super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F));
373 v.push((section_length & 0xFF) as u8);
374 v.extend_from_slice(&service_id.to_be_bytes());
375 v.push(0xC0 | ((version & 0x1F) << 1) | 0x01);
376 v.push(0);
377 v.push(0);
378 v.extend_from_slice(&tsid.to_be_bytes());
379 v.extend_from_slice(&onid.to_be_bytes());
380 v.push(0);
381 v.push(table_id);
382 for (eid, start, dur, rs, fca, desc) in events {
383 v.extend_from_slice(&eid.to_be_bytes());
384 v.extend_from_slice(start);
385 v.extend_from_slice(dur);
386 let dll = desc.len() as u16;
387 v.push(((*rs & 0x07) << 5) | (u8::from(*fca) << 4) | ((dll >> 8) as u8 & 0x0F));
388 v.push((dll & 0xFF) as u8);
389 v.extend_from_slice(desc);
390 }
391 v.extend_from_slice(&[0, 0, 0, 0]);
392 v
393 }
394
395 #[test]
396 fn parse_pf_actual_and_other_map_to_correct_kind() {
397 for (tid, expected) in [
398 (TABLE_ID_PF_ACTUAL, EitKind::PresentFollowingActual),
399 (TABLE_ID_PF_OTHER, EitKind::PresentFollowingOther),
400 ] {
401 let bytes = build_eit(tid, 1, 0, 0x20, 0x30, &[]);
402 assert_eq!(EitSection::parse(&bytes).unwrap().kind, expected);
403 }
404 }
405
406 #[test]
407 fn schedule_tables_0x50_through_0x5f_all_decode_as_schedule_actual() {
408 for tid in TABLE_ID_SCHEDULE_ACTUAL_FIRST..=TABLE_ID_SCHEDULE_ACTUAL_LAST {
409 let bytes = build_eit(tid, 1, 0, 0x20, 0x30, &[]);
410 assert_eq!(
411 EitSection::parse(&bytes).unwrap().kind,
412 EitKind::ScheduleActual
413 );
414 }
415 }
416
417 #[test]
418 fn schedule_tables_0x60_through_0x6f_all_decode_as_schedule_other() {
419 for tid in TABLE_ID_SCHEDULE_OTHER_FIRST..=TABLE_ID_SCHEDULE_OTHER_LAST {
420 let bytes = build_eit(tid, 1, 0, 0x20, 0x30, &[]);
421 assert_eq!(
422 EitSection::parse(&bytes).unwrap().kind,
423 EitKind::ScheduleOther
424 );
425 }
426 }
427
428 #[test]
429 fn event_loop_with_descriptor_bytes_preserved() {
430 let desc = vec![0x4D, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05];
431 let bytes = build_eit(
432 TABLE_ID_PF_ACTUAL,
433 1,
434 0,
435 0x20,
436 0x30,
437 &[(
438 42,
439 [0xDF, 0xA1, 0x12, 0x34, 0x56],
440 [0x00, 0x30, 0x00],
441 4,
442 false,
443 desc.clone(),
444 )],
445 );
446 let eit = EitSection::parse(&bytes).unwrap();
447 assert_eq!(eit.events.len(), 1);
448 assert_eq!(eit.events[0].event_id, 42);
449 assert_eq!(eit.events[0].descriptors.raw(), &desc[..]);
450 }
451
452 #[test]
453 fn running_status_extracted() {
454 let bytes = build_eit(
455 TABLE_ID_PF_ACTUAL,
456 1,
457 0,
458 0x20,
459 0x30,
460 &[(1, [0; 5], [0; 3], 2, false, vec![])],
461 );
462 assert_eq!(
463 EitSection::parse(&bytes).unwrap().events[0].running_status,
464 2
465 );
466 }
467
468 #[test]
469 fn free_ca_mode_flag_extracted() {
470 let bytes = build_eit(
471 TABLE_ID_PF_ACTUAL,
472 1,
473 0,
474 0x20,
475 0x30,
476 &[(1, [0; 5], [0; 3], 0, true, vec![])],
477 );
478 assert!(EitSection::parse(&bytes).unwrap().events[0].free_ca_mode);
479 }
480
481 #[test]
482 fn serialize_round_trip_preserves_all_events() {
483 let desc1: [u8; 2] = [0x54, 0x00];
484 let eit = EitSection {
485 kind: EitKind::PresentFollowingActual,
486 table_id: TABLE_ID_PF_ACTUAL,
487 service_id: 0x0100,
488 version_number: 3,
489 current_next_indicator: true,
490 section_number: 0,
491 last_section_number: 0,
492 transport_stream_id: 0x1234,
493 original_network_id: 0x0020,
494 segment_last_section_number: 0,
495 last_table_id: TABLE_ID_PF_ACTUAL,
496 events: vec![
497 EitEvent {
498 event_id: 1,
499 start_time_raw: [0xDF, 0xA1, 0x12, 0x34, 0x56],
500 duration_raw: [0x00, 0x30, 0x00],
501 running_status: 4,
502 free_ca_mode: false,
503 descriptors: DescriptorLoop::new(&desc1),
504 },
505 EitEvent {
506 event_id: 2,
507 start_time_raw: [0xDF, 0xA1, 0x13, 0x00, 0x00],
508 duration_raw: [0x01, 0x00, 0x00],
509 running_status: 1,
510 free_ca_mode: true,
511 descriptors: DescriptorLoop::new(&[]),
512 },
513 ],
514 };
515 let mut buf = vec![0u8; eit.serialized_len()];
516 eit.serialize_into(&mut buf).unwrap();
517 let re = EitSection::parse(&buf).unwrap();
518 assert_eq!(eit, re);
519 }
520
521 #[test]
522 fn zero_events_is_valid() {
523 let bytes = build_eit(TABLE_ID_PF_ACTUAL, 1, 0, 0x20, 0x30, &[]);
524 let eit = EitSection::parse(&bytes).unwrap();
525 assert_eq!(eit.events.len(), 0);
526 }
527
528 #[test]
529 #[cfg(feature = "chrono")]
530 fn event_start_time_decodes_to_utc_datetime() {
531 let mjd: u16 = 59945;
533 let ev = EitEvent {
534 event_id: 1,
535 start_time_raw: [(mjd >> 8) as u8, (mjd & 0xFF) as u8, 0x12, 0x34, 0x56],
536 duration_raw: [0, 0, 0],
537 running_status: 0,
538 free_ca_mode: false,
539 descriptors: DescriptorLoop::new(&[]),
540 };
541 let dt = ev.start_time().unwrap();
542 use chrono::Datelike;
543 assert_eq!(dt.year(), 2023);
544 assert_eq!(dt.month(), 1);
545 assert_eq!(dt.day(), 1);
546 use chrono::Timelike;
547 assert_eq!(dt.hour(), 12);
548 assert_eq!(dt.minute(), 34);
549 assert_eq!(dt.second(), 56);
550 }
551
552 #[test]
553 fn parse_rejects_wrong_tag() {
554 let bytes = build_eit(0x00, 1, 0, 0x20, 0x30, &[]);
555 let err = EitSection::parse(&bytes).unwrap_err();
556 assert!(matches!(
557 err,
558 Error::UnexpectedTableId { table_id: 0x00, .. }
559 ));
560 }
561
562 #[test]
563 fn parse_rejects_truncated_header() {
564 let bytes = [0x4Eu8, 0xF0, 0x00];
565 let err = EitSection::parse(&bytes).unwrap_err();
566 assert!(matches!(
567 err,
568 Error::BufferTooShort {
569 need: 18,
570 have: 3,
571 ..
572 }
573 ));
574 }
575
576 #[test]
577 fn parse_rejects_event_descriptor_loop_overflow() {
578 let section_length: u16 =
579 (EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + EVENT_HEADER_LEN + CRC_LEN) as u16;
580 let mut v = Vec::new();
581 v.push(TABLE_ID_PF_ACTUAL);
582 v.push(super::super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F));
583 v.push((section_length & 0xFF) as u8);
584 v.extend_from_slice(&1u16.to_be_bytes());
585 v.push(0xC1);
586 v.push(0);
587 v.push(0);
588 v.extend_from_slice(&0x0020u16.to_be_bytes());
589 v.extend_from_slice(&0x0030u16.to_be_bytes());
590 v.push(0);
591 v.push(TABLE_ID_PF_ACTUAL);
592 v.extend_from_slice(&1u16.to_be_bytes());
593 v.extend_from_slice(&[0u8; 5]);
594 v.extend_from_slice(&[0u8; 3]);
595 v.push(0x00);
596 v.push(0x0A);
597 v.extend_from_slice(&[0u8; 4]);
598 let err = EitSection::parse(&v).unwrap_err();
601 assert!(matches!(
602 err,
603 Error::SectionLengthOverflow { declared: 10, .. }
604 ));
605 }
606
607 #[test]
608 fn structured_fields_segment_and_last_table_id_preserved() {
609 let desc: [u8; 2] = [0x54, 0x00];
610 let bytes = build_eit(
611 TABLE_ID_SCHEDULE_ACTUAL_FIRST,
612 0x0100,
613 7,
614 0x0020,
615 0x0030,
616 &[(
617 42,
618 [0xDF, 0xA1, 0x12, 0x34, 0x56],
619 [0x00, 0x30, 0x00],
620 4,
621 false,
622 desc.to_vec(),
623 )],
624 );
625 let eit = EitSection::parse(&bytes).unwrap();
626 assert_eq!(eit.kind, EitKind::ScheduleActual);
627 assert_eq!(eit.table_id, TABLE_ID_SCHEDULE_ACTUAL_FIRST);
628 assert_eq!(eit.service_id, 0x0100);
629 assert_eq!(eit.version_number, 7);
630 assert!(eit.current_next_indicator);
631 assert_eq!(eit.section_number, 0);
632 assert_eq!(eit.last_section_number, 0);
633 assert_eq!(eit.transport_stream_id, 0x0020);
634 assert_eq!(eit.original_network_id, 0x0030);
635 assert_eq!(eit.segment_last_section_number, 0);
636 assert_eq!(eit.last_table_id, TABLE_ID_SCHEDULE_ACTUAL_FIRST);
637 assert_eq!(eit.events.len(), 1);
638 assert_eq!(eit.events[0].event_id, 42);
639 assert_eq!(eit.events[0].running_status, 4);
640 assert!(!eit.events[0].free_ca_mode);
641 assert_eq!(eit.events[0].descriptors.raw(), &desc[..]);
643 }
644
645 #[test]
646 fn parse_rejects_zero_section_length() {
647 let mut buf = vec![0u8; 64];
648 buf[0] = TABLE_ID_PF_ACTUAL;
649 buf[1] = 0xF0;
650 buf[2] = 0x00;
651 for b in &mut buf[3..] {
652 *b = 0xFF;
653 }
654 assert!(matches!(
655 EitSection::parse(&buf).unwrap_err(),
656 Error::SectionLengthOverflow { .. }
657 ));
658 }
659
660 #[test]
661 fn parse_rejects_trailing_slack_bytes() {
662 let section_length: u16 =
663 (EXTENSION_HEADER_LEN + POST_EXTENSION_LEN + EVENT_HEADER_LEN + 1 + CRC_LEN) as u16;
664 let mut v = Vec::new();
665 v.push(TABLE_ID_PF_ACTUAL);
666 v.push(super::super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F));
667 v.push((section_length & 0xFF) as u8);
668 v.extend_from_slice(&1u16.to_be_bytes());
669 v.push(0xC1);
670 v.push(0);
671 v.push(0);
672 v.extend_from_slice(&0x0020u16.to_be_bytes());
673 v.extend_from_slice(&0x0030u16.to_be_bytes());
674 v.push(0);
675 v.push(TABLE_ID_PF_ACTUAL);
676 v.extend_from_slice(&1u16.to_be_bytes());
677 v.extend_from_slice(&[0u8; 5]);
678 v.extend_from_slice(&[0u8; 3]);
679 v.push(0x00);
680 v.push(0x00);
681 v.push(0xFF);
682 v.extend_from_slice(&[0u8; 4]);
683 let err = EitSection::parse(&v).unwrap_err();
684 assert!(matches!(
685 err,
686 Error::BufferTooShort {
687 what: "EitSection trailing event bytes",
688 ..
689 }
690 ));
691 }
692}