sharksforarms / deku

Declarative binary reading and writing: bit-level, symmetric, serialization/deserialization
Apache License 2.0
1.05k stars 54 forks source link

Replacing indices with references during parsing. #383

Open vext01 opened 6 months ago

vext01 commented 6 months ago

Hi,

We have a parser for a compiler IR where we use indices for the (immutable) on-disk serialisation format, and we read it in with Deku.

We'd like to be able to convert the indices to immutable references for ease of use once parsing is complete.

I mocked this up:

#![allow(dead_code)]

use deku::prelude::*;

#[deku_derive(DekuRead)]
#[derive(Debug)]
struct TopLevel {
    num_foos: usize,
    #[deku(count = "num_foos")]
    foos: Vec<Foo>,

    num_bars: usize,
    #[deku(count = "num_bars")]
    bars: Vec<Bar>,
}

#[deku_derive(DekuRead)]
#[derive(Debug)]
struct Foo {
    val: u8,
}

#[deku_derive(DekuRead)]
#[derive(Debug)]
struct Bar {
    #[deku(temp)]
    foo_idx: usize,
    // XXX: How to we convert the `foo_idx` to a reference to foos[0] in the super-struct?
    //foo_ref: &'a Foo,
}

fn main() {
    // Assume little-endian.
    let data: Vec<u8> = vec![
        // num_foos=1
        1, 0, 0, 0, 0, 0, 0, 0,
        // foos[0]
        // val
        0xaa,
        // num_bars=1
        1, 0, 0, 0, 0, 0, 0, 0,
        // bars[0]
        // foo_idx
        0, 0, 0, 0 ,0, 0, 0, 0
    ];
    let (_rest, mut val) = TopLevel::from_bytes((data.as_ref(), 0)).unwrap();

    dbg!(&val);
}

Is there a way to convert the foo_idx into a reference to foos[0] during parsing?

I can imagine an API that perhaps looks like this (or similar):

#[deku_derive(DekuRead)]
#[deku(endian = "big")]
#[derive(Debug)]
struct Bar {
    #[deku(temp)]
    foo_idx: usize,
    #[deku(skip, map = "|_| -> &deku_parent.foos[foo_idx]")]
    foo_ref: &Foo,
}

Supposing deku had the deku_parent thing (which can probably be done already, albeit verbosely, with ctx), something tells me that this wouldn't sit well with the borrow checker. The parent struct is still being parsed and therefore needs to be mutable, but we are making immutable references to it.

skipping foo_ref also requires &Foo to have a Default, which makes no sense. Maybe you'd have to use raw pointers to allow a NULL pointer...

Another option is to not to the conversion during parsing, but to post-process it, or generate some kind of "index to reference" map after the fact.

But anyway, I'm wondering, Is there an idiom for doing this kind of thing with deku?

Thanks