原理
Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。
Varint 中的每个字节(最后一个字节除外)都设置了最高有效位(msb),这一位表示是否还会有更多字节出现。每个字节的低 7 位用于以 7 位组的形式存储数字的二进制补码表示,最低有效组首位。
编码方式
1)将被编码数转换为二进制表示
2)从低位到高位按照 7位 一组进行划分
3)将大端序转为小端序,即以分组为单位进行首尾顺序交换
因为 protobuf 使用是小端序,所以需要转换一下
4)给每组加上最高有效位(最后一个字节高位补0,其余各字节高位补1)组成编码后的数据。
5)最后转成 10 进制。
数字123456进行 varint 编码:
1)123456 用二进制表示为1 11100010 01000000,
2)每次从低向高取 7位 变成111 1000100 1000000
3)大端序转为小端序,即交换字节顺序变成1000000 1000100 111
4)然后加上最高有效位(即:最后一个字节高位补0,其余各字节高位补1)变成11000000 11000100 00000111
5)最后再转成 10进制,所以经过 varint 编码后 123456 占用三个字节分别为192 196 7。
解码的过程就是将字节依次取出,去掉最高有效位,因为是小端排序所以先解码的字节要放在低位,之后解码出来的二进制位继续放在之前已经解码出来的二进制的高位最后转换为10进制数完成varint编码的解码过程。
缺点
负数需要10个字节显示(因为计算机定义负数的符号位为数字的最高位)。
具体是先将负数是转成了 long 类型,再进行 varint 编码,这就是占用 10个 字节的原因了。
protobuf 采取的解决方式:使用 sint32/sint64 类型表示负数,通过先采用 Zigzag 编码,将正数、负数和0都映射到无符号数,最后再采用 varint 编码。
ZigZag 是将符号数统一映射到无符号号数的一种编码方案,具体映射函数为:
Zigzag(n) = (n << 1) ^ (n >> 31), n 为 sint32 时
Zigzag(n) = (n << 1) ^ (n >> 63), n 为 sint64 时
protocol buffer 中 message 是一系列键值对。message 的二进制版本只是使用字段号(field’s number 和 wire_type)作为 key。
每个字段的名称和声明类型只能在解码端通过引用消息类型的定义(即 .proto 文件)来确定。
这一点也是人们常常说的 protocol buffer 比 JSON,XML 安全一点的原因,如果没有数据结构描述 .proto 文件,拿到数据以后是无法解析成正常的数据的。
Protocol Buffer 利用 Varint 原理压缩数据,同时使用Tag - Value (Tag - Length - Value)的编码结构的实现,减少了分隔符的使用,数据存储更加紧凑。
protocol buffers 在序列化方面,与 XML 相比,有诸多优点:
更加简单
数据体积小 3- 10 倍
更快的反序列化速度,提高 20 - 100 倍
可以自动化生成更易于编码方式使用的数据访问类
```