#binary-encoding #serialization #bit #deserialize #encode #decode #encode-decode

zlo

一种使用Serde将结构体转换为非常紧凑的位表示的二元序列化/反序列化策略

1个不稳定版本

使用旧的Rust 2015

0.1.0 2017年9月25日

#2241 in 编码

MIT许可证

75KB
1.5K SLoC

zlo

Docs

一个编码/解码器对,使用位紧凑的二进制编码方案。编码对象的尺寸几乎与在运行的Rust程序中对象占用的内存大小相同或更小。它是bincode的分支,因此API相似,还包含熟悉的SizeLimit对象。

它是为了在多玩家游戏中的网络使用而制作的,适合在网络中编码频繁发送但尺寸很小的数据差异,而bincode会产生相对较大的数据块,使用如LZO等通用算法压缩通常会与zlo编码数据相比带来非常小的改进,但代价是增加了长压缩时间。

二进制格式在不同版本之间的稳定性得到保证。

示例

#[macro_use]
extern crate serde_derive;
extern crate zlo;

use zlo::{serialize, deserialize, Infinite};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Entity {
    x: f32,
    y: f32,
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct World(Vec<Entity>);

fn main() {
    let world = World(vec![Entity { x: 0.0, y: 4.0 }, Entity { x: 10.0, y: 20.5 }]);

    let encoded: Vec<u8> = serialize(&world, Infinite).unwrap();

    assert!(encoded.len() < 8 + 4 * 4);

    let decoded: World = deserialize(&encoded[..]).unwrap();

    assert_eq!(world, decoded);
}

性能,输出大小

zlo反序列化器不是零拷贝,且在大多数情况下不能实现。这是因为在此编码中位乱序的数据出现频率很低。

处理最快的原始数据类型是无符号整数和布尔值。性能还取决于写入的数据量。例如,编码小的64位整数仅略慢于适合该类型的值。

zlo主要用于序列化数据的差异,即包含大量Option、小的整数和差异化的zigzag浮点数的结构体。在这些条件下,zlo可以产生非常紧凑的数据。

与Bincode相比

在编码大的64位整数时,zlo可能比bincode慢7倍。平均而言,在编码大量整数或浮点数时,zlo可以预期比bincode慢3到5倍。在编码纯字节时,zlo的性能与bincode相当,但zlo不是零拷贝,因此与bincode相比,在反序列化时可能会产生额外的分配开销。

与 bincode 相比,在编码数字时,输出大小平均可减小至 1.5 倍;在编码大量 bools 或 Option::None 时,可减小至 8 倍。

需要将数据压缩到更少的比特数吗?

  • 考虑通过它们的组成部分(指数和分数)来比较浮点数。
  • 或者,你的值是否保持在已知的特定范围内?那么考虑将其序列化为整数,有符号或无符号均可。
  • 它是多维的?也许你可以从单个记录中推断出相关元素。
  • 序列化多维单位值,如单位向量或四元数?可以更紧凑地序列化,例如,2D 单位向量可以简化为角度。

参见 示例

有损分数编码

目前没有计划实现。此外,即使考虑有损分数,也只有在 Rust 问题 #44580 稳定后才能实现。

详情

布尔值以单个比特编码,整数以类似于 LEB128 但不等于 LEB128 的形式编码,浮点数以以下复杂的方式进行编码,元组和结构体通过逐个字段编码,枚举通过先写出表示变体的标记,然后写出内容进行编码。

由于写入的字节数量不固定,zlo 没有可配置的字节序。

需要注意的实现细节

  • 单独的比特从最低位到最高位写入。
  • 无符号整数使用以下原则进行编码
PC
0 如果 整数 > 0, 写入比特 1,否则 写入比特 0 并 返回
1 写入整数的下一个 8 位
2 如果 这是整个类型的最高有效字节, 返回
3 如果 没有更多的比特要写入, 写入比特 0 并 返回
4 写入比特 1
5 跳转到 1
  • 有符号整数是 zigzag 编码的(与 protobuf 相同),然后以与无符号整数相同的方式进行编码
  • 浮点数按以下方式进行编码:始终写入符号位,指数以 2 位或完整形式写入,小数部分在 f32 的情况下只写入高 16 位,在 f64 的情况下写入高 32 位,或者如果它不合适,则写入完整的小数部分。
  • isize/usize可变 i64/u64 的形式进行编码,以提高可移植性。
  • 枚举变体以变量 u32 而不是 usize 进行编码。对于所有实际用途,u32 足够使用。
  • str(可变 u64, &[u8]) 的形式进行编码,其中 u64 是编码字符串中包含的字节数。

许可

zlo 在 MIT 许可证下授权(LICENSE-MIThttp://opensource.org/licenses/MIT

依赖项

~110–355KB