use crate::error::{Error, Result};
use broadcast_common::clock33::{self, pack_marked, unpack_marked};
use broadcast_common::len::{need_in, need_out};
use broadcast_common::{Parse, Serialize};
pub const TABLE_ID: u8 = 0x09;
const MIN_HEADER_LEN: usize = 3;
const PTS_FIELD_LEN: usize = 5;
use super::CRC_LEN;
const MIN_SECTION_LEN: usize = MIN_HEADER_LEN + PTS_FIELD_LEN + CRC_LEN;
const PTS_PREFIX: u8 = 0b0010;
const PTS_BITS: u32 = 33;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
#[non_exhaustive]
pub struct GreenAccessUnitSection<'a> {
pub display_in_pts: u64,
pub green_au: &'a [u8],
#[cfg_attr(feature = "serde", serde(skip))]
pub bits: super::SectionBits,
}
impl<'a> GreenAccessUnitSection<'a> {
#[must_use]
pub fn new(display_in_pts: u64, green_au: &'a [u8]) -> Self {
Self {
display_in_pts,
green_au,
bits: super::SectionBits::psi().short_form(),
}
}
}
impl<'a> Parse<'a> for GreenAccessUnitSection<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
need_in(bytes, MIN_SECTION_LEN, "GreenAccessUnitSection")?;
if bytes[0] != TABLE_ID {
return Err(Error::UnexpectedTableId {
table_id: bytes[0],
what: "GreenAccessUnitSection",
expected: &[TABLE_ID],
});
}
let total = super::check_section_length(
bytes.len(),
MIN_HEADER_LEN,
super::section_length_of(bytes),
MIN_SECTION_LEN,
)?;
let mut p = [0u8; PTS_FIELD_LEN];
p.copy_from_slice(&bytes[MIN_HEADER_LEN..MIN_HEADER_LEN + PTS_FIELD_LEN]);
let marked = unpack_marked(&p);
if marked.prefix != PTS_PREFIX {
return Err(Error::ReservedBits(broadcast_common::error::ReservedBits {
field: "Display_in_PTS prefix",
expected: u64::from(PTS_PREFIX),
found: u64::from(marked.prefix),
}));
}
if marked.markers != clock33::MARKERS_ALL_SET {
return Err(Error::ReservedBits(broadcast_common::error::ReservedBits {
field: "Display_in_PTS marker_bits",
expected: u64::from(clock33::MARKERS_ALL_SET),
found: u64::from(marked.markers),
}));
}
Ok(Self {
display_in_pts: marked.timestamp,
green_au: &bytes[MIN_HEADER_LEN + PTS_FIELD_LEN..total - CRC_LEN],
bits: super::SectionBits::parse(bytes[1], None),
})
}
}
impl Serialize for GreenAccessUnitSection<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
MIN_SECTION_LEN + self.green_au.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let pts = broadcast_common::len::fit_bits(self.display_in_pts, PTS_BITS, "Display_in_PTS")?;
let len = self.serialized_len();
need_out(buf, len)?;
buf[0] = TABLE_ID;
buf[1] = self.bits.b1()?;
super::write_section_length(buf, len - MIN_HEADER_LEN)?;
let p = MIN_HEADER_LEN;
buf[p..p + PTS_FIELD_LEN].copy_from_slice(&pack_marked(pts, PTS_PREFIX));
let start = MIN_HEADER_LEN + PTS_FIELD_LEN;
buf[start..start + self.green_au.len()].copy_from_slice(self.green_au);
super::write_crc(buf, len - CRC_LEN);
Ok(len)
}
}
impl<'a> crate::traits::TableDef<'a> for GreenAccessUnitSection<'a> {
const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
const NAME: &'static str = "GREEN_ACCESS_UNIT";
}
#[cfg(test)]
#[allow(clippy::unusual_byte_groupings)] mod tests {
use super::*;
#[test]
fn spec_vector_table_2_162() {
let pts: u64 = 0x1_2345_6789 & (clock33::WRAP_33BIT - 1);
let s = GreenAccessUnitSection::new(pts, &[0x41, 0xFF, 0x10]);
let mut b = vec![0u8; s.serialized_len()];
s.serialize_into(&mut b).unwrap();
assert_eq!(&b[..2], &[0x09, 0x30]);
assert_eq!(b[3] >> 4, 0b0010);
assert_eq!(b[3] & 1, 1);
assert_eq!(b[5] & 1, 1);
assert_eq!(b[7] & 1, 1);
assert_eq!(&b[8..11], &[0x41, 0xFF, 0x10]);
assert_eq!(broadcast_common::crc32_mpeg2::compute(&b), 0);
let p = GreenAccessUnitSection::parse(&b).unwrap();
assert_eq!(p.display_in_pts, pts);
assert_eq!(p, s);
let s = GreenAccessUnitSection::new(90_000, &[]);
let mut b = vec![0u8; s.serialized_len()];
s.serialize_into(&mut b).unwrap();
assert_eq!(&b[3..8], &[0x21, 0x00, 0x05, 0xBF, 0x21]);
}
#[test]
fn rejects_bad_markers_pts_overflow_and_ssi_kept() {
let s = GreenAccessUnitSection::new(1, &[]);
let mut b = vec![0u8; s.serialized_len()];
s.serialize_into(&mut b).unwrap();
let mut bad = b.clone();
bad[5] &= 0xFE;
assert_eq!(
GreenAccessUnitSection::parse(&bad),
Err(Error::ReservedBits(broadcast_common::error::ReservedBits {
field: "Display_in_PTS marker_bits",
expected: 0b111,
found: 0b101,
}))
);
let mut bad = b.clone();
bad[3] = 0x31;
assert_eq!(
GreenAccessUnitSection::parse(&bad),
Err(Error::ReservedBits(broadcast_common::error::ReservedBits {
field: "Display_in_PTS prefix",
expected: 0b0010,
found: 3,
}))
);
assert!(matches!(
&(GreenAccessUnitSection::new(clock33::WRAP_33BIT, &[]).serialize_into(&mut [0u8; 32])),
Err(crate::error::Error::FieldOverflow(_))
));
let mut with_ssi = s.clone();
with_ssi.bits.section_syntax_indicator = true;
let mut b2 = vec![0u8; with_ssi.serialized_len()];
with_ssi.serialize_into(&mut b2).unwrap();
assert_eq!(b2[1] & 0x80, 0x80);
assert_eq!(GreenAccessUnitSection::parse(&b2).unwrap(), with_ssi);
}
}