sharksforarms / deku

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

Some problems while reading bytes 4-by-4 from a buffer #260

Closed Dentrax closed 2 years ago

Dentrax commented 2 years ago

My full working code is the following:

use deku::prelude::*;

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "big")]
pub struct Packet {
    pub foo: bool,
    #[deku(bits = "32")]
    pub bar: u32,
    #[deku(bits = "32")]
    pub baz: f32,
    #[deku(bits = "16")]
    pub qux: u16,
}

fn main() {
    // represents a UDP packet
    // each line represents different variable on struct
    const BUFFER: &[u8] = &[
        0x01, 0x00, 0x00, 0x00, // bool - foo
        0x05, 0x00, 0x00, 0x00, // u32  - bar
        0x07, 0x00, 0x00, 0x00, // f32  - baz
        0x09, 0x00, 0x00, 0x00, // u16  - qux
    ];

    let (_, val) = Packet::from_bytes((BUFFER.as_ref(), 0)).unwrap();

    assert_eq!(val.foo, true);  // BUFFER[0..4],    expected: true, got: true
    assert_eq!(val.bar, 5);     // BUFFER[4..8],    expected: 5, got: 5
    assert_eq!(val.baz, 7_f32); // BUFFER[8..12],   expected: 7, got: 1e-44
    assert_eq!(val.qux, 9);     // BUFFER[12..16],  expected: 9, got: 0
}

Simply, I want to achieve the following scenario using deku:

foo: buf[0..4]
bar: buf[4..8]
baz: buf[8..12]
qux: buf[12..16]

Problems

I'm getting the following error:

Parse("too much data: container of 16 bits cannot hold 32 bits")

This is expected. But what I want here is that it should read 16 bits anyway, and the remaining 16 bits should be filled with 0. By doing so, I'm able to continue to read 4 bytes from the buffer, each time.

Using #[deku(bits = "16")] breaks packet order, since each variable consumes 4 bytes on a UDP packet. That's why I have to read 32 bit for each time.

For example, val.baz returns 1e-44. But I expect 7 instead. Because of: 0x07, 0x00, 0x00, 0x00

What would your advices? Thanks in advance!

wcampbell0x2a commented 2 years ago

At first glance, a bool only reads in a u8. Not at my computer, but I think this is correct.

const BUFFER: &[u8] = &[
        0x01,  // bool - foo
        0x00, 0x00, 0x00, 0x05, // u32  -bar
        0x00, 0x00, 0x00, 0x07, // u32  -baz
        0x00, 0x00, // u16  -qux
        0x00, 0x09, 0x00, 0x00, 0x00,
    ];
Dentrax commented 2 years ago

Actually my BUFFER represents a UDP packet. That's why I put 4 bytes for each line. I should not edit this; instead, i should find a way out to parse the buffer into struct without lose 4 byte variable alignment.

wcampbell0x2a commented 2 years ago

There is a very WIP project here for some UDP packet handling: https://github.com/sharksforarms/hatchet/blob/master/src/layer/udp/mod.rs

sharksforarms commented 2 years ago

@Dentrax sorry for the delay. Here's an example below using the pad attributes and a 4 byte bool. Also is it possible your endian-ness is little?

Also you're making assumptions around floats, floats are stored very differently in memory. For example, 7_f32

See IEEE754

image

https://www.h-schmidt.net/FloatConverter/IEEE754.html

Float example:

#[derive(Debug, DekuRead, DekuWrite, PartialEq)]
pub struct Test(f32);

fn main() {
    let t = Test(7_f32);
    assert_eq!(vec![0x00, 0x00, 0xe0, 0x40], t.to_bytes().unwrap());
    assert_eq!(Test(7_f32), Test::try_from(t.to_bytes().unwrap().as_ref()).unwrap());
}

Original example

use deku::prelude::*;

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "little")]
pub struct Packet {
    #[deku(bytes = "1", pad_bytes_after = "3")]
    pub foo: bool,
    // #[deku(bytes = "4")] // optional, u32 is 4 bytes
    pub bar: u32,
    #[deku(bytes = "2", pad_bytes_after = "2")]
    pub baz: u32,
    // #[deku(bytes = "2")] // optional, u16 is 4 bytes
    pub qux: u16,
}

fn main() {
    // represents a UDP packet
    // each line represents different variable on struct
    const BUFFER: &[u8] = &[
        0x01, 0x00, 0x00, 0x00, // bool - foo
        0x05, 0x00, 0x00, 0x00, // u32  - bar
        0x07, 0x00, 0x00, 0x00, // f32  - baz
        0x09, 0x00, 0x00, 0x00, // u16  - qux
    ];

    let (_, val) = Packet::from_bytes((BUFFER.as_ref(), 0)).unwrap();

    assert_eq!(val.foo, true);  // BUFFER[0..4],    expected: true, got: true
    assert_eq!(val.bar, 5);     // BUFFER[4..8],    expected: 5, got: 5
    assert_eq!(val.baz, 7); // BUFFER[8..12],   expected: 7, got: 1e-44
    assert_eq!(val.qux, 9);     // BUFFER[12..16],  expected: 9, got: 0
}

Let me know if you have further questions! Closing for now.