44 个版本 (21 个重大更改)
新 0.21.0 | 2024年8月16日 |
---|---|
0.20.0 | 2024年7月25日 |
0.19.3 | 2024年5月30日 |
0.18.0 | 2024年3月20日 |
0.0.2 | 2022年10月14日 |
#53 在 解析器实现
每月94,855 次下载
在 139 个 crate 中使用 (3 个直接使用)
2.5MB
54K SLoC
read-fonts
这个crate负责解析和读取OpenType字体。它旨在是一个高性能实现,适用于诸如 形状 等任务,同时仍然提供一个方便的高级API。
安全性
通过库根部的 #![forbid(unsafe_code)]
属性禁止不安全代码。
代码生成
这个crate中的大部分代码都是自动生成的。这些生成的代码位于 generated
目录中。该目录中的每个文件都通过Rust的 include!
宏包含在 src
目录中的一个模块中。这使我们能够将生成的代码与任何自定义实现代码分离,同时允许它们存在于同一个模块中。
我们生成的代码
除了需要手动处理的某些异常之外,我们对每个我们覆盖的规范部分中的每个 table、record、enum 和 flags 类型生成代码。
表格
所有表都是类型为 TableRef<Marker>
的别名,其中 Marker
是一个结构体,用于指示表的类型。例如,GDEF 表 被定义为 TableRef<GdefMarker>
。 TableRef
本身是字节数组切片的包装器,其中 marker 类型提供了对这些字节的类型化访问。
标记类型只能从特定的字节数组创建,并且始终与该数组相关联。它是通过一个 parse
方法创建的,该方法执行一次验证切片,确保所有预期的字段都存在。这包括检查数组的界限,以及确保存在依赖于表版本的字段。
可变长度和版本相关字段
注意:以下描述的设计尚未与替代方案进行基准测试,可能会更改
对于具有可变长度或仅在某些表版本中存在的字段,标记结构体有一个相应的字段,其中存储了该长度或偏移量。这意味着在运行时不需要再次检查版本或长度。
TableRef 方法
对于每个表,我们在类型 TableRef<Marker>
上定义方法,以提供对该表字段的访问。这些方法使用标记类型来确定给定字段的字节数据范围,然后解释这些字节为适当的类型。
记录
与表不同,表本质上是一组读取到字节数组的方法的集合,而 记录 通常表示为包含大端编码标量类型的简单打包结构体。这意味着,通常,记录是零拷贝类型,可以转换为原始字节。
这种情况的例外是,当一个记录具有可变长度时;在这种情况下,记录仍然是一个简单的结构体,但不能从原始字节转换,必须进行复制。
标志和枚举
对于标志,我们生成一个基于 bitflags!
宏 生成的类型的类型。对于枚举,我们生成一个原始 Rust 枚举。
依赖关系
~0.4–1MB
~22K SLoC