11 个版本
| 0.3.0 | 2023年5月2日 |
|---|---|
| 0.2.0 | 2023年4月3日 |
| 0.2.0-rc.8 | 2023年3月30日 |
| 0.2.0-rc.2 | 2023年2月21日 |
| 0.1.4 | 2021年7月21日 |
1361 在 过程宏 中
每月 182 次下载
在 5 个crate中(2 个直接) 使用
94KB
1.5K SLoC
Alkahest - 优秀的序列化库。
Alkahest 是一个快速、无依赖、无开销、无安全风险的基于模式的序列化库。它适用于广泛的使用场景,但特别适合定制高性能网络协议。
基准测试
此基准测试模仿了一些游戏网络协议。
alkahest |
bincode |
rkyv |
speedy |
|
|---|---|---|---|---|
serialize |
10.69 us (✅ 1.00x) |
11.08 us (✅ 1.04x slower) |
12.43 us (❌ 1.16x slower) |
11.13 us (✅ 1.04x slower) |
read |
1.19 us (✅ 1.00x) |
9.19 us (❌ 7.74x slower) |
2.10 us (❌ 1.77x slower) |
1.54 us (❌ 1.30x slower) |
由 criterion-table 创建
另请参阅 基准测试结果(在0.2版本发布前为草稿),来自 https://github.com/djkoloski/rust_serialization_benchmark。
功能
-
基于模式的序列化。Alkahest使用称为
Formula的数据模式来序列化和反序列化数据。因此,可以独立于序列化或反序列化的数据类型来控制数据布局。 -
支持多种公式。整数、浮点数、布尔值、元组、数组、切片、字符串以及使用
derive宏自定义数据布局的用户定义公式。该宏适用于任何复杂性的结构和枚举,并支持泛型。 -
序列化序列无开销。Alkahest支持直接将迭代器序列化为切片公式。不再需要为序列化和立即丢弃而分配一个
Vec。 -
延迟反序列化。Alkahest提供了
Lazy<F>类型来懒加载地反序列化任何公式F。可以使用Lazy稍后执行实际的反序列化。
Lazy<[F]>还可以生成按需反序列化元素的迭代器。
延迟在类型级别上进行控制,可以应用于更大公式中的任何元素。 -
无故障序列化。给定足够大或不断增长的缓冲区,任何实现
Serialize的值都可以无错误地进行序列化。不再需要不必要的解包或解决“序列化失败时该怎么做?”的问题。序列化的唯一错误条件是“数据不匹配”。
计划中的功能
- 可序列化公式描述符
- 兼容性规则
- 用于C和Rust公式描述符代码生成的外部工具。
它是如何工作的。更详细地来说
Alkahest将数据模式定义(即Formula)与序列化和反序列化代码分开。这样做,这个库在可序列化数据类型和反序列化数据类型不同的情况下提供了更好的保证。它还支持从迭代器而不是集合进行序列化,以及将反序列化到延迟包装器,该包装器延迟了昂贵的过程,并且如果值从未被访问,可以完全省略它。用户通过选择适当的Deserialize实现来在类型级别上控制延迟。例如,将反序列化到Vec<T>是急切的,因为Vec<T>使用所有T实例构建,并为它们分配了内存。而alkahest::SliceIter实现了Iterator,并在Iterator::next和其他方法中反序列化元素。它还提供了对任何元素的常数时间随机访问。
灵活性是以仅使用字节切片进行序列化和反序列化以及与某些其他二进制格式相比的序列化数据更大占用空间为代价的。
关于支持密集数据打包的问题仍然开放。可能在类型级别上进行控制是可取的。
错误和恐慌
API的设计原则如下:只要缓冲区足够大,任何值都可以成功序列化。数据不能导致panic,但可能由于特质的实现不当而引发。
在库中没有任何不安全的代码,它生成的任何代码都不包含不安全的代码。在std不是不安全的前提下,不可能出现未定义的行为。
向前和向后兼容性
没有数据模式是永恒不变的。新字段和变体被添加,其他则被弃用和删除。
有一套规则确保公式之间的向前兼容性,还有另一套规则用于向后兼容性。
尚未实现兼容性验证。
向前兼容性
向前兼容性是指能够反序列化使用较新公式序列化的数据。
待办:列出所有规则
向后兼容性
向后兼容性是指使用旧公式序列化的数据能够反序列化的能力。
待办:列出所有规则
公式、序列化和反序列化特性。
该包使用三个基本特性工作。分别是 Formula、Serialize 和 Deserialize。还有一个支持特性 - BareFormula。
Alkahest 提供了用于推导 Formula、Serialize 和 Deserialize 的宏 alkahest。
公式
Formula 特性用于允许类型作为数据模式。任何使用给定公式序列化的值都应该能够使用相同的公式反序列化。仅共享 Formula 类型就可以让模块和包轻松通信。 Formula 规定了二进制数据布局,并且它必须是平台无关的。
理论上,Formula 类型可以来自不同的文件,这为跨语言通信打开了可能性。
Formula 已经为许多类型实现了。原始类型如 bool、整数和浮点类型都实现了 Formula。这不包括 isize 和 usize。取而代之的是,提供了 FixedUsize 和 FixedIsize 类型,其大小由功能标志控制。 注意! 大小和地址序列化为 FixedUsize。如果 usize 值太大,则将其截断。这可能会导致生成损坏的数据并在调试时崩溃。如果在 FixedUsize 中遇到这种情况,请增加其大小。它也适用于元组、数组、切片、Option 和 Vec(后者需要 "alloc" 特性)。
定义新公式的最简单方法是为结构体或枚举推导 Formula 特性。支持泛型,但可能需要在 Serialize 和 Deserialize 推导宏的属性中指定复杂界限。唯一的约束是所有字段都必须实现 Formula。
序列化
Serialize<Formula> 特性用于实现根据特定公式进行的序列化。序列化写入可变字节切片,且不应执行动态分配。任何使用公式序列化的类型的二进制结果必须遵循该公式。最后,如果序列化的原始类型流相同,则二进制结果也应相同。类型可能使用不同的公式进行序列化,从而产生不同的二进制结果。
Serialize 对许多类型实现了。最值得注意的是,对所有原始类型 T(除了 usize 和 isize)实现了 T: Serialize<T> 和 &T: Serialize<T>。另一个重要的实现是 Serialize<F> for I where I: IntoIterator, I::Item: Serialize<F>,允许直接从迭代器和集合中序列化到切片。使用 Ref<F> 的序列化使用 F 的序列化公式,然后存储相对地址和大小。不需要动态分配。
为类型推导 Serialize 将生成 Serialize 实现,公式在属性 #[alkahest(FormulaRef)] 或 #[alkahest(serialize(FormulaRef))] 中指定。通常 FormulaRef 是一个类型。当使用泛型时,它还包含泛型参数和界限。如果没有指定公式,则假定 Self。还应该为类型推导 Formula。不建议为手动实现 Formula 的公式推导 Serialize,因为 Serialize 推导宏生成的代码使用了由 Formula 推导宏生成的非公共项。因此,两者都应该有手动实现或都进行推导。
对于结构,Serialize 推导宏要求所有字段都存在于 Serialize 和 Formula 结构中,并且顺序相同(如果是同一结构,则非常简单)。
对于枚举,Serialize 推导宏检查对于每个变体,在 Formula 枚举中存在相应的变体。变体内容与结构类似进行比较。序列化会插入变体 ID,并将变体作为结构序列化。变体的尺寸可能不同。如果需要,外值序列化会插入填充。
Serialize 可以用于具有枚举类型 Formula 的结构。在这种情况下,应使用 #[alkahest(@variant_ident)] 或 #[alkahest(serialize(@variant_ident))] 来指定变体。然后,Serialize 派生宏将生成类似于此变体是结构 Formula 的序列化代码,除了变体的 ID 将在字段之前序列化。
Serialize 仅当 Formula 也是枚举时才能用于枚举。可序列化的枚举可以省略 Formula 中的某些(或所有)变体。它不能缺少 Formula 中的变体。每个变体随后遵循结构的规则。
为了方便起见,Infallible 为枚举公式实现了 Serialize。
反序列化
使用 Deserialize<'de, Formula> 特性来根据特定公式实现反序列化。反序列化从字节切片中读取,构建反序列化的值。反序列化 不应 执行动态分配,除非需要构建和初始化反序列化值。例如,如果反序列化出非零数量的 Vec<T>,则允许分配。它 不应 过度分配。
类似于 Serialize,alkahest 提供了一系列现成的 Deserialize 特性实现。可以使用原始公式 T 反序列化 From<T> 类型。
可以使用公式 F 反序列化的值也可以使用 Ref<F> 反序列化,它读取地址和长度,然后使用公式 F 继续操作。
Vec<T> 可以使用切片公式进行反序列化。针对 Deserialize<'de, [F]> 的实现,针对 alkahest::SliceIter<'de, T> 类型,该类型实现了 Iterator 并懒加载地反序列化类型 T: Deserialize<'de, F> 的元素。 SliceIter 可被复制,可以从两端迭代,并且可以在常数时间内跳过元素。为了方便,SliceIter 也使用数组公式进行反序列化。
为一个类型推导 Deserialize 将生成 Deserialize 的实现,公式在属性 #[alkahest(FormulaRef)] 或 #[alkahest(deserialize(FormulaRef))] 中指定。 FormulaRef 通常是一个类型。当使用泛型时,它还包含泛型参数和限制。如果没有指定公式,则假定 Self。还应为该类型推导 Formula。不建议为具有手动 Formula 实现的公式推导 Deserialize,因为 Deserialize 推导宏生成的代码使用了由 Formula 推导宏生成的非公开项。因此,两者都应有手动实现或都进行推导。
与 serde 的互操作性
Alkahest 很酷,但 serde 几乎被普遍使用,而且有很好的理由。在设计 Formula 时,可能希望包括支持序列化的现有类型,尤其是如果它来自另一个包。这个包提供了 Bincode 和 Bincoded<T> 公式来覆盖这一点。任何具有 serde::Serialize 实现的类型都可以使用 Bincode 公式进行序列化,自然地,它将使用 bincode 包进行序列化。 Bincoded<T> 是 Bincode 的一个受限版本,它仅适用于 T。
使用示例
// This requires two default features - "alloc" and "derive".
#[cfg(all(feature = "derive", feature = "alloc"))]
fn main() {
use alkahest::{alkahest, serialize_to_vec, deserialize};
// Define simple formula. Make it self-serializable.
#[derive(Clone, Debug, PartialEq, Eq)]
#[alkahest(Formula, SerializeRef, Deserialize)]
struct MyDataType {
a: u32,
b: Vec<u8>,
}
// Prepare data to serialize.
let value = MyDataType {
a: 1,
b: vec![2, 3],
};
// Use infallible serialization to `Vec`.
let mut data = Vec::new();
// Note that this value can be serialized by reference.
// This is default behavior for `Serialized` derive macro.
// Some types required ownership transfer for serialization.
// Notable example is iterators.
let (size, _) = serialize_to_vec::<MyDataType, _>(&value, &mut data);
let de = deserialize::<MyDataType, MyDataType>(&data[..size]).unwrap();
assert_eq!(de, value);
}
#[cfg(not(all(feature = "derive", feature = "alloc")))]
fn main() {}
基准测试
Alkahest 包含了一个基准测试,用于测试与其他流行的序列化包。只需运行 cargo bench --all-features 来查看结果。
许可协议
根据以下任一协议许可:
- Apache License, Version 2.0, (license/APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 协议 (license/MIT 或 https://open-source.org.cn/licenses/MIT)
任选其一。
贡献
除非你明确说明,否则根据 Apache-2.0 许可协议定义,你有意提交以包含在作品中的任何贡献,都将按照上述方式双许可,不附加任何额外条款或条件。
依赖项
~0.4–0.9MB
~21K SLoC