5个版本 (2个稳定版)
1.0.1 | 2024年6月8日 |
---|---|
1.0.0 | 2023年10月24日 |
0.2.0 | 2023年7月13日 |
0.1.1 | 2023年7月9日 |
0.1.0 | 2023年7月9日 |
#115 在 嵌入式开发
被用于 fry
23KB
132 行
include_data - 直接将类型化数据包含到您的可执行文件中
有时,您希望直接在可执行文件中包含数据,但又不希望将该数据转换为Rust代码来执行static
初始化。这在嵌入式环境中非常有用,或者如果您有一些(通常相对较小)始终需要的数据,并且不想从文件系统中加载它并作为单独的文件分发。
Rust标准库(和核心库)包含用于此目的的&[u8; N]
的宏include_bytes
。此宏将为您提供对包含数据的文件中的二进制数组的静态引用:也就是说,一个&[u8]
。
但是,如果您想使用静态数据,通常希望它具有特定的类型,而不仅仅是&[u8]
。例如,您可能知道包含的文件是f64
的序列,或UTF-32文件,或某种自定义类型。这个crate提供了用于类型化编译时数据包含的宏。这由两个主要宏提供
include_data
- 输出任何类型include_slice
- 对于任何T
,输出一个&[T]
切片,这是合理的
这个crate是无标准库的(no_std
)并且也没有使用alloc
。
用法
这个库可以与任何实现了bytemuck::AnyBitPattern
的类型无缝工作。这包括
- 原始数值类型(如
u16
、i32
、f64
等) - 原始数值类型的数组(例如
[f32; N]
)
例如
static MY_INTEGER: i32 = include_data!("../tests/test_data/file_exactly_4_bytes_long");
static SOME_TEXT: &[u32] = include_slice!(u32, "../tests/test_data/some_utf-32_file");
const FOUR_BYTES: [u8; 4] = include_data!("../tests/test_data/file_exactly_4_bytes_long");
请注意,include_data
可以赋值给const
,而include_slice
则不行。
为原始数值类型提供了include_slice
的别名,使用它们是个人偏好的问题。例如
static SOME_TEXT: &[u32] = include_u32s!("../tests/test_data/some_utf-32_file");
与自定义类型的用法
您可以将数据包含在任何您喜欢的自定义类型中。如果您的自定义类型满足bytemuck::AnyBitPattern
的要求,那么这是最好的方式,在这种情况下,您只需使用include_data
即可。
#[repr(C)]
#[derive(Copy, Clone)]
struct Foo {
integer: u16,
pair: [u8; 2],
}
// Safety: the requirements for `AnyBitPattern` have been manually checked.
unsafe impl bytemuck::Zeroable for Foo {}
unsafe impl bytemuck::AnyBitPattern for Foo {}
static FOO_DATA: Foo = include_data!("../tests/test_data/file_exactly_4_bytes_long");
或者,如果您的类型不能实现bytemuck::AnyBitPattern
(特别是如果它是一个您无法控制的 foreign 类型),则可以使用include_unsafe
。在这种情况下,您必须保证包含的文件对于目标类型是有效的。这可能取决于宿主平台、编译器版本和编译器配置文件(等等):请记住,Rust 没有稳定的 ABI。显然,这是非常不安全的,应该尽量避免。
#[repr(C)]
struct StructWithPadding {
byte: u8,
two_bytes: u16,
}
// Safety: we guarantee that the included file contains bytes which are
// a valid bit-pattern for our struct, when compiled on this host.
static BAR_DATA: StructWithPadding = unsafe { include_unsafe!("../tests/test_data/file_exactly_4_bytes_long") };
安全性
此crate导出的所有宏都是安全的,除了include_unsafe
(当然,前提是bytemuck::AnyBitPattern
的实现是可靠的)。如果输入文件大小与目标类型不匹配(或者对于切片来说不能被它整除),或者文件无法读取,编译将失败。
include_unsafe
是非常不安全的,应该非常谨慎地使用。请参阅文档以获取完整详细信息。
特定平台的操作
多字节序列的解释取决于机器的字节序。在这些宏的情况下,多字节序列将被解释为目标类型的字节序,而不是编译宿主机的字节序。
传递给这些宏的路径的解释是宿主平台的特定操作,并且与include_bytes
相同。
MSRV
最低支持的Rust版本是1.64.0。
请注意,这个crate是对编译器的固定版本进行测试的,仅仅是因为许多测试检查了确切的错误消息。用于测试目的的当前固定版本可以在rust-toolchain.toml
中找到。
替代方案
根据您想要实现的目标,这个crate可能不是最佳选择。以下是一些可能的替代方案,具体取决于情况
static_toml
:如果包含的数据适合在toml
规范中,这个crate将在编译时解析它,并将结果包含为静态、类型安全的数据。const_gen
:一个工具,帮助您使用build.rs
进行编译时常量值的代码生成。比include_data
更复杂和冗长,但更灵活。- Rust标准库中提供了
OnceLock
和LazyLock
,这两个结构体都可以用于将复杂值赋给static
,但这个过程是在运行时进行的,并且会有一定的运行时开销。如果你的类型需要运行时构建,这些结构体是非常好的选择,但对于“简单类型数据”值来说,使用include_data
会更便宜。
如果你知道其他选项,请提交一个issue或打开一个PR。
现有技术
这个crate所使用的技巧在Jack Wrenn的博客文章“include-transmute”中有所介绍。其中一些技巧是Jack原创的,而其他一些则可以从该文章链接的论坛线程中找到。如果你是这些讨论的参与者,或者在该领域有先前的工作,请与我联系。我也感谢Jack对这篇crate早期草稿的评论。
依赖项
约135KB