mpeg2ts_reader/psi/
mod.rs

1//! Types for processing tables of *Program Specific Information* in a transport stream.
2//!
3//! # Concepts
4//!
5//! * There are multiple standard types of Program Specific Information, like the *Program
6//!   Association Table* and *Program Map Table*.  Standards derived from mpegts may define their
7//!   own table types.
8//! * A PSI *Table* can split into *Sections*
9//! * A Section can be split across a small number of individual transport stream *Packets*
10//! * The payload of a section may use *section-syntax* or *compact-syntax*, as indicated by the
11//!   [`section_syntax_indicator`](struct.SectionCommonHeader.html#structfield.section_syntax_indicator)
12//!   attribute
13//!   * *Section-syntax* sections have additional header data, represented by the
14//!     `TableSyntaxHeader` type
15//!   * *Compact-syntax* sections lack this extra header data
16//!
17//! # Core types
18//!
19//! * [`SectionPacketConsumer`](struct.SectionPacketConsumer.html) converts *Packets* into *Sections*
20//!
21//! Note that the specific types of table such as Program Association Table are defined elsewhere
22//! with only the generic functionality in this module.
23
24pub mod pat;
25pub mod pmt;
26
27use crate::mpegts_crc;
28use crate::packet;
29use log::warn;
30use std::fmt;
31
32// TODO: there is quite some duplication between XxxSectionSyntaxYyy and XxxCompactSyntaxYyy types
33//       refactor to reduce the repeated code.
34
35/// Trait for types which process the data within a PSI section following the 12-byte
36/// `section_length` field (which is one of the items available in the `SectionCommonHeader` that
37/// is passed in.
38///
39///  - For PSI tables that use 'section syntax', the existing
40///    [`SectionSyntaxSectionProcessor`](struct.SectionSyntaxSectionProcessor.html) implementation of this trait
41///    can be used.
42///  - This trait should be implemented directly for PSI tables that use 'compact' syntax (i.e.
43///    they lack the 5-bytes-worth of fields represented by [`TableSyntaxHeader`](struct.TableSyntaxHeader.html))
44///
45/// Implementations of this trait will need to use the `section_length` method of the `header`
46/// param passed to `start_section()` to determine when the complete section has been supplied
47/// (if the complete header is not supplied in the call to `start_section()` more data may be
48/// supplied in one or more subsequent calls to `continue_section()`.
49pub trait SectionProcessor {
50    /// The type of the context object that the caller will pass through to the methods of this
51    /// trait
52    type Context;
53
54    /// Note that the first 3 bytes of `section_data` contain the header fields that have also
55    /// been supplied to this call in the `header` parameter.  This is to allow implementers to
56    /// calculate a CRC over the whole section if required.
57    fn start_section(
58        &mut self,
59        ctx: &mut Self::Context,
60        header: &SectionCommonHeader,
61        section_data: &[u8],
62    );
63    /// may be called to pass the implementation additional slices of section data, if the
64    /// complete section was not already passed.
65    fn continue_section(&mut self, ctx: &mut Self::Context, section_data: &[u8]);
66
67    /// called if there is a problem in the transport stream that means any in-progress section
68    /// data should be discarded.
69    fn reset(&mut self);
70}
71
72/// Represents the value of the Transport Stream `current_next_indicator` field.
73#[derive(Debug, PartialEq, Eq)]
74pub enum CurrentNext {
75    /// The section version number applies to the currently applicable section data
76    Current,
77    /// The section version number applies to the next applicable section data
78    Next,
79}
80
81impl CurrentNext {
82    fn from(v: u8) -> CurrentNext {
83        match v {
84            0 => CurrentNext::Next,
85            1 => CurrentNext::Current,
86            _ => panic!("invalid current_next_indicator value {}", v),
87        }
88    }
89}
90
91/// Represents the fields that appear within table sections that use the common 'section syntax'.
92///
93/// This will only be used for a table section if the
94/// [`section_syntax_indicator`](struct.SectionCommonHeader.html#structfield.section_syntax_indicator)
95/// field in the `SectionCommonHeader` of the section is `true`.
96pub struct TableSyntaxHeader<'buf> {
97    buf: &'buf [u8],
98}
99
100impl<'buf> TableSyntaxHeader<'buf> {
101    /// The size of the header; 5 bytes
102    pub const SIZE: usize = 5;
103
104    /// Constructs a new TableSyntaxHeader, wrapping the given slice, which will all parsing of
105    /// the header's fields.
106    ///
107    /// Panics if the given slice is less than `TableSyntaxHeader::SIZE` bytes long.
108    pub fn new(buf: &'buf [u8]) -> TableSyntaxHeader<'buf> {
109        assert!(buf.len() >= Self::SIZE);
110        TableSyntaxHeader { buf }
111    }
112    /// The initial 16-bit field within a 'section syntax' PSI table (which immediately follows the
113    /// `section_length` field).
114    /// _13818-1_ refers to this field as,
115    ///  - `transport_stream_id` when it appears within a Program Association Section
116    ///  - part of the `reserved` field when it appears within a Conditional Access Section
117    ///  - `program_number` when it appears within a Program Map Section
118    ///  - `table_id_extension` when it appears within a Private Section
119    pub fn id(&self) -> u16 {
120        u16::from(self.buf[0]) << 8 | u16::from(self.buf[1])
121    }
122    /// A 5-bit value that can be used to quickly check if this table has changed since the last
123    /// time it was periodically inserted within the transport stream being read.
124    pub fn version(&self) -> u8 {
125        (self.buf[2] >> 1) & 0b0001_1111
126    }
127    /// Is this table applicable now, or will it become applicable at some future time.
128    /// NB I've not seen sample data that uses anything other than `CurrentNext::Current`, so
129    /// handling of tables with 'future' applicability may not actually work properly.
130    pub fn current_next_indicator(&self) -> CurrentNext {
131        CurrentNext::from(self.buf[2] & 1)
132    }
133    /// The number of this section, within a potentially multi-section table.
134    ///
135    /// It is common for only one section to appear within any PSI table, in which case this value
136    /// will always be `0` within a given stream.  The value of `last_section_number()` can be
137    /// used to tell if multiple sections are expected.
138    pub fn section_number(&self) -> u8 {
139        self.buf[3]
140    }
141    /// Indicates the value of `section_number()` that will appear within the last section within
142    /// a table.  In many streams, this value is always `0`, however multiple table sections may
143    /// need be used if the table needs to carry a large number of entries.
144    pub fn last_section_number(&self) -> u8 {
145        self.buf[4]
146    }
147}
148impl<'buf> fmt::Debug for TableSyntaxHeader<'buf> {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
150        f.debug_struct("TableSyntaxHeader")
151            .field("id", &self.id())
152            .field("version", &self.version())
153            .field("current_next_indicator", &self.current_next_indicator())
154            .field("section_number", &self.section_number())
155            .field("last_section_number", &self.last_section_number())
156            .finish()
157    }
158}
159
160/// An implementation of `WholeSectionSyntaxPayloadParser` which will delegate to another
161/// instance of `WholeSectionSyntaxPayloadParser` only if the CRC of the section data is
162/// correct.
163pub struct CrcCheckWholeSectionSyntaxPayloadParser<P>
164where
165    P: WholeSectionSyntaxPayloadParser,
166{
167    inner: P,
168}
169impl<P> CrcCheckWholeSectionSyntaxPayloadParser<P>
170where
171    P: WholeSectionSyntaxPayloadParser,
172{
173    const CRC_SIZE: usize = 4;
174
175    /// create a new CrcCheckWholeSectionSyntaxPayloadParser which wraps and delegates to the given
176    /// `WholeSectionSyntaxPayloadParser` instance
177    pub fn new(inner: P) -> CrcCheckWholeSectionSyntaxPayloadParser<P> {
178        CrcCheckWholeSectionSyntaxPayloadParser { inner }
179    }
180}
181
182impl<P> WholeSectionSyntaxPayloadParser for CrcCheckWholeSectionSyntaxPayloadParser<P>
183where
184    P: WholeSectionSyntaxPayloadParser,
185{
186    type Context = P::Context;
187
188    fn section<'a>(
189        &mut self,
190        ctx: &mut Self::Context,
191        header: &SectionCommonHeader,
192        table_syntax_header: &TableSyntaxHeader<'a>,
193        data: &'a [u8],
194    ) {
195        assert!(header.section_syntax_indicator);
196        if data.len() < SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE + Self::CRC_SIZE {
197            // must be big enough to hold the CRC!
198            warn!(
199                "section data length too small for table_id {}: {}",
200                header.table_id,
201                data.len()
202            );
203            return;
204        }
205        // don't apply CRC checks when fuzzing, to give more chances of test data triggering
206        // parser bugs,
207        if !cfg!(fuzzing) && mpegts_crc::sum32(data) != 0 {
208            warn!("section crc check failed for table_id {}", header.table_id,);
209            return;
210        }
211        self.inner.section(ctx, header, table_syntax_header, data);
212    }
213}
214
215/// Trait for types that parse fully reconstructed PSI table sections (which requires the caller
216/// to have buffered section data if it spanned multiple TS packets.
217pub trait WholeSectionSyntaxPayloadParser {
218    /// Type of the context object that will be passed to all methods.
219    type Context;
220
221    /// Method that will receive a complete PSI table section, where the `data` parameter will
222    /// be `header.section_length` bytes long
223    fn section<'a>(
224        &mut self,
225        _: &mut Self::Context,
226        header: &SectionCommonHeader,
227        table_syntax_header: &TableSyntaxHeader<'a>,
228        data: &'a [u8],
229    );
230}
231
232/// Trait for types that parse fully reconstructed PSI table sections.
233///
234/// This requires the caller to have buffered section data if it spanned multiple TS packets,
235/// and the `BufferCompactSyntaxParser` type is available to perform such buffering.
236pub trait WholeCompactSyntaxPayloadParser {
237    /// Type of the context object that will be passed to all methods.
238    type Context;
239
240    /// Method that will receive a complete PSI table section, where the `data` parameter will
241    /// be `header.section_length` bytes long
242    fn section(&mut self, _: &mut Self::Context, header: &SectionCommonHeader, data: &[u8]);
243}
244
245enum BufferSectionState {
246    Buffering(usize),
247    Complete,
248}
249
250/// Implements `SectionSyntaxPayloadParser` so that any sections that cross TS-packet boundaries
251/// are collected into a single byte-buffer for easier parsing.  In the common case that the
252/// section fits entirely in a single TS packet, the implementation is zero-copy.
253pub struct BufferSectionSyntaxParser<P>
254where
255    P: WholeSectionSyntaxPayloadParser,
256{
257    buf: Vec<u8>,
258    state: BufferSectionState,
259    parser: P,
260}
261impl<P> BufferSectionSyntaxParser<P>
262where
263    P: WholeSectionSyntaxPayloadParser,
264{
265    /// wraps the given `WholeSectionSyntaxPayloadParser` instance in a new
266    /// `BufferSectionSyntaxParser`.
267    pub fn new(parser: P) -> BufferSectionSyntaxParser<P> {
268        BufferSectionSyntaxParser {
269            buf: vec![],
270            state: BufferSectionState::Complete,
271            parser,
272        }
273    }
274}
275impl<P> SectionSyntaxPayloadParser for BufferSectionSyntaxParser<P>
276where
277    P: WholeSectionSyntaxPayloadParser,
278{
279    type Context = P::Context;
280
281    fn start_syntax_section<'a>(
282        &mut self,
283        ctx: &mut Self::Context,
284        header: &SectionCommonHeader,
285        table_syntax_header: &TableSyntaxHeader<'a>,
286        data: &'a [u8],
287    ) {
288        let section_length_with_header = header.section_length + SectionCommonHeader::SIZE;
289        if section_length_with_header <= data.len() {
290            // section data is entirely within this packet,
291            self.state = BufferSectionState::Complete;
292            self.parser.section(
293                ctx,
294                header,
295                table_syntax_header,
296                &data[..section_length_with_header],
297            )
298        } else {
299            // we will need to wait for continuation packets before we have the whole section,
300            self.buf.clear();
301            self.buf.extend_from_slice(data);
302            let to_read = section_length_with_header - data.len();
303            self.state = BufferSectionState::Buffering(to_read);
304        }
305    }
306
307    fn continue_syntax_section(&mut self, ctx: &mut Self::Context, data: &[u8]) {
308        match self.state {
309            BufferSectionState::Complete => {
310                warn!("attempt to add extra data when section already complete");
311            }
312            BufferSectionState::Buffering(remaining) => {
313                let new_remaining = if data.len() > remaining {
314                    0
315                } else {
316                    remaining - data.len()
317                };
318                if new_remaining == 0 {
319                    self.buf.extend_from_slice(&data[..remaining]);
320                    self.state = BufferSectionState::Complete;
321                    let header = SectionCommonHeader::new(&self.buf[..SectionCommonHeader::SIZE]);
322                    let table_syntax_header =
323                        TableSyntaxHeader::new(&self.buf[SectionCommonHeader::SIZE..]);
324                    self.parser
325                        .section(ctx, &header, &table_syntax_header, &self.buf[..]);
326                } else {
327                    self.buf.extend_from_slice(data);
328                    self.state = BufferSectionState::Buffering(new_remaining);
329                }
330            }
331        }
332    }
333    fn reset(&mut self) {
334        self.buf.clear();
335        self.state = BufferSectionState::Complete;
336    }
337}
338
339/// Implements `CompactSyntaxPayloadParser` so that any sections that cross TS-packet boundaries
340/// are collected into a single byte-buffer for easier parsing.  In the common case that the
341/// section fits entirely in a single TS packet, the implementation is zero-copy.
342pub struct BufferCompactSyntaxParser<P>
343where
344    P: WholeCompactSyntaxPayloadParser,
345{
346    buf: Vec<u8>,
347    state: BufferSectionState,
348    parser: P,
349}
350impl<P> BufferCompactSyntaxParser<P>
351where
352    P: WholeCompactSyntaxPayloadParser,
353{
354    /// wraps the given `WholeSectionSyntaxPayloadParser` instance in a new
355    /// `BufferSectionSyntaxParser`.
356    pub fn new(parser: P) -> BufferCompactSyntaxParser<P> {
357        BufferCompactSyntaxParser {
358            buf: vec![],
359            state: BufferSectionState::Complete,
360            parser,
361        }
362    }
363}
364impl<P> CompactSyntaxPayloadParser for BufferCompactSyntaxParser<P>
365where
366    P: WholeCompactSyntaxPayloadParser,
367{
368    type Context = P::Context;
369
370    fn start_compact_section(
371        &mut self,
372        ctx: &mut Self::Context,
373        header: &SectionCommonHeader,
374        data: &[u8],
375    ) {
376        let section_length_with_header = header.section_length + SectionCommonHeader::SIZE;
377        if section_length_with_header <= data.len() {
378            // section data is entirely within this packet,
379            self.state = BufferSectionState::Complete;
380            self.parser
381                .section(ctx, header, &data[..section_length_with_header])
382        } else {
383            // we will need to wait for continuation packets before we have the whole section,
384            self.buf.clear();
385            self.buf.extend_from_slice(data);
386            let to_read = section_length_with_header - data.len();
387            self.state = BufferSectionState::Buffering(to_read);
388        }
389    }
390
391    fn continue_compact_section(&mut self, ctx: &mut Self::Context, data: &[u8]) {
392        match self.state {
393            BufferSectionState::Complete => {
394                warn!("attempt to add extra data when section already complete");
395            }
396            BufferSectionState::Buffering(remaining) => {
397                let new_remaining = if data.len() > remaining {
398                    0
399                } else {
400                    remaining - data.len()
401                };
402                if new_remaining == 0 {
403                    self.buf.extend_from_slice(&data[..remaining]);
404                    self.state = BufferSectionState::Complete;
405                    let header = SectionCommonHeader::new(&self.buf[..SectionCommonHeader::SIZE]);
406                    self.parser.section(ctx, &header, &self.buf[..]);
407                } else {
408                    self.buf.extend_from_slice(data);
409                    self.state = BufferSectionState::Buffering(new_remaining);
410                }
411            }
412        }
413    }
414    fn reset(&mut self) {
415        self.buf.clear();
416        self.state = BufferSectionState::Complete;
417    }
418}
419
420/// A wrapper around some other implementation of `SectionSyntaxPayloadParser` that passes-through
421/// section data, unless the `TableSyntaxHeader` indicates a version_number which is the same as
422/// the last data that was passed though.
423///
424/// This avoids the underlying code needing to re-parse duplicate copies of the section, which are
425/// usually inserted periodically in the Transport Stream.
426pub struct DedupSectionSyntaxPayloadParser<SSPP>
427where
428    SSPP: SectionSyntaxPayloadParser,
429{
430    inner: SSPP,
431    last_version: Option<u8>,
432    ignore_rest: bool,
433}
434impl<SSPP> DedupSectionSyntaxPayloadParser<SSPP>
435where
436    SSPP: SectionSyntaxPayloadParser,
437{
438    /// Wraps the given `SectionSyntaxPayloadParser` in a new `DedupSectionSyntaxPayloadParser`
439    /// instance.
440    pub fn new(inner: SSPP) -> DedupSectionSyntaxPayloadParser<SSPP> {
441        DedupSectionSyntaxPayloadParser {
442            inner,
443            last_version: None,
444            ignore_rest: false,
445        }
446    }
447}
448impl<SSPP> SectionSyntaxPayloadParser for DedupSectionSyntaxPayloadParser<SSPP>
449where
450    SSPP: SectionSyntaxPayloadParser,
451{
452    type Context = SSPP::Context;
453
454    fn start_syntax_section<'a>(
455        &mut self,
456        ctx: &mut Self::Context,
457        header: &SectionCommonHeader,
458        table_syntax_header: &TableSyntaxHeader<'a>,
459        data: &'a [u8],
460    ) {
461        if let Some(last) = self.last_version {
462            if last == table_syntax_header.version() {
463                self.ignore_rest = true;
464                return;
465            }
466        }
467        self.ignore_rest = false;
468        self.last_version = Some(table_syntax_header.version());
469        self.inner
470            .start_syntax_section(ctx, header, table_syntax_header, data);
471    }
472
473    fn continue_syntax_section(&mut self, ctx: &mut Self::Context, data: &[u8]) {
474        if !self.ignore_rest {
475            self.inner.continue_syntax_section(ctx, data)
476        }
477    }
478    fn reset(&mut self) {
479        self.inner.reset();
480        self.last_version = None;
481        self.ignore_rest = false;
482    }
483}
484
485/// Trait for types that will handle MPEGTS PSI table sections with 'section syntax'.
486pub trait SectionSyntaxPayloadParser {
487    /// The type of the context object passed to all methods
488    type Context;
489
490    /// NB the `data` buffer passed to _will_ include the bytes which are represented by `header`
491    /// and `table_syntax_header` (in order that the called code can check any CRC that covers the
492    /// whole section).
493    fn start_syntax_section<'a>(
494        &mut self,
495        ctx: &mut Self::Context,
496        header: &SectionCommonHeader,
497        table_syntax_header: &TableSyntaxHeader<'a>,
498        data: &'a [u8],
499    );
500
501    /// may be called to pass the implementation additional slices of section data, if the
502    /// complete section was not already passed.
503    fn continue_syntax_section(&mut self, ctx: &mut Self::Context, data: &[u8]);
504
505    /// called if there is a problem in the transport stream that means any in-progress section
506    /// data should be discarded.
507    fn reset(&mut self);
508}
509
510/// Trait for types that will handle MPEGTS PSI table sections with 'compact syntax'.
511pub trait CompactSyntaxPayloadParser {
512    /// The type of the context object passed to all methods
513    type Context;
514
515    /// NB the `data` buffer passed to _will_ include the bytes which are represented by `header`
516    /// (in order that the called code can check any CRC that covers the
517    /// whole section).
518    fn start_compact_section(
519        &mut self,
520        ctx: &mut Self::Context,
521        header: &SectionCommonHeader,
522        data: &[u8],
523    );
524
525    /// may be called to pass the implementation additional slices of section data, if the
526    /// complete section was not already passed.
527    fn continue_compact_section(&mut self, ctx: &mut Self::Context, data: &[u8]);
528
529    /// called if there is a problem in the transport stream that means any in-progress section
530    /// data should be discarded.
531    fn reset(&mut self);
532}
533
534/// An implementation of `SectionProcessor` to be used for sections that implement 'compact syntax'
535/// (rather than 'section syntax').
536///
537/// Delegates handling to the `CompactSyntaxPayloadParser` instance given at construction time.
538pub struct CompactSyntaxSectionProcessor<SP>
539where
540    SP: CompactSyntaxPayloadParser,
541{
542    payload_parser: SP,
543    ignore_rest: bool,
544}
545impl<SP> CompactSyntaxSectionProcessor<SP>
546where
547    SP: CompactSyntaxPayloadParser,
548{
549    const SECTION_LIMIT: usize = 1021;
550
551    /// Wraps the given `CompactSyntaxPayloadParser` instance in a new
552    /// `CompactSyntaxSectionProcessor`.
553    pub fn new(payload_parser: SP) -> CompactSyntaxSectionProcessor<SP> {
554        CompactSyntaxSectionProcessor {
555            payload_parser,
556            ignore_rest: false,
557        }
558    }
559}
560impl<SP> SectionProcessor for CompactSyntaxSectionProcessor<SP>
561where
562    SP: CompactSyntaxPayloadParser,
563{
564    type Context = SP::Context;
565
566    fn start_section(
567        &mut self,
568        ctx: &mut Self::Context,
569        header: &SectionCommonHeader,
570        data: &[u8],
571    ) {
572        if header.section_syntax_indicator {
573            // Maybe this should actually be allowed in some cases?
574            warn!(
575                "CompactSyntaxSectionProcessor requires that section_syntax_indicator NOT be set in the section header"
576            );
577            self.ignore_rest = true;
578            return;
579        }
580        if data.len() < SectionCommonHeader::SIZE {
581            warn!("CompactSyntaxSectionProcessor data {} too short for header {} (TODO: implement buffering)", data.len(), SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE);
582            self.ignore_rest = true;
583            return;
584        }
585        if header.section_length > Self::SECTION_LIMIT {
586            warn!(
587                "CompactSyntaxSectionProcessor section_length={} is too large (limit {})",
588                header.section_length,
589                Self::SECTION_LIMIT
590            );
591            self.ignore_rest = true;
592            return;
593        }
594        self.ignore_rest = false;
595        self.payload_parser.start_compact_section(ctx, header, data)
596    }
597
598    fn continue_section(&mut self, ctx: &mut Self::Context, data: &[u8]) {
599        if !self.ignore_rest {
600            self.payload_parser.continue_compact_section(ctx, data)
601        }
602    }
603    fn reset(&mut self) {
604        self.payload_parser.reset()
605    }
606}
607
608/// An implementation of `SectionProcessor` to be used for sections that implement 'section syntax'
609/// (rather than 'compact syntax').
610///
611/// Parses the `TableSyntaxHeader` at the front of the section data, and then delegates handling
612/// to the `SectionSyntaxPayloadParser` instance given at construction time.
613pub struct SectionSyntaxSectionProcessor<SP>
614where
615    SP: SectionSyntaxPayloadParser,
616{
617    payload_parser: SP,
618    ignore_rest: bool,
619}
620impl<SP> SectionSyntaxSectionProcessor<SP>
621where
622    SP: SectionSyntaxPayloadParser,
623{
624    const SECTION_LIMIT: usize = 1021;
625
626    /// Wraps the given `SectionSyntaxPayloadParser` instance in a new
627    /// `SectionSyntaxSectionProcessor`.
628    pub fn new(payload_parser: SP) -> SectionSyntaxSectionProcessor<SP> {
629        SectionSyntaxSectionProcessor {
630            payload_parser,
631            ignore_rest: false,
632        }
633    }
634}
635impl<SP> SectionProcessor for SectionSyntaxSectionProcessor<SP>
636where
637    SP: SectionSyntaxPayloadParser,
638{
639    type Context = SP::Context;
640
641    fn start_section(
642        &mut self,
643        ctx: &mut Self::Context,
644        header: &SectionCommonHeader,
645        data: &[u8],
646    ) {
647        if !header.section_syntax_indicator {
648            warn!(
649                "SectionSyntaxSectionProcessor requires that section_syntax_indicator be set in the section header"
650            );
651            self.ignore_rest = true;
652            return;
653        }
654        if data.len() < SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE {
655            warn!("SectionSyntaxSectionProcessor data {} too short for header {} (TODO: implement buffering)", data.len(), SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE);
656            self.ignore_rest = true;
657            return;
658        }
659        if header.section_length > Self::SECTION_LIMIT {
660            warn!(
661                "SectionSyntaxSectionProcessor section_length={} is too large (limit {})",
662                header.section_length,
663                Self::SECTION_LIMIT
664            );
665            self.ignore_rest = true;
666            return;
667        }
668        self.ignore_rest = false;
669        let table_syntax_header = TableSyntaxHeader::new(&data[SectionCommonHeader::SIZE..]);
670        self.payload_parser
671            .start_syntax_section(ctx, header, &table_syntax_header, data)
672    }
673
674    fn continue_section(&mut self, ctx: &mut Self::Context, data: &[u8]) {
675        if !self.ignore_rest {
676            self.payload_parser.continue_syntax_section(ctx, data)
677        }
678    }
679    fn reset(&mut self) {
680        self.payload_parser.reset()
681    }
682}
683
684/// Header common to all PSI sections, whether they then use 'section syntax' or 'compact syntax'.
685#[derive(Debug)]
686pub struct SectionCommonHeader {
687    /// The type of table of which this is a section
688    pub table_id: u8,
689    /// `true` for 'section syntax`, `false` for 'compact syntax'.
690    pub section_syntax_indicator: bool,
691    /// indicates that the data in the table is for private use not defined in _ISO/IEC 13818-1_
692    /// (section types implemented in this crate are to be used with data that has `e` in
693    /// this field, but other crates might be written to support private table sections).
694    pub private_indicator: bool,
695    /// the number of bytes in the section data immediately following this field (which may be
696    /// more bytes than will fit into a single TS packet).
697    pub section_length: usize,
698}
699
700impl SectionCommonHeader {
701    /// The fixed size of the CommonSectionHeader data in the Transport Stream; 3 bytes.
702    pub const SIZE: usize = 3;
703
704    /// Parses the data in the given slice into a new `SectionCommonHeader`.
705    ///
706    /// Panics if the slice is not exactly 3 bytes long.
707    pub fn new(buf: &[u8]) -> SectionCommonHeader {
708        assert_eq!(buf.len(), Self::SIZE);
709        SectionCommonHeader {
710            table_id: buf[0],
711            section_syntax_indicator: buf[1] & 0b1000_0000 != 0,
712            private_indicator: buf[1] & 0b0100_0000 != 0,
713            section_length: ((u16::from(buf[1] & 0b0000_1111) << 8) | u16::from(buf[2])) as usize,
714        }
715    }
716}
717
718/// A type for locating the headers of PSI sections, which may be split across multiple TS packets,
719/// and passing each piece to the given `SectionProcessor` as it is discovered.
720pub struct SectionPacketConsumer<P>
721where
722    P: SectionProcessor,
723{
724    parser: P,
725}
726
727// TODO: maybe just implement PacketFilter directly
728
729impl<P, Ctx> SectionPacketConsumer<P>
730where
731    P: SectionProcessor<Context = Ctx>,
732{
733    /// Construct a new instance that will delegate processing of section data found in TS packet
734    /// payloads to the given `SectionProcessor` instance.
735    pub fn new(parser: P) -> SectionPacketConsumer<P> {
736        SectionPacketConsumer { parser }
737    }
738
739    /// process the payload of the given TS packet, passing each piece of section data discovered
740    /// to the `SectionProcessor` instance given at time of construction.
741    pub fn consume(&mut self, ctx: &mut Ctx, pk: &packet::Packet<'_>) {
742        match pk.payload() {
743            Some(pk_buf) => {
744                if pk.payload_unit_start_indicator() {
745                    // this packet payload contains the start of a new PSI section
746                    let pointer = pk_buf[0] as usize;
747                    let section_data = &pk_buf[1..];
748                    if pointer > 0 {
749                        if pointer >= section_data.len() {
750                            warn!("PSI pointer beyond end of packet payload");
751                            self.parser.reset();
752                            return;
753                        }
754                        let remainder = &section_data[..pointer];
755                        self.parser.continue_section(ctx, remainder);
756                        // the following call to begin_new_section() will assert that
757                        // append_to_current() just finalised the preceding section
758                    }
759                    let next_sect = &section_data[pointer..];
760                    if next_sect.len() < SectionCommonHeader::SIZE {
761                        warn!(
762                            "TODO: not enough bytes to read section header - implement buffering"
763                        );
764                        self.parser.reset();
765                        return;
766                    }
767                    let header = SectionCommonHeader::new(&next_sect[..SectionCommonHeader::SIZE]);
768                    self.parser.start_section(ctx, &header, next_sect);
769                } else {
770                    // this packet is a continuation of an existing PSI section
771                    self.parser.continue_section(ctx, pk_buf);
772                }
773            }
774            None => {
775                warn!("no payload present in PSI packet");
776            }
777        }
778    }
779}
780
781#[cfg(test)]
782mod test {
783    use super::*;
784    use crate::demultiplex;
785    use crate::demultiplex::PacketFilter;
786    use crate::packet::Packet;
787    use hex_literal::*;
788    use std::cell::RefCell;
789    use std::rc::Rc;
790
791    pub struct NullFilterSwitch;
792    impl PacketFilter for NullFilterSwitch {
793        type Ctx = NullDemuxContext;
794        fn consume(&mut self, _ctx: &mut Self::Ctx, _pk: &Packet<'_>) {
795            unimplemented!()
796        }
797    }
798
799    demux_context!(NullDemuxContext, NullFilterSwitch);
800    impl NullDemuxContext {
801        fn do_construct(&mut self, _req: demultiplex::FilterRequest<'_, '_>) -> NullFilterSwitch {
802            unimplemented!()
803        }
804    }
805
806    struct NullSectionProcessor;
807    impl SectionProcessor for NullSectionProcessor {
808        type Context = NullDemuxContext;
809        fn start_section<'a>(
810            &mut self,
811            _ctx: &mut Self::Context,
812            _header: &SectionCommonHeader,
813            _section_data: &'a [u8],
814        ) {
815        }
816        fn continue_section<'a>(&mut self, _ctx: &mut Self::Context, _section_data: &'a [u8]) {}
817        fn reset(&mut self) {}
818    }
819
820    #[test]
821    fn continuation_outside_section() {
822        let mut buf = [0u8; 188];
823        buf[0] = 0x47;
824        buf[3] |= 0b00010000; // PayloadOnly
825        let pk = Packet::new(&buf[..]);
826        let mut psi_buf = SectionPacketConsumer::new(NullSectionProcessor);
827        let mut ctx = NullDemuxContext::new();
828        psi_buf.consume(&mut ctx, &pk);
829    }
830
831    #[test]
832    fn small_section() {
833        let mut buf = [0u8; 188];
834        buf[0] = 0x47;
835        buf[1] |= 0b01000000; // payload_unit_start_indicator
836        buf[3] |= 0b00010000; // PayloadOnly
837        buf[7] = 3; // section_length
838        let pk = Packet::new(&buf[..]);
839        let mut psi_buf = SectionPacketConsumer::new(NullSectionProcessor);
840        let mut ctx = NullDemuxContext::new();
841        psi_buf.consume(&mut ctx, &pk);
842    }
843
844    struct MockWholeSectParse {
845        state: Rc<RefCell<bool>>,
846    }
847    impl WholeSectionSyntaxPayloadParser for MockWholeSectParse {
848        type Context = ();
849        fn section<'a>(
850            &mut self,
851            _: &mut Self::Context,
852            _header: &SectionCommonHeader,
853            _table_syntax_header: &TableSyntaxHeader<'_>,
854            _data: &[u8],
855        ) {
856            *self.state.borrow_mut() = true;
857        }
858    }
859
860    #[test]
861    fn section_spanning_packets() {
862        // state to track if MockSectParse.section() got called,
863        let state = Rc::new(RefCell::new(false));
864        let mut p = BufferSectionSyntaxParser::new(CrcCheckWholeSectionSyntaxPayloadParser::new(
865            MockWholeSectParse {
866                state: state.clone(),
867            },
868        ));
869        let ctx = &mut ();
870        {
871            let sect = hex!(
872                "
873            42f13040 84e90000 233aff44 40ff8026
874            480d1900 0a424243 2054574f 20484473
875            0c66702e 6262632e 636f2e75 6b5f0400
876            00233a7e 01f744c4 ff802148 09190006
877            49545620 4844730b 7777772e 6974762e
878            636f6d5f 04000023 3a7e01f7 4500ff80
879            2c480f19 000c4368 616e6e65 6c203420
880            48447310 7777772e 6368616e 6e656c34
881            2e636f6d 5f040000 233a7e01 f74484ff
882            8026480d 19000a42 4243204f 4e452048
883            44730c66 702e6262 632e636f 2e756b5f
884            04000023 3a7e01"
885            );
886
887            let common_header = SectionCommonHeader::new(&sect[..SectionCommonHeader::SIZE]);
888            let table_header = TableSyntaxHeader::new(&sect[SectionCommonHeader::SIZE..]);
889            p.start_syntax_section(ctx, &common_header, &table_header, &sect[..]);
890        }
891        {
892            let sect = hex!(
893                "
894                f746c0ff 8023480a 19000743 42424320
895                4844730c 66702e62 62632e63 6f2e756b
896                5f040000 233a7e01 f74f80ff 801e480a
897                16000746 696c6d34 2b317310 7777772e
898                6368616e 6e656c34 2e636f6d 4540ff80
899                27480f19 000c4368 616e6e65 6c203520
900                4844730b 7777772e 66697665 2e74765f
901                04000023 3a7e01f7 f28b26c4 ffffffff
902                ffffffff ffffffff ffffffff ffffffff
903                ffffffff ffffffff ffffffff ffffffff
904                ffffffff ffffffff ffffffff ffffffff
905                ffffffff ffffffff"
906            );
907
908            p.continue_syntax_section(ctx, &sect[..]);
909        }
910
911        assert!(*state.borrow());
912    }
913
914    #[test]
915    fn table_syntax() {
916        let sect = hex!("4084e90000");
917        let header = TableSyntaxHeader::new(&sect);
918        assert_eq!(header.current_next_indicator(), CurrentNext::Current);
919        assert_eq!(header.id(), 16516);
920        assert_eq!(header.section_number(), 0);
921        assert_eq!(header.last_section_number(), 0);
922        assert_eq!(header.version(), 20);
923        // smoke test Debug impl (e.g. should not panic!)
924        assert!(!format!("{:?}", header).is_empty());
925    }
926
927    #[test]
928    fn table_next_syntax() {
929        let sect = hex!("4084e80000");
930        let header = TableSyntaxHeader::new(&sect);
931        assert_eq!(header.current_next_indicator(), CurrentNext::Next);
932    }
933
934    #[test]
935    fn dedup_section() {
936        struct CallCounts {
937            start: usize,
938            cont: usize,
939            reset: usize,
940        }
941        struct Mock {
942            inner: Rc<RefCell<CallCounts>>,
943        }
944        let counts = Rc::new(RefCell::new(CallCounts {
945            start: 0,
946            cont: 0,
947            reset: 0,
948        }));
949        impl SectionSyntaxPayloadParser for Mock {
950            type Context = ();
951
952            fn start_syntax_section<'a>(
953                &mut self,
954                _ctx: &mut Self::Context,
955                _header: &SectionCommonHeader,
956                _table_syntax_header: &TableSyntaxHeader<'a>,
957                _data: &'a [u8],
958            ) {
959                self.inner.borrow_mut().start += 1;
960            }
961
962            fn continue_syntax_section<'a>(&mut self, _ctx: &mut Self::Context, _data: &'a [u8]) {
963                self.inner.borrow_mut().cont += 1;
964            }
965
966            fn reset(&mut self) {
967                self.inner.borrow_mut().reset += 1;
968            }
969        }
970        let mut dedup = DedupSectionSyntaxPayloadParser::new(Mock {
971            inner: counts.clone(),
972        });
973
974        let sect = hex!("42f130 4084e90000");
975
976        let common_header = SectionCommonHeader::new(&sect[..SectionCommonHeader::SIZE]);
977        let table_header = TableSyntaxHeader::new(&sect[SectionCommonHeader::SIZE..]);
978        assert_eq!(table_header.version(), 20);
979
980        let ctx = &mut ();
981        dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
982        dedup.continue_syntax_section(ctx, &[]);
983        assert_eq!(counts.borrow().start, 1);
984        assert_eq!(counts.borrow().cont, 1);
985        // now we submit a table header with the same version,
986        dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
987        dedup.continue_syntax_section(ctx, &[]);
988        // still 1
989        assert_eq!(counts.borrow().start, 1);
990        assert_eq!(counts.borrow().cont, 1);
991
992        // now lets use the same section header as above but with an updated version
993        let sect = hex!("42f131 4084ea0000");
994
995        let common_header = SectionCommonHeader::new(&sect[..SectionCommonHeader::SIZE]);
996        let table_header = TableSyntaxHeader::new(&sect[SectionCommonHeader::SIZE..]);
997        assert_eq!(table_header.version(), 21);
998
999        dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
1000        dedup.continue_syntax_section(ctx, &[]);
1001        // now 2
1002        assert_eq!(counts.borrow().start, 2);
1003        assert_eq!(counts.borrow().cont, 2);
1004
1005        // if we now reset, then the deduplication should no longer be in effect and the submission
1006        // of the same section version again should now be passed through to our callback
1007
1008        assert_eq!(counts.borrow().reset, 0);
1009        dedup.reset();
1010        assert_eq!(counts.borrow().reset, 1);
1011
1012        // now 3
1013        dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
1014        dedup.continue_syntax_section(ctx, &[]);
1015        assert_eq!(counts.borrow().start, 3);
1016        assert_eq!(counts.borrow().cont, 3);
1017    }
1018
1019    #[test]
1020    fn compact_section_syntax() {
1021        struct CallCounts {
1022            start: u64,
1023            reset: u64,
1024            continue_section: u64,
1025        }
1026        struct Mock {
1027            inner: Rc<RefCell<CallCounts>>,
1028        }
1029        impl CompactSyntaxPayloadParser for Mock {
1030            type Context = ();
1031
1032            fn start_compact_section<'a>(
1033                &mut self,
1034                _ctx: &mut Self::Context,
1035                _header: &SectionCommonHeader,
1036                _data: &'a [u8],
1037            ) {
1038                self.inner.borrow_mut().start += 1;
1039            }
1040
1041            fn continue_compact_section<'a>(&mut self, _ctx: &mut Self::Context, _data: &'a [u8]) {
1042                self.inner.borrow_mut().continue_section += 1;
1043            }
1044
1045            fn reset(&mut self) {
1046                self.inner.borrow_mut().reset += 1;
1047            }
1048        }
1049        let counts = Rc::new(RefCell::new(CallCounts {
1050            start: 0,
1051            reset: 0,
1052            continue_section: 0,
1053        }));
1054        let mut proc = CompactSyntaxSectionProcessor::new(Mock {
1055            inner: counts.clone(),
1056        });
1057
1058        let ctx = &mut ();
1059
1060        // section_syntax_indicator is 0 in the table header, so this still not be passed through
1061        // to the mock
1062        let sect = hex!("42f131");
1063        let common_header = SectionCommonHeader::new(&sect[..SectionCommonHeader::SIZE]);
1064        assert!(common_header.section_syntax_indicator);
1065        proc.start_section(ctx, &common_header, &sect);
1066        assert_eq!(0, counts.borrow().start);
1067        proc.continue_section(ctx, &[]);
1068        assert_eq!(0, counts.borrow().continue_section);
1069
1070        let sect = hex!("427131");
1071        let common_header = SectionCommonHeader::new(&sect[..SectionCommonHeader::SIZE]);
1072        assert!(!common_header.section_syntax_indicator);
1073        // we trim the data slice down to 2 bytes which should cause the length check inside
1074        // CompactSyntaxSectionProcessor to fail,
1075        proc.start_section(ctx, &common_header, &sect[..2]);
1076        assert_eq!(0, counts.borrow().start);
1077        proc.continue_section(ctx, &[]);
1078        assert_eq!(0, counts.borrow().continue_section);
1079
1080        // section_length of 1022 (0x3fe) in this header is too long
1081        let header = hex!("4273fe");
1082        let mut sect = vec![];
1083        sect.extend_from_slice(&header);
1084        sect.resize(header.len() + 1022, 0); // fill remainder with zeros so we can accidentally fail because the buffer is too short
1085        let common_header = SectionCommonHeader::new(&sect[..SectionCommonHeader::SIZE]);
1086        assert!(!common_header.section_syntax_indicator);
1087        // we trim the data slice down to 2 bytes which should cause the length check inside
1088        // CompactSyntaxSectionProcessor to fail,
1089        proc.start_section(ctx, &common_header, &sect);
1090        assert_eq!(0, counts.borrow().start);
1091        proc.continue_section(ctx, &[]);
1092        assert_eq!(0, counts.borrow().continue_section);
1093
1094        // not too long, so this should now be accepted, and we should see counts.start increment
1095        let sect = hex!("427000");
1096        let common_header = SectionCommonHeader::new(&sect[..SectionCommonHeader::SIZE]);
1097        assert!(!common_header.section_syntax_indicator);
1098        proc.start_section(ctx, &common_header, &sect);
1099        assert_eq!(1, counts.borrow().start);
1100
1101        proc.continue_section(ctx, &[]);
1102        assert_eq!(1, counts.borrow().continue_section);
1103
1104        proc.reset();
1105        assert_eq!(1, counts.borrow().reset);
1106    }
1107
1108    #[test]
1109    fn buffer_compact() {
1110        const SECT: [u8; 7] = hex!("427003 01020304");
1111        struct Mock {
1112            section_count: usize,
1113        }
1114        impl WholeCompactSyntaxPayloadParser for Mock {
1115            type Context = ();
1116
1117            fn section<'a>(
1118                &mut self,
1119                _: &mut Self::Context,
1120                header: &SectionCommonHeader,
1121                data: &'a [u8],
1122            ) {
1123                assert_eq!(0x42, header.table_id);
1124                // trim of the last byte which we've deliberately supplied, but which is not
1125                // supposed to be part of the section
1126                assert_eq!(data, &SECT[0..SECT.len() - 1]);
1127                self.section_count += 1;
1128            }
1129        }
1130        let mock = Mock { section_count: 0 };
1131        let mut parser = BufferCompactSyntaxParser::new(mock);
1132        let ctx = &mut ();
1133
1134        let common_header = SectionCommonHeader::new(&SECT[..SectionCommonHeader::SIZE]);
1135
1136        // we supply the inital section data, but then reset the BufferCompactSyntaxParser. this
1137        // should nave no ill effect on the second attempt where we supply the complete data,
1138        parser.start_compact_section(ctx, &common_header, &SECT[..SECT.len() - 3]);
1139        parser.reset();
1140        assert_eq!(0, parser.parser.section_count);
1141
1142        parser.start_compact_section(ctx, &common_header, &SECT[..SECT.len() - 3]);
1143        parser.continue_compact_section(ctx, &SECT[SECT.len() - 3..SECT.len() - 2]);
1144        // this call will deliver 1 byte more than the 2 specified for our section_length, and
1145        // that second byte should be ignored
1146        parser.continue_compact_section(ctx, &SECT[SECT.len() - 2..]);
1147        assert_eq!(1, parser.parser.section_count);
1148
1149        // the section is already complete, so BufferCompactSyntaxParser should drop any further
1150        // data supplied in error
1151        parser.continue_compact_section(ctx, &SECT[SECT.len() - 2..]);
1152        assert_eq!(1, parser.parser.section_count);
1153    }
1154
1155    #[test]
1156    fn section_syntax_section_processor() {
1157        struct Mock {
1158            start_syntax_section_count: usize,
1159            continue_syntax_section_count: usize,
1160            reset_count: usize,
1161        }
1162        impl SectionSyntaxPayloadParser for Mock {
1163            type Context = ();
1164
1165            fn start_syntax_section<'a>(
1166                &mut self,
1167                _ctx: &mut Self::Context,
1168                _header: &SectionCommonHeader,
1169                _table_syntax_header: &TableSyntaxHeader<'a>,
1170                _data: &'a [u8],
1171            ) {
1172                self.start_syntax_section_count += 1;
1173            }
1174
1175            fn continue_syntax_section(&mut self, _ctx: &mut Self::Context, _data: &[u8]) {
1176                self.continue_syntax_section_count += 1;
1177            }
1178
1179            fn reset(&mut self) {
1180                self.reset_count += 1;
1181            }
1182        }
1183        let sect = hex!("42f130 4084e90000");
1184
1185        {
1186            let mock = Mock {
1187                start_syntax_section_count: 0,
1188                continue_syntax_section_count: 0,
1189                reset_count: 0,
1190            };
1191            let mut proc = SectionSyntaxSectionProcessor::new(mock);
1192            // copy the sample data
1193            let mut not_section_syntax = sect.to_vec();
1194            // set section_syntax_indicator to 0
1195            not_section_syntax[1] &= 0b0111_1111;
1196            let ctx = &mut ();
1197            let common_header =
1198                SectionCommonHeader::new(&not_section_syntax[..SectionCommonHeader::SIZE]);
1199            assert!(!common_header.section_syntax_indicator);
1200            proc.start_section(ctx, &common_header, &not_section_syntax);
1201            assert_eq!(proc.payload_parser.start_syntax_section_count, 0);
1202        }
1203
1204        {
1205            let mock = Mock {
1206                start_syntax_section_count: 0,
1207                continue_syntax_section_count: 0,
1208                reset_count: 0,
1209            };
1210            let mut proc = SectionSyntaxSectionProcessor::new(mock);
1211            // copy the sample data, but now make it too short to be a valid
1212            // SectionCommonHeader + TableSyntaxHeader
1213            let too_short = &sect[0..7];
1214            let ctx = &mut ();
1215            let common_header = SectionCommonHeader::new(&too_short[..SectionCommonHeader::SIZE]);
1216            assert!(common_header.section_syntax_indicator);
1217            proc.start_section(ctx, &common_header, &too_short);
1218            assert_eq!(proc.payload_parser.start_syntax_section_count, 0);
1219        }
1220
1221        {
1222            let mock = Mock {
1223                start_syntax_section_count: 0,
1224                continue_syntax_section_count: 0,
1225                reset_count: 0,
1226            };
1227            let mut proc = SectionSyntaxSectionProcessor::new(mock);
1228            let mut section_length_too_large = sect.to_vec();
1229            let bad_section_length: u16 = 1021 + 1;
1230            assert_eq!(bad_section_length >> 8 & 0b1111_0000, 0);
1231            section_length_too_large[1] =
1232                section_length_too_large[1] & 0b1111_0000 | (bad_section_length >> 8) as u8;
1233            section_length_too_large[2] = (bad_section_length & 0xff) as u8;
1234            let ctx = &mut ();
1235            let common_header =
1236                SectionCommonHeader::new(&section_length_too_large[..SectionCommonHeader::SIZE]);
1237            assert!(common_header.section_syntax_indicator);
1238            proc.start_section(ctx, &common_header, &section_length_too_large);
1239            assert_eq!(proc.payload_parser.start_syntax_section_count, 0);
1240        }
1241    }
1242
1243    #[test]
1244    fn should_reject_section_too_small_for_crc() {
1245        let sect = hex!(
1246            "
1247            42f13040 84e90000 233aff44 40ff8026
1248            480d1900 0a424243 2054574f 20484473
1249            0c66702e 6262632e 636f2e75 6b5f0400
1250            00233a7e 01f744c4 ff802148 09190006
1251            49545620 4844730b 7777772e 6974762e
1252            636f6d5f 04000023 3a7e01f7 4500ff80
1253            2c480f19 000c4368 616e6e65 6c203420
1254            48447310 7777772e 6368616e 6e656c34
1255            2e636f6d 5f040000 233a7e01 f74484ff
1256            8026480d 19000a42 4243204f 4e452048
1257            44730c66 702e6262 632e636f 2e756b5f
1258            04000023 3a7e01
1259            f746c0ff 8023480a 19000743 42424320
1260            4844730c 66702e62 62632e63 6f2e756b
1261            5f040000 233a7e01 f74f80ff 801e480a
1262            16000746 696c6d34 2b317310 7777772e
1263            6368616e 6e656c34 2e636f6d 4540ff80
1264            27480f19 000c4368 616e6e65 6c203520
1265            4844730b 7777772e 66697665 2e74765f
1266            04000023 3a7e01f7 f28b26c4 ffffffff
1267            ffffffff ffffffff ffffffff ffffffff
1268            ffffffff ffffffff ffffffff ffffffff
1269            ffffffff ffffffff ffffffff ffffffff
1270            ffffffff ffffffff"
1271        );
1272
1273        let mut sect_length_too_small = sect.to_vec();
1274        let bad_section_length: u16 = 11;
1275        assert_eq!(bad_section_length >> 8 & 0b1111_0000, 0);
1276        sect_length_too_small[1] =
1277            sect_length_too_small[1] & 0b1111_0000 | (bad_section_length >> 8) as u8;
1278        sect_length_too_small[2] = (bad_section_length & 0xff) as u8;
1279        sect_length_too_small.truncate(bad_section_length as usize);
1280        let state = Rc::new(RefCell::new(false));
1281        let mut crc_check = CrcCheckWholeSectionSyntaxPayloadParser::new(MockWholeSectParse {
1282            state: state.clone(),
1283        });
1284        let common_header =
1285            SectionCommonHeader::new(&sect_length_too_small[..SectionCommonHeader::SIZE]);
1286        let table_header =
1287            TableSyntaxHeader::new(&sect_length_too_small[SectionCommonHeader::SIZE..]);
1288        let ctx = &mut ();
1289        crc_check.section(ctx, &common_header, &table_header, &sect_length_too_small);
1290        assert!(!*state.borrow());
1291    }
1292}