#data #static #byte #data-file #binary-data #casting #include

无std include_data

直接将类型化数据包含到您的可执行文件中

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

MIT 许可证

23KB
132

include_data - 直接将类型化数据包含到您的可执行文件中

github badge docs.rs badge Crates.io badge licence badge

有时,您希望直接在可执行文件中包含数据,但又不希望将该数据转换为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的类型无缝工作。这包括

  • 原始数值类型(如u16i32f64等)
  • 原始数值类型的数组(例如[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标准库中提供了OnceLockLazyLock,这两个结构体都可以用于将复杂值赋给static,但这个过程是在运行时进行的,并且会有一定的运行时开销。如果你的类型需要运行时构建,这些结构体是非常好的选择,但对于“简单类型数据”值来说,使用include_data会更便宜。

如果你知道其他选项,请提交一个issue或打开一个PR。

现有技术

这个crate所使用的技巧在Jack Wrenn的博客文章“include-transmute”中有所介绍。其中一些技巧是Jack原创的,而其他一些则可以从该文章链接的论坛线程中找到。如果你是这些讨论的参与者,或者在该领域有先前的工作,请与我联系。我也感谢Jack对这篇crate早期草稿的评论。

依赖项

约135KB