Ink & Switch 公开介绍了 bijou64。它是为 Subduction CRDT 同步协议开发的 u64 可变长度整数编码。

有意思的地方不只是“比 LEB128 快”。更关键的是,它想解决一个更细的协议问题:同一个整数,如果能写成多种字节序列,签名、内容寻址、去重和同步就可能看到不同的字节。

比如 0。在 LEB128 里,0 可以写成 0x00,也可以写成 0x80 0x00。数值一样,字节不同。

普通解析器可能只觉得这是“过长编码”。但在签名和内容寻址里,字节就是边界。差一个字节,哈希、签名、去重结果都可能变。

我的判断是:bijou64 真正值得看的点,是把“规范编码”从运行时检查,尽量前移到格式结构里。它不是 LEB128 的通用替代品,但很适合提醒新协议设计者:格式能少留一个坑,就不要把坑交给每个实现去填。

LEB128 的问题,不是慢,而是同值多码

LEB128 很成熟。Protocol Buffers、DWARF 等生态里都能看到类似 varint 思路:每个字节拿 7 位放数据,最高位表示后面还有没有字节。

这个设计紧凑,也好实现。但它天然有一个规范化问题:如果实现不拒绝过长编码,同一个值就可能有多种字节表示。

0 是最直观的例子:

数值可能的 LEB128 字节表示问题
00x00最短表示
00x80 0x00数值仍是 0,但字节变了
0多个 0x80 后接 0x00更长的非规范表示

这不是说 LEB128 有一个专属安全漏洞。更准确地说,很多可变长度编码、序列化格式和签名协议都会遇到同类风险:值相等,不代表字节相等。

历史上,ASN.1、JWT、比特币交易可塑性等案例都反复说明过一件事:规范化规则写在文档里,不等于每个实现都会永远做对。

对二进制协议设计者,这意味着一个很现实的动作:如果格式还没冻结,就要尽早决定“唯一表示”是格式保证,还是靠所有解码器拒绝非规范输入。

对做签名、内容寻址、CRDT 同步一致性的工程师,动作更具体:不要只测 decode 后的值相等,还要测字节级规范性。否则跨语言实现一多,问题会从单元测试里漏出去。

bijou64 用首字节把长度和唯一性锁住

bijou64 的结构很直接。

首字节 0–247 直接表示对应整数。248–255 不再表示普通数值,而是作为长度标签,告诉解码器后面还有多少 payload 字节。

后续 payload 再配合 offset。比如 0xF8 0x00 表示 248,而不是重复表示 0。这样一来,小整数有直表,大整数有固定区间,每个 u64 范围内的整数都对应唯一编码。

可以把差异压成一张表:

维度LEB128bijou64对协议设计的影响
小整数7 位分组,continuation bit 续写0–247 单字节直表常见小值仍紧凑
长度判断逐字节看最高位首字节 248–255 标记总长度解码路径更可预测
规范性通常靠拒绝过长编码用 offset 避免重复表示少一个易漏的规范化检查
边界检查取决于实现9 字节最大档仍需 u64 上界检查不是完全免检查

这里要注意一个限制:bijou64 不是“完全不用检查”。最大 9 字节档仍然需要确认没有越过 u64 上界。

它的改进点更准确地说是:把一大类重复表示问题从格式上消掉。剩下的边界检查仍然要做。

这对新协议更有用。尤其是还在设计同步日志、块格式、哈希输入、签名载荷的人,可以把 bijou64 当成一个候选方案,而不是等到格式稳定后再补一层 canonicalization 规则。

快在哪里,什么时候不该换

公开基准显示,bijou64 解码大约比 LEB128 快 2–10 倍。在 full-u64 均匀分布下,bijou64 处理 4096 个值约 3 微秒,LEB128 约 30 微秒。

原因不玄。bijou64 从首字节就知道总长度,payload 是连续大端整数。LEB128 要逐字节扫描 continuation bit,还要对 7 位分组做掩码和移位。

但这个跑分不能读成“全面替代”。已公开基准主要来自 Apple M2 Pro、AMD Zen 5,另有 Zen 3 的观察。不同 CPU、语言、编译器和内存访问模式下,结果可能变。

编码端也不是全胜。原文提到,在 248–65,535 的 small 分布里,LEB128 约快 1.24 倍。

空间也一样。bijou64 不是所有场景最省字节的 varint。在 realistic workloads 中,它和 LEB128 的线缆字节数通常只差几个百分点。

所以更现实的路线是:

场景更合理的动作
新二进制协议、存储格式还没冻结可以评估 bijou64,重点看唯一编码和跨实现测试
已部署多年、依赖 LEB128 的格式不建议只为跑分迁移,除非规范化风险或解析成本已经很痛
签名、内容寻址、CRDT 同步载荷优先检查是否存在同值多码问题,再决定是否换编码
只追求最小体积的场景不要默认 bijou64 更省,必须按真实分布测

LEB128 的优势仍然很硬:实现多,工具链成熟,调试经验多,跨语言生态稳定。对很多项目来说,这些比微基准更值钱。

bijou64 目前有 Rust crate,采用 MIT / Apache-2.0 双许可,规范为 CC BY-SA 4.0,也有 Wasm/JavaScript wrapper。但它仍是新格式。缺的不是一张更漂亮的跑分图,而是独立实现、测试向量、互操作经验,以及恶意输入下的长期验证。

我更在意的观察点只有三个:有没有更多独立实现;跨语言测试向量是否覆盖边界值和非规范输入;真实同步负载里,唯一编码带来的收益能否抵消新格式的生态成本。

如果答案逐步变好,bijou64 会成为新协议里很稳的候选。如果只是为了“比 LEB128 快”,理由还不够厚。