Protocol Buffer

Protocol Buffer, 简称 ProtoBuf
由 Google 开发,是一种开源跨平台的序列化协议

Proto2 Diff Proto3

Proto3是Proto2的简化版本

默认值

Proto3不允许自定义默认值, 在Proto3中的所有字段都具有一致的零默认值

必填字段

Proto3删除了required字段的支持

编码

varints

可变宽整数, 使用 1 到 10个字节之间的长度对无符号64位整数进行编码,较小的值可以使用较少的字节

在varint中,每个字节都有一个标志位,表示它后面的字节是否时varint的一部分
这个字节是MSB(Most Significant Bit / 最高有效位), 低 7 位是有效载荷

0000 0001
^ msb

1000 0011 0000 0001 // 131
^ msb     ^ msb

那么是如何得出是 131 的呢

1000 0011 0000 0001 // 原始输入
 000 0011  000 0001 // 去除MSB
 000 0001  000 0011 // 按小端排列
 10000011           // 连接
 128 + 2 + 1 = 131  // 解释为十进制

字段编号和类型

当对消息进行编码时,每个键值对会变为一个记录
其中包含字段编号(field number 简称 fn)、类型(wire type 简称 wt)和有效负载
其中字段编号和类型 使用 varint 格式编码, 最低三位表示类型

Wire Type解析对应数据类型
0varint 变长整型int32, int64, uint32, uint64, sint32, sint64, bool, enum
1固定8字节fixed64, sfixed64, double
2后面需要跟随一个长度string, bytes, required字段, 嵌套类型
3废弃
4废弃
5固定4字节fixed32, sfixed32, float
// 消息定义
message example {
  int32 i32 = 1;
  fixed64 f64 = 2;
  string str = 3;
  sint32 s32 = 4;
}

// 假设序列化时的对象信息
{
  i32: 257,
  f64: 1,
  s32: -2,
  b: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd]),
}
//编码后 <Buffer 08 81 02 11 01 00 00 00 00 00 00 00 1a 04 aa bb cc dd 20 03>

首先分析第一个字节 0x08 二进制 0000 1000
去除MSB, 000 1000
后三位表示类型 wt = 000 = 0, 表示数据类型为变长整形
剩余字段表示字段编号 fn = 0001 = 1

第二个字节 0x81 => 1000 0001
MSB为1, 表示后面的数据是其的一部分
第三字节 0x02 => 0000 0010
MSB为0, 表示是数据最后一个字节
按照小端排列并连接起来得到
000 0010 000 0001 => 257

即前三个字节表示, 字段编号1, 数据类型为变长整形
对应到消息定义的类型为int32, 对应数据为 257

接着分析第四个字节 0x11 => 0001 0001
fn = 2, wt = 1
即有效载荷为 01 00 00 00 00 00 00 00
根据小端排序后 00 00 00 00 00 00 00 01
对应到消息定义的类型为fixed64, 对应数据为 1

接着分析第十三个字节 0x1a => 0001 1010
fn = 3, wt = 2
当 wt = 2 时, 后面紧跟一个 varint 编码的数值表示有效载荷的长度
第十四字节 0x04 , 表示后面的有效载荷的长度为4, 为 0xaa, 0xbb, 0xcc, 0xdd

接着分析第十九个字节 0x20 => 0010 0000
fn = 4, wt = 0
第二十个字节为 0x03 => 0000 0011
对应到消息定义的类型为s32, sintN使用了ZigZag编码,而不是二进制补码的来编码负整数

总结

  • 当小负数时应该选择 sint32, sint64 相比 int32 和 int64 在负数时更具效率, 因为使用了 ZigZag 编码
  • 当数字大于 2^56 那么定长8字节的 fixed64 比 uint64 更高效, 因为 uint64 每个字节只有7位有效负荷
  • 当数字大于 2^28 那么定长4字节的 fixed32 比 uint32 更高效, 原因一样

参考资料