2个版本
0.1.1 | 2024年4月28日 |
---|---|
0.1.0 | 2024年4月21日 |
#590 在 编码
被 3 个crates 使用
20KB
279 行
skrillax-serde
serde 是一个知名且受尊敬的Rust序列化和反序列化框架。虽然我们 可以 使其在我们的上下文中工作,但不幸的是,Silkroad在几个方面有点让人烦恼,这使得使用 serde
有点繁琐。因此,这为Silkroad上下文中的序列化和反序列化提供了一种更专注的方法。
Silkroad做了一些事情,使得一般地序列化数据包变得困难
- 它们不是自描述的 - 虽然这是可以预料的
- 列表可以以不同的方式表示,具体取决于特定的操作
- 属性可能会根据先前值出现或隐藏
- 属性可能会根据外部信息出现或隐藏
例如,Silkroad中有三种列表表示方式:基于长度的、基于断点的或基于续集的。第一种非常明显;前面加上长度,然后所有元素都跟随,没有任何分隔符。"基于断点的"和"基于续集的"在元素之间有分隔符,但最后一个元素的分隔符不同。两者都使用 1
作为分隔符,但分别使用 2
或 0
作为结束元素。为什么你需要多种方式来编码值列表?我不知道。其他奇怪之处包括一些字符串使用宽字符(2字节)编码,或者内容完全取决于它属于哪种类型的项。
使用serde来实现这一点并不困难,实际上,实现很简单,但使用起来会更累,因为你必须不断查找 deserialize_with
并记住确切的功能。特别是当你必须序列化/反序列化一个依赖于先前字段的字段时,你会遇到困难(例如:一个 ChatMessage
数据包有一个可选字段,该字段取决于已经编码在先前的、非相邻字段中的消息类型)。为了使工作更容易,并使这些怪癖“可实现”,存在这个定制的 'serde'。
Silkroad操作序列化/反序列化
大部分的序列化/反序列化逻辑都实现在 -derive
库中。这个库只包含原始值的序列化和反序列化定义,例如时间、u8
等类似类型。然而,我想解释一下在 Silkroad 世界中数据序列化的基本原理。
让我们从基础开始:Silkroad 以小端字节序(little endian byte order)编码数据(大部分情况下),所以一个长度为 256
的数据包(这是一个 u16
),将显示为 0x00 0x01
,而不是 0x01 0x00
。数据字段之间没有分隔符,所以值直接相邻。如果你有三个不同大小的字段,序列化的大小将始终等于这些字段在内存中占用的总空间。固定长度的数组也只编码它包含的数据(元素的数量是已知的,每个元素的大小也是已知的)。可变长度的列表(在 Rust 术语中称为 Vec
)有三种不同的编码方式。要么长度前缀,通常为 u8
的大小,但也可以是 u16
,然后所有元素都跟在后面,没有进一步的分隔符。或者,使用一个 u8
来表示是否还有更多(值为 1
),然后是下一个元素,或者它是结束(值为 0
或 2
),这取决于它被用于何处。类似于枚举的元素(例如,错误代码或通常,某物的不同变体)通常包含一个表示“变体 ID”的 u8
,后面跟该变体的具体字段。因此,不包含任何数据的变体将只包含变体 ID 而没有其他内容。最后,还有可选字段,它们包含一个指定数据是否存在(值为 1
)或不存在(值为 0
)的 u8
。其他所有内容都使用之前定义的原子(atoms)的任何组合。
依赖关系
~1.4–2.3MB
~41K SLoC