1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
//! Type definitions for wrappers which represent a layer of indirection within
//! a file.

use crate::NamedArgs;
use crate::{
    io::{Read, Seek, SeekFrom},
    BinRead, BinResult, Endian,
};
use core::fmt;
use core::num::{
    NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16,
    NonZeroU32, NonZeroU64, NonZeroU8,
};
use core::ops::{Deref, DerefMut};

/// A type alias for [`FilePtr`] with 8-bit offsets.
pub type FilePtr8<T> = FilePtr<u8, T>;
/// A type alias for [`FilePtr`] with 16-bit offsets.
pub type FilePtr16<T> = FilePtr<u16, T>;
/// A type alias for [`FilePtr`] with 32-bit offsets.
pub type FilePtr32<T> = FilePtr<u32, T>;
/// A type alias for [`FilePtr`] with 64-bit offsets.
pub type FilePtr64<T> = FilePtr<u64, T>;
/// A type alias for [`FilePtr`] with 128-bit offsets.
pub type FilePtr128<T> = FilePtr<u128, T>;

/// A type alias for [`FilePtr`] with non-zero 8-bit offsets.
pub type NonZeroFilePtr8<T> = FilePtr<NonZeroU8, T>;
/// A type alias for [`FilePtr`] with non-zero 16-bit offsets.
pub type NonZeroFilePtr16<T> = FilePtr<NonZeroU16, T>;
/// A type alias for [`FilePtr`] with non-zero 32-bit offsets.
pub type NonZeroFilePtr32<T> = FilePtr<NonZeroU32, T>;
/// A type alias for [`FilePtr`] with non-zero 64-bit offsets.
pub type NonZeroFilePtr64<T> = FilePtr<NonZeroU64, T>;
/// A type alias for [`FilePtr`] with non-zero 128-bit offsets.
pub type NonZeroFilePtr128<T> = FilePtr<NonZeroU128, T>;

/// A wrapper type which represents a layer of indirection within a file.
///
/// `FilePtr<P, T>` is composed of two types. The pointer type `P` is the
/// absolute offset to a value within the data, and the value type `T` is
/// the actual pointed-to value. Once a `FilePtr` has been
/// [finalized](crate::BinRead::after_parse), [dereferencing] it will yield the
/// pointed-to value.
///
/// When deriving `BinRead`, [offset](crate::docs::attribute#offset) directives
/// can be used to adjust the offset before the pointed-to value is read.
///
/// [dereferencing]: core::ops::Deref
///
/// # Examples
///
/// ```
/// # use binrw::{prelude::*, io::Cursor, FilePtr};
/// #
/// #[derive(BinRead)]
/// struct Test {
///     indirect_value: FilePtr<u32, u8>
/// }
///
/// let test: Test = Cursor::new(b"\0\0\0\x08\0\0\0\0\xff").read_be().unwrap();
/// assert_eq!(test.indirect_value.ptr, 8);
/// assert_eq!(*test.indirect_value, 0xFF);
/// ```
///
/// Example data mapped out:
///
/// ```hex
///           [pointer]           [value]
/// 00000000: 0000 0008 0000 0000 ff                   ............
/// ```
pub struct FilePtr<Ptr: IntoSeekFrom, T> {
    /// The raw offset to the value.
    pub ptr: Ptr,

    /// The pointed-to value.
    pub value: Option<T>,
}

impl<Ptr, Value> BinRead for FilePtr<Ptr, Value>
where
    Ptr: for<'a> BinRead<Args<'a> = ()> + IntoSeekFrom,
    Value: BinRead,
    for<'a> Value::Args<'a>: Clone,
{
    type Args<'a> = FilePtrArgs<Value::Args<'a>>;

    /// Reads the offset of the value from the reader.
    ///
    /// The actual value will not be read until
    /// [`after_parse()`](Self::after_parse) is called.
    fn read_options<R: Read + Seek>(
        reader: &mut R,
        endian: Endian,
        _: Self::Args<'_>,
    ) -> BinResult<Self> {
        Ok(FilePtr {
            ptr: Ptr::read_options(reader, endian, ())?,
            value: None,
        })
    }

    /// Finalizes the `FilePtr` by seeking to and reading the pointed-to value.
    fn after_parse<R>(
        &mut self,
        reader: &mut R,
        endian: Endian,
        args: Self::Args<'_>,
    ) -> BinResult<()>
    where
        R: Read + Seek,
    {
        self.after_parse_with_parser(
            Value::read_options,
            Value::after_parse,
            reader,
            endian,
            args,
        )
    }
}

impl<Ptr, Value> FilePtr<Ptr, Value>
where
    Ptr: for<'a> BinRead<Args<'a> = ()> + IntoSeekFrom,
{
    fn read_with_parser<R, Parser, AfterParse, Args>(
        parser: Parser,
        after_parse: AfterParse,
        reader: &mut R,
        endian: Endian,
        args: FilePtrArgs<Args>,
    ) -> BinResult<Self>
    where
        R: Read + Seek,
        Args: Clone,
        Parser: Fn(&mut R, Endian, Args) -> BinResult<Value>,
        AfterParse: Fn(&mut Value, &mut R, Endian, Args) -> BinResult<()>,
    {
        let mut file_ptr = Self {
            ptr: Ptr::read_options(reader, endian, ())?,
            value: None,
        };
        file_ptr.after_parse_with_parser(parser, after_parse, reader, endian, args)?;
        Ok(file_ptr)
    }

    fn after_parse_with_parser<R, Parser, AfterParse, Args>(
        &mut self,
        parser: Parser,
        after_parse: AfterParse,
        reader: &mut R,
        endian: Endian,
        args: FilePtrArgs<Args>,
    ) -> BinResult<()>
    where
        R: Read + Seek,
        Args: Clone,
        Parser: Fn(&mut R, Endian, Args) -> BinResult<Value>,
        AfterParse: Fn(&mut Value, &mut R, Endian, Args) -> BinResult<()>,
    {
        let relative_to = args.offset;
        let before = reader.stream_position()?;
        reader.seek(SeekFrom::Start(relative_to))?;
        reader.seek(self.ptr.into_seek_from())?;

        let mut inner: Value = parser(reader, endian, args.inner.clone())?;

        after_parse(&mut inner, reader, endian, args.inner)?;
        reader.seek(SeekFrom::Start(before))?;

        self.value = Some(inner);
        Ok(())
    }

    /// Custom parser for use with the
    /// [`parse_with`](crate::docs::attribute#custom-parserswriters) directive that reads
    /// and then immediately finalizes a [`FilePtr`], returning the pointed-to
    /// value as the result.
    ///
    /// # Errors
    ///
    /// If reading fails, an [`Error`](crate::Error) variant will be returned.
    #[binrw::parser(reader, endian)]
    pub fn parse<Args>(args: FilePtrArgs<Args>, ...) -> BinResult<Value>
    where
        Args: Clone,
        Value: for<'a> BinRead<Args<'a> = Args>,
    {
        Ok(Self::read_with_parser(
            Value::read_options,
            Value::after_parse,
            reader,
            endian,
            args,
        )?
        .into_inner())
    }

    /// Custom parser for use with the
    /// [`parse_with`](crate::docs::attribute#custom-parserswriters) directive that reads and then
    /// immediately finalizes a [`FilePtr`] using the specified parser, returning the pointed-to
    /// value as the result.
    ///
    /// # Errors
    ///
    /// If reading fails, an [`Error`](crate::Error) variant will be returned.
    pub fn parse_with<R, F, Args>(
        parser: F,
    ) -> impl Fn(&mut R, Endian, FilePtrArgs<Args>) -> BinResult<Value>
    where
        R: Read + Seek,
        Args: Clone,
        F: Fn(&mut R, Endian, Args) -> BinResult<Value>,
    {
        move |reader, endian, args| {
            let after_parse = |_: &mut Value, _: &mut R, _: Endian, _: Args| Ok(());
            Ok(Self::read_with_parser(&parser, after_parse, reader, endian, args)?.into_inner())
        }
    }

    /// Custom parser for use with the
    /// [`parse_with`](crate::docs::attribute#custom-parserswriters) directive that reads and then
    /// immediately finalizes a [`FilePtr`] using the specified parser, returning the [`FilePtr`]
    /// as the result.
    ///
    /// # Errors
    ///
    /// If reading fails, an [`Error`](crate::Error) variant will be returned.
    pub fn with<R, F, Args>(
        parser: F,
    ) -> impl Fn(&mut R, Endian, FilePtrArgs<Args>) -> BinResult<Self>
    where
        R: Read + Seek,
        Args: Clone,
        F: Fn(&mut R, Endian, Args) -> BinResult<Value>,
    {
        move |reader, endian, args| {
            let after_parse = |_: &mut Value, _: &mut R, _: Endian, _: Args| Ok(());
            Self::read_with_parser(&parser, after_parse, reader, endian, args)
        }
    }

    /// Consumes this object, returning the pointed-to value.
    ///
    /// # Panics
    ///
    /// Will panic if `FilePtr` hasn’t been finalized by calling
    /// [`after_parse()`](Self::after_parse).
    pub fn into_inner(self) -> Value {
        self.value.unwrap()
    }
}

/// Dereferences the value.
///
/// # Panics
///
/// Will panic if `FilePtr` hasn’t been finalized by calling
/// [`after_parse()`](Self::after_parse).
impl<Ptr: IntoSeekFrom, Value: BinRead> Deref for FilePtr<Ptr, Value> {
    type Target = Value;

    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)"
            ),
        }
    }
}

/// # Panics
/// Will panic if the `FilePtr` has not been read yet using
/// [`BinRead::after_parse`](BinRead::after_parse)
impl<Ptr: IntoSeekFrom, Value: BinRead> DerefMut for FilePtr<Ptr, Value> {
    fn deref_mut(&mut self) -> &mut Value {
        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, Value> fmt::Debug for FilePtr<Ptr, Value>
where
    Ptr: fmt::Debug + IntoSeekFrom,
    Value: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(ref value) = self.value {
            fmt::Debug::fmt(value, f)
        } else {
            f.debug_tuple("UnreadPointer").field(&self.ptr).finish()
        }
    }
}

impl<Ptr, Value> PartialEq<FilePtr<Ptr, Value>> for FilePtr<Ptr, Value>
where
    Ptr: PartialEq + IntoSeekFrom,
    Value: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}

/// A trait to convert from an integer into
/// [`SeekFrom::Current`](crate::io::SeekFrom::Current).
pub trait IntoSeekFrom: Copy + fmt::Debug {
    /// Converts the value.
    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(TryInto::try_into(self).unwrap())
                }
            }
        )*
    };
}

impl_into_seek_from!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);

macro_rules! impl_into_seek_from_for_non_zero {
    ($($t:ty),*) => {
        $(
            impl IntoSeekFrom for $t {
                fn into_seek_from(self) -> SeekFrom {
                    self.get().into_seek_from()
                }
            }
        )*
    };
}

impl_into_seek_from_for_non_zero!(
    NonZeroI128,
    NonZeroI16,
    NonZeroI32,
    NonZeroI64,
    NonZeroI8,
    NonZeroU128,
    NonZeroU16,
    NonZeroU32,
    NonZeroU64,
    NonZeroU8
);

/// Named arguments for the [`BinRead::read_options()`] implementation of [`FilePtr`].
///
/// The `inner` field can be omitted completely if the inner type doesn’t
/// require arguments, in which case a default value will be used.
#[derive(Clone, Default, NamedArgs)]
pub struct FilePtrArgs<Inner> {
    /// An absolute offset added to the [`FilePtr::ptr`](crate::FilePtr::ptr)
    /// offset before reading the pointed-to value.
    #[named_args(default = 0)]
    pub offset: u64,

    /// The [arguments](crate::BinRead::Args) for the inner type.
    #[named_args(try_optional)]
    pub inner: Inner,
}