protobuf编码
proto2
Protocol Buffers 是⼀种轻便⾼效的结构化数据存储格式,可以⽤于结构化数据序列化,适合做数据存储或 RPC 数据交换格式。可⽤于通讯协议、数据存储等领域的语⾔⽆关、平台⽆关、可扩展的序列化结构数据格式。
字段规则
required: 字段必须存在
optional: 字段没有或有⼀个
repeated: 字段重复,0个或多个
proto 数据类型
.proto
Type Notes C++
Type
Java
Type
Python
Type[2]
Go
Type
double固定8字节长度double double float*float64 float固定4字节长度float float float*float32 int32可变长度编码。对负数编码低效,如果字段可能是负数,⽤sint32代替int32int int*int32 int64可变长度编码。对负数编码低效,如果字段可能是负数,⽤sint64代替int64long int/long[3]*int64 uint32可变长度编码,⽆符号整数uint32int[1]int/long[3]*uint32 uint64可变长度编码,⽆符号整数uint64long[1]int/long[3]*uint64
sint32可变长度编码。有符号整数。 These more efficiently encode negative numbers than
regular int32s.
int32int int*int32
sint64可变长度编码。有符号整数。These more efficiently encode negative numbers than
regular int64s.
int64long int/long[3]*int64
fixed32固定4字节长度,⽆符号整数。 More efficient than uint32 if values are often greater
than 228.
uint32int[1]int/long[3]*uint32
fixed64固定8字节长度,⽆符号整数。 More efficient than uint64 if values are often greater
than 256.
uint64long[1]int/long[3]*uint64
sfixed32固定4字节长度,有符号整数int32int int*int32 sfixed64固定8字节长度,有符号整数int64long int/long[3]*int64 bool bool boolean bool*bool string UTF-8 encoded or 7-bit ASCII text.string String str/unicode[4]*string bytes包含任意字节序列string ByteString str[]byte
编码规则
1.varints
理解简单protobuf编码,⾸先要知道varints。varints使⽤⼀个字节或多个字节对整数序列化⽅法。
varints中的每个字节除了最后⼀个字节,有⼀个最有效位(most significant bit ,msb),这意味指⽰之后有其他字节。每个字节的低7位⼀组数的补码
例如:
1
0000  0001
300
1010  1100  0000  0010
只取每个字节低七位
010  1100  000  0010
⼩端序->⼤端序
000  0010  010  1100 -->  0001  0010  1100  =300
2.消息结构
protobuf 消息是⼀系列key-value对,对于⼆进制消息,字段数字作为关键字。字段的命名和类型在解码时确定。
在进⾏消息编码时,key/value被连接成字节流。在解码时,解析器可以直接跳过不识别的字段,这样就可以保证新⽼版本消息定义在新⽼程序之间的兼容性,从⽽有效的避免了使⽤older消息格式的older程序在解析newer程序发来的newer消息时,⼀旦遇到未知(新添加的)字段时⽽引发的解析和对象初始化的错误。最后,我们介绍⼀下字段标号和字段类型是如何进⾏编码的。每⼀个 wire-format消息的key实际上是有两个值组成:proto⽂件中定义的字段标号和wire type。
Type Meaning Used For
0Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
164-bit fixed64, sfixed64, double
2Length-delimited string, bytes, embedded messages, packed repeated fields
3Start group groups (deprecated)
4End group groups (deprecated)
532-bit fixed32, sfixed32, float
key = (field_number << 3) | wire_type key的最后3个bits⽤于存储字段的类型信息。那么在使⽤该编码时,Protocol Buffer所⽀持的字段类型将不会超过8种。
例如:150(⼗进制)在protobuf⼆进制⽂件中是 08 96 01
08 --> 00001000  field_number=1,wire_type=0
96 01 -> 1001 0110  0000 0001 -->  001 0110  000  0001  -->  000 0001  001  0110  --> 1001  0110 =150
3.Signed Integers
wire_type=0 的类型都以varint 进⾏编码,所以对于int32和int64,对于负数使⽤补码,int32 需要5个字节,int64需要10个字节,有符号整数使⽤ZIgZag编码
ZigZag 将有符号整数映射到⽆符号整数,以此得到⼀个绝对值较⼩的数字(例如-1)j就可以获得⼀个较⼩的varint编码值。
Signed Original Encoded As
00
-11
12
-23
21474836474294967294
-21474836484294967295
sint32:      (n << 1) ^ (n >> 31)
sint64:      (n << 1) ^ (n >> 63)
4.Non-varint Numbers
wire-type=1:fixed64, sfixed64, double 固定64bit
wire-type=5:fixed32, sfixed32, float,固定32bit
5.Strings
wire-type=2,字符串长度使⽤varint编码
例如
message Test2 {
optional string b = 2;
}unicode文件格式
b=“testing” ,编码后结果:
12 07 74 65 73 74 69 6e 67
12: field number=2,wire-type=2  (2<< 3)|2=0x12
长度为7 varint编码 0x07
74 65 73 74 69 6e 67  UTF8 编码
6.Embedded Messages嵌套消息
wire-type=2
message Test1 {
optional int32 a = 1;
}
message Test3 {
optional Test1 c = 3;
}
Test1's a field set to 150
Test3 编码结果: 1a 03 08 96 01
1a: field_number=3,wire_type=2  (3<< 3)|2=11010=0x1a
03: 字节数
08 96 01 : c编码结果
7.Optional And Repeated Elements
proto2 :消息被定义repeated (没有 [packed=true]选项),编码的消息有0或多个使⽤相同字段标号的key-value对,这些重复的值不必连续出现; 他们可能会与其他字段交错,但在解码时顺序保留
optional字段,编码后的消息可能有也可能没有包含该字段号的键值对。
proto3 使⽤packed encoding:
包含零个元素的打包重复字段不会出现在编码消息中。这个字段的所有元素都打包到⼀个wire-type=2的key-value对中
message Test4 {
repeated int32 d = 4 [packed=true];
}
22        // key (field number 4, wire type 2)
06        // payload size (6 bytes)
03        // first element (varint 3)
8E 02    // second element (varint 270)
9E A7 05  // third element (varint 86942)
proto2默认不设置 packed=true
repeated编码采⽤空格(0x20)分隔
结果是20 03 20 8e 02 20 9e a7  05