use crate::{Error, Result};
#[derive(Clone, Copy)]
pub(crate) struct Bytes<'a> {
pub bytes: &'a [u8],
pub file: &'static str,
}
impl<'a> Bytes<'a> {
pub fn new(bytes: &'a [u8], file: &'static str) -> Self {
Self { bytes, file }
}
pub fn err(&self, message: impl Into<String>) -> Error {
Error::format(self.file, message)
}
pub fn slice(&self, at: usize, len: usize) -> Result<&'a [u8]> {
at.checked_add(len)
.and_then(|end| self.bytes.get(at..end))
.ok_or_else(|| {
self.err(format!(
"{len} bytes at {at} run past the end ({} bytes)",
self.bytes.len()
))
})
}
pub fn array<const N: usize>(&self, at: usize) -> Result<[u8; N]> {
Ok(self.slice(at, N)?.try_into().expect("N bytes"))
}
pub fn u8(&self, at: usize) -> Result<u8> {
Ok(self.array::<1>(at)?[0])
}
pub fn u16(&self, at: usize) -> Result<u16> {
self.array(at).map(u16::from_le_bytes)
}
pub fn u32(&self, at: usize) -> Result<u32> {
self.array(at).map(u32::from_le_bytes)
}
pub fn i32(&self, at: usize) -> Result<i32> {
self.array(at).map(i32::from_le_bytes)
}
pub fn u64(&self, at: usize) -> Result<u64> {
self.array(at).map(u64::from_le_bytes)
}
pub fn i64(&self, at: usize) -> Result<i64> {
self.array(at).map(i64::from_le_bytes)
}
pub fn f32(&self, at: usize) -> Result<f32> {
self.array(at).map(f32::from_le_bytes)
}
pub fn f64(&self, at: usize) -> Result<f64> {
self.array(at).map(f64::from_le_bytes)
}
pub fn f32s<const N: usize>(&self, at: usize) -> Result<[f32; N]> {
let mut out = [0.0; N];
for (i, v) in out.iter_mut().enumerate() {
*v = self.f32(at + i * 4)?;
}
Ok(out)
}
pub fn records(&self, stride: usize) -> Result<impl Iterator<Item = Bytes<'a>> + 'a> {
if !self.bytes.len().is_multiple_of(stride) {
return Err(self.err(format!(
"{} bytes are not a whole number of {stride}-byte records",
self.bytes.len()
)));
}
let file = self.file;
Ok(self
.bytes
.chunks_exact(stride)
.map(move |bytes| Bytes { bytes, file }))
}
pub fn records_n(
&self,
stride: usize,
count: usize,
) -> Result<impl Iterator<Item = Bytes<'a>> + 'a> {
let needed = stride
.checked_mul(count)
.ok_or_else(|| self.err("record count overflows"))?;
if self.bytes.len() < needed {
return Err(self.err(format!(
"{count} records of {stride} bytes need {needed} bytes, have {}",
self.bytes.len()
)));
}
let file = self.file;
Ok(self.bytes[..needed]
.chunks_exact(stride)
.map(move |bytes| Bytes { bytes, file }))
}
pub fn text(&self, at: usize, len: usize) -> Result<String> {
let bytes = self.slice(at, len)?;
std::str::from_utf8(bytes)
.map(str::to_owned)
.map_err(|_| self.err(format!("text at {at} is not UTF-8")))
}
pub fn text_ref(&self, at: usize, strings: Bytes<'_>) -> Result<String> {
strings.text(self.u32(at)? as usize, self.u32(at + 4)? as usize)
}
}