?
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代替 | int32 | int | int | *int32 |
int64 | 可變長度編碼。對負數編碼低效,如果字段可能是負數,用sint64代替 | int64 | long | int/long[3] | *int64 |
uint32 | 可變長度編碼,無符號整數 | uint32 | int[1] | int/long[3] | *uint32 |
uint64 | 可變長度編碼,無符號整數 | uint64 | long[1] | int/long[3] | *uint64 |
sint32 | 可變長度編碼。有符號整數。 These more efficiently encode negative numbers than regular int32s. | int32 | int | int | *int32 |
sint64 | 可變長度編碼。有符號整數。These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long[3] | *int64 |
fixed32 | 固定4字節長度,無符號整數。 More efficient than uint32 if values are often greater than 228. | uint32 | int[1] | int/long[3] | *uint32 |
fixed64 | 固定8字節長度,無符號整數。 More efficient than uint64 if values are often greater than 256. | uint64 | long[1] | int/long[3] | *uint64 |
sfixed32 | 固定4字節長度,有符號整數 | int32 | int | int | *int32 |
sfixed64 | 固定8字節長度,有符號整數 | int64 | long | 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 |
---|---|---|
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 |
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 |
---|---|
0 | 0 |
-1 | 1 |
1 | 2 |
-2 | 3 |
2147483647 | 4294967294 |
-2147483648 | 4294967295 |
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; }
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