use broadcast_common::Parse;
use broadcast_common::bits::{BitReader, BitWriter};
use crate::audio_data_dlc::AudioDataDlc;
use crate::bed_definition::BedDefinition1;
use crate::error::{Error, Result};
use crate::frame_rate::FrameRate;
use crate::object_definition::ObjectDefinition1;
use crate::plex::{plex_bits, read_plex, write_plex};
pub const ELEMENT_ID_ATMOS_FRAME: u32 = 0x08;
pub const ELEMENT_ID_BED_DEFINITION1: u32 = 0x10;
pub const ELEMENT_ID_OBJECT_DEFINITION1: u32 = 0x40;
pub const ELEMENT_ID_AUDIO_DATA_DLC: u32 = 0x200;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ElementId {
AtmosFrame,
BedDefinition1,
ObjectDefinition1,
AudioDataDlc,
Reserved(u32),
}
impl ElementId {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::AtmosFrame => "ATMOS_FRAME",
Self::BedDefinition1 => "BED_DEFINITION1",
Self::ObjectDefinition1 => "OBJECT_DEFINITION1",
Self::AudioDataDlc => "AUDIO_DATA_DLC",
Self::Reserved(_) => "reserved",
}
}
#[must_use]
pub fn from_wire(v: u32) -> Self {
match v {
ELEMENT_ID_ATMOS_FRAME => Self::AtmosFrame,
ELEMENT_ID_BED_DEFINITION1 => Self::BedDefinition1,
ELEMENT_ID_OBJECT_DEFINITION1 => Self::ObjectDefinition1,
ELEMENT_ID_AUDIO_DATA_DLC => Self::AudioDataDlc,
other => Self::Reserved(other),
}
}
#[must_use]
pub fn to_wire(self) -> u32 {
match self {
Self::AtmosFrame => ELEMENT_ID_ATMOS_FRAME,
Self::BedDefinition1 => ELEMENT_ID_BED_DEFINITION1,
Self::ObjectDefinition1 => ELEMENT_ID_OBJECT_DEFINITION1,
Self::AudioDataDlc => ELEMENT_ID_AUDIO_DATA_DLC,
Self::Reserved(v) => v,
}
}
}
broadcast_common::impl_spec_display!(ElementId, Reserved);
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum AnyElement<'a> {
BedDefinition1(BedDefinition1),
ObjectDefinition1(ObjectDefinition1<'a>),
AudioDataDlc(AudioDataDlc<'a>),
Unknown {
element_id: u32,
data: &'a [u8],
},
}
impl<'a> AnyElement<'a> {
fn element_id_wire(&self) -> u32 {
match self {
Self::BedDefinition1(_) => ELEMENT_ID_BED_DEFINITION1,
Self::ObjectDefinition1(_) => ELEMENT_ID_OBJECT_DEFINITION1,
Self::AudioDataDlc(_) => ELEMENT_ID_AUDIO_DATA_DLC,
Self::Unknown { element_id, .. } => *element_id,
}
}
fn body_len(&self) -> usize {
use broadcast_common::Serialize;
match self {
Self::BedDefinition1(b) => b.serialized_len(),
Self::ObjectDefinition1(o) => o.serialized_len(),
Self::AudioDataDlc(d) => d.serialized_len(),
Self::Unknown { data, .. } => data.len(),
}
}
#[must_use]
pub fn serialized_len(&self) -> usize {
element_header_len(self.element_id_wire(), self.body_len()) + self.body_len()
}
pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
use broadcast_common::Serialize;
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::BufferTooShort {
need,
have: buf.len(),
what: "AnyElement",
});
}
let body_len = self.body_len();
write_element_header(&mut buf[..need], self.element_id_wire(), body_len)?;
let header_len = need - body_len;
match self {
Self::BedDefinition1(b) => {
b.serialize_into(&mut buf[header_len..need])?;
}
Self::ObjectDefinition1(o) => {
o.serialize_into(&mut buf[header_len..need])?;
}
Self::AudioDataDlc(d) => {
d.serialize_into(&mut buf[header_len..need])?;
}
Self::Unknown { data, .. } => {
buf[header_len..need].copy_from_slice(data);
}
}
Ok(need)
}
pub fn parse_with_frame_rate(
bytes: &'a [u8],
frame_rate: Option<FrameRate>,
) -> Result<(Self, usize)> {
let mut r = BitReader::new(bytes);
let element_id = read_plex(&mut r, 8, "ElementID")? as u32;
let element_size = read_plex(&mut r, 8, "ElementSize")?;
debug_assert!(r.is_byte_aligned());
let header_len = r.bits_read() / 8;
let element_size = usize::try_from(element_size).map_err(|_| Error::InvalidValue {
field: "ElementSize",
value: element_size,
reason: "does not fit in this platform's usize",
})?;
let body_end = header_len
.checked_add(element_size)
.ok_or(Error::InvalidValue {
field: "ElementSize",
value: element_size as u64,
reason: "overflowed usize",
})?;
if body_end > bytes.len() {
return Err(Error::BufferTooShort {
need: body_end,
have: bytes.len(),
what: "element body",
});
}
let body = &bytes[header_len..body_end];
let element = match element_id {
ELEMENT_ID_BED_DEFINITION1 => Self::BedDefinition1(BedDefinition1::parse(body)?),
ELEMENT_ID_OBJECT_DEFINITION1 => {
let frame_rate = frame_rate.ok_or(Error::InvalidValue {
field: "ObjectDefinition1",
value: 0,
reason: "parsing an OBJECT_DEFINITION1 element requires the enclosing \
ATMOSFrame's FrameRate as context",
})?;
Self::ObjectDefinition1(ObjectDefinition1::parse_with_frame_rate(body, frame_rate)?)
}
ELEMENT_ID_AUDIO_DATA_DLC => Self::AudioDataDlc(AudioDataDlc::parse(body)?),
other => Self::Unknown {
element_id: other,
data: body,
},
};
Ok((element, body_end))
}
}
pub(crate) fn element_header_len(element_id: u32, body_len: usize) -> usize {
let bits = plex_bits(u64::from(element_id), 8) + plex_bits(body_len as u64, 8);
(bits as usize).div_ceil(8)
}
pub(crate) fn write_element_header(buf: &mut [u8], element_id: u32, body_len: usize) -> Result<()> {
let mut w = BitWriter::new(buf);
write_plex(&mut w, u64::from(element_id), 8, "ElementID")?;
write_plex(&mut w, body_len as u64, 8, "ElementSize")?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn element_id_round_trips() {
for wire in [
ELEMENT_ID_ATMOS_FRAME,
ELEMENT_ID_BED_DEFINITION1,
0x20,
ELEMENT_ID_OBJECT_DEFINITION1,
0x80,
0x100,
ELEMENT_ID_AUDIO_DATA_DLC,
] {
assert_eq!(ElementId::from_wire(wire).to_wire(), wire);
}
}
#[test]
fn unknown_element_round_trips_verbatim() {
let data = [0xAAu8, 0xBB, 0xCC];
let element = AnyElement::Unknown {
element_id: 0x20,
data: &data,
};
let mut buf = alloc::vec![0u8; element.serialized_len()];
element.serialize_into(&mut buf).unwrap();
let (parsed, consumed) = AnyElement::parse_with_frame_rate(&buf, None).unwrap();
assert_eq!(consumed, buf.len());
match parsed {
AnyElement::Unknown {
element_id,
data: parsed_data,
} => {
assert_eq!(element_id, 0x20);
assert_eq!(parsed_data, &data);
}
other => panic!("expected Unknown, got {other:?}"),
}
}
#[test]
fn object_definition_without_frame_rate_context_errs() {
let element = AnyElement::Unknown {
element_id: ELEMENT_ID_OBJECT_DEFINITION1,
data: &[0u8; 4],
};
let mut wire = alloc::vec![0u8; element.serialized_len()];
element.serialize_into(&mut wire).unwrap();
let err = AnyElement::parse_with_frame_rate(&wire, None).unwrap_err();
assert!(matches!(err, Error::InvalidValue { .. }));
}
}