use alloc::vec::Vec;
#[cfg(any(feature = "ts", feature = "rtp"))]
use broadcast_common::bits::{BitReader, BitWriter};
#[cfg(any(feature = "ts", feature = "rtp"))]
use crate::error::{Error, Result};
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) const W_DID: u32 = 10;
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) const W_SDID: u32 = 10;
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) const W_DATA_COUNT: u32 = 10;
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) const W_USER_DATA_WORD: u32 = 10;
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) const W_CHECKSUM: u32 = 10;
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) fn check_field_width(what: &'static str, value: u64, bits: u32) -> Result<u64> {
if bits < 64 && value >= (1u64 << bits) {
return Err(Error::FieldTooWide {
what,
value: value as u32,
bits,
});
}
Ok(value)
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct AncContent {
pub did: u16,
pub sdid: u16,
pub data_count: u16,
pub user_data_words: Vec<u16>,
pub checksum: u16,
}
impl AncContent {
#[must_use]
pub fn udw_loop_count(&self) -> usize {
usize::from(self.data_count & 0xFF)
}
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) fn content_bit_width(&self) -> usize {
(W_DID + W_SDID + W_DATA_COUNT + W_CHECKSUM) as usize
+ self.udw_loop_count() * W_USER_DATA_WORD as usize
}
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) fn write_into(&self, w: &mut BitWriter<'_>) -> Result<()> {
let need = self.udw_loop_count();
let have = self.user_data_words.len();
if have != need {
return Err(Error::InconsistentUdwLength { have, need });
}
w.write_bits(check_field_width("DID", u64::from(self.did), W_DID)?, W_DID)?;
w.write_bits(
check_field_width("SDID", u64::from(self.sdid), W_SDID)?,
W_SDID,
)?;
w.write_bits(
check_field_width("Data_Count", u64::from(self.data_count), W_DATA_COUNT)?,
W_DATA_COUNT,
)?;
for udw in &self.user_data_words {
w.write_bits(
check_field_width("User_Data_Word", u64::from(*udw), W_USER_DATA_WORD)?,
W_USER_DATA_WORD,
)?;
}
w.write_bits(
check_field_width("Checksum_Word", u64::from(self.checksum), W_CHECKSUM)?,
W_CHECKSUM,
)?;
Ok(())
}
#[cfg(any(feature = "ts", feature = "rtp"))]
pub(crate) fn read_from(r: &mut BitReader<'_>) -> Result<Self> {
let did = r.read_bits(W_DID)? as u16;
let sdid = r.read_bits(W_SDID)? as u16;
let data_count = r.read_bits(W_DATA_COUNT)? as u16;
let n = usize::from(data_count & 0xFF);
let mut user_data_words = Vec::with_capacity(n);
for _ in 0..n {
user_data_words.push(r.read_bits(W_USER_DATA_WORD)? as u16);
}
let checksum = r.read_bits(W_CHECKSUM)? as u16;
Ok(Self {
did,
sdid,
data_count,
user_data_words,
checksum,
})
}
}
#[cfg(all(test, any(feature = "ts", feature = "rtp")))]
mod tests {
use super::*;
use alloc::vec;
fn sample() -> AncContent {
AncContent {
did: 0x161,
sdid: 0x101,
data_count: 0x002,
user_data_words: vec![0x2CF, 0x101],
checksum: 0x233,
}
}
#[test]
fn round_trip() {
let c = sample();
let bits = c.content_bit_width();
assert_eq!(bits, 40 + 2 * 10);
let mut buf = vec![0u8; bits.div_ceil(8)];
{
let mut w = BitWriter::new(&mut buf);
c.write_into(&mut w).unwrap();
}
let mut r = BitReader::new(&buf);
let reparsed = AncContent::read_from(&mut r).unwrap();
assert_eq!(reparsed, c);
}
#[test]
fn rejects_inconsistent_udw_length() {
let mut c = sample();
c.user_data_words.pop();
let mut buf = vec![0u8; 8];
let mut w = BitWriter::new(&mut buf);
assert!(matches!(
c.write_into(&mut w),
Err(Error::InconsistentUdwLength { have: 1, need: 2 })
));
}
}