deku icon indicating copy to clipboard operation
deku copied to clipboard

Variable length ID, e.g. 1 bytes and sometimes 3 bytes

Open Ciantic opened this issue 10 months ago • 3 comments

I'm working on Bluetooth packets, easiest way to parse would be to match the packets with variable length IDs such as:

  • LeMeta packets with 3 bytes (the id, length and sub id)
  • Normal packets with 2 bytes (the id and length)

However I can't figure out how to make such a ID type that could be used for this.

I've tried &'static [u8] but it complains:

no function or associated item named from_reader_with_ctx found for reference &'static [u8] in the current scope function or associated item not found in &[u8]

use deku::prelude::*;

// This fails because DekuRead is not implemented for `&'static [u8]`
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(id_type = "&'static [u8]")]
pub enum HciEventMsg {
    #[deku(id = "&[0x03, 0x13, 0x01]")]
    LeConnectionComplete {
        status: HciStatus,
        connection_handle: u16,
        role: Role,
        peer_address_type: AddressType,
        peer_address: [u8; 6],
        connection_interval: u16,
        peripheral_latency: u16,
        supervision_timeout: u16,
        central_clock_accuracy: ClockAccuracy,
    },

    #[deku(id = "&[0x0E, 0x04]")]
    CommandComplete {
        num_hci_command_packets: u8,
        command_opcode: u16,
        status: HciStatus,
    },

    #[deku(id = "&[0x0F, 0x04]")]
    CommandStatus {
        status: HciStatus,
        num_hci_command_packets: u8,
        command_opcode: u16,
    },
    // Other messages...
}

Ciantic avatar Apr 15 '25 12:04 Ciantic

~~Try id_type = [u8; 3].~~ Nevermind you have variable length. I think you'd have to put the SubId in a Enum of its own.

That being said, it would be nice to reference the bytes as you do in the above example.

wcampbell0x2a avatar Apr 15 '25 13:04 wcampbell0x2a

@wcampbell0x2a it makes the structure odd looking, because I want to use the lengths as well, e.g.


#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(id_type = "u8")]
pub enum LeMeta {
    #[deku(id = "0x01")]
    LeConnectionComplete {
        status: HciStatus,
        connection_handle: u16,
        role: Role,
        peer_address_type: AddressType,
        peer_address: [u8; 6],
        connection_interval: u16,
        peripheral_latency: u16,
        supervision_timeout: u16,
        central_clock_accuracy: ClockAccuracy,
    },

    // Other messages
}

#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(id_type = "u8")]
pub enum HciEventMsg {
    #[deku(id = "0x03")]
    LeMeta { 
        len: u8, // This is always 0x13 IF LeConnectionComplete
        le_meta: LeMeta 
    },

    #[deku(id = "0x0E")]
    CommandComplete {
        len: u8, // This is always 0x04
        num_hci_command_packets: u8,
        command_opcode: u16,
        status: HciStatus,
    },

    #[deku(id = "0x0F")]
    CommandStatus {
        len: u8, // This is always 0x04
        status: HciStatus, 
        num_hci_command_packets: u8,
        command_opcode: u16,
    },
    // Other messages...
}

This works, but now the lengths that was easily defined with 0x04 and 0x13 doesn't look great.

I'd like to use the pattern from my original post, it's clearer, these static length values are hard to deal with.

Ciantic avatar Apr 15 '25 13:04 Ciantic

Maybe I can create a wrapper struct:


#[derive(Debug, Clone, PartialEq, Eq, DekuWrite, Hash)]
struct Bytes(&'static [u8]);

impl<'a, Ctx: Copy> DekuReader<'a, Ctx> for Bytes
// where
//     u8: DekuReader<'a, Ctx>,
{
    fn from_reader_with_ctx<R: Read + Seek>(
        reader: &mut Reader<R>,
        inner_ctx: Ctx,
    ) -> Result<Self, DekuError>
    where
        Self: Sized,
    {
        todo!()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(id_type = "Bytes")]
pub enum HciEventMsg {
    #[deku(id = "Bytes(&[0x03, 0x13, 0x01])")]
    LeConnectionComplete {
        status: HciStatus,
        connection_handle: u16,
        role: Role,
        peer_address_type: AddressType,
        peer_address: [u8; 6],
        connection_interval: u16,
        peripheral_latency: u16,
        supervision_timeout: u16,
        central_clock_accuracy: ClockAccuracy,
    },

    #[deku(id = "Bytes(&[0x0E, 0x04])")]
    CommandComplete {
        num_hci_command_packets: u8,
        command_opcode: u16,
        status: HciStatus,
    },

    #[deku(id = "Bytes(&[0x0F, 0x04])")]
    CommandStatus {
        status: HciStatus,
        num_hci_command_packets: u8,
        command_opcode: u16,
    },
    // Other messages...
}

Not sure yet how to implement the DekuReader the types are kind of wonky.

Ciantic avatar Apr 15 '25 13:04 Ciantic