use crate::{
SEEK_TABLE_INTEGRITY_SIZE, SKIPPABLE_HEADER_SIZE,
error::{Error, Result},
seek_table::Format,
};
pub enum OffsetFrom {
Start(u64),
End(i64),
}
pub trait Seekable {
fn set_offset(&mut self, offset: OffsetFrom) -> Result<u64>;
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
fn seek_table_integrity(&mut self, format: Format) -> Result<[u8; SEEK_TABLE_INTEGRITY_SIZE]>;
}
#[derive(Debug, Clone)]
pub struct BytesWrapper<'a> {
src: &'a [u8],
pos: usize,
}
impl<'a> BytesWrapper<'a> {
pub fn new(src: &'a [u8]) -> Self {
Self { src, pos: 0 }
}
}
impl Seekable for BytesWrapper<'_> {
fn set_offset(&mut self, offset: OffsetFrom) -> Result<u64> {
let pos = match offset {
OffsetFrom::Start(pos) => usize::try_from(pos).ok(),
OffsetFrom::End(delta) => isize::try_from(delta)
.map(|d| self.src.len().checked_add_signed(d))
.ok()
.flatten(),
}
.ok_or(Error::offset_out_of_range())?;
if pos > self.src.len() {
return Err(Error::offset_out_of_range());
}
self.pos = pos;
Ok(pos as u64)
}
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let len = buf.len().min(self.src.len() - self.pos);
buf[..len].copy_from_slice(&self.src[self.pos..self.pos + len]);
self.pos += len;
Ok(len)
}
fn seek_table_integrity(&mut self, format: Format) -> Result<[u8; SEEK_TABLE_INTEGRITY_SIZE]> {
let offset = match format {
Format::Head => (self.src.len() >= SKIPPABLE_HEADER_SIZE + SEEK_TABLE_INTEGRITY_SIZE)
.then_some(SKIPPABLE_HEADER_SIZE),
Format::Foot => self.src.len().checked_sub(SEEK_TABLE_INTEGRITY_SIZE),
}
.ok_or(Error::offset_out_of_range())?;
let mut buf = [0u8; SEEK_TABLE_INTEGRITY_SIZE];
buf.copy_from_slice(&self.src[offset..offset + SEEK_TABLE_INTEGRITY_SIZE]);
Ok(buf)
}
}
#[cfg(feature = "std")]
impl From<OffsetFrom> for std::io::SeekFrom {
fn from(value: OffsetFrom) -> Self {
use std::io::SeekFrom;
match value {
OffsetFrom::Start(n) => SeekFrom::Start(n),
OffsetFrom::End(n) => SeekFrom::End(n),
}
}
}
#[cfg(feature = "std")]
impl<T> Seekable for T
where
T: std::io::Read + std::io::Seek,
{
fn set_offset(&mut self, offset: OffsetFrom) -> Result<u64> {
Ok(self.seek(offset.into())?)
}
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
Ok(self.read(buf)?)
}
fn seek_table_integrity(&mut self, format: Format) -> Result<[u8; SEEK_TABLE_INTEGRITY_SIZE]> {
match format {
Format::Head => self.seek(std::io::SeekFrom::Start(SKIPPABLE_HEADER_SIZE as u64))?,
Format::Foot => {
self.seek(std::io::SeekFrom::End(-(SEEK_TABLE_INTEGRITY_SIZE as i64)))?
}
};
let mut buf = [0u8; SEEK_TABLE_INTEGRITY_SIZE];
self.read_exact(&mut buf)?;
Ok(buf)
}
}