#[cfg(feature = "std")]
use std::borrow::Cow;
use zerocopy::byteorder::{LE, U16};
use zerocopy::FromBytes;
pub type Result<T> = core::result::Result<T, BinaryReaderError>;
pub struct BinaryReader<'a> {
pub data: &'a [u8],
}
impl<'a> BinaryReader<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data }
}
#[inline(always)]
pub fn read_u8(&mut self) -> Result<u8> {
if !self.data.is_empty() {
let value = self.data[0];
self.data = &self.data[1..];
Ok(value)
} else {
Err(BinaryReaderError::NeedsMoreData)
}
}
#[inline(always)]
pub fn read_bool(&mut self) -> Result<bool> {
if !self.data.is_empty() {
let value = self.data[0];
self.data = &self.data[1..];
Ok(value != 0)
} else {
Err(BinaryReaderError::NeedsMoreData)
}
}
#[inline(always)]
pub fn read_bytes(&mut self, len: usize) -> Result<&'a [u8]> {
if self.data.len() < len {
Err(BinaryReaderError::NeedsMoreData)
} else {
let (lo, hi) = self.data.split_at(len);
self.data = hi;
Ok(lo)
}
}
#[inline(always)]
pub fn read_cbytes<const N: usize>(&mut self) -> Result<[u8; N]> {
if self.data.len() < N {
Err(BinaryReaderError::NeedsMoreData)
} else {
let (lo, hi) = self.data.split_at(N);
self.data = hi;
Ok(*<&[u8; N]>::try_from(lo).unwrap())
}
}
#[inline(always)]
pub fn read_cbytes_ref<const N: usize>(&mut self) -> Result<&[u8; N]> {
if self.data.len() < N {
Err(BinaryReaderError::NeedsMoreData)
} else {
let (lo, hi) = self.data.split_at(N);
self.data = hi;
Ok(<&[u8; N]>::try_from(lo).unwrap())
}
}
#[inline(always)]
pub fn read_u16(&mut self) -> Result<u16> {
Ok(u16::from_le_bytes(self.read_cbytes()?))
}
#[inline(always)]
pub fn read_u32(&mut self) -> Result<u32> {
Ok(u32::from_le_bytes(self.read_cbytes()?))
}
#[inline(always)]
pub fn read_u64(&mut self) -> Result<u64> {
Ok(u64::from_le_bytes(self.read_cbytes()?))
}
#[inline(always)]
pub fn read_i16(&mut self) -> Result<i16> {
Ok(i16::from_le_bytes(self.read_cbytes()?))
}
#[inline(always)]
pub fn read_i32(&mut self) -> Result<i32> {
Ok(i32::from_le_bytes(self.read_cbytes()?))
}
#[inline(always)]
pub fn read_i64(&mut self) -> Result<i64> {
Ok(i64::from_le_bytes(self.read_cbytes()?))
}
pub fn read_7bit_encoded_i32(&mut self) -> Result<i32> {
const MORE: u8 = 0x80;
let mut shift: u32 = 0;
let mut n: u32 = 0;
loop {
let b = self.read_u8()?;
n |= ((b & 0x7f) as u32) << shift;
if (b & MORE) == 0 {
break;
}
shift += 7;
if shift >= 32 {
return Err(BinaryReaderError::Invalid);
}
}
Ok(n as i32)
}
pub fn read_7bit_encoded_i64(&mut self) -> Result<i64> {
const MORE: u8 = 0x80;
let mut shift: u32 = 0;
let mut n: u64 = 0;
loop {
let b = self.read_u8()?;
n |= ((b & 0x7f) as u64) << shift;
if (b & MORE) == 0 {
break;
}
shift += 7;
if shift >= 64 {
return Err(BinaryReaderError::Invalid);
}
}
Ok(n as i64)
}
pub fn read_utf8_bytes(&mut self) -> Result<&'a [u8]> {
let len_i32 = self.read_7bit_encoded_i32()?;
let Ok(len_usize) = usize::try_from(len_i32) else {
return Err(BinaryReaderError::Invalid);
};
self.read_bytes(len_usize)
}
#[cfg(feature = "bstr")]
pub fn read_utf8_bstr(&mut self) -> Result<&'a bstr::BStr> {
Ok(bstr::BStr::new(self.read_utf8_bytes()?))
}
pub fn read_utf8_str(&mut self) -> Result<&'a str> {
let bytes = self.read_utf8_bytes()?;
if let Ok(s) = core::str::from_utf8(bytes) {
Ok(s)
} else {
Err(BinaryReaderError::NeedsMoreData)
}
}
#[cfg(feature = "std")]
pub fn read_utf8_string_lossy(&mut self) -> Result<Cow<'a, str>> {
let bytes = self.read_utf8_bytes()?;
Ok(String::from_utf8_lossy(bytes))
}
pub fn read_utf16_wchars(&mut self) -> Result<&'a [U16<LE>]> {
let bytes_len_i32 = self.read_7bit_encoded_i32()?;
let Ok(bytes_len_usize) = usize::try_from(bytes_len_i32) else {
return Err(BinaryReaderError::Invalid);
};
let bytes = self.read_bytes(bytes_len_usize)?;
let Ok(wchars) = <[U16<LE>]>::ref_from_bytes(bytes) else {
return Err(BinaryReaderError::Invalid);
};
Ok(wchars)
}
#[cfg(feature = "std")]
pub fn read_utf16_string(&mut self) -> Result<String> {
let wchars = self.read_utf16_wchars()?;
let wchars_u16: Vec<u16> = wchars.iter().map(|c| c.get()).collect();
String::from_utf16(&wchars_u16).map_err(|_| BinaryReaderError::Invalid)
}
#[cfg(feature = "std")]
pub fn read_utf16_string_lossy(&mut self) -> Result<String> {
let wchars = self.read_utf16_wchars()?;
let wchars_u16: Vec<u16> = wchars.iter().map(|c| c.get()).collect();
Ok(String::from_utf16_lossy(&wchars_u16))
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum BinaryReaderError {
NeedsMoreData,
Invalid,
}
impl core::error::Error for BinaryReaderError {}
impl core::fmt::Display for BinaryReaderError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::NeedsMoreData => f.write_str(
"The value could not be decoded because the input data was not complete.",
),
Self::Invalid => {
f.write_str("The value could not be decoded because the input data is malformed.")
}
}
}
}