use std::path::Path;
use crate::error::Error;
#[derive(Clone, Copy, Debug)]
pub struct Source<'a> {
pub path: &'a Path,
pub member: Option<&'a str>,
}
impl<'a> Source<'a> {
#[must_use]
pub fn new(path: &'a Path) -> Self {
Self { path, member: None }
}
#[must_use]
pub fn member(path: &'a Path, member: &'a str) -> Self {
Self {
path,
member: Some(member),
}
}
#[cold]
#[inline(never)]
#[must_use]
pub fn malformed(&self, offset: u64, what: impl Into<String>) -> Error {
Error::Malformed {
file: self.path.to_path_buf(),
member: self.member.map(str::to_owned),
offset,
what: what.into(),
}
}
}
#[inline]
pub(crate) fn subslice(data: &[u8], offset: u64, size: u64) -> Option<&[u8]> {
let start = usize::try_from(offset).ok()?;
let len = usize::try_from(size).ok()?;
let end = start.checked_add(len)?;
data.get(start..end)
}
#[inline]
pub(crate) fn array<const N: usize>(data: &[u8], offset: usize) -> Option<[u8; N]> {
let end = offset.checked_add(N)?;
data.get(offset..end)?.try_into().ok()
}
#[inline]
pub(crate) fn u8_at(data: &[u8], offset: usize) -> Option<u8> {
data.get(offset).copied()
}
#[inline]
pub(crate) fn u16_at(data: &[u8], offset: usize) -> Option<u16> {
array(data, offset).map(u16::from_le_bytes)
}
#[inline]
pub(crate) fn u32_at(data: &[u8], offset: usize) -> Option<u32> {
array(data, offset).map(u32::from_le_bytes)
}
#[inline]
pub(crate) fn u64_at(data: &[u8], offset: usize) -> Option<u64> {
array(data, offset).map(u64::from_le_bytes)
}
#[inline]
pub(crate) fn to_u64(value: usize) -> u64 {
u64::try_from(value).unwrap_or(u64::MAX)
}
#[inline]
pub(crate) fn entry_offset(base: u64, index: u64, size: usize) -> u64 {
base.saturating_add(index.saturating_mul(to_u64(size)))
}
#[inline]
pub(crate) fn until_nul(bytes: &[u8]) -> &[u8] {
match bytes.iter().position(|&b| b == 0) {
Some(end) => bytes.get(..end).unwrap_or(bytes),
None => bytes,
}
}
#[inline]
pub(crate) fn c_string(bytes: &[u8]) -> Option<&[u8]> {
let end = bytes.iter().position(|&b| b == 0)?;
bytes.get(..end)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn readers() {
let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(u16_at(&data, 0), Some(0x0201));
assert_eq!(u32_at(&data, 4), Some(0x0807_0605));
assert_eq!(u64_at(&data, 0), Some(0x0807_0605_0403_0201));
assert_eq!(u32_at(&data, 5), None);
assert_eq!(u16_at(&data, usize::MAX), None);
assert_eq!(subslice(&data, 6, 2), Some(&data[6..]));
assert_eq!(subslice(&data, 6, 3), None);
assert_eq!(subslice(&data, u64::MAX, 1), None);
assert_eq!(until_nul(b"ab\0c"), b"ab");
assert_eq!(until_nul(b"abc"), b"abc");
assert_eq!(c_string(b"abc"), None);
assert_eq!(c_string(b"a\0"), Some(&b"a"[..]));
}
}