如何将 C 结构反序列化为 Rust 结构

时间:2021-07-29 23:59:03

标签: c rust

假设在 C 中存在这样的内核头文件:

typedef struct {
  uint32_t type;
  uint32_t length;
  uint32_t extra;
  uint32_t flags;

  uint32_t reserved0;
  uint32_t reserved1;

  uint32_t magic;
  uint32_t crc32;
} zbi_header_t;

也就是说,内核镜像的第一个字节就是这个结构体。

为了将这个头文件解析成 Rust,我有这样的东西:

#[derive(Debug)]
#[repr(C)]
pub struct ZbiHeader {
    zbi_type: u32,
    length: u32,
    extra: u32,
    flags: u32,
    reserved0: u32,
    reserved1: u32,
    magic: u32,
    crc32: u32,
}

请注意,我添加了 repr(C)

将大小为 &[u8] 的切片 sizeof(zbi_header_t) 转换为该结构的最佳 Rust 方法是什么?

Can I take a byte array and deserialize it into a struct? 谈到对齐问题,但我们会在这里讨论吗?

在这种所有值都是 uint32_t 的特殊情况下,我认为编译器不会做任何填充,但我不知道这是否有保证,而且我正在考虑如果中间有一个 u8 会怎样。可能是编译器总是禁用此文件的填充?顺便说一句,标题来自 https://fuchsia.googlesource.com/fuchsia/+/6d0df1b8f7cbadf04553501f995156b224dd6347/zircon/system/public/zircon/boot/image.h#102 但我找不到任何规则说它应该忽略填充等。

2 个答案:

答案 0 :(得分:0)

填充不会成为问题 - 因为您有 #[repr(C)],所以 Rust ZbiHeader 的表示将与 C zbi_header_t、填充和所有的表示相匹配。

至于对齐问题,这完全取决于您如何获得 &[u8] 字节切片。如果您知道它是一个 zbi_header_t 转换为字节指针的事实,那么该指针应该对齐良好。如果您只是从文件输入缓冲区中获取一个随机的 &[u8] 切片,那么您就不必担心了。

答案 1 :(得分:0)

有多种方法可以做到这一点。最简单的方法是如果已知字节切片对齐良好。

let header:&ZbiHeader = unsafe{ &*slice.as_ptr().cast() };

如果字节切片未对齐,事情会变得更复杂一些。在这种情况下,您必须改用 ptr::read_unaligned

let header:ZbiHeader = unsafe{ slice.as_ptr().cast().read_unaligned() };

我相信您显示的结构没有任何填充,并且具有与 [u32;8] 相同的内存表示。 Look here for more about the C's struct layout. 如果您的结构确实有填充,则它不会被初始化,因此您不应使用 &[u8],而应使用 &[MaybeUninit<u8>]。或者,如果可能,传递结构本身或指向它的指针而不是字节数组。

相关问题