Google Protobuf 二进制编码

Protocol Buffer 会将对象序列化为二进制数据。在这篇文章中,我们简单了解下它是如何对数据进行编码的,即:了解下底层的编码格式。当然并非必须的,日常使用 Protocol Buffer 不需要精通这些细节。不过,对于想要进行性能优化的人来说,理解底层格式会很有帮助。

Protocol Buffer 中定义的数据类型进行编码时,会对应到以下几种类型。不同的类型在编码时的处理也不同。

1. VARINT

Varint(Variable-length integer 的缩写)是 Protocol Buffer(简称 PB)中用于表示整数的一种紧凑编码方案。它根据整数的大小使用一个或多个字节进行编码,较小的数字使用较少的字节,较大的数字使用较多的字节。这可以在存储和传输整数时节省空间。

syntax = "proto3";

message Person
{
  int32 num1 = 1;
  int64 num2 = 2;
  bool num3 = 3;
}

假设我们实例化的对象:

num1: 214748
num2: 20
num3: true

序列化之后的二进制数据:

00001000 11011100 10001101 00001101 
00010000 00010100 
00011000 00000001
  • 第一组:前 7 个二进制位表示字段的编号 1,后三个二进制位值 0,表示使用 VARINT 方式存储
  • 第二组:高位为 1,说明后面还有字节
  • 第三组:高位为 1,说明后面还有字节
  • 第四组:高位为 0,后面没有字节,前 3 个字节为第一个字段的值
  • 第五组:前 7 个二进制位表示字段的编号 2,后三个二进制位值 0,表示使用 VARINT 方式存储
  • 第六组:对应的十进制值为 20
  • 第七组:前 7 个二进制位表示字段的编号 3,后三个二进制位值 0,表示使用 VARINT 方式存储
  • 第八组:对应的十进制为 1

2. I64 和 I32

I32I64 是用于表示整数的数据类型。它们分别代表 32 位和 64 位有符号整数。这些数据使用 4 和 8 字节进行数据存储。

syntax = "proto3";

message Person
{
  fixed32 num1 = 1;
  fixed64 num2 = 2;
}

假设我们实例化的对象:

num1: 10
num2: 20

序列化之后的二进制数据:

00001101 00001010 00000000 00000000 00000000 
00010001 00010100 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  • 第一组:前 7 个二进制位表示字段的编号 1,后三个二进制位值 5,表示使用 I32 方式存储
  • 第二组 ~ 第五组:共计 4 个字节存储第一个字段数据
  • 第六组:前 7 个二进制位表示字段的编号 2,后三个二进制位值 1,表示使用 I64 方式存储
  • 第七组 ~ 最后组:共计 8 个字节存储第二个字段数据

3. LEN

使用长度+数据的方式进行存储。

syntax = "proto3";

message Person
{
  string num1 = 1;
}

假设我们实例化的对象:

num1: "abc"

序列化之后的二进制数据:

00001010 00000011 01100001 01100010 01100011
  • 第一组:前 7 个二进制位表示字段的编号 1,后三个二进制位值 2,表示使用 LEN 方式存储
  • 第二组:对应的十进制值为 3,表示接下来会有 3 个字节的数据
  • 第三组 ~ 最后:第一个字段的值

4. 复杂类型

syntax = "proto3";

message Person
{
  repeated int32 num1 = 1;
  map<int32, string> num2 = 2;
}

假设我们实例化的对象:

num1: 10
num1: 20
num2 {
  key: 1
  value: "aaa"
}
num2 {
  key: 2
  value: "bbbb"
}

序列化之后的二进制数据:

00001010 00000010 00001010 00010100 

00010010 00000111 00001000 00000001 00010010 00000011 01100001 01100001 01100001 

00010010 00001000 00001000 00000010 00010010 00000100 01100010 01100010 01100010
  • 第一行:
    • 第一组:表示字段编号为 1,使用 LEN 方式进行存储
    • 第二组:表示元素个数、长度
    • 第三组 ~ 最后:各个元素的值
  • 第二行
    • 第一组:表示字段编号为 2,使用 LEN 方式存储
    • 第二组:表示 key 长度为 7 = int32 + string
    • 第三组:表示编码号为 2,使用 VARINT 方式存储
    • 第四组:表示 key 的值为 1
    • 第五组:表示字段编号为 2,使用 LEN 方式存储
    • 第六组:表示 value 长度为 3
    • 第七组 ~ 最后:表示 value 的值
  • 第三行:同第二行

至此,我们能够大概了解 Proto Buffer 是如何进行数据序列化。

未经允许不得转载:一亩三分地 » Google Protobuf 二进制编码
评论 (0)

9 + 5 =