use super::error::Bad;
pub(super) struct Reader<'a> {
b: &'a [u8],
p: usize,
depth: u32,
}
const MAX_NESTING: u32 = 250;
impl<'a> Reader<'a> {
#[allow(dead_code)] pub(super) fn new(b: &'a [u8]) -> Self {
Self { b, p: 0, depth: 0 }
}
pub(super) fn at(b: &'a [u8], p: usize) -> Self {
Self { b, p, depth: 0 }
}
pub(super) fn nested<T>(
&mut self,
f: impl FnOnce(&mut Self) -> Result<T, Bad>,
) -> Result<T, Bad> {
if self.depth >= MAX_NESTING {
return Err(Bad::Code("functions nested too deep".to_string()));
}
self.depth += 1;
let r = f(self);
self.depth -= 1;
r
}
pub(super) fn pos(&self) -> usize {
self.p
}
pub(super) fn take(&mut self, n: usize) -> Result<&'a [u8], Bad> {
let end = self.p.checked_add(n).ok_or(Bad::Truncated)?;
let slice = self.b.get(self.p..end).ok_or(Bad::Truncated)?;
self.p = end;
Ok(slice)
}
pub(super) fn u8(&mut self) -> Result<u8, Bad> {
Ok(self.take(1)?[0])
}
pub(super) fn u32(&mut self) -> Result<u32, Bad> {
Ok(u32::from_le_bytes(self.take(4)?.try_into().unwrap()))
}
pub(super) fn count(&self, n: u64, min_size: usize) -> Result<usize, Bad> {
let left = (self.b.len() - self.p) as u64;
match n.checked_mul(min_size as u64) {
Some(need) if need <= left => Ok(n as usize),
_ => Err(Bad::Truncated),
}
}
pub(super) fn bytes(&mut self) -> Result<&'a [u8], Bad> {
let n = self.u32()? as usize;
self.take(n)
}
#[allow(dead_code)]
pub(super) fn peek_underlying_slice(&self) -> &'a [u8] {
self.b
}
#[allow(dead_code)]
pub(super) fn skip_to(&mut self, to: usize) -> Result<(), Bad> {
if to < self.p || to > self.b.len() {
return Err(Bad::Code(format!(
"skip_to {to} out of range (cur {}, len {})",
self.p,
self.b.len()
)));
}
self.p = to;
Ok(())
}
}
#[allow(dead_code)] pub(super) fn read_puc_varint(r: &mut Reader) -> Result<u64, Bad> {
let mut acc: u64 = 0;
for _ in 0..10 {
let byte = r.u8()?;
if acc >> 57 != 0 {
return Err(Bad::IntOverflow);
}
acc = (acc << 7) | (byte & 0x7f) as u64;
if byte & 0x80 == 0 {
return Ok(acc);
}
}
Err(Bad::IntOverflow)
}