#byte #fixed-size #packed #endian #binary #transmute #binary-data

no-std packbytes

将结构体转换为和从打包表示形式 - 堆栈上固定大小的字节数组

2个不稳定版本

0.2.0 2024年3月8日
0.1.0 2024年2月15日

#671 in 数据结构

Download history 10/week @ 2024-03-14 21/week @ 2024-03-28 6/week @ 2024-04-04 1/week @ 2024-05-16 1/week @ 2024-05-23

每月 91 次下载
mlv 中使用

MIT 许可证

27KB
617

crates.io | 文档

将结构体转换为和从打包表示形式 - 堆栈上固定大小的字节数组。

您可以使用它们从 std::io::Reader 读取结构体,或者通过单个 readwrite 调用将它们写入 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)执行前面的操作,通过将结构体转换为和从打包的字节数据表示形式。这是通过 ToBytesFromBytes 特征完成的,这些特征可以为大多数结构体自动派生。比较

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_bytesto_bytes 方法可以被认为是将结构体从其原生内存表示形式解包和打包的过程。

为了方便读写,提供了 read_packedwrite_packed 方法。

当不是每个字节序列都代表有效数据(例如,当字段只能达到一小组值时),可以使用特质 TryFromBytes

字节序

默认情况下,FromBytesToBytes 泛型宏假设数据应按小端序存储,这也是 from_bytesto_bytes 方法使用的。您可以通过设置属性 #[packbytes(be)] 来设置为大端序或 #[packbytes(ne)] 来设置为平台原生字节序。

no_std 支持

除了便利方法外,此包中的所有内容都不需要 std。可以关闭 std 功能。事实上,由于所有操作都在栈上发生,甚至不需要 alloc

依赖

~320–780KB
~19K SLoC