Need to use bincode to deserealize field which size depends on value from another field

I have an issue with deserealizing the struct with field that contains dynamic size (to be exactly, size of this field should be received from another field of same struct):

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Response {
    server_ephemeral: [u8; 32],
    g_len: u8,
    #[serde(skip)]
    g: [u8; g_len as usize],
    n_len: u8,
    #[serde(skip)]
    n: Vec<u8>,
    salt: [u8; 32],
}

let decoded: Response = bincode::deserialize(&v[..]).unwrap();

the code above should be replacement of next code:

let mut reader = Cursor::new(input.data.as_ref().unwrap()[2..].to_vec());

let mut server_ephemeral = vec![0u8; 32];
reader.read_exact(&mut server_ephemeral)?;
let g_len = reader.read_u8()?;
let mut g: Vec<u8> = vec![0u8; g_len as usize];
reader.read_exact(&mut g)?;
let n_len = reader.read_u8()?;
let mut n: Vec<u8> = vec![0u8; n_len as usize];
reader.read_exact(&mut n)?;
let mut salt = vec![0u8; 32];
reader.read_exact(&mut salt)?;

but for now it's not clear if bincode can handle such cases. Could somebody explain if this possible ?

to start, the following code does not compile:

struct Response {
    server_ephemeral: [u8; 32],
    g_len: u8,
    g: [u8; g_len as usize], // you can't use a variable as the length of an array
    n_len: u8,
    n: Vec<u8>,
    salt: [u8; 32],
}

you should just use a Vec, or if you don't want the complexity of a vec: a Box<[u8]>.

Second, if deriving those traits isn't working, you can implement them yourself.

bincode serializes collections with a length prefix. If you control the data format you can just replace each length / array pair of fields with a single Vec.

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Response {
    server_ephemeral: [u8; 32],
    g: Vec<u8>,
    n: Vec<u8>,
    salt: [u8; 32],
}

fn main() {
    let value = Response {
        server_ephemeral: [100; 32],
        g: vec![1, 2, 3, 4],
        n: vec![5, 6, 7, 8],
        salt: [1; 32],
    };

    let data = bincode::serialize(&value).unwrap();

    let result = bincode::deserialize(&data).unwrap();

    assert_eq!(value, result)
}

Rust doesn't have any way to support variable size structs the way you would require with your original struct.

You could create a custom type that wraps an array with a fixed maximum size and serializes / deserializes only the parts that are used. That is almost certainly unnecessary complexity here though.

3 Likes

Serde's data model doesn't allow that. Bincode is not a general-purpose parser for foreign data formats, but a parser for its own format, with lengths specified in bincode's way. The variable-length field in bincode could be Vec<u8>.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.