pub mod pat;
pub mod pmt;
use crate::mpegts_crc;
use crate::packet;
use log::warn;
use std::fmt;
pub trait SectionProcessor {
type Context;
fn start_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
section_data: &'a [u8],
);
fn continue_section<'a>(&mut self, ctx: &mut Self::Context, section_data: &'a [u8]);
fn reset(&mut self);
}
#[derive(Debug, PartialEq)]
pub enum CurrentNext {
Current,
Next,
}
impl CurrentNext {
fn from(v: u8) -> CurrentNext {
match v {
0 => CurrentNext::Next,
1 => CurrentNext::Current,
_ => panic!("invalid current_next_indicator value {}", v),
}
}
}
pub struct TableSyntaxHeader<'buf> {
buf: &'buf [u8],
}
impl<'buf> TableSyntaxHeader<'buf> {
pub const SIZE: usize = 5;
pub fn new(buf: &'buf [u8]) -> TableSyntaxHeader<'buf> {
assert!(buf.len() >= Self::SIZE);
TableSyntaxHeader { buf }
}
pub fn id(&self) -> u16 {
u16::from(self.buf[0]) << 8 | u16::from(self.buf[1])
}
pub fn version(&self) -> u8 {
(self.buf[2] >> 1) & 0b0001_1111
}
pub fn current_next_indicator(&self) -> CurrentNext {
CurrentNext::from(self.buf[2] & 1)
}
pub fn section_number(&self) -> u8 {
self.buf[3]
}
pub fn last_section_number(&self) -> u8 {
self.buf[4]
}
}
impl<'buf> fmt::Debug for TableSyntaxHeader<'buf> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("TableSyntaxHeader")
.field("id", &self.id())
.field("version", &self.version())
.field("current_next_indicator", &self.current_next_indicator())
.field("section_number", &self.section_number())
.field("last_section_number", &self.last_section_number())
.finish()
}
}
pub struct CrcCheckWholeSectionSyntaxPayloadParser<P>
where
P: WholeSectionSyntaxPayloadParser,
{
inner: P,
}
impl<P> CrcCheckWholeSectionSyntaxPayloadParser<P>
where
P: WholeSectionSyntaxPayloadParser,
{
const CRC_SIZE: usize = 4;
pub fn new(inner: P) -> CrcCheckWholeSectionSyntaxPayloadParser<P> {
CrcCheckWholeSectionSyntaxPayloadParser { inner }
}
}
impl<P> WholeSectionSyntaxPayloadParser for CrcCheckWholeSectionSyntaxPayloadParser<P>
where
P: WholeSectionSyntaxPayloadParser,
{
type Context = P::Context;
fn section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
table_syntax_header: &TableSyntaxHeader<'a>,
data: &'a [u8],
) {
assert!(header.section_syntax_indicator);
if data.len() < SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE + Self::CRC_SIZE {
warn!(
"section data length too small for table_id {}: {}",
header.table_id,
data.len()
);
return;
}
if !cfg!(fuzzing) && mpegts_crc::sum32(data) != 0 {
warn!("section crc check failed for table_id {}", header.table_id,);
return;
}
self.inner.section(ctx, header, table_syntax_header, data);
}
}
pub trait WholeSectionSyntaxPayloadParser {
type Context;
fn section<'a>(
&mut self,
_: &mut Self::Context,
header: &SectionCommonHeader,
table_syntax_header: &TableSyntaxHeader<'a>,
data: &'a [u8],
);
}
pub trait WholeCompactSyntaxPayloadParser {
type Context;
fn section<'a>(&mut self, _: &mut Self::Context, header: &SectionCommonHeader, data: &'a [u8]);
}
enum BufferSectionState {
Buffering(usize),
Complete,
}
pub struct BufferSectionSyntaxParser<P>
where
P: WholeSectionSyntaxPayloadParser,
{
buf: Vec<u8>,
state: BufferSectionState,
parser: P,
}
impl<P> BufferSectionSyntaxParser<P>
where
P: WholeSectionSyntaxPayloadParser,
{
pub fn new(parser: P) -> BufferSectionSyntaxParser<P> {
BufferSectionSyntaxParser {
buf: vec![],
state: BufferSectionState::Complete,
parser,
}
}
}
impl<P> SectionSyntaxPayloadParser for BufferSectionSyntaxParser<P>
where
P: WholeSectionSyntaxPayloadParser,
{
type Context = P::Context;
fn start_syntax_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
table_syntax_header: &TableSyntaxHeader<'a>,
data: &'a [u8],
) {
let section_length_with_header = header.section_length + SectionCommonHeader::SIZE;
if section_length_with_header <= data.len() {
self.state = BufferSectionState::Complete;
self.parser.section(
ctx,
header,
table_syntax_header,
&data[..section_length_with_header],
)
} else {
self.buf.clear();
self.buf.extend_from_slice(data);
let to_read = section_length_with_header - data.len();
self.state = BufferSectionState::Buffering(to_read);
}
}
fn continue_syntax_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]) {
match self.state {
BufferSectionState::Complete => {
warn!("attempt to add extra data when section already complete");
}
BufferSectionState::Buffering(remaining) => {
let new_remaining = if data.len() > remaining {
0
} else {
remaining - data.len()
};
if new_remaining == 0 {
self.buf.extend_from_slice(&data[..remaining]);
self.state = BufferSectionState::Complete;
let header = SectionCommonHeader::new(&self.buf[..SectionCommonHeader::SIZE]);
let table_syntax_header =
TableSyntaxHeader::new(&self.buf[SectionCommonHeader::SIZE..]);
self.parser
.section(ctx, &header, &table_syntax_header, &self.buf[..]);
} else {
self.buf.extend_from_slice(data);
self.state = BufferSectionState::Buffering(new_remaining);
}
}
}
}
fn reset(&mut self) {
self.buf.clear();
self.state = BufferSectionState::Complete;
}
}
pub struct BufferCompactSyntaxParser<P>
where
P: WholeCompactSyntaxPayloadParser,
{
buf: Vec<u8>,
state: BufferSectionState,
parser: P,
}
impl<P> BufferCompactSyntaxParser<P>
where
P: WholeCompactSyntaxPayloadParser,
{
pub fn new(parser: P) -> BufferCompactSyntaxParser<P> {
BufferCompactSyntaxParser {
buf: vec![],
state: BufferSectionState::Complete,
parser,
}
}
}
impl<P> CompactSyntaxPayloadParser for BufferCompactSyntaxParser<P>
where
P: WholeCompactSyntaxPayloadParser,
{
type Context = P::Context;
fn start_compact_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
data: &'a [u8],
) {
let section_length_with_header = header.section_length + SectionCommonHeader::SIZE;
if section_length_with_header <= data.len() {
self.state = BufferSectionState::Complete;
self.parser
.section(ctx, header, &data[..section_length_with_header])
} else {
self.buf.clear();
self.buf.extend_from_slice(data);
let to_read = section_length_with_header - data.len();
self.state = BufferSectionState::Buffering(to_read);
}
}
fn continue_compact_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]) {
match self.state {
BufferSectionState::Complete => {
warn!("attempt to add extra data when section already complete");
}
BufferSectionState::Buffering(remaining) => {
let new_remaining = if data.len() > remaining {
0
} else {
remaining - data.len()
};
if new_remaining == 0 {
self.buf.extend_from_slice(&data[..remaining]);
self.state = BufferSectionState::Complete;
let header = SectionCommonHeader::new(&self.buf[..SectionCommonHeader::SIZE]);
self.parser.section(ctx, &header, &self.buf[..]);
} else {
self.buf.extend_from_slice(data);
self.state = BufferSectionState::Buffering(new_remaining);
}
}
}
}
fn reset(&mut self) {
self.buf.clear();
self.state = BufferSectionState::Complete;
}
}
pub struct DedupSectionSyntaxPayloadParser<SSPP>
where
SSPP: SectionSyntaxPayloadParser,
{
inner: SSPP,
last_version: Option<u8>,
ignore_rest: bool,
}
impl<SSPP> DedupSectionSyntaxPayloadParser<SSPP>
where
SSPP: SectionSyntaxPayloadParser,
{
pub fn new(inner: SSPP) -> DedupSectionSyntaxPayloadParser<SSPP> {
DedupSectionSyntaxPayloadParser {
inner,
last_version: None,
ignore_rest: false,
}
}
}
impl<SSPP> SectionSyntaxPayloadParser for DedupSectionSyntaxPayloadParser<SSPP>
where
SSPP: SectionSyntaxPayloadParser,
{
type Context = SSPP::Context;
fn start_syntax_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
table_syntax_header: &TableSyntaxHeader<'a>,
data: &'a [u8],
) {
if let Some(last) = self.last_version {
if last == table_syntax_header.version() {
self.ignore_rest = true;
return;
}
}
self.ignore_rest = false;
self.last_version = Some(table_syntax_header.version());
self.inner
.start_syntax_section(ctx, header, table_syntax_header, data);
}
fn continue_syntax_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]) {
if !self.ignore_rest {
self.inner.continue_syntax_section(ctx, data)
}
}
fn reset(&mut self) {
self.inner.reset();
self.last_version = None;
self.ignore_rest = false;
}
}
pub trait SectionSyntaxPayloadParser {
type Context;
fn start_syntax_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
table_syntax_header: &TableSyntaxHeader<'a>,
data: &'a [u8],
);
fn continue_syntax_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]);
fn reset(&mut self);
}
pub trait CompactSyntaxPayloadParser {
type Context;
fn start_compact_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
data: &'a [u8],
);
fn continue_compact_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]);
fn reset(&mut self);
}
pub struct CompactSyntaxSectionProcessor<SP>
where
SP: CompactSyntaxPayloadParser,
{
payload_parser: SP,
ignore_rest: bool,
}
impl<SP> CompactSyntaxSectionProcessor<SP>
where
SP: CompactSyntaxPayloadParser,
{
const SECTION_LIMIT: usize = 1021;
pub fn new(payload_parser: SP) -> CompactSyntaxSectionProcessor<SP> {
CompactSyntaxSectionProcessor {
payload_parser,
ignore_rest: false,
}
}
}
impl<SP> SectionProcessor for CompactSyntaxSectionProcessor<SP>
where
SP: CompactSyntaxPayloadParser,
{
type Context = SP::Context;
fn start_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
data: &'a [u8],
) {
if header.section_syntax_indicator {
warn!(
"CompactSyntaxSectionProcessor requires that section_syntax_indicator NOT be set in the section header"
);
self.ignore_rest = true;
return;
}
if data.len() < SectionCommonHeader::SIZE {
warn!("CompactSyntaxSectionProcessor data {} too short for header {} (TODO: implement buffering)", data.len(), SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE);
self.ignore_rest = true;
return;
}
if header.section_length > Self::SECTION_LIMIT {
warn!(
"CompactSyntaxSectionProcessor section_length={} is too large (limit {})",
header.section_length,
Self::SECTION_LIMIT
);
self.ignore_rest = true;
return;
}
self.ignore_rest = false;
self.payload_parser.start_compact_section(ctx, header, data)
}
fn continue_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]) {
if !self.ignore_rest {
self.payload_parser.continue_compact_section(ctx, data)
}
}
fn reset(&mut self) {
self.payload_parser.reset()
}
}
pub struct SectionSyntaxSectionProcessor<SP>
where
SP: SectionSyntaxPayloadParser,
{
payload_parser: SP,
ignore_rest: bool,
}
impl<SP> SectionSyntaxSectionProcessor<SP>
where
SP: SectionSyntaxPayloadParser,
{
const SECTION_LIMIT: usize = 1021;
pub fn new(payload_parser: SP) -> SectionSyntaxSectionProcessor<SP> {
SectionSyntaxSectionProcessor {
payload_parser,
ignore_rest: false,
}
}
}
impl<SP> SectionProcessor for SectionSyntaxSectionProcessor<SP>
where
SP: SectionSyntaxPayloadParser,
{
type Context = SP::Context;
fn start_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
data: &'a [u8],
) {
if !header.section_syntax_indicator {
warn!(
"SectionSyntaxSectionProcessor requires that section_syntax_indicator be set in the section header"
);
self.ignore_rest = true;
return;
}
if data.len() < SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE {
warn!("SectionSyntaxSectionProcessor data {} too short for header {} (TODO: implement buffering)", data.len(), SectionCommonHeader::SIZE + TableSyntaxHeader::SIZE);
self.ignore_rest = true;
return;
}
if header.section_length > Self::SECTION_LIMIT {
warn!(
"SectionSyntaxSectionProcessor section_length={} is too large (limit {})",
header.section_length,
Self::SECTION_LIMIT
);
self.ignore_rest = true;
return;
}
self.ignore_rest = false;
let table_syntax_header = TableSyntaxHeader::new(&data[SectionCommonHeader::SIZE..]);
self.payload_parser
.start_syntax_section(ctx, header, &table_syntax_header, data)
}
fn continue_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]) {
if !self.ignore_rest {
self.payload_parser.continue_syntax_section(ctx, data)
}
}
fn reset(&mut self) {
self.payload_parser.reset()
}
}
#[derive(Debug)]
pub struct SectionCommonHeader {
pub table_id: u8,
pub section_syntax_indicator: bool,
pub private_indicator: bool,
pub section_length: usize,
}
impl SectionCommonHeader {
pub const SIZE: usize = 3;
pub fn new(buf: &[u8]) -> SectionCommonHeader {
assert_eq!(buf.len(), Self::SIZE);
SectionCommonHeader {
table_id: buf[0],
section_syntax_indicator: buf[1] & 0b1000_0000 != 0,
private_indicator: buf[1] & 0b0100_0000 != 0,
section_length: ((u16::from(buf[1] & 0b0000_1111) << 8) | u16::from(buf[2])) as usize,
}
}
}
pub struct SectionPacketConsumer<P>
where
P: SectionProcessor,
{
parser: P,
}
impl<P, Ctx> SectionPacketConsumer<P>
where
P: SectionProcessor<Context = Ctx>,
{
pub fn new(parser: P) -> SectionPacketConsumer<P> {
SectionPacketConsumer { parser }
}
pub fn consume(&mut self, ctx: &mut Ctx, pk: &packet::Packet<'_>) {
match pk.payload() {
Some(pk_buf) => {
if pk.payload_unit_start_indicator() {
let pointer = pk_buf[0] as usize;
let section_data = &pk_buf[1..];
if pointer > 0 {
if pointer >= section_data.len() {
warn!("PSI pointer beyond end of packet payload");
self.parser.reset();
return;
}
let remainder = §ion_data[..pointer];
self.parser.continue_section(ctx, remainder);
}
let next_sect = §ion_data[pointer..];
if next_sect.len() < SectionCommonHeader::SIZE {
warn!(
"TODO: not enough bytes to read section header - implement buffering"
);
self.parser.reset();
return;
}
let header = SectionCommonHeader::new(&next_sect[..SectionCommonHeader::SIZE]);
self.parser.start_section(ctx, &header, next_sect);
} else {
self.parser.continue_section(ctx, pk_buf);
}
}
None => {
warn!("no payload present in PSI packet");
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::demultiplex;
use crate::packet::Packet;
use hex_literal::*;
use std::cell::RefCell;
use std::rc::Rc;
packet_filter_switch! {
NullFilterSwitch<NullDemuxContext> {
Pat: demultiplex::PatPacketFilter<NullDemuxContext>,
Pmt: demultiplex::PmtPacketFilter<NullDemuxContext>,
Nul: demultiplex::NullPacketFilter<NullDemuxContext>,
}
}
demux_context!(NullDemuxContext, NullFilterSwitch);
impl NullDemuxContext {
fn do_construct(&mut self, req: demultiplex::FilterRequest<'_, '_>) -> NullFilterSwitch {
unimplemented!()
}
}
struct NullSectionProcessor;
impl SectionProcessor for NullSectionProcessor {
type Context = NullDemuxContext;
fn start_section<'a>(
&mut self,
_ctx: &mut Self::Context,
_header: &SectionCommonHeader,
_section_data: &'a [u8],
) {
}
fn continue_section<'a>(&mut self, _ctx: &mut Self::Context, _section_data: &'a [u8]) {}
fn reset(&mut self) {}
}
#[test]
fn continuation_outside_section() {
let mut buf = [0u8; 188];
buf[0] = 0x47;
buf[3] |= 0b00010000;
let pk = Packet::new(&buf[..]);
let mut psi_buf = SectionPacketConsumer::new(NullSectionProcessor);
let mut ctx = NullDemuxContext::new();
psi_buf.consume(&mut ctx, &pk);
}
#[test]
fn small_section() {
let mut buf = [0u8; 188];
buf[0] = 0x47;
buf[1] |= 0b01000000;
buf[3] |= 0b00010000;
buf[7] = 3;
let pk = Packet::new(&buf[..]);
let mut psi_buf = SectionPacketConsumer::new(NullSectionProcessor);
let mut ctx = NullDemuxContext::new();
psi_buf.consume(&mut ctx, &pk);
}
#[test]
fn section_spanning_packets() {
let state = Rc::new(RefCell::new(false));
struct MockSectParse {
state: Rc<RefCell<bool>>,
}
impl WholeSectionSyntaxPayloadParser for MockSectParse {
type Context = ();
fn section<'a>(
&mut self,
_: &mut Self::Context,
_header: &SectionCommonHeader,
_table_syntax_header: &TableSyntaxHeader<'_>,
_data: &[u8],
) {
*self.state.borrow_mut() = true;
}
}
let mut p = BufferSectionSyntaxParser::new(CrcCheckWholeSectionSyntaxPayloadParser::new(
MockSectParse {
state: state.clone(),
},
));
let ctx = &mut ();
{
let sect = hex!(
"
42f13040 84e90000 233aff44 40ff8026
480d1900 0a424243 2054574f 20484473
0c66702e 6262632e 636f2e75 6b5f0400
00233a7e 01f744c4 ff802148 09190006
49545620 4844730b 7777772e 6974762e
636f6d5f 04000023 3a7e01f7 4500ff80
2c480f19 000c4368 616e6e65 6c203420
48447310 7777772e 6368616e 6e656c34
2e636f6d 5f040000 233a7e01 f74484ff
8026480d 19000a42 4243204f 4e452048
44730c66 702e6262 632e636f 2e756b5f
04000023 3a7e01"
);
let common_header = SectionCommonHeader::new(§[..SectionCommonHeader::SIZE]);
let table_header = TableSyntaxHeader::new(§[SectionCommonHeader::SIZE..]);
p.start_syntax_section(ctx, &common_header, &table_header, §[..]);
}
{
let sect = hex!(
"
f746c0ff 8023480a 19000743 42424320
4844730c 66702e62 62632e63 6f2e756b
5f040000 233a7e01 f74f80ff 801e480a
16000746 696c6d34 2b317310 7777772e
6368616e 6e656c34 2e636f6d 4540ff80
27480f19 000c4368 616e6e65 6c203520
4844730b 7777772e 66697665 2e74765f
04000023 3a7e01f7 f28b26c4 ffffffff
ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff ffffffff ffffffff
ffffffff ffffffff"
);
p.continue_syntax_section(ctx, §[..]);
}
assert!(*state.borrow());
}
#[test]
fn table_syntax() {
let sect = hex!("4084e90000");
let header = TableSyntaxHeader::new(§);
assert_eq!(header.current_next_indicator(), CurrentNext::Current);
assert_eq!(header.id(), 16516);
assert_eq!(header.section_number(), 0);
assert_eq!(header.last_section_number(), 0);
assert_eq!(header.version(), 20);
assert!(!format!("{:?}", header).is_empty());
}
#[test]
fn dedup_section() {
struct CallCounts {
start: usize,
cont: usize,
reset: usize,
}
struct Mock {
inner: Rc<RefCell<CallCounts>>,
}
let counts = Rc::new(RefCell::new(CallCounts {
start: 0,
cont: 0,
reset: 0,
}));
impl SectionSyntaxPayloadParser for Mock {
type Context = ();
fn start_syntax_section<'a>(
&mut self,
_ctx: &mut Self::Context,
header: &SectionCommonHeader,
table_syntax_header: &TableSyntaxHeader<'a>,
data: &'a [u8],
) {
self.inner.borrow_mut().start += 1;
}
fn continue_syntax_section<'a>(&mut self, _ctx: &mut Self::Context, data: &'a [u8]) {
self.inner.borrow_mut().cont += 1;
}
fn reset(&mut self) {
self.inner.borrow_mut().reset += 1;
}
}
let mut dedup = DedupSectionSyntaxPayloadParser::new(Mock {
inner: counts.clone(),
});
let sect = hex!("42f130 4084e90000");
let common_header = SectionCommonHeader::new(§[..SectionCommonHeader::SIZE]);
let table_header = TableSyntaxHeader::new(§[SectionCommonHeader::SIZE..]);
assert_eq!(table_header.version(), 20);
let ctx = &mut ();
dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
dedup.continue_syntax_section(ctx, &[]);
assert_eq!(counts.borrow().start, 1);
assert_eq!(counts.borrow().cont, 1);
dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
dedup.continue_syntax_section(ctx, &[]);
assert_eq!(counts.borrow().start, 1);
assert_eq!(counts.borrow().cont, 1);
let sect = hex!("42f131 4084ea0000");
let common_header = SectionCommonHeader::new(§[..SectionCommonHeader::SIZE]);
let table_header = TableSyntaxHeader::new(§[SectionCommonHeader::SIZE..]);
assert_eq!(table_header.version(), 21);
dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
dedup.continue_syntax_section(ctx, &[]);
assert_eq!(counts.borrow().start, 2);
assert_eq!(counts.borrow().cont, 2);
assert_eq!(counts.borrow().reset, 0);
dedup.reset();
assert_eq!(counts.borrow().reset, 1);
dedup.start_syntax_section(ctx, &common_header, &table_header, &[]);
dedup.continue_syntax_section(ctx, &[]);
assert_eq!(counts.borrow().start, 3);
assert_eq!(counts.borrow().cont, 3);
}
#[test]
fn compact_section_syntax() {
struct CallCounts {
start: usize,
cont: usize,
reset: usize,
}
struct Mock {
inner: Rc<RefCell<CallCounts>>,
}
impl CompactSyntaxPayloadParser for Mock {
type Context = ();
fn start_compact_section<'a>(
&mut self,
ctx: &mut Self::Context,
header: &SectionCommonHeader,
data: &'a [u8],
) {
self.inner.borrow_mut().start += 1;
}
fn continue_compact_section<'a>(&mut self, ctx: &mut Self::Context, data: &'a [u8]) {
todo!()
}
fn reset(&mut self) {
todo!()
}
}
let counts = Rc::new(RefCell::new(CallCounts {
start: 0,
cont: 0,
reset: 0,
}));
let mut proc = CompactSyntaxSectionProcessor::new(Mock {
inner: counts.clone(),
});
let ctx = &mut ();
let sect = hex!("42f131");
let common_header = SectionCommonHeader::new(§[..SectionCommonHeader::SIZE]);
assert!(common_header.section_syntax_indicator);
proc.start_section(ctx, &common_header, §);
assert_eq!(0, counts.borrow().start);
let sect = hex!("427131");
let common_header = SectionCommonHeader::new(§[..SectionCommonHeader::SIZE]);
assert!(!common_header.section_syntax_indicator);
proc.start_section(ctx, &common_header, §[..2]);
assert_eq!(0, counts.borrow().start);
let header = hex!("4273fe");
let mut sect = vec![];
sect.extend_from_slice(&header);
sect.resize(header.len() + 1022, 0);
let common_header = SectionCommonHeader::new(§[..SectionCommonHeader::SIZE]);
assert!(!common_header.section_syntax_indicator);
proc.start_section(ctx, &common_header, §);
assert_eq!(0, counts.borrow().start);
let sect = hex!("427000");
let common_header = SectionCommonHeader::new(§[..SectionCommonHeader::SIZE]);
assert!(!common_header.section_syntax_indicator);
proc.start_section(ctx, &common_header, §);
assert_eq!(1, counts.borrow().start);
}
}