我有一个从磁盘读取
Vec<u8>
的函数,我想将其解释为 ext2 超级块(这是一个打包结构)。
所以我用了:
unsafe {&*(raw_super.as_ptr() as *const Super block)}
问题是当函数返回时
Vec
被丢弃,所以超级块只有0s
我想要一种将 Vec 转换为超级块并拥有超级块中的数据的方法,如果可能的话,需要小的成本抽象
正如其他人之前在评论中指出的那样,您必须首先断言几个先决条件:
u8
和SuperBlock
的对齐方式是相同的(但是u8
和#[repr(packed)]
结构总是满足packed
隐式地packed(1)
)Vec<u8>
(它是len()
)的大小与SuperBlock
ext2
因此您可以将
Vec<u8>
转换为 Box<SuperBlock>
,如下所示:
// `C` so the order of the fields in memory is known
// `packed` so the alignment is 1 and there is no padding
#[repr(C, packed)]
pub struct SuperBlock {
foo: u16,
bar: u32,
}
// ext2 superblocks are always little endian, so this works only for those platforms
#[cfg(target_endian = "little")]
impl SuperBlock {
/// ## Errors
/// Returns the vector unchanged when it's length isn't exactly the size of a `SuperBlock`
pub fn from_vec(v: Vec<u8>) -> Result<Box<Self>, Vec<u8>> {
if v.len() != std::mem::size_of::<SuperBlock>() {
return Err(v);
}
// SAFETY:
// `#[repr(packed)] structs have an alignment of 1, the same as u8
// v.len() and size_of::<SuperBlock>() are the same as asserted above
Ok(unsafe { Box::from_raw(Box::into_raw(v.into_boxed_slice()).cast::<SuperBlock>()) })
}
}
从技术上讲,这不会drop
Vec
,而是为返回的Box<SuperBlock>
重用相同的分配
bytemuck
板条箱将 Vec<u8>
中的数据安全地转换为 SuperBlock
。例如:
use bytemuck::{Pod, Zeroable};
#[derive(Copy, Clone, Pod, Zeroable)]
#[repr(C, packed)]
pub struct SuperBlock {
foo: u16,
bar: u32,
}
pub struct OwnedSuperBlock {
data: Vec<u8>,
}
impl OwnedSuperBlock {
pub fn new(data: Vec<u8>) -> Self {
OwnedSuperBlock { data }
}
pub fn as_super_block(&self) -> &SuperBlock {
bytemuck::from_bytes(&self.data)
}
}
由于
bytemuck::Pod
特征和派生宏提供的保证,此代码不使用不安全。它们确保将向量中的字节转换为 &SuperBlock
是安全的,同时还检查字节大小是否正确。最后,整个事情是零复制的,并且涉及最小的运行时成本(仅是数据大小检查)。