use crate::error::{Error, Result};
pub const ALIGN: usize = 8;
#[must_use]
pub const fn align_up(n: usize) -> usize {
n.next_multiple_of(ALIGN)
}
#[derive(Clone, Debug)]
pub struct Reader<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> Reader<'a> {
#[must_use]
pub const fn new(buf: &'a [u8]) -> Self {
Self { buf, pos: 0 }
}
#[must_use]
pub const fn remaining(&self) -> usize {
self.buf.len() - self.pos
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.remaining() == 0
}
#[must_use]
pub const fn position(&self) -> usize {
self.pos
}
fn take(&mut self, n: usize) -> Result<&'a [u8]> {
let available = self.remaining();
if n > available {
return Err(Error::Truncated {
needed: n,
available,
});
}
let out = &self.buf[self.pos..self.pos + n];
self.pos += n;
Ok(out)
}
pub fn u8(&mut self) -> Result<u8> {
Ok(self.take(1)?[0])
}
pub fn u16(&mut self) -> Result<u16> {
let b = self.take(2)?;
Ok(u16::from_le_bytes([b[0], b[1]]))
}
pub fn u32(&mut self) -> Result<u32> {
let b = self.take(4)?;
Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
}
pub fn u64(&mut self) -> Result<u64> {
let b = self.take(8)?;
Ok(u64::from_le_bytes([
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
]))
}
pub fn opt_u64(&mut self) -> Result<Option<u64>> {
if self.is_empty() {
return Ok(None);
}
self.u64().map(Some)
}
pub fn bytes(&mut self, n: usize) -> Result<&'a [u8]> {
self.take(n)
}
pub fn skip(&mut self, n: usize) -> Result<()> {
self.take(n).map(|_| ())
}
pub fn align(&mut self) -> Result<()> {
self.skip(align_up(self.pos) - self.pos)
}
pub fn var_bytes(&mut self) -> Result<&'a [u8]> {
let len = self.u32()?;
let len = usize::try_from(len).map_err(|_| Error::LengthOverflow)?;
let out = self.take(len)?;
self.align()?;
Ok(out)
}
pub fn var_str(&mut self) -> Result<&'a str> {
core::str::from_utf8(self.var_bytes()?).map_err(|_| Error::NotUtf8)
}
pub fn sub(&mut self, n: usize) -> Result<Reader<'a>> {
Ok(Reader::new(self.take(n)?))
}
}
#[derive(Debug)]
pub struct Writer<'a> {
buf: &'a mut [u8],
pos: usize,
}
impl<'a> Writer<'a> {
#[must_use]
pub fn new(buf: &'a mut [u8]) -> Self {
Self { buf, pos: 0 }
}
#[must_use]
pub const fn position(&self) -> usize {
self.pos
}
#[must_use]
pub const fn remaining(&self) -> usize {
self.buf.len() - self.pos
}
#[must_use]
pub fn written(&self) -> &[u8] {
&self.buf[..self.pos]
}
fn room(&mut self, n: usize) -> Result<usize> {
let available = self.remaining();
if n > available {
return Err(Error::BufferFull {
needed: n,
available,
});
}
let at = self.pos;
self.pos += n;
Ok(at)
}
pub fn u8(&mut self, v: u8) -> Result<()> {
let at = self.room(1)?;
self.buf[at] = v;
Ok(())
}
pub fn u16(&mut self, v: u16) -> Result<()> {
self.raw(&v.to_le_bytes())
}
pub fn u32(&mut self, v: u32) -> Result<()> {
self.raw(&v.to_le_bytes())
}
pub fn u64(&mut self, v: u64) -> Result<()> {
self.raw(&v.to_le_bytes())
}
pub fn raw(&mut self, v: &[u8]) -> Result<()> {
let at = self.room(v.len())?;
self.buf[at..at + v.len()].copy_from_slice(v);
Ok(())
}
pub fn align(&mut self) -> Result<()> {
let pad = align_up(self.pos) - self.pos;
let at = self.room(pad)?;
self.buf[at..at + pad].fill(0);
Ok(())
}
pub fn var_bytes(&mut self, v: &[u8]) -> Result<()> {
let len = u32::try_from(v.len()).map_err(|_| Error::LengthOverflow)?;
self.u32(len)?;
self.raw(v)?;
self.align()
}
pub fn var_str(&mut self, v: &str) -> Result<()> {
self.var_bytes(v.as_bytes())
}
pub fn patch_u32(&mut self, at: usize, v: u32) -> Result<()> {
if at + 4 > self.pos {
return Err(Error::BufferFull {
needed: at + 4,
available: self.pos,
});
}
self.buf[at..at + 4].copy_from_slice(&v.to_le_bytes());
Ok(())
}
}