2个不稳定版本
0.1.0 | 2024年3月20日 |
---|---|
0.0.0-alpha.1 | 2024年3月19日 |
#2916 in 魔法豆
157 每月下载量
用于 3 crates
89KB
2K SLoC
将Rust值从VAA有效负载线格式进行序列化和反序列化。
截至本文写作时(2022年6月),VAA有效负载线格式没有合适的规范,因此此实现主要是从现有消息逆向工程得出的。虽然本文件其余部分讨论了各种类型在线上的表示,但这应被视为对本crate中实现方式的解释,而不是官方文档。在由本crate产生的负载序列化与wormhole合约使用的序列化不同的情况下,实际合约使用的序列化被视为规范序列化。
除非您想与现有的虫洞VAA有效负载进行交互,否则这个库可能不是您要找的。如果您只是使用虫洞桥来发送自己的有效负载,那么使用具有自动生成代码的模式(如protobufs或flatbuffers)可能是一个更好的选择。
线格式
VAA有效负载的线格式不是一个自描述格式(与json和toml不同)。因此,在反序列化字节流之前,必须知道需要生成的类型。
当前线格式支持以下原始类型
bool
编码为一个字节,其中值为0表示false,值为1表示true。所有其他值都是无效的。
整数
i8
、i16
、i32
、i64
、i128
、u8
、u16
、u32
、u64
和u128
都是支持的,并作为全宽大端整数进行编码,即i16
是2个字节,u64
是8个字节,等等。
char
编码为大端u32
,此外还必须是一个有效的Unicode Scalar Value
。
序列
可变长度异构序列被编码为一个字节长度,然后是序列中每个元素序列化形式的连接。请注意,这意味着序列不能有超过255个元素。此外,在序列化过程中,长度必须提前知道。
字节数组 - &[u8]
、Vec<u8>
和Cow<'a, [u8]>
字节数组被视为可变长度序列的子集,并以一个字节长度后跟相应数量的数据字节进行编码。同样,由于字节数组的长度必须适应一个字节,因此它不能超过255个字节。
&str
、String
字符串类型与&[u8]
以相同的方式进行编码,额外的限制是字节数组必须是有效的UTF-8。
元组
元组是异构序列,其中长度是固定的且提前知道。在这种情况下,长度不会在线上编码,元组中每个元素的序列化会连接起来生成最终值。
Option<T>
线格式不支持可选值。在尝试序列化一个Option::None
时,选项总是反序列化为Some(T)
,这会导致错误。
Structs
结构体以与元组相同的方式表示,结构体的序列化格式与具有相同字段顺序的元组的序列化格式相同。唯一的例外是没有字段的单元结构体(unit structs),它在序列化格式中根本不表示。
[T;N]
数组被视为具有相同字段的元组,并且具有相同的序列化格式。
枚举
枚举被编码为一个表示变体的单字节,随后是变体的序列化。
- 单元变体 - 不编码任何额外数据。
- 新类型变体 - 使用内部类型的序列化进行编码。
- 元组变体 - 以常规元组的形式编码。
- 结构体变体 - 以常规结构体的形式编码。
由于枚举变体是编码为一个单字节,而不是变体的名称本身,因此需要在每个枚举变体上使用 #[serde(rename = "<value>")]
来确保它们可以正确地序列化和反序列化。
示例
use std::borrow::Cow;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
enum TestEnum<'a> {
#[serde(rename = "19")]
Unit,
#[serde(rename = "235")]
NewType(u64),
#[serde(rename = "179")]
Tuple(u32, u64, Vec<u16>),
#[serde(rename = "97")]
Struct {
#[serde(borrow, with = "serde_bytes")]
data: Cow<'a, [u8]>,
footer: u32,
},
}
assert!(matches!(serde_wormhole::from_slice(&[19]).unwrap(), TestEnum::Unit));
映射类型
映射类型被编码为一个 (key, value)
元组的序列。对于 Vec<(K, V)>
的编码与 BTreeMap<K, V>
的编码相同。在序列化过程中,必须事先知道映射中的元素数量。与其他序列一样,映射中的最大元素数量为255。
依赖关系
~0.7–1.3MB
~29K SLoC