1个不稳定版本
0.1.0 | 2024年2月15日 |
---|
#30 在 #transmute
在 2 个crate中使用(通过 packbytes)
21KB
400 行
将结构体转换为和从固定大小的堆栈上的字节数组表示形式。
您可以使用它们从 std::io::Reader
中读取结构体或将它们写入 std::io::Writer
,只需一个 read
或 write
调用即可。
动机
当将结构化数据读入Rust结构体(如文件的头信息)时,人们可能会想简单地这样做
use std::io;
struct MyStruct {
version: u8,
kind: u16,
matrix: [i32; 16]
}
// Do not do this
fn read_my_struct<R: io::Read>(reader: &mut R) -> io::Result<MyStruct> {
let mut bytes = [0; std::mem::size_of::<MyStruct>()];
reader.read_exact(&mut bytes)?;
Ok(unsafe { std::mem::transmute(bytes) })
}
使用 unsafe
是不行的!Rust结构体的内存对齐通常在字段之后包含一些填充,而文件中的数据是紧密打包的,一个接一个。因此,只有在结构体是 repr(packed)
时才安全,这不仅对性能不利,还可能导致某些平台上的未定义行为。此外,数据的 字节序 必须与平台的字节序相匹配。
此crate允许通过将结构体转换为和从打包的字节表示形式来安全地进行前面的操作(不使用 unsafe
),这是通过 ToBytes
和 FromBytes
特性来完成的,这些特性可以自动派生到大多数结构体中。比较
use std::io;
use packbytes::{FromBytes, ByteArray};
#[derive(FromBytes)]
struct MyStruct {
version: u8,
kind: u16,
matrix: [i32; 16]
}
fn read_my_struct<R: io::Read>(reader: &mut R) -> io::Result<MyStruct> {
let mut bytes = [0; <MyStruct as FromBytes>::Bytes::SIZE];
reader.read_exact(&mut bytes)?;
Ok(MyStruct::from_bytes(bytes))
}
可以将 from_bytes
和 to_bytes
方法视为将结构体从其本机内存表示形式解包和打包。
为了方便,提供了 read_packed
和 write_packed
方法来读取和写入。
当不是每个字节序列都表示有效数据时(例如,当字段只能获得一组很小的值时),可以使用特性 TryFromBytes
。
字节序
默认情况下,FromBytes
和 ToBytes
继承宏假定数据应该以小端序存储,这也是 from_bytes
和 to_bytes
方法所使用的。您可以通过设置属性 #[packbytes(be)]
以大端序或 #[packbytes(ne)]
以平台本地字节序来更改这一点。
no_std
支持
除了便捷方法外,这个 crate 中的所有内容都不需要 std
。可以关闭 std
功能。实际上,由于所有操作都在堆栈上执行,甚至不需要 alloc
。
lib.rs
:
packbytes
crate 的继承宏。
依赖项
~275–730KB
~17K SLoC