use crate::decoded::Decoded;
pub use crate::dynamic_hdr::hdr10plus::Hdr10PlusMetadata;
pub use crate::dynamic_hdr::slhdr::SlHdrMetadata;
use crate::encode::IntoPackets;
use crate::error::DecodeError;
use crate::warn::DynamicHdrWarning;
pub(crate) const MAX_DYNAMIC_HDR_PAYLOAD: usize = 2200;
struct BitReader<'a> {
data: &'a [u8],
byte_pos: usize,
bit_pos: u8,
}
impl<'a> BitReader<'a> {
fn new(data: &'a [u8]) -> Self {
Self {
data,
byte_pos: 0,
bit_pos: 0,
}
}
fn read_u8(&mut self, bits: u8) -> Result<u8, DecodeError> {
debug_assert!((1..=8).contains(&bits));
Ok(self.read_bits(bits)? as u8)
}
fn read_u16(&mut self, bits: u8) -> Result<u16, DecodeError> {
debug_assert!((1..=16).contains(&bits));
Ok(self.read_bits(bits)? as u16)
}
fn read_u32(&mut self, bits: u8) -> Result<u32, DecodeError> {
debug_assert!((1..=32).contains(&bits));
self.read_bits(bits)
}
fn read_bool(&mut self) -> Result<bool, DecodeError> {
Ok(self.read_bits(1)? != 0)
}
fn remaining_bits(&self) -> usize {
let remaining_bytes = self.data.len().saturating_sub(self.byte_pos);
remaining_bytes * 8 - self.bit_pos as usize
}
fn read_bits(&mut self, mut n: u8) -> Result<u32, DecodeError> {
if self.remaining_bits() < n as usize {
return Err(DecodeError::MalformedPayload);
}
let mut result: u32 = 0;
while n > 0 {
let avail = 8 - self.bit_pos;
let take = n.min(avail);
let shift = avail - take;
let mask = ((1u16 << take) - 1) as u8;
let bits = (self.data[self.byte_pos] >> shift) & mask;
result = (result << take) | bits as u32;
self.bit_pos += take;
if self.bit_pos == 8 {
self.byte_pos += 1;
self.bit_pos = 0;
}
n -= take;
}
Ok(result)
}
}
struct BitWriter {
buf: [u8; MAX_DYNAMIC_HDR_PAYLOAD],
byte_pos: usize,
bit_pos: u8,
}
impl BitWriter {
fn new() -> Self {
Self {
buf: [0u8; MAX_DYNAMIC_HDR_PAYLOAD],
byte_pos: 0,
bit_pos: 0,
}
}
fn write_u8(&mut self, value: u8, bits: u8) {
debug_assert!((1..=8).contains(&bits));
self.write_bits(value as u32, bits);
}
fn write_u16(&mut self, value: u16, bits: u8) {
debug_assert!((1..=16).contains(&bits));
self.write_bits(value as u32, bits);
}
fn write_u32(&mut self, value: u32, bits: u8) {
debug_assert!((1..=32).contains(&bits));
self.write_bits(value, bits);
}
fn write_bool(&mut self, value: bool) {
self.write_bits(value as u32, 1);
}
fn finish(self) -> ([u8; MAX_DYNAMIC_HDR_PAYLOAD], usize) {
let len = if self.bit_pos == 0 {
self.byte_pos
} else {
self.byte_pos + 1
};
(self.buf, len)
}
fn write_bits(&mut self, value: u32, mut n: u8) {
while n > 0 {
assert!(
self.byte_pos < MAX_DYNAMIC_HDR_PAYLOAD,
"BitWriter overflow"
);
let avail = 8 - self.bit_pos;
let take = n.min(avail);
let shift = n - take;
let bits = ((value >> shift) as u8) & (((1u16 << take) - 1) as u8);
self.buf[self.byte_pos] |= bits << (avail - take);
self.bit_pos += take;
if self.bit_pos == 8 {
self.byte_pos += 1;
self.bit_pos = 0;
}
n -= take;
}
}
}
#[allow(clippy::large_enum_variant)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum DynamicHdrInfoFrame {
Hdr10Plus(Hdr10PlusMetadata),
SlHdr(SlHdrMetadata),
Unknown {
format_id: u8,
#[cfg(any(feature = "alloc", feature = "std"))]
payload: alloc::vec::Vec<u8>,
},
}
impl DynamicHdrInfoFrame {
pub fn decode_sequence(
packets: &[[u8; 31]],
) -> Result<Decoded<DynamicHdrInfoFrame, DynamicHdrWarning>, DecodeError> {
if packets.is_empty() {
return Err(DecodeError::EmptySequence);
}
let format_id = packets[0][7];
let total_bytes = u16::from_le_bytes([packets[0][5], packets[0][6]]);
let mut decoded = Decoded::new(DynamicHdrInfoFrame::Unknown {
format_id,
#[cfg(any(feature = "alloc", feature = "std"))]
payload: alloc::vec::Vec::new(),
});
let mut payload_buf = [0u8; MAX_DYNAMIC_HDR_PAYLOAD];
let mut payload_len = 0usize;
for (i, packet) in packets.iter().enumerate() {
let frag = DynamicHdrFragment::decode(packet)?;
for w in frag.iter_warnings() {
decoded.push_warning(w.clone());
}
if frag.value.seq_num != i as u8 {
decoded.push_warning(DynamicHdrWarning::OutOfOrderPacket {
index: i as u8,
found: frag.value.seq_num,
});
}
if i > 0 {
if frag.value.total_bytes != total_bytes {
decoded.push_warning(DynamicHdrWarning::InconsistentTotalBytes {
packet: i as u8,
expected: total_bytes,
found: frag.value.total_bytes,
});
}
if frag.value.format_id != format_id {
decoded.push_warning(DynamicHdrWarning::InconsistentFormatId {
packet: i as u8,
expected: format_id,
found: frag.value.format_id,
});
}
}
let chunk = &frag.value.chunk[..frag.value.chunk_len as usize];
let new_len = payload_len.saturating_add(chunk.len());
if new_len <= MAX_DYNAMIC_HDR_PAYLOAD {
payload_buf[payload_len..new_len].copy_from_slice(chunk);
payload_len = new_len;
}
}
let payload = &payload_buf[..payload_len];
let mut fmt_warn_count = 0usize;
let mut fmt_warns: [Option<DynamicHdrWarning>; 4] = [const { None }; 4];
let decoded_value = {
let mut push_fmt_warn = |w: DynamicHdrWarning| {
if fmt_warn_count < 4 {
fmt_warns[fmt_warn_count] = Some(w);
fmt_warn_count += 1;
}
};
match format_id {
0x02 => match SlHdrMetadata::decode(payload, &mut push_fmt_warn) {
Ok(meta) => DynamicHdrInfoFrame::SlHdr(meta),
Err(e) => return Err(e),
},
0x04 => match Hdr10PlusMetadata::decode(payload, &mut push_fmt_warn) {
Ok(meta) => DynamicHdrInfoFrame::Hdr10Plus(meta),
Err(e) => return Err(e),
},
_ => {
#[cfg(any(feature = "alloc", feature = "std"))]
{
DynamicHdrInfoFrame::Unknown {
format_id,
payload: payload.to_vec(),
}
}
#[cfg(not(any(feature = "alloc", feature = "std")))]
{
DynamicHdrInfoFrame::Unknown { format_id }
}
}
}
};
decoded.value = decoded_value;
for w in fmt_warns[..fmt_warn_count]
.iter_mut()
.filter_map(|opt| opt.take())
{
decoded.push_warning(w);
}
Ok(decoded)
}
}
pub struct DynamicHdrIter {
format_id: u8,
total_bytes: u16,
offset: usize,
seq_num: u8,
#[cfg(any(feature = "alloc", feature = "std"))]
payload: alloc::vec::Vec<u8>,
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload: [u8; MAX_DYNAMIC_HDR_PAYLOAD],
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload_len: usize,
}
impl Iterator for DynamicHdrIter {
type Item = [u8; 31];
fn next(&mut self) -> Option<[u8; 31]> {
#[cfg(any(feature = "alloc", feature = "std"))]
let payload_len = self.payload.len();
#[cfg(not(any(feature = "alloc", feature = "std")))]
let payload_len = self.payload_len;
if self.offset >= payload_len {
return None;
}
let chunk_len = (payload_len - self.offset).min(23);
let mut hp = [0u8; 30];
hp[0] = 0x20; hp[1] = 0x01; hp[2] = (4 + chunk_len) as u8;
hp[3] = self.seq_num;
let tb = self.total_bytes.to_le_bytes();
hp[4] = tb[0];
hp[5] = tb[1];
hp[6] = self.format_id;
hp[7..7 + chunk_len].copy_from_slice(&self.payload[self.offset..self.offset + chunk_len]);
let checksum = crate::checksum::compute_checksum(&hp);
let mut packet = [0u8; 31];
packet[..3].copy_from_slice(&hp[..3]);
packet[3] = checksum;
packet[4..].copy_from_slice(&hp[3..]);
self.offset += chunk_len;
self.seq_num += 1;
Some(packet)
}
}
impl IntoPackets for DynamicHdrInfoFrame {
type Iter = DynamicHdrIter;
type Warning = DynamicHdrWarning;
fn into_packets(self) -> Decoded<DynamicHdrIter, DynamicHdrWarning> {
match self {
DynamicHdrInfoFrame::Unknown {
format_id,
#[cfg(any(feature = "alloc", feature = "std"))]
payload,
} => {
#[cfg(any(feature = "alloc", feature = "std"))]
let total_bytes = payload.len() as u16;
#[cfg(not(any(feature = "alloc", feature = "std")))]
let total_bytes: u16 = 0;
Decoded::new(DynamicHdrIter {
format_id,
total_bytes,
offset: 0,
seq_num: 0,
#[cfg(any(feature = "alloc", feature = "std"))]
payload,
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload: [0u8; MAX_DYNAMIC_HDR_PAYLOAD],
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload_len: 0,
})
}
DynamicHdrInfoFrame::Hdr10Plus(meta) => {
let (buf, len) = meta.encode();
Decoded::new(DynamicHdrIter {
format_id: 0x04,
total_bytes: len as u16,
offset: 0,
seq_num: 0,
#[cfg(any(feature = "alloc", feature = "std"))]
payload: buf[..len].to_vec(),
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload: buf,
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload_len: len,
})
}
DynamicHdrInfoFrame::SlHdr(meta) => {
let (buf, len) = meta.encode();
Decoded::new(DynamicHdrIter {
format_id: 0x02,
total_bytes: len as u16,
offset: 0,
seq_num: 0,
#[cfg(any(feature = "alloc", feature = "std"))]
payload: buf[..len].to_vec(),
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload: buf,
#[cfg(not(any(feature = "alloc", feature = "std")))]
payload_len: len,
})
}
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct DynamicHdrFragment {
pub seq_num: u8,
pub total_bytes: u16,
pub format_id: u8,
pub chunk: [u8; 23],
pub chunk_len: u8,
}
impl DynamicHdrFragment {
pub fn decode(
packet: &[u8; 31],
) -> Result<Decoded<DynamicHdrFragment, DynamicHdrWarning>, DecodeError> {
let length = packet[2];
if length > 27 {
return Err(DecodeError::Truncated { claimed: length });
}
let mut decoded = Decoded::new(DynamicHdrFragment {
seq_num: 0,
total_bytes: 0,
format_id: 0,
chunk: [0u8; 23],
chunk_len: 0,
});
let total: u8 = packet.iter().fold(0u8, |acc, &b| acc.wrapping_add(b));
if total != 0x00 {
let expected = crate::checksum::compute_checksum(packet[..30].try_into().unwrap());
decoded.push_warning(DynamicHdrWarning::ChecksumMismatch {
expected,
found: packet[3],
});
}
decoded.value.seq_num = packet[4];
decoded.value.total_bytes = u16::from_le_bytes([packet[5], packet[6]]);
decoded.value.format_id = packet[7];
let chunk_len = length.saturating_sub(4).min(23);
decoded.value.chunk_len = chunk_len;
decoded.value.chunk[..chunk_len as usize]
.copy_from_slice(&packet[8..8 + chunk_len as usize]);
Ok(decoded)
}
}
mod hdr10plus;
mod slhdr;
#[cfg(test)]
mod tests;