The whole design and the documentation around endianess and how they interact with `ctx` is confusing as heck
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "big")]
pub struct IpHeader {
#[deku(bits = 4)]
version: u8,
#[deku(ctx = "*version")]
header: IpSubHeader,
}
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "big", ctx = "version: u8", id = "version")]
pub enum IpSubHeader {
#[deku(id = 4)]
Ipv4(Ipv4Header),
#[deku(id = 6)]
Ipv6(Ipv6Header),
}
// type definition for Ipv4Header and Ipv6Header
Every thing is specified with endian = "big", yet I'm still getting these errors:
mismatched types
expected type `u8`
found tuple `(Endian, u8)`
mismatched types
expected `()`, found `Endian`
After reading through the documentation, and reading through several issues with the same error, I still didn't know that:
- you HAVE TO add a
ctx = "_: Endian"to receive the endian setting from the parent struct - At the time of writing this comment, I still don't know if child structs AUTOMATICALLY inherit endianess from parent if not specified in child struct, aka without the
endian = "endian".
- Please FIX the doc. Actually SPELL IT OUT that you have to add a
ctx = "_: Endian"in child struct when you specifyendian = XXXin parent struct. And SPELL IT OUT that whether child struct automatically inherit endianess from parent struct. - Please consider using a struct to receive ctx, so that the user can choose to skip parameters they don't care about.
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "big", ctx = "{ version: u8 }", id = "version")]
pub enum IpHeader {
#[deku(id = 4)]
Ipv4(Ipv4Header),
#[deku(id = 6)]
Ipv6(Ipv6Header),
}
Or just
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
// #[deku(endian = "big", ctx = "_: Endian")] // I don't care about specifying endianness on this level and I don't want to set ctx manually, just inherit from parent
pub struct Ipv4Header {
#[deku(bits = 4)]
pub ihl: u8, // Internet Header Length
#[deku(bits = 6)]
pub dscp: u8, // Differentiated Services Code Point
#[deku(bits = 2)]
pub ecn: u8, // Explicit Congestion Notification
pub length: u16, // Total Length
pub identification: u16, // Identification
#[deku(bits = 3)]
pub flags: u8, // Flags
#[deku(bits = 13)]
pub offset: u16, // Fragment Offset
pub ttl: u8, // Time To Live
pub protocol: u8, // Protocol
pub checksum: u16, // Header checksum
pub src: Ipv4Addr, // Source IP Address
pub dst: Ipv4Addr, /* Destination IP Address */
}
Here is the actual working code if you want to explicitly specify endianness, for those confused:
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "big")]
pub struct IpPacket {
#[deku(bits = 4)]
version: u8,
#[deku(ctx = "*version")]
header: IpHeader,
}
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "big", ctx = "_: Endian, version: u8", id = "version")]
pub enum IpHeader {
#[deku(id = 4)]
Ipv4(Ipv4Header),
#[deku(id = 6)]
Ipv6(Ipv6Header),
}
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "big", ctx = "_: Endian")]
pub struct Ipv4Header {
#[deku(bits = 4)]
pub ihl: u8, // Internet Header Length
#[deku(bits = 6)]
pub dscp: u8, // Differentiated Services Code Point
#[deku(bits = 2)]
pub ecn: u8, // Explicit Congestion Notification
pub length: u16, // Total Length
pub identification: u16, // Identification
#[deku(bits = 3)]
pub flags: u8, // Flags
#[deku(bits = 13)]
pub offset: u16, // Fragment Offset
pub ttl: u8, // Time To Live
pub protocol: u8, // Protocol
pub checksum: u16, // Header checksum
pub src: Ipv4Addr, // Source IP Address
pub dst: Ipv4Addr, /* Destination IP Address */
}
#[derive(Debug, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "big", ctx = "_: Endian")]
pub struct Ipv6Header {
#[deku(bits = 6)]
pub dscp: u8, // Differentiated Services Code Point
#[deku(bits = 2)]
pub ecn: u8, // Explicit Congestion Notification
#[deku(bits = 20)]
pub fl: u32, // flow label
pub length: u16,
pub protocol: u8,
pub hl: u8, // hop limit
pub src: Ipv6Addr,
pub dest: Ipv6Addr,
}
@hechang27-sprt thanks for the feedback. Agreed, the docs could use some love, the ctx system is particularly confusing.
Contributions are appreciated.
a good starting point would be expanding on the context docs in lib.rs or extending the endian-ness/ctx here: https://github.com/sharksforarms/deku/blob/36fc010a8714cf0f7e7954e9b08b19b95e5e94f4/src/attributes.rs#L118-L168
Just to add - it's not just endianness, probably any customization. I spent a lot of time debugging an issue that stemmed from me only having ctx = "order: Order" on a child type - I thought that's enough to reuse parent's bit order, but the parsing kept failing until I added an explicit bit_order = "lsb" in the child too.
Feels pretty weird to have to duplicate it in two forms, but perhaps docs would clear it up.