【问题标题】:Serialize 'struct' with dynamically sized data to '&[u8]'使用动态大小的数据将“struct”序列化为“&[u8]”
【发布时间】:2021-09-03 16:27:39
【问题描述】:

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

#[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&lt;u8&gt; 这样动态调整大小的结构成员,并且它们的大小需要在运行时确定。理想情况下,我想将Vec&lt;u8&gt; 中的每个元素编码为字节,并添加一个前缀来指示要读取的字节数。

目前我有这个:

#[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,如this thread 中所述。不幸的是,这里最好的解决方案是重写 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]

【问题讨论】:

  • @kmdreko 那太好了,但我不知道如何更改选项。我编辑了帖子,你能告诉我在哪里可以更改配置吗?

标签: rust serde


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    如果您对bincode 的唯一问题是usize 长度前缀,您可以通过with_varint_encoding 选项configure 使用可变长度前缀。

    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]
    

    【讨论】:

    • 是否可以仅为结构中的某些成员设置with_varint_encoding 选项?更改此配置似乎会更改所有无符号整数的所有序列化,除了 u8
    • @Kevin 我不知道该怎么做,抱歉。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-16
    • 2011-12-02
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 2020-05-14
    • 1970-01-01
    相关资源
    最近更新 更多