use super::*;
use core::fmt;
use core::ops::{Deref, DerefMut};
pub struct FilePtr<Ptr: IntoSeekFrom, BR: BinRead> {
pub ptr: Ptr,
pub value: Option<BR>,
}
pub type FilePtr8<T> = FilePtr<u8, T>;
pub type FilePtr16<T> = FilePtr<u16, T>;
pub type FilePtr32<T> = FilePtr<u32, T>;
pub type FilePtr64<T> = FilePtr<u64, T>;
pub type FilePtr128<T> = FilePtr<u128, T>;
impl<Ptr: BinRead<Args = ()> + IntoSeekFrom, BR: BinRead> BinRead for FilePtr<Ptr, BR> {
type Args = BR::Args;
fn read_options<R: Read + Seek>(
reader: &mut R,
options: &ReadOptions,
_: Self::Args,
) -> BinResult<Self> {
#[cfg(feature = "debug_template")]
let options = &{
let mut options = *options;
let pos = reader.stream_pos().unwrap();
let type_name = &core::any::type_name::<Ptr>();
if let Some(name) = options.variable_name {
binary_template::write_named(
options.endian,
pos,
type_name,
&format!("ptr_to_{}", name),
);
} else {
binary_template::write(options.endian, pos, type_name);
}
options.dont_output_to_template = true;
options
};
Ok(FilePtr {
ptr: Ptr::read_options(reader, options, ())?,
value: None,
})
}
fn after_parse<R>(&mut self, reader: &mut R, ro: &ReadOptions, args: BR::Args) -> BinResult<()>
where
R: Read + Seek,
{
let relative_to = ro.offset;
let before = reader.stream_pos()?;
reader.seek(SeekFrom::Start(relative_to))?;
reader.seek(self.ptr.into_seek_from())?;
let mut inner: BR = BinRead::read_options(reader, ro, args)?;
inner.after_parse(reader, ro, args)?;
self.value = Some(inner);
reader.seek(SeekFrom::Start(before))?;
Ok(())
}
}
impl<Ptr: BinRead<Args = ()> + IntoSeekFrom, BR: BinRead> FilePtr<Ptr, BR> {
pub fn parse<R: Read + Seek>(
reader: &mut R,
options: &ReadOptions,
args: BR::Args,
) -> BinResult<BR> {
let mut ptr: Self = Self::read_options(reader, options, args)?;
let saved_pos = reader.stream_pos()?;
ptr.after_parse(reader, options, args)?;
reader.seek(SeekFrom::Start(saved_pos))?;
Ok(ptr.into_inner())
}
pub fn into_inner(self) -> BR {
self.value.unwrap()
}
}
pub trait IntoSeekFrom: Copy {
fn into_seek_from(self) -> SeekFrom;
}
macro_rules! impl_into_seek_from {
($($t:ty),*) => {
$(
impl IntoSeekFrom for $t {
fn into_seek_from(self) -> SeekFrom {
SeekFrom::Current(self as i64)
}
}
)*
};
}
impl_into_seek_from!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
impl<Ptr: IntoSeekFrom, BR: BinRead> Deref for FilePtr<Ptr, BR> {
type Target = BR;
fn deref(&self) -> &Self::Target {
match self.value.as_ref() {
Some(x) => x,
None => panic!(
"Deref'd FilePtr before reading (make sure to use FilePtr::after_parse first)"
),
}
}
}
impl<Ptr: IntoSeekFrom, BR: BinRead> DerefMut for FilePtr<Ptr, BR> {
fn deref_mut(&mut self) -> &mut BR {
match self.value.as_mut() {
Some(x) => x,
None => panic!(
"Deref'd FilePtr before reading (make sure to use FilePtr::after_parse first)"
),
}
}
}
impl<Ptr, BR> fmt::Debug for FilePtr<Ptr, BR>
where
Ptr: BinRead<Args = ()> + IntoSeekFrom,
BR: BinRead + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref value) = self.value {
fmt::Debug::fmt(value, f)
} else {
write!(f, "UnreadPointer")
}
}
}
impl<Ptr, BR> PartialEq<FilePtr<Ptr, BR>> for FilePtr<Ptr, BR>
where
Ptr: BinRead<Args = ()> + IntoSeekFrom,
BR: BinRead + PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.deref() == other.deref()
}
}