use crate::parser::buffer::Buffer;
use crate::{
errors::DataParseError, impl_get_with_prefix, options::ParseOptions, utils::ParseResult,
};
#[derive(Default)]
pub struct DataParser<'a> {
pub(crate) buffer: Buffer<'a>,
pub(crate) cursor: usize,
pub(crate) options: ParseOptions,
}
impl<'a> DataParser<'a> {
pub fn with_options<B: Into<Buffer<'a>>>(buffer: B, options: ParseOptions) -> Self {
Self {
buffer: buffer.into(),
cursor: 0,
options,
}
}
pub fn set_options(&mut self, options: ParseOptions) {
self.options = options;
}
pub fn new<B: Into<Buffer<'a>>>(buffer: B) -> Self {
Self {
buffer: buffer.into(),
cursor: 0,
options: ParseOptions::default(),
}
}
pub(crate) fn remaining(&self) -> usize {
self.buffer.len().saturating_sub(self.cursor)
}
pub(crate) fn take(&mut self, n: usize) -> ParseResult<&[u8]> {
if self.remaining() < n {
return if self.options.verbose_errors {
let remaining_bytes = &self.buffer[self.cursor..];
eprintln!(
"Failed to take {} bytes at offset {} — only {} bytes left: {:?}",
n,
self.cursor,
self.remaining(),
remaining_bytes
);
Err(DataParseError::Custom {
e: format!(
"Not enough bytes at offset {} (needed {}, have {})",
self.cursor,
n,
self.remaining()
),
})
} else {
Err(DataParseError::UnexpectedEOF)
};
}
let start = self.cursor;
let end = self.cursor + n;
self.cursor = end;
Ok(&self.buffer[start..end])
}
pub(crate) fn read_array<const N: usize>(&mut self) -> ParseResult<[u8; N]> {
let slice = self.take(N)?;
let mut array = [0u8; N];
array.copy_from_slice(slice);
Ok(array)
}
pub fn current_len(&self) -> usize {
self.buffer.len()
}
pub fn peek(&self, n: usize) -> ParseResult<&[u8]> {
if self.remaining() < n {
return Err(DataParseError::UnexpectedEOF);
}
Ok(&self.buffer[..n])
}
pub fn get_bytes(&mut self, byte_len: usize) -> ParseResult<Vec<u8>> {
if self.remaining() < byte_len {
return Err(DataParseError::UnexpectedEOF);
}
let buf = self.take(byte_len)?.to_vec();
Ok(buf)
}
pub fn get_byte(&mut self) -> ParseResult<u8> {
Ok(*self.take(1)?[0..1].first().unwrap())
}
pub fn get_bool(&mut self) -> ParseResult<bool> {
Ok(self.get_byte()? != 0)
}
impl_get_with_prefix!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64);
}