use std::borrow::Cow;
use crate::xo65::Xo65Bytes;
#[derive(Debug)]
pub struct ParseError {
msg: Cow<'static, str>,
source: Option<Box<ParseError>>,
}
impl ParseError {
pub(crate) fn new<S>(msg: S) -> Self
where
S: Into<Cow<'static, str>>,
{
Self {
msg: msg.into(),
source: None,
}
}
}
impl std::error::Error for ParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|source| source as _)
}
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.msg.fmt(f)
}
}
macro_rules! parse_err {
($msg:expr $(,)?) => {{
$crate::parse::ParseError::new(::std::format!($msg))
}};
($fmt:expr, $($args:tt)*) => {{
$crate::parse::ParseError::new(::std::format!($fmt, $($args)*))
}};
}
pub(crate) use parse_err;
macro_rules! parse_bail {
($msg:expr $(,)?) => {{
return Err($crate::parse::parse_err!($msg));
}};
($fmt:expr, $($args:tt)*) => {{
return Err($crate::parse::parse_err!($fmt, $($args)*));
}};
}
pub(crate) use parse_bail;
pub(crate) type ParseResult<T> = Result<T, ParseError>;
pub(crate) trait ParseErrorContext: Sized {
fn context(self, msg: &'static str) -> Self;
fn with_context<F, S>(self, f: F) -> Self
where
F: FnOnce() -> S,
S: Into<Cow<'static, str>>;
}
impl<T> ParseErrorContext for ParseResult<T> {
fn context(self, msg: &'static str) -> Self {
self.map_err(|e| ParseError {
msg: msg.into(),
source: Some(Box::new(e)),
})
}
fn with_context<F, S>(self, f: F) -> Self
where
F: FnOnce() -> S,
S: Into<Cow<'static, str>>,
{
self.map_err(|e| ParseError {
msg: f().into(),
source: Some(Box::new(e)),
})
}
}
pub(crate) trait ParseAt<'data>: Sized {
fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self>;
}
impl<const LEN: usize> ParseAt<'_> for [u8; LEN] {
fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
<&[u8; LEN]>::parse_at(bytes, off).copied()
}
}
impl<'data, const LEN: usize> ParseAt<'data> for &'data [u8; LEN] {
fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self> {
let bytes = bytes.get(*off..)?;
let buf = bytes.first_chunk::<LEN>()?;
*off += LEN;
Ok(buf)
}
}
macro_rules! impl_parse_at_for_int {
($($ty:ty)*) => {
$(
impl ParseAt<'_> for $ty {
fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
const SIZE: usize = std::mem::size_of::<$ty>();
let res = <[u8; SIZE]>::parse_at(bytes, off)?;
let res = Self::from_le_bytes(res);
Ok(res)
}
}
)*
}
}
impl_parse_at_for_int!(i8 i16 i32 u8 u16 u32);
pub(crate) fn parse_bytes_at<'data>(
bytes: &Xo65Bytes<'data>,
off: &mut usize,
len: usize,
) -> ParseResult<&'data [u8]> {
let res = bytes.get(*off..*off + len)?.as_inner();
*off += len;
Ok(res)
}
pub(crate) fn parse_uleb128_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<u32> {
let mut res: u32 = 0;
let mut coef: u32 = 1;
loop {
let b = u8::parse_at(bytes, off)?;
res |= u32::from(b & 0x7F)
.checked_mul(coef)
.ok_or_else(|| parse_err!("parse_uleb128_at: integer overflow"))?;
if (b & 0x80) == 0 {
break;
}
coef = coef
.checked_mul(0x80)
.ok_or_else(|| parse_err!("parse_uleb128_at: integer overflow"))?
}
Ok(res)
}