use std::ffi::CStr;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("out-of-bounds read of {pos:#X}+{len} (size {size:#X})")]
Read { pos: usize, len: usize, size: usize },
#[error("out-of-bounds seek to {pos:#X} (size {size:#X})")]
Seek { pos: usize, size: usize },
#[error("error at {pos:#X}: {source}")]
Other { pos: usize, #[source] source: Box<dyn std::error::Error + Send + Sync> },
}
pub type Result<T, E=Error> = std::result::Result<T, E>;
impl Error {
pub fn pos(&self) -> usize {
match self {
Error::Seek { pos, .. } => *pos,
Error::Read { pos, .. } => *pos,
Error::Other { pos, .. } => *pos,
}
}
pub fn pos_mut(&mut self) -> &mut usize {
match self {
Error::Seek { pos, .. } => pos,
Error::Read { pos, .. } => pos,
Error::Other { pos, .. } => pos,
}
}
}
#[derive(Clone, Debug, thiserror::Error)]
#[error("mismatched {type_}. expected: {expected}, got: {got}", type_ = std::any::type_name::<T>())]
pub struct CheckError<T: std::fmt::Display> {
pub expected: T,
pub got: T,
}
#[derive(Clone, Debug, thiserror::Error)]
pub struct CheckBytesError {
pub expected: Vec<u8>,
pub got: Vec<u8>,
}
impl std::fmt::Display for CheckBytesError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut got = Vec::new();
let mut exp = Vec::new();
for (&g, &e) in std::iter::zip(&self.got, &self.expected) {
got.extend(std::ascii::escape_default(g).map(char::from));
exp.extend(std::ascii::escape_default(e).map(char::from));
while got.len() < exp.len() { got.push('â–‘') }
while exp.len() < got.len() { exp.push('â–‘') }
}
writeln!(f, "mismatched bytes.")?;
writeln!(f, "expected: b\"{}\"", String::from_iter(exp))?;
write !(f, "got: b\"{}\"", String::from_iter(got))
}
}
#[cfg(doc)]
#[doc(hidden)]
pub type T = ();
#[derive(Clone)]
pub struct Reader<'a> {
pos: usize,
data: &'a [u8],
}
impl<'a> std::fmt::Debug for Reader<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Reader")
.field("pos", &self.pos)
.field("data", &format_args!("[_; {}]", self.data.len()))
.finish()
}
}
impl<'a> Reader<'a> {
#[inline(always)]
pub fn new(data: &'a [u8]) -> Reader<'a> {
Self {
pos: 0,
data,
}
}
#[must_use]
#[inline(always)]
pub fn pos(&self) -> usize {
self.pos
}
#[must_use]
#[inline(always)]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.remaining().is_empty()
}
#[must_use]
#[inline(always)]
pub fn remaining(&self) -> &'a [u8] {
&self.data[self.pos()..]
}
#[must_use]
#[inline(always)]
pub fn data(&self) -> &'a [u8] {
self.data
}
#[cfg(doc)]
pub fn T(&mut self) -> Result<T> {}
#[inline(always)]
pub fn slice(&mut self, len: usize) -> Result<&'a [u8]> {
if len > self.remaining().len() {
return Err(Error::Read { pos: self.pos(), len, size: self.len() });
}
let pos = self.pos;
self.pos += len;
Ok(&self.data[pos..pos+len])
}
#[inline(always)]
pub fn array<const N: usize>(&mut self) -> Result<[u8; N]> {
let mut x = [0; N];
self.read_into(&mut x)?;
Ok(x)
}
#[inline(always)]
pub fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
buf.copy_from_slice(self.slice(buf.len())?);
Ok(())
}
#[inline(always)]
pub fn cstr(&mut self) -> Result<&'a CStr> {
match CStr::from_bytes_until_nul(self.remaining()) {
Ok(cs) => {
self.pos += cs.to_bytes_with_nul().len();
Ok(cs)
}
Err(err) => {
Err(Error::Other { pos: self.pos(), source: Box::new(err) })
}
}
}
#[inline(always)]
pub fn seek(&mut self, pos: usize) -> Result<()> {
if pos > self.len() {
return Err(Error::Seek { pos, size: self.len() })
}
self.pos = pos;
Ok(())
}
#[inline(always)]
pub fn at(&self, pos: usize) -> Result<Self> {
let mut a = self.clone();
a.seek(pos)?;
Ok(a)
}
#[inline(always)]
pub fn check(&mut self, v: &[u8]) -> Result<()> {
let pos = self.pos();
let u = self.slice(v.len())?;
if u != v {
self.pos = pos;
return Err(Error::Other { pos, source: CheckBytesError {
got: u.to_owned(),
expected: v.to_owned(),
}.into() })
}
Ok(())
}
#[cfg(doc)]
pub fn check_T(&mut self, v: T) -> Result<()> {}
#[cfg(doc)]
pub fn ptrN(&mut self) -> Result<Self> {}
#[inline(always)]
pub fn align(&mut self, size: usize) -> Result<&'a [u8]> {
self.slice((size-(self.pos()%size))%size)
}
#[inline(always)]
pub fn align_zeroed(&mut self, size: usize) -> Result<()> {
let pos = self.pos();
let u = self.align(size)?;
if u.iter().any(|a| *a != 0) {
self.pos = pos;
let v = vec![0; size];
return Err(Error::Other { pos, source: CheckBytesError {
got: u.to_owned(),
expected: v.to_owned(),
}.into() })
}
Ok(())
}
pub fn dump(&self) -> crate::dump::Dump<'a> {
crate::dump::Dump::new(self.data()).start(self.pos())
}
}
mod seal { pub trait Sealed: Sized {} }
impl seal::Sealed for Reader<'_> {}
macro_rules! primitives {
(
$(#[$trait_attrs:meta])* trait $trait:ident;
$suf:ident, $conv:ident;
{ $($type:ident),* }
{ $($ptr:tt),* }
) => { paste::paste! {
#[doc(hidden)]
impl<'a> Reader<'a> {
$(#[inline(always)] pub fn [<$type $suf>](&mut self) -> Result<$type> {
Ok($type::$conv(self.array()?))
})*
$(#[inline(always)] pub fn [<check_ $type $suf>](&mut self, v: $type) -> Result<()> {
let pos = self.pos();
let u = self.[< $type $suf >]()?;
if u != v {
self.pos = pos;
return Err(Error::Other { pos, source: CheckError {
got: u,
expected: v,
}.into() })
}
Ok(())
})*
$(#[inline(always)] pub fn [<ptr$ptr $suf>](&mut self) -> Result<Self> {
self.clone().at(self.[<u$ptr $suf>]()? as usize)
})*
}
$(#[$trait_attrs])*
pub trait $trait: seal::Sealed {
$(#[doc(hidden)] fn $type(&mut self) -> Result<$type>;)*
$(#[doc(hidden)] fn [<check_ $type>](&mut self, v: $type) -> Result<()>;)*
$(#[doc(hidden)] fn [<ptr $ptr>](&mut self) -> Result<Self>;)*
}
impl<'a> $trait for Reader<'a> {
$(#[doc(hidden)] #[inline(always)] fn $type(&mut self) -> Result<$type> {
self.[<$type $suf>]()
})*
$(#[doc(hidden)] #[inline(always)] fn [<check_ $type>](&mut self, v: $type) -> Result<()> {
self.[<check_ $type $suf>](v)
})*
$(#[doc(hidden)] #[inline(always)] fn [<ptr$ptr>](&mut self) -> Result<Self> {
self.[<ptr$ptr $suf>]()
})*
}
} }
}
primitives!(
trait Le;
_le, from_le_bytes;
{
u8, u16, u32, u64, u128,
i8, i16, i32, i64, i128,
f32, f64
}
{ 8, 16, 32, 64, 128 }
);
primitives!(
trait Be;
_be, from_be_bytes;
{
u8, u16, u32, u64, u128,
i8, i16, i32, i64, i128,
f32, f64
}
{ 8, 16, 32, 64, 128 }
);