sharksforarms / deku

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

Is there a way to get the current index of a sub-parser in a vec in the parent parser? #363

Open vext01 opened 1 year ago

vext01 commented 1 year ago

Best explained by example:

struct Parent {
  num: usize,
  #[deku(count = "num")]
  subs: Vec<Sub>
}

struct Sub {
  idx: usize,
  ...
}

How can I get each Sub::idx to be set to the index of the Sub in Parent::subs?

Cheers

vext01 commented 1 year ago

I thought this may be possible with ctx, but it can only see previously parsed fields.

wcampbell0x2a commented 1 year ago

You could achieve it with a reader.

We could add a __deku variable that could be used.

vext01 commented 1 year ago

How would I access the parent parser in a reader though?

wcampbell0x2a commented 1 year ago
use deku::{
    bitvec::{BitSlice, Msb0},
    prelude::*,
};

#[derive(DekuRead, Debug, PartialEq, Eq)]
struct Parent {
    #[deku(bytes = "1")]
    num: usize,
    #[deku(reader = "Parent::read(deku::rest, *num)")]
    #[deku(count = "num")]
    subs: Vec<Sub>,
}

impl Parent {
    fn read(
        rest: &BitSlice<u8, Msb0>,
        count: usize,
    ) -> Result<(&BitSlice<u8, Msb0>, Vec<Sub>), DekuError> {
        let mut subs = Vec::with_capacity(count);
        let mut left_rest = rest;
        for n in 0..count {
            let (l_rest, mut sub) = Sub::read(left_rest, ())?;
            sub.idx = n;
            println!("{:?}", sub);
            subs.push(sub);
            left_rest = l_rest;
        }
        Ok((left_rest, subs))
    }
}

#[derive(DekuRead, Debug, PartialEq, Eq)]
struct Sub {
    #[deku(skip)]
    idx: usize,
    a: u8,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_00() {
        let bytes: &[u8] = &[0x02, 0x0f, 0xf0];
        let parent = Parent::try_from(bytes).unwrap();
        assert_eq!(
            parent,
            Parent {
                num: 2,
                subs: vec![Sub { idx: 0, a: 0x0f }, Sub { idx: 1, a: 0xf0 }]
            }
        )
    }
}
vext01 commented 1 year ago

Thanks. Right, so you have to manually parse the sub-struct.

It would indeed be useful to have some way to do it without that burden.

perhaps for a Vec field, you pass down some implicit context or similar?