ProtoBuf—编码原理

protobuf编码原理

  • protobuf将协议缓冲区消息(message)编译压缩成二进制格式的文件
  1. 当消息被编码时,键和值被连接成一个字节流,tag-value。
  2. protobuf中message是一系列键值对,message的二进制版本只是使用字段号(field`s number和wire_type)作为key,key的后3位位表示的是wire_type。
  3. 每个字段的名称和声明类型只能在解码端通过引用消息类型的定义。如果没有数据结构描述.proto文件,拿到数据以后是无法解释成正常数据结构的。
  4. 当消息被解码时,解析器需要能够跳过它无法识别的字段。这样,可以将新字段添加到消息中,而不会破坏不了解它们的旧程序。也就具备了向后兼容性。
    ProtoBuf—编码原理_第1张图片
    为此,wire-type消息中每一对的“键”实际上是两个值——.proto文件中的字段编号,加上提供足够信息来查找以下值的长度的wire-type。在大多数语言实现中,这个键被称为tag。
Type Meaning Used For
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimited string, bytes, embedded messages, packed repeated fields
3(已废弃) Start group groups (deprecated)
4(已废弃) End group groups (deprecated)
5 32-bit fixed32, sfixed32, float

Base 128 Varints编码

  1. Varints是一种紧凑型数字表示法,它是用一个或多个字节序列化整数的方法,是一种变长编码。
  2. varint 中的每个字节,除了最后一个字节,都设置了最高有效位(msb)
    a. 这表明还有更多字节要到来。即:如果用不到1个byte,msb=0;如果需要用多个字节,msb=1。
    b. 每个字节的低 7 位用于存储以 7 位为一组的数字的二进制补码表示,最低有效组在前。
    补充:protobuf使用小端序,即低字节用高地址,高字节用低地址。
  3. 解码过程:
    a. 如果是多个字节,先去掉每个字节的msb(通过逻辑或运算),每个字节只留下7位;
    b. 将剩余的7位,按字节逆序排列。比如,现有3个字节并去掉每个字节msb的结果如下:第1个字节是0000010,第2个字节是0001010,第三个字节是0001100;那他们逆序之后是0001100,0001010,0000010。将逆序之后排列得到的数字计算结果。

1. Message Structure编码

ProtoBuf—编码原理_第2张图片
ProtoBuf—编码原理_第3张图片

2. Signed Integers

varint类型中包含了无符号的uint,如果是一个无符号数,用varint来编码,则一定需要10个byte长度。32位的有符号数会转换位无符号位来处理。为了避免这种空间浪费,Google protobuf定义了sint32这种类型,采用zigzag编码,将所有整数映射成无符号整数,然后再采用varint编码方式编码。这样只要绝对值够小,编码后也会有一个足够小的varint的编码值。
补充:在计算机中,定义附属的符号位一般是数字的最高位,一个负数一般会被表示为一个很大的整数。
zigzag编码映射函数如下,如果被解析,会被解析成原来的有符号的数字。
zigzag(n) = (n << 1) ^ (n >> 31) //sint32
zigzag(n) = (n << 1) ^ (n >> 63) //sint64

Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2147483647 4294967294
-2147483648 4294967295

3. Non-varint Numbers

Non-varint Numbers指的是1,5类型的数据;
● 如果是1类型,解析时告诉解析器,该类型的数据需要一个64位大小的数据块即可;
● 如果是5类型,需要32位的数据块。

4. 字符串

ProtoBuf—编码原理_第4张图片

wire-type是2的类型的数据,都用TLV(Tag-Length-Value)这种指定长度的编码方式来编码。tag的编码方式统一的(eg:都统一编码为16进制),length用varints编码,content是由length指定长度的bytes。
注:虽然嵌入式message属于类型3,但是也用TLV编码。

ProtoBuf—编码原理_第5张图片

potobuf总结

  1. protobuf 利用varint原理压缩数据之后,二进制数据非常紧凑,打包在一定程度上使pb体积更小,如果选用它作为网络数据传输,势必相同数据,消耗的网络流量更少。但是并没有压缩float、double这些浮点类型,直接使用的64,32位大小的数据块,10个字节。
  2. protobuf比json和XML少了{、}、:这些符号,体积会减少一些,再加上varint压缩,gzip压缩以后体积更小!
  3. protobuf是tag-value(TLV)的编码方式实现,减少了分隔符的使用,数据存储的更加紧凑。
  4. Protocol Buffer 另外一个核心价值在于提供了一套工具,一个编译工具,自动化生成 get/set 代码。简化了多语言交互的复杂度,使得编码解码工作有了生产力。

你可能感兴趣的:(吾解,golang)