使用动态大小的数据将 'struct' 序列化为 '&[u8]'

Serialize 'struct' with dynamically sized data to '&[u8]'

我正在尝试为结构创建通用序列化方案。按照类似 给出的答案,我有以下设置:

#[repr(packed)]
struct MyStruct {
    bytes: [u8; 4]
}

unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

fn main() {
    let s = MyStruct { bytes: [0u8, 1u8, 2u8, 3u8].to_owned() };
    
    let bytes: &[u8] = unsafe { any_as_u8_slice(&s) };
    
    println!("{:?}", bytes);
}

(playground)

输出:

[0, 1, 2, 3]

这很好用,但是它没有考虑像 Vec<u8> 这样动态调整大小的结构成员,并且它们的大小需要在运行时确定。理想情况下,我想将 Vec<u8> 中的每个元素编码为字节,并添加一个前缀以指示要读取的字节数。

目前我有这个:

#[repr(packed)]
struct MyStruct {
    bytes: Vec<u8>
}

unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

fn main() {
    let s = MyStruct { bytes: [0u8, 1u8, 2u8, 3u8].to_vec() };
    
    let bytes: &[u8] = unsafe { any_as_u8_slice(&s) };
    
    println!("{:?}", bytes);
}

(playground)

输出:

[208, 25, 156, 239, 136, 85, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0]

我假设上面的输出引用了某种指针,但我不确定。

目前,bincode crate 与 serde crate 一起执行此操作,但它将向量的长度序列化为 usize。我宁愿指定它并将长度编码为 u8,如 中所述。不幸的是,这里最好的解决方案是重写 Bincode 库,这让我不得不寻找任何替代解决方案。

编辑

使用 serdebincode 的实现:

use serde::{Serialize};

#[derive(Clone, Debug, Serialize)]
struct MyStruct {
    bytes: Vec<u8>
}

fn main() {
    let s = MyStruct { bytes: [0u8, 1u8, 2u8, 3u8].to_vec() };
    
    let bytes = bincode::serialize(&s).unwrap();
    
    println!("{:?}", bytes);
}

输出:

[4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3]

想要的输出:

[4, 0, 1, 2, 3]

您看到的 Vec 的输出完全符合预期。一个 Vec 有三个元素,一个指针,长度,和它的容量。这是guaranteed by the standard library。在你的情况下,你有指针,长度和容量都是小端的数字 4。

不可能按照您想要的方式将包含 Vec 的结构转换为 &[u8]&[u8] 切片是一块连续的内存,但是,Vec 从根本上说是一种间接寻址,这意味着它的元素不会与结构的其余部分连续存储。
至少,您需要将字节收集到 Vec<u8> 或类似的文件中,因为您需要从多个地方复制数据。

如果 bincode 的唯一问题是 usize 长度前缀,您可以 configure it to use a variable-length prefix by using the with_varint_encoding 选项。

use bincode::{DefaultOptions, Options};
use serde::Serialize;

#[derive(Clone, Debug, Serialize)]
struct MyStruct {
    bytes: Vec<u8>,
}

fn main() {
    let s = MyStruct {
        bytes: [0u8, 1u8, 2u8, 3u8].to_vec(),
    };

    let bytes = DefaultOptions::new()
        .with_varint_encoding()
        .serialize(&s);

    println!("{:?}", bytes);
}

输出:

[4, 0, 1, 2, 3]