Module binrw::file_ptr

source ·
Expand description

Type definitions and helpers for handling indirection within a file.

Best practices

Indirections that are not collections (e.g. a single offset to a global file header) can use FilePtr to immediately read the offset and then parse the pointed-to value. However, using FilePtr inside a collection is inefficient because it seeks to and reads each pointed-to value immediately after the offset is read. In these cases, it is faster to read the offset table into a collection (e.g. Vec<u32>) and then either pass it to parse_from_iter or write a function that is called to lazily load values as needed.

Using parse_from_iter to read an offset table

With relative offsets

In this example, the offsets in the offset table start counting from the beginning of the values section, and are in a random order.

Since the values section exists immediately after the offset table, no seeking is required before reading the values.

Since the offsets are in a random order, the position of the stream must be returned to a known state using restore_position on the values field. Then, seek_before is used on the next field to skip past the values data and continue reading the rest of the object.

use binrw::file_ptr::parse_from_iter;

#[derive(BinRead)]
#[br(big)]
struct Object {
    count: u16,
    #[br(args { count: count.into() })]
    offsets: Vec<u16>,
    #[br(parse_with = parse_from_iter(offsets.iter().copied()), restore_position)]
    values: Vec<u8>,
    #[br(seek_before(SeekFrom::Current(count.into())))]
    extra: u16,
}

With absolute offsets

In this example, the offsets in the offset table start from the beginning of the file, and are in sequential order.

Since the offsets start from the beginning of the file, it is necessary to use seek_before to reposition the stream to the beginning of the file before reading the values.

Since the offsets are in order, no seeking is required after the values are read, since the stream will already be pointed at the end of the values section.

use binrw::file_ptr::parse_from_iter;

#[derive(BinRead)]
#[br(big)]
struct Object {
    count: u16,
    #[br(args { count: count.into() })]
    offsets: Vec<u16>,
    #[br(
        parse_with = parse_from_iter(offsets.iter().copied()),
        seek_before(SeekFrom::Start(0))
    )]
    values: Vec<u8>,
    extra: u16,
}

Using a function to lazily load values

In this example, only the offset table is parsed. Values pointed to by the offset table are loaded on demand by calling Object::get as needed at runtime.


#[derive(BinRead)]
#[br(big)]
struct Item(u8);

#[derive(BinRead)]
#[br(big, stream = s)]
struct Object {
    count: u16,
    #[br(args { count: count.into() })]
    offsets: Vec<u16>,
    #[br(try_calc = s.stream_position())]
    data_offset: u64,
}

impl Object {
    pub fn get<R: Read + Seek>(&self, source: &mut R, index: usize) -> Option<BinResult<Item>> {
        self.offsets.get(index).map(|offset| {
            let offset = self.data_offset + u64::from(*offset);
            source.seek(SeekFrom::Start(offset))?;
            Item::read(source)
        })
    }
}

Structs

Traits

Functions

  • Creates a parser that reads a collection of values from an iterator of file offsets using the BinRead implementation of Value.
  • Creates a parser that reads a collection of values from an iterator of file offsets using the given parser function.

Type Aliases