2个不稳定版本
0.2.0 | 2024年3月8日 |
---|---|
0.1.0 | 2024年2月15日 |
#671 in 数据结构
每月 91 次下载
在 mlv 中使用
27KB
617 行
将结构体转换为和从打包表示形式 - 堆栈上固定大小的字节数组。
您可以使用它们从 std::io::Reader
读取结构体,或者通过单个 read
或 write
调用将它们写入 std::io::Writer
。
动机
当将结构化数据读取到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
支持
除了便利方法外,此包中的所有内容都不需要 std
。可以关闭 std
功能。事实上,由于所有操作都在栈上发生,甚至不需要 alloc
。
依赖
~320–780KB
~19K SLoC