1use super::*;
29use core::fmt;
30use core::ops::{Deref, DerefMut};
31
32pub struct FilePtr<Ptr: IntoSeekFrom, BR: BinRead> {
60 pub ptr: Ptr,
61 pub value: Option<BR>,
62}
63
64pub type FilePtr8<T> = FilePtr<u8, T>;
66pub type FilePtr16<T> = FilePtr<u16, T>;
68pub type FilePtr32<T> = FilePtr<u32, T>;
70pub type FilePtr64<T> = FilePtr<u64, T>;
72pub type FilePtr128<T> = FilePtr<u128, T>;
74
75impl<Ptr: BinRead<Args = ()> + IntoSeekFrom, BR: BinRead> BinRead for FilePtr<Ptr, BR> {
76 type Args = BR::Args;
77
78 fn read_options<R: Read + Seek>(
79 reader: &mut R,
80 options: &ReadOptions,
81 _: Self::Args,
82 ) -> BinResult<Self> {
83 #[cfg(feature = "debug_template")]
84 let options = &{
85 let mut options = *options;
86
87 let pos = reader.stream_pos().unwrap();
88 let type_name = &core::any::type_name::<Ptr>();
89 if let Some(name) = options.variable_name {
90 binary_template::write_named(
91 options.endian,
92 pos,
93 type_name,
94 &format!("ptr_to_{}", name),
95 );
96 } else {
97 binary_template::write(options.endian, pos, type_name);
98 }
99 options.dont_output_to_template = true;
100
101 options
102 };
103
104 Ok(FilePtr {
105 ptr: Ptr::read_options(reader, options, ())?,
106 value: None,
107 })
108 }
109
110 fn after_parse<R>(&mut self, reader: &mut R, ro: &ReadOptions, args: BR::Args) -> BinResult<()>
111 where
112 R: Read + Seek,
113 {
114 let relative_to = ro.offset;
115 let before = reader.stream_pos()?;
116 reader.seek(SeekFrom::Start(relative_to))?;
117 reader.seek(self.ptr.into_seek_from())?;
118
119 let mut inner: BR = BinRead::read_options(reader, ro, args)?;
120
121 inner.after_parse(reader, ro, args)?;
122
123 self.value = Some(inner);
124
125 reader.seek(SeekFrom::Start(before))?;
126 Ok(())
127 }
128}
129
130impl<Ptr: BinRead<Args = ()> + IntoSeekFrom, BR: BinRead> FilePtr<Ptr, BR> {
131 pub fn parse<R: Read + Seek>(
134 reader: &mut R,
135 options: &ReadOptions,
136 args: BR::Args,
137 ) -> BinResult<BR> {
138 let mut ptr: Self = Self::read_options(reader, options, args)?;
139 let saved_pos = reader.stream_pos()?;
140 ptr.after_parse(reader, options, args)?;
141 reader.seek(SeekFrom::Start(saved_pos))?;
142 Ok(ptr.into_inner())
143 }
144
145 pub fn into_inner(self) -> BR {
151 self.value.unwrap()
152 }
153}
154
155pub trait IntoSeekFrom: Copy {
157 fn into_seek_from(self) -> SeekFrom;
158}
159
160macro_rules! impl_into_seek_from {
161 ($($t:ty),*) => {
162 $(
163 impl IntoSeekFrom for $t {
164 fn into_seek_from(self) -> SeekFrom {
165 SeekFrom::Current(self as i64)
166 }
167 }
168 )*
169 };
170}
171
172impl_into_seek_from!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
173
174impl<Ptr: IntoSeekFrom, BR: BinRead> Deref for FilePtr<Ptr, BR> {
177 type Target = BR;
178
179 fn deref(&self) -> &Self::Target {
180 match self.value.as_ref() {
181 Some(x) => x,
182 None => panic!(
183 "Deref'd FilePtr before reading (make sure to use FilePtr::after_parse first)"
184 ),
185 }
186 }
187}
188
189impl<Ptr: IntoSeekFrom, BR: BinRead> DerefMut for FilePtr<Ptr, BR> {
192 fn deref_mut(&mut self) -> &mut BR {
193 match self.value.as_mut() {
194 Some(x) => x,
195 None => panic!(
196 "Deref'd FilePtr before reading (make sure to use FilePtr::after_parse first)"
197 ),
198 }
199 }
200}
201
202impl<Ptr, BR> fmt::Debug for FilePtr<Ptr, BR>
203where
204 Ptr: BinRead<Args = ()> + IntoSeekFrom,
205 BR: BinRead + fmt::Debug,
206{
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 if let Some(ref value) = self.value {
209 fmt::Debug::fmt(value, f)
210 } else {
211 write!(f, "UnreadPointer")
212 }
213 }
214}
215
216impl<Ptr, BR> PartialEq<FilePtr<Ptr, BR>> for FilePtr<Ptr, BR>
217where
218 Ptr: BinRead<Args = ()> + IntoSeekFrom,
219 BR: BinRead + PartialEq,
220{
221 fn eq(&self, other: &Self) -> bool {
222 self.deref() == other.deref()
223 }
224}