假设在 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 但我找不到任何规则说它应该忽略填充等。
答案 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>]
。或者,如果可能,传递结构本身或指向它的指针而不是字节数组。