use alloc::vec::Vec;
use core::{
mem,
mem::{MaybeUninit, align_of, size_of},
};
use crate::{AlignedBytes, ByteRepr, IoError, ReadBoundsError, Result, try_cast_bytes};
use alloc::boxed::Box;
use super::Path;
pub trait ElfReader {
fn path(&self) -> &Path;
fn len(&self) -> usize;
fn read(&self, buf: &mut [u8], offset: usize) -> Result<()>;
fn borrow_bytes(&self, _offset: usize, _len: usize) -> Result<Option<&[u8]>> {
Ok(None)
}
fn as_fd(&self) -> Option<isize>;
fn file_name(&self) -> &str {
self.path().file_name()
}
}
pub(crate) trait ElfReaderExt: ElfReader {
#[inline]
fn with_bytes<'a, V, S, T>(
&'a self,
offset: usize,
len: usize,
scratch: &'a mut S,
f: impl FnOnce(&'a [V]) -> Result<T>,
) -> Result<T>
where
V: ByteRepr + 'a,
S: ByteScratch + ?Sized,
{
if let Some(bytes) = self.borrow_bytes(offset, len)?
&& let Some(values) = try_cast_bytes::<V>(bytes)
{
return f(values);
}
let Some(bytes) = scratch.resize_bytes(len) else {
return Err(IoError::ReadOutOfBounds(Box::new(ReadBoundsError::new(
offset,
len,
self.len(),
)))
.into());
};
self.read(bytes, offset)?;
let Some(values) = try_cast_bytes::<V>(bytes) else {
return Err(IoError::ReadBufferNotAligned {
align: align_of::<V>(),
}
.into());
};
f(values)
}
#[inline]
fn read_to_vec<T: ByteRepr>(&self, offset: usize, count: usize) -> Result<Vec<T>> {
let byte_len = count
.checked_mul(size_of::<T>())
.ok_or(IoError::ReadBufferTooLarge)?;
let mut values = Vec::<MaybeUninit<T>>::new();
values
.try_reserve_exact(count)
.map_err(|_| IoError::OutOfMemory)?;
unsafe {
values.set_len(count);
}
let bytes =
unsafe { core::slice::from_raw_parts_mut(values.as_mut_ptr().cast::<u8>(), byte_len) };
self.read(bytes, offset)?;
Ok(unsafe { assume_init_vec(values) })
}
}
impl<T: ElfReader + ?Sized> ElfReaderExt for T {}
pub(crate) trait ByteScratch {
fn resize_bytes(&mut self, len: usize) -> Option<&mut [u8]>;
}
impl ByteScratch for AlignedBytes {
#[inline]
fn resize_bytes(&mut self, len: usize) -> Option<&mut [u8]> {
self.resize(len)?;
Some(self.as_bytes_mut())
}
}
impl ByteScratch for Vec<u8> {
#[inline]
fn resize_bytes(&mut self, len: usize) -> Option<&mut [u8]> {
self.resize(len, 0);
Some(self.as_mut_slice())
}
}
#[inline]
unsafe fn assume_init_vec<T>(mut values: Vec<MaybeUninit<T>>) -> Vec<T> {
let len = values.len();
let cap = values.capacity();
let ptr = values.as_mut_ptr().cast::<T>();
mem::forget(values);
unsafe { Vec::from_raw_parts(ptr, len, cap) }
}
pub trait IntoElfReader<'a> {
type Reader: ElfReader + 'a;
fn into_reader(self) -> Result<Self::Reader>;
}
impl<R: ElfReader + ?Sized> ElfReader for Box<R> {
#[inline]
fn path(&self) -> &Path {
(**self).path()
}
#[inline]
fn len(&self) -> usize {
(**self).len()
}
#[inline]
fn read(&self, buf: &mut [u8], offset: usize) -> Result<()> {
(**self).read(buf, offset)
}
#[inline]
fn borrow_bytes(&self, offset: usize, len: usize) -> Result<Option<&[u8]>> {
(**self).borrow_bytes(offset, len)
}
#[inline]
fn as_fd(&self) -> Option<isize> {
(**self).as_fd()
}
}
impl<'a, R> IntoElfReader<'a> for Box<R>
where
R: ElfReader + 'a + ?Sized,
{
type Reader = Box<R>;
#[inline]
fn into_reader(self) -> Result<Self::Reader> {
Ok(self)
}
}