use crate::{Error, Result};
pub(crate) struct Reader<'a> {
bytes: &'a [u8],
offset: usize,
}
impl<'a> Reader<'a> {
pub(crate) fn new(bytes: &'a [u8]) -> Self {
Self { bytes, offset: 0 }
}
pub(crate) fn take(&mut self, length: usize, label: &str) -> Result<&'a [u8]> {
let end = self
.offset
.checked_add(length)
.ok_or_else(|| Error::new(format!("{label} length overflow")))?;
if end > self.bytes.len() {
return Err(Error::new(format!("{label} is truncated")));
}
let value = &self.bytes[self.offset..end];
self.offset = end;
Ok(value)
}
pub(crate) fn array<const N: usize>(&mut self, label: &str) -> Result<[u8; N]> {
self.take(N, label)?
.try_into()
.map_err(|_| Error::new(format!("{label} has an invalid length")))
}
pub(crate) fn u32(&mut self, label: &str) -> Result<u32> {
Ok(u32::from_be_bytes(self.array(label)?))
}
pub(crate) fn u64(&mut self, label: &str) -> Result<u64> {
Ok(u64::from_be_bytes(self.array(label)?))
}
pub(crate) fn string(&mut self, label: &str) -> Result<String> {
let length = usize::try_from(self.u32(label)?)
.map_err(|_| Error::new(format!("{label} length exceeds usize")))?;
let bytes = self.take(length, label)?;
let value =
std::str::from_utf8(bytes).map_err(|_| Error::new(format!("{label} is not UTF-8")))?;
copy_string(value, label)
}
pub(crate) fn position(&self) -> usize {
self.offset
}
pub(crate) fn finish(self, label: &str) -> Result<()> {
if self.offset != self.bytes.len() {
return Err(Error::new(format!("{label} has trailing bytes")));
}
Ok(())
}
}
pub(crate) fn copy_string(value: &str, label: &str) -> Result<String> {
let mut output = String::new();
output
.try_reserve_exact(value.len())
.map_err(|_| Error::new(format!("unable to allocate {label}")))?;
output.push_str(value);
Ok(output)
}
pub(crate) fn push_string(output: &mut Vec<u8>, value: &str, label: &str) -> Result<()> {
let length =
u32::try_from(value.len()).map_err(|_| Error::new(format!("{label} exceeds u32")))?;
let additional = 4usize
.checked_add(value.len())
.ok_or_else(|| Error::new(format!("{label} encoded length overflow")))?;
output
.len()
.checked_add(additional)
.ok_or_else(|| Error::new(format!("{label} encoded length overflow")))?;
output
.try_reserve(additional)
.map_err(|_| Error::new(format!("unable to allocate {label}")))?;
output.extend_from_slice(&length.to_be_bytes());
output.extend_from_slice(value.as_bytes());
Ok(())
}