Protobuf 编码自我理解

Posted by 翡翠小屋 on May 11, 2020

注:本文默认你对 Protocol buffer 有基本的了解。

Google 发布的 Protocol Buffer 是很有趣的一种数据存储格式,但一般人并不会深入理解它到底是怎么编码的,使用的时候也都是按官方的做法,定义描述文件然后编译,而实际上,protobuf 的编码方式很有意思,对数据有一定压缩,几乎没有多余字段,并且还有一定的自描述性。

首先是正整数的编码,它使用了一种叫 Varint 的格式,简单的说就是结果里每个字节的后 7位(二进制)是数据,最高位代表十分还有数据。不管是 Long 还是 byte 类型的整数,只要数值比128小,就能编码成一个字节。

而一个整数如果会是负数,就是把它统一成正数,然后向左移动一位,最右边的位用来表示符号,这样我们就吧问题归结到正数的编码上了。

Protobuf 给每个数据项加了一个编号,然后编号左移3位,这3位用来代表类型,然后把这个结果当 Varint 写入,后续根据类型来。

整数按你设置的格式,用 varint 或者 (fix32/fix64)4/8字节写入。

float,double 就是 4/8字节完整写入。

数组、map 其实就是依次写入所有数据项,而它们有同样的编号,读取的时候按编号判断。

而字符串和结构在 Protobuf 里其实是统一的,它们被统一认为是一块有长度的数据块,所有先写入这个数据块的长度(varint),然后再把这个数据块写入。

最后,结构怎么变成数据块?按上面的规则递归就行了

Protobuf 通过递归和 Varint 巧妙的解决了序列化问题,确实相当精巧。

很久以前写过一个 C++ 版本的 protobuf 编码器,最近又准备搞个 Java 版的,可以在不想写 proto 文件的情况下序列化类。

github/protobuf-serializable

这个就当短期目标,一定要完成吧。