use alloc::borrow::Cow;
use core::fmt;
use crate::read::{Reader, Result};
pub trait Relocate {
fn relocate(&self, offset: usize, size: u8, value: u64) -> Result<u64>;
}
impl<T: Relocate + ?Sized> Relocate for &T {
fn relocate(&self, offset: usize, size: u8, value: u64) -> Result<u64> {
(**self).relocate(offset, size, value)
}
}
#[derive(Clone)]
pub struct RelocateReader<R: Reader, T: Relocate> {
section: R,
reader: R,
relocate: T,
}
impl<R: Reader, T: Relocate> RelocateReader<R, T> {
pub fn new(section: R, relocate: T) -> Self {
let reader = section.clone();
RelocateReader {
section,
reader,
relocate,
}
}
pub fn inner(&self) -> &R {
&self.reader
}
}
impl<R: Reader, T: Relocate> fmt::Debug for RelocateReader<R, T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("RelocateReader")
.field(&self.reader)
.finish()
}
}
impl<R, T> Reader for RelocateReader<R, T>
where
R: Reader,
T: Relocate + Clone,
{
type Endian = R::Endian;
fn read_relocatable(&mut self, size: u8) -> Result<u64> {
let offset = self.reader.offset_from(&self.section);
let value = self.reader.read_sized(size)?;
let value = self.relocate.relocate(offset, size, value)?;
Ok(value & crate::common::size_mask(size))
}
#[inline]
fn endian(&self) -> Self::Endian {
self.reader.endian()
}
#[inline]
fn len(&self) -> usize {
self.reader.len()
}
#[inline]
fn empty(&mut self) {
self.reader.empty();
}
#[inline]
fn truncate(&mut self, len: usize) -> Result<()> {
self.reader.truncate(len)
}
#[inline]
fn offset_from(&self, base: &Self) -> usize {
self.reader.offset_from(&base.reader)
}
#[inline]
fn skip(&mut self, len: usize) -> Result<()> {
self.reader.skip(len)
}
#[inline]
fn split(&mut self, len: usize) -> Result<Self> {
let mut other = self.clone();
other.reader.truncate(len)?;
self.reader.skip(len)?;
Ok(other)
}
#[inline]
fn to_slice(&self) -> Result<Cow<'_, [u8]>> {
self.reader.to_slice()
}
#[inline]
fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> {
self.reader.read_slice(buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::endianity::LittleEndian;
use crate::read::EndianSlice;
#[derive(Debug, Clone)]
struct AddAtFour;
impl Relocate for AddAtFour {
fn relocate(&self, offset: usize, _size: u8, value: u64) -> Result<u64> {
Ok(if offset == 4 {
value + 0x1_0000_0100
} else {
value
})
}
}
#[test]
fn test_relocate_reader() {
let buf = [1, 0, 0, 0, 2, 0, 0, 0];
let mut reader = RelocateReader::new(EndianSlice::new(&buf, LittleEndian), AddAtFour);
assert_eq!(reader.read_relocatable(4), Ok(1));
let mut tail = reader.split(4).unwrap();
assert_eq!(tail.read_relocatable(4), Ok(0x102));
}
}