Protocol Buffers 编码原理

图1

protobuf数据类型如上,可分为可变长类型和不可变长类型。

Base 128 Varints

为保证传输效率,允许使用1到10个字节编码无符号64位整数,小值使用更少的字节。
MSB(most significant bit )或被称为符号位,变量当中每个字节都有一个MSB,表示后面的字节是否是该变量的一部分。
MSB 是 1 的话,则表示还有后序字节,一直读到 MSB 为 0 的字节为止。

例如,数字 1 被编码为 01 ,为单个字节,所以表示为:

0000 0001
^ msb

150 被编码为 9601, 表示为:

10010110 00000001
^ msb ^ msb

转换过程如下:

10010110 00000001        // 原始输入    
 0010110  0000001        // 丢掉持续位.
 0000001  0010110        // 转换成大端.
   00000010010110        // 连接.
128 + 16 + 4 + 2 = 150   // 被看作无符号64位整数.

消息结构

protobuf 消息是一系列键值对。即消息 = key + value,key = 字段编号(tag) + 字段类型(type)。
具体类型如上图,因为类型比较少,所以 Protocol Buffers 在编码的时候只用了 3 比特,实际传输的时候是以 (tag<<3)|type 的方式传输的。
例如:

message Foo {
  int32  foo = 1;
  string bar = 2;
}

Foo 的 foo 字段取值为 1 的话,则对应的编码是:0x08 0x01。foo 的类型是 int32,对应的 type 取 0。而它的 tag 又是 1,所以第一个字节是 (1<<3)|0 = 0x08,第二个字节是数字 1 的 VarInts 编码,即 0x01。

7       0 7      0
+-----+---+--------+
|00001|000|00000001|
+-----+---+--------+
  tag type   data

如果 Foo 的 bar 字段取值为 的话,则对应的编码是:0x12 0x03 0xe5 0x90 0x95。bar 的类型是 string,对应的 type 取 2。而它的 tag 又是 2,所以第一个字节是 (2<<3)|2 = 0x12,第二个字节表示字符串的长度为 3,再后面 3 个字节是汉字吕 UTF-8 编码。

 7       0 7      0 23        0
+-----+---+--------+===========+
|00010|010|00000011|0xe59095   |
+-----+---+--------+===========+
  tag type  length    utf-8

更多数字类型

Bools and Enums

bool 类型被编码为 0001. enum类型被编码为 int32

Signed Integers

对于负数的编码如 -1,使用补码表示将占用10字节, 因为上文中 varint 是无符号的,所以不同于有符号类型,sint32和sint64 vs int32或int64,对负整数的编码不同。
sintN 使用“ZigZag”编码而不是补码,“ZigZag” 规则为正整数p被编码为2 * p,而负整数n被编码为2 * |n| - 1。因此,编码在正数和负数之间“曲折”。

例如:

Signed Original | Encoded As
 0	            | 0
-1	            | 1
 1	            | 2
-2	            | 3| …
0x7fffffff		| 0xfffffffe
-0x80000000		| 0xffffffff

即对于 sint32 : (n << 1) ^ (n >> 31)
对于 sint64 : (n << 1) ^ (n >> 63)

Non-varint Numbers

double 和 fixed64 作为 I64,float 和 fixed32 作为 I32 。

Length-Delimited Records

LEN 类型有一个动态长度,由紧跟在标签后面的变量指定,标签后面跟着有效 payload。如上文的 string bar结构。

Submessages

子消息字段也使用 LEN 类型,如:

message Test1 {
  optional int32 a = 1;
}
message Test3 {
  optional Test1 c = 3;
}

设置 Test1 中 a 值为 150,得到编码为 1a03089601,分割后:

1a 03 [08 96 01]

[]中的三个字节为 Test1 的编码,1a = 3 << 3 | 2 ,03 为 payload的长度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值