extern crate alloc;
use alloc::vec::Vec;
use broadcast_common::{Parse, Serialize};
use crate::ber::{ber_length_size, decode_ber_length, encode_ber_length};
use crate::error::{Error, Result};
use crate::types::{UlBytes, ul_bytes_from_prefix};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ItemLengthMode {
TwoByte,
Ber,
}
impl ItemLengthMode {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::TwoByte => "2-byte length",
Self::Ber => "BER length",
}
}
#[must_use]
pub fn registry_designator_byte(self) -> u8 {
match self {
Self::TwoByte => 0x53,
Self::Ber => 0x13,
}
}
#[must_use]
pub fn from_registry_designator_byte(b: u8) -> Option<Self> {
match b {
0x53 => Some(Self::TwoByte),
0x13 => Some(Self::Ber),
_ => None,
}
}
}
broadcast_common::impl_spec_display!(ItemLengthMode);
const SET_KEY_FIXED: [(usize, u8); 8] = [
(0, 0x06),
(1, 0x0E),
(2, 0x2B),
(3, 0x34),
(4, 0x02),
(6, 0x01),
(9, 0x01),
(11, 0x01),
];
const SET_KEY_BYTE10: u8 = 0x01;
const SET_KEY_BYTE12: u8 = 0x01;
#[must_use]
pub fn is_local_set_key(key: &UlBytes) -> bool {
SET_KEY_FIXED.iter().all(|&(i, v)| key[i] == v)
&& ItemLengthMode::from_registry_designator_byte(key[5]).is_some()
&& key[8] == 0x0D
&& key[9] == SET_KEY_BYTE10
&& key[10] == 0x01
&& key[12] == SET_KEY_BYTE12
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum StructuralSetKind {
Preface,
Identification,
ContentStorage,
EssenceContainerData,
MaterialPackage,
SourcePackage,
TimelineTrack,
EventTrackDm,
StaticTrackDm,
Sequence,
SourceClip,
TimecodeComponent,
DmSegment,
DmSourceClip,
Filler,
PackageMarkerObject,
FileDescriptor,
GenericPictureEssenceDescriptor,
CdciEssenceDescriptor,
RgbaEssenceDescriptor,
GenericSoundEssenceDescriptor,
GenericDataEssenceDescriptor,
MultipleDescriptor,
NetworkLocator,
TextLocator,
ApplicationPlugInObject,
ApplicationReferencedObject,
Unknown([u8; 2]),
}
impl StructuralSetKind {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Preface => "Preface",
Self::Identification => "Identification",
Self::ContentStorage => "Content Storage",
Self::EssenceContainerData => "Essence Container Data",
Self::MaterialPackage => "Material Package",
Self::SourcePackage => "Source Package",
Self::TimelineTrack => "Timeline Track",
Self::EventTrackDm => "Event Track (DM)",
Self::StaticTrackDm => "Static Track (DM)",
Self::Sequence => "Sequence",
Self::SourceClip => "Source Clip",
Self::TimecodeComponent => "Timecode Component",
Self::DmSegment => "DM Segment",
Self::DmSourceClip => "DM Source Clip",
Self::Filler => "Filler",
Self::PackageMarkerObject => "Package Marker Object",
Self::FileDescriptor => "File Descriptor",
Self::GenericPictureEssenceDescriptor => "Generic Picture Essence Descriptor",
Self::CdciEssenceDescriptor => "CDCI Essence Descriptor",
Self::RgbaEssenceDescriptor => "RGBA Essence Descriptor",
Self::GenericSoundEssenceDescriptor => "Generic Sound Essence Descriptor",
Self::GenericDataEssenceDescriptor => "Generic Data Essence Descriptor",
Self::MultipleDescriptor => "Multiple Descriptor",
Self::NetworkLocator => "Network Locator",
Self::TextLocator => "Text Locator",
Self::ApplicationPlugInObject => "Application Plug-In Object",
Self::ApplicationReferencedObject => "Application Referenced Object",
Self::Unknown(_) => "unknown",
}
}
#[must_use]
pub fn from_bytes(b14: u8, b15: u8) -> Self {
match (b14, b15) {
(0x01, 0x2F) => Self::Preface,
(0x01, 0x30) => Self::Identification,
(0x01, 0x18) => Self::ContentStorage,
(0x01, 0x23) => Self::EssenceContainerData,
(0x01, 0x36) => Self::MaterialPackage,
(0x01, 0x37) => Self::SourcePackage,
(0x01, 0x3B) => Self::TimelineTrack,
(0x01, 0x39) => Self::EventTrackDm,
(0x01, 0x3A) => Self::StaticTrackDm,
(0x01, 0x0F) => Self::Sequence,
(0x01, 0x11) => Self::SourceClip,
(0x01, 0x14) => Self::TimecodeComponent,
(0x01, 0x41) => Self::DmSegment,
(0x01, 0x45) => Self::DmSourceClip,
(0x01, 0x09) => Self::Filler,
(0x01, 0x60) => Self::PackageMarkerObject,
(0x01, 0x25) => Self::FileDescriptor,
(0x01, 0x27) => Self::GenericPictureEssenceDescriptor,
(0x01, 0x28) => Self::CdciEssenceDescriptor,
(0x01, 0x29) => Self::RgbaEssenceDescriptor,
(0x01, 0x42) => Self::GenericSoundEssenceDescriptor,
(0x01, 0x43) => Self::GenericDataEssenceDescriptor,
(0x01, 0x44) => Self::MultipleDescriptor,
(0x01, 0x32) => Self::NetworkLocator,
(0x01, 0x33) => Self::TextLocator,
(0x01, 0x61) => Self::ApplicationPlugInObject,
(0x01, 0x62) => Self::ApplicationReferencedObject,
other => Self::Unknown([other.0, other.1]),
}
}
#[must_use]
pub fn to_bytes(self) -> [u8; 2] {
match self {
Self::Preface => [0x01, 0x2F],
Self::Identification => [0x01, 0x30],
Self::ContentStorage => [0x01, 0x18],
Self::EssenceContainerData => [0x01, 0x23],
Self::MaterialPackage => [0x01, 0x36],
Self::SourcePackage => [0x01, 0x37],
Self::TimelineTrack => [0x01, 0x3B],
Self::EventTrackDm => [0x01, 0x39],
Self::StaticTrackDm => [0x01, 0x3A],
Self::Sequence => [0x01, 0x0F],
Self::SourceClip => [0x01, 0x11],
Self::TimecodeComponent => [0x01, 0x14],
Self::DmSegment => [0x01, 0x41],
Self::DmSourceClip => [0x01, 0x45],
Self::Filler => [0x01, 0x09],
Self::PackageMarkerObject => [0x01, 0x60],
Self::FileDescriptor => [0x01, 0x25],
Self::GenericPictureEssenceDescriptor => [0x01, 0x27],
Self::CdciEssenceDescriptor => [0x01, 0x28],
Self::RgbaEssenceDescriptor => [0x01, 0x29],
Self::GenericSoundEssenceDescriptor => [0x01, 0x42],
Self::GenericDataEssenceDescriptor => [0x01, 0x43],
Self::MultipleDescriptor => [0x01, 0x44],
Self::NetworkLocator => [0x01, 0x32],
Self::TextLocator => [0x01, 0x33],
Self::ApplicationPlugInObject => [0x01, 0x61],
Self::ApplicationReferencedObject => [0x01, 0x62],
Self::Unknown([a, b]) => [a, b],
}
}
}
impl core::fmt::Display for StructuralSetKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Unknown([a, b]) => write!(f, "unknown(0x{a:02X}{b:02X})"),
other => f.write_str(other.name()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LocalSetItem<'a> {
pub tag: u16,
pub value: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocalSet<'a> {
pub key: UlBytes,
pub items: Vec<LocalSetItem<'a>>,
}
impl<'a> LocalSet<'a> {
#[must_use]
pub fn kind(&self) -> StructuralSetKind {
StructuralSetKind::from_bytes(self.key[13], self.key[14])
}
#[must_use]
pub fn item_length_mode(&self) -> ItemLengthMode {
ItemLengthMode::from_registry_designator_byte(self.key[5])
.unwrap_or(ItemLengthMode::TwoByte)
}
#[must_use]
pub fn get(&self, tag: u16) -> Option<&'a [u8]> {
self.items.iter().find(|i| i.tag == tag).map(|i| i.value)
}
#[must_use]
pub fn build_key(kind: StructuralSetKind, mode: ItemLengthMode) -> UlBytes {
let [b14, b15] = kind.to_bytes();
[
0x06,
0x0E,
0x2B,
0x34,
0x02,
mode.registry_designator_byte(),
0x01,
0x01, 0x0D,
0x01,
0x01,
0x01,
0x01,
b14,
b15,
0x00,
]
}
pub fn parse_prefix(bytes: &'a [u8]) -> Result<(Self, usize)> {
if bytes.len() < 16 {
return Err(Error::BufferTooShort {
need: 16,
have: bytes.len(),
what: "Local Set key",
});
}
let key: UlBytes = ul_bytes_from_prefix(bytes);
if !is_local_set_key(&key) {
return Err(Error::KeyPrefixMismatch {
what: "Local Set (Table 16)",
});
}
let mode = ItemLengthMode::from_registry_designator_byte(key[5])
.unwrap_or(ItemLengthMode::TwoByte);
let (len, len_size) = decode_ber_length(&bytes[16..])?;
let value_start = 16 + len_size;
let len = usize::try_from(len).map_err(|_| Error::BufferTooShort {
need: usize::MAX,
have: bytes.len(),
what: "Local Set value (length exceeds platform usize)",
})?;
let value_end = value_start.checked_add(len).ok_or(Error::BufferTooShort {
need: usize::MAX,
have: bytes.len(),
what: "Local Set value (length overflow)",
})?;
if bytes.len() < value_end {
return Err(Error::BufferTooShort {
need: value_end,
have: bytes.len(),
what: "Local Set value",
});
}
let mut cursor = &bytes[value_start..value_end];
let mut items = Vec::new();
while !cursor.is_empty() {
if cursor.len() < 2 {
return Err(Error::BufferTooShort {
need: 2,
have: cursor.len(),
what: "Local Set item tag",
});
}
let tag = u16::from_be_bytes([cursor[0], cursor[1]]);
let rest = &cursor[2..];
let (item_len, item_len_size) = match mode {
ItemLengthMode::TwoByte => {
if rest.len() < 2 {
return Err(Error::BufferTooShort {
need: 2,
have: rest.len(),
what: "Local Set item 2-byte length",
});
}
(u64::from(u16::from_be_bytes([rest[0], rest[1]])), 2)
}
ItemLengthMode::Ber => decode_ber_length(rest)?,
};
let item_len = item_len as usize;
let value_start = item_len_size;
let value_end = value_start
.checked_add(item_len)
.ok_or(Error::BufferTooShort {
need: usize::MAX,
have: rest.len(),
what: "Local Set item value (length overflow)",
})?;
if rest.len() < value_end {
return Err(Error::BufferTooShort {
need: value_end,
have: rest.len(),
what: "Local Set item value",
});
}
items.push(LocalSetItem {
tag,
value: &rest[value_start..value_end],
});
cursor = &rest[value_end..];
}
Ok((LocalSet { key, items }, value_end))
}
}
impl<'a> Parse<'a> for LocalSet<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (set, consumed) = Self::parse_prefix(bytes)?;
if consumed != bytes.len() {
return Err(Error::BufferTooShort {
need: consumed,
have: bytes.len(),
what: "Local Set (trailing bytes after exact-fit parse)",
});
}
Ok(set)
}
}
impl Serialize for LocalSet<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
let mode = self.item_length_mode();
let items_len: usize = self
.items
.iter()
.map(|i| {
2 + match mode {
ItemLengthMode::TwoByte => 2,
ItemLengthMode::Ber => ber_length_size(i.value.len() as u64),
} + i.value.len()
})
.sum();
16 + ber_length_size(items_len as u64) + items_len
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let total = self.serialized_len();
if buf.len() < total {
return Err(Error::BufferTooShort {
need: total,
have: buf.len(),
what: "Local Set",
});
}
buf[..16].copy_from_slice(&self.key);
let mode = self.item_length_mode();
let items_len: usize = self
.items
.iter()
.map(|i| {
2 + match mode {
ItemLengthMode::TwoByte => 2,
ItemLengthMode::Ber => ber_length_size(i.value.len() as u64),
} + i.value.len()
})
.sum();
let len_size = encode_ber_length(items_len as u64, &mut buf[16..])?;
let mut pos = 16 + len_size;
for item in &self.items {
buf[pos..pos + 2].copy_from_slice(&item.tag.to_be_bytes());
pos += 2;
match mode {
ItemLengthMode::TwoByte => {
let len = u16::try_from(item.value.len()).map_err(|_| {
Error::InvalidPropertyLength {
tag: item.tag,
name: "Local Set item",
found: item.value.len(),
expected: usize::from(u16::MAX),
}
})?;
buf[pos..pos + 2].copy_from_slice(&len.to_be_bytes());
pos += 2;
}
ItemLengthMode::Ber => {
pos += encode_ber_length(item.value.len() as u64, &mut buf[pos..])?;
}
}
buf[pos..pos + item.value.len()].copy_from_slice(item.value);
pos += item.value.len();
}
Ok(pos)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn structural_set_kind_round_trips_every_named_variant() {
let variants = [
StructuralSetKind::Preface,
StructuralSetKind::Identification,
StructuralSetKind::ContentStorage,
StructuralSetKind::EssenceContainerData,
StructuralSetKind::MaterialPackage,
StructuralSetKind::SourcePackage,
StructuralSetKind::TimelineTrack,
StructuralSetKind::EventTrackDm,
StructuralSetKind::StaticTrackDm,
StructuralSetKind::Sequence,
StructuralSetKind::SourceClip,
StructuralSetKind::TimecodeComponent,
StructuralSetKind::DmSegment,
StructuralSetKind::DmSourceClip,
StructuralSetKind::Filler,
StructuralSetKind::PackageMarkerObject,
StructuralSetKind::FileDescriptor,
StructuralSetKind::GenericPictureEssenceDescriptor,
StructuralSetKind::CdciEssenceDescriptor,
StructuralSetKind::RgbaEssenceDescriptor,
StructuralSetKind::GenericSoundEssenceDescriptor,
StructuralSetKind::GenericDataEssenceDescriptor,
StructuralSetKind::MultipleDescriptor,
StructuralSetKind::NetworkLocator,
StructuralSetKind::TextLocator,
StructuralSetKind::ApplicationPlugInObject,
StructuralSetKind::ApplicationReferencedObject,
];
for v in variants {
let bytes = v.to_bytes();
assert_eq!(StructuralSetKind::from_bytes(bytes[0], bytes[1]), v);
}
assert_eq!(
StructuralSetKind::from_bytes(0xFE, 0xFD),
StructuralSetKind::Unknown([0xFE, 0xFD])
);
}
#[test]
fn local_set_round_trip_two_byte_mode() {
let key = LocalSet::build_key(StructuralSetKind::Preface, ItemLengthMode::TwoByte);
let set = LocalSet {
key,
items: alloc::vec![
LocalSetItem {
tag: 0x3B02,
value: &[1, 2, 3, 4, 5, 6, 7, 8],
},
LocalSetItem {
tag: 0x3B05,
value: &[0x01, 0x03],
},
],
};
let mut buf = alloc::vec![0u8; set.serialized_len()];
set.serialize_into(&mut buf).unwrap();
let parsed = LocalSet::parse(&buf).unwrap();
assert_eq!(parsed, set);
assert_eq!(parsed.kind(), StructuralSetKind::Preface);
assert_eq!(parsed.get(0x3B02), Some(&[1, 2, 3, 4, 5, 6, 7, 8][..]));
}
#[test]
fn local_set_round_trip_ber_mode() {
let key = LocalSet::build_key(StructuralSetKind::Identification, ItemLengthMode::Ber);
let big_value = alloc::vec![0x42u8; 70000];
let set = LocalSet {
key,
items: alloc::vec![LocalSetItem {
tag: 0x3C01,
value: &big_value,
}],
};
let mut buf = alloc::vec![0u8; set.serialized_len()];
set.serialize_into(&mut buf).unwrap();
let parsed = LocalSet::parse(&buf).unwrap();
assert_eq!(parsed, set);
assert_eq!(parsed.item_length_mode(), ItemLengthMode::Ber);
}
#[test]
fn non_local_set_key_rejected() {
let bytes = [0u8; 20];
assert!(matches!(
LocalSet::parse_prefix(&bytes),
Err(Error::KeyPrefixMismatch { .. })
));
}
}