use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::len::fit_bits;
const BITS_PER_BYTE: usize = 8;
const MISALIGNED_BYTES: &str = "whole-byte access at a non-byte-aligned bit position";
pub(crate) struct BitReader<'a> {
data: &'a [u8],
inner: broadcast_common::bits::BitReader<'a>,
what: &'static str,
}
impl<'a> BitReader<'a> {
pub(crate) fn new(data: &'a [u8], what: &'static str) -> Self {
Self {
data,
inner: broadcast_common::bits::BitReader::new(data),
what,
}
}
fn short(&self, need_bits: usize) -> Error {
Error::BufferTooShort(broadcast_common::error::BufferTooShort {
need: (self.inner.bits_read() + need_bits).div_ceil(BITS_PER_BYTE),
have: self.data.len(),
what: self.what,
})
}
pub(crate) fn remaining_bits(&self) -> usize {
self.inner.bits_remaining()
}
pub(crate) fn remaining_bytes(&self) -> usize {
self.remaining_bits() / BITS_PER_BYTE
}
pub(crate) fn bits(&mut self, n: usize) -> Result<u32> {
let v = self.bits64(n)?;
Ok(u32::try_from(v).unwrap_or(u32::MAX))
}
pub(crate) fn bits64(&mut self, n: usize) -> Result<u64> {
let width = u32::try_from(n).unwrap_or(u32::MAX);
self.inner.read_bits(width).map_err(|_| self.short(n))
}
pub(crate) fn u8(&mut self, n: usize) -> Result<u8> {
Ok(u8::try_from(self.bits(n)?).unwrap_or(u8::MAX))
}
pub(crate) fn u16(&mut self, n: usize) -> Result<u16> {
Ok(u16::try_from(self.bits(n)?).unwrap_or(u16::MAX))
}
pub(crate) fn flag(&mut self) -> Result<bool> {
Ok(self.bits(1)? == 1)
}
pub(crate) fn bytes(&mut self, n: usize) -> Result<&'a [u8]> {
if !self.inner.is_byte_aligned() {
return Err(Error::ValueOutOfRange {
field: self.what,
reason: MISALIGNED_BYTES,
});
}
let start = self.inner.bits_read() / BITS_PER_BYTE;
self.inner
.skip_bits(n * BITS_PER_BYTE)
.map_err(|_| self.short(n * BITS_PER_BYTE))?;
Ok(&self.data[start..start + n])
}
pub(crate) fn rest(&mut self) -> Result<&'a [u8]> {
let n = self.remaining_bytes();
self.bytes(n)
}
}
#[derive(Default)]
pub(crate) struct BitWriter {
buf: Vec<u8>,
used: usize,
}
impl BitWriter {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn put(&mut self, value: u64, n: usize, field: &'static str) -> Result<()> {
let n32 = u32::try_from(n).unwrap_or(u32::MAX);
let v = fit_bits(value, n32, field)?;
for i in (0..n).rev() {
let bit = (v >> i) & 1 == 1;
if self.used == 0 {
self.buf.push(0);
}
if bit && let Some(last) = self.buf.last_mut() {
*last |= 1 << (BITS_PER_BYTE - 1 - self.used);
}
self.used = (self.used + 1) % BITS_PER_BYTE;
}
Ok(())
}
pub(crate) fn flag(&mut self, v: bool, field: &'static str) -> Result<()> {
self.put(u64::from(v), 1, field)
}
pub(crate) fn bytes(&mut self, b: &[u8]) -> Result<()> {
if self.used != 0 {
return Err(Error::ValueOutOfRange {
field: "byte run",
reason: MISALIGNED_BYTES,
});
}
self.buf.extend_from_slice(b);
Ok(())
}
pub(crate) fn into_vec(self) -> Vec<u8> {
self.buf
}
}
macro_rules! impl_h222_descriptor {
($ty:ty, $tag:expr, $what:literal, $name:literal) => {
impl<'a> broadcast_common::Parse<'a> for $ty {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> crate::error::Result<Self> {
let body = $crate::descriptors::descriptor_body(
bytes,
$tag,
$what,
concat!("unexpected tag for ", $what),
)?;
Self::parse_body(body)
}
}
impl broadcast_common::Serialize for $ty {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
self.write_body().map_or(0, |b| 2 + b.len())
}
fn serialize_into(&self, buf: &mut [u8]) -> crate::error::Result<usize> {
let body = self.write_body()?;
let len = 2 + body.len();
::broadcast_common::len::need_out(buf, len)?;
$crate::descriptors::write_descriptor_header(buf, $tag, body.len())?;
buf[2..len].copy_from_slice(&body);
Ok(len)
}
}
impl crate::traits::DescriptorDef<'_> for $ty {
const TAG: u8 = $tag;
const NAME: &'static str = $name;
}
};
}
pub(crate) use impl_h222_descriptor;
pub(crate) fn invalid(tag: u8, reason: &'static str) -> Error {
Error::InvalidDescriptor { tag, reason }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reader_bytes_rejects_misaligned_access() {
let mut r = BitReader::new(&[0xFF, 0xAA, 0xBB], "test");
r.bits(3).unwrap();
assert!(matches!(
r.bytes(1).unwrap_err(),
Error::ValueOutOfRange { field: "test", .. }
));
let mut r = BitReader::new(&[0xFF, 0xAA, 0xBB], "test");
r.bits(8).unwrap();
assert_eq!(r.bytes(2).unwrap(), &[0xAA, 0xBB]);
}
#[test]
fn reader_rest_surfaces_misalignment_instead_of_dropping_data() {
let mut r = BitReader::new(&[0xFF, 0xAA, 0xBB], "test");
r.bits(3).unwrap();
assert!(matches!(
r.rest().unwrap_err(),
Error::ValueOutOfRange { field: "test", .. }
));
let mut r = BitReader::new(&[0xFF, 0xAA, 0xBB], "test");
r.bits(8).unwrap();
assert_eq!(r.rest().unwrap(), &[0xAA, 0xBB]);
assert!(r.rest().unwrap().is_empty());
}
#[test]
fn writer_bytes_rejects_misaligned_access() {
let mut w = BitWriter::new();
w.put(1, 3, "x").unwrap();
assert!(matches!(
w.bytes(&[1]).unwrap_err(),
Error::ValueOutOfRange { .. }
));
let mut w = BitWriter::new();
w.put(0xAB, 8, "x").unwrap();
w.bytes(&[1]).unwrap();
assert_eq!(w.into_vec(), [0xAB, 1]);
}
}