protobuf編碼


protobuf能夠跨平台提供輕量的序列化和反序列化,得益於其平台無關的編碼格式,本文就介紹下其中的編碼格式。

Varints

在protobuf中大量使用到了Varints的編碼格式,這是一個可變長度的編碼格式用於編碼整形數字。
Varint的最小單位是byte,即8位,每byte第一位(msb)是標志位用於標記是否還有后續byte。

===1===
0000 0001
===300===
1010 1100  0000 0010

上面300的例子首先讀入第一個字節發現第一位為1,表示還有后續byte,然后讀取后一個byte,第一位為0就判斷已經讀完,然后組裝數值將其余位數取出0101100 0000010,然后反轉並拼接成為000 0010-010 1100,這樣就組成了300。

其他類型

負數,sint與int

在protobuf的定義中sint和int似乎看上去是重復的,但其實這兩種類型的底層編碼式不同的。這里以-3為例:
-3使用int編碼會變成FD FF FF FF FF FF FF FF FF 01,首先這也是varint編碼去掉每個byte的首位標志位然后反轉順序就成了FF FF FF FF FF FF FF FD是-3的補碼。
而使用sint編碼會變成05,貌似和-3沒有關系,其實它是-3經過一個zigzag轉換而得來的。

Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2147483647 4294967294
-2147483648 4294967295
這樣轉換可以大大減少負數的表示長度,所以建議在經常出現負數的地方使用sint而不是int可以縮短編碼的大小。 ###非varient數值 對於double/fixed64,float/fixed32會分別使用固定的64位或32位進行表示。 ###不定長數據類型 對於string,byte等類型,就使用這種類型,首先使用一個varint表示長度,之后是數據的內容,列入字符串aaa就是03 61 61 61 其中03表示長度為3,61是a的utf8編碼。值得注意的是,嵌套的message類型、repeat字段也都是使用這種形式進行編碼到對象中的。

message編碼

有了上面這些在准備,我們可以進入真正的消息的編碼了。protobuf中每個字段都是根據定義的tag來進行定位的,在序列化的數據中,每個字段首先是一個Varint用於標記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

前面的位數用於標志tag,例如0000 1000,去掉第一個標志位后三個類型位,就表示tag為1的字段。
對於repeat類型由於歷史原因,proto2中默認是將數組元素並排排列在內的例如[1,2,3]會保存成[08 01 08 02 08 03]。后來對repeat類型進行了改進引入packed在定義的后面加上packed:

repeated int32 int = 1 [packed=true];

這樣序列化的數據就成了0A 03 01 02 03,其中0A表示使用不定長編碼,之后03表示長度,接下來是數據,就這個例子就能節約1byte。packed只適用於varint或固定長度數值表示的字段,對於string或者嵌套類型不適用,在proto3中支持的類型默認會使用packed。

實例

syntax = "proto2";
message Person {
	required string name=1;
	required int32 age=2;
	repeated Address add=3;	
}
message Address{
	required string add=1;
}
---實例---
name: "MyName"
age: 18
add {
  add: "MyAdd1"
}
add {
  add: "MyAdd2"
}
---Hex---
0A 06 4D 79 4E 61 6D 65 10 12 1A 08 0A 06 4D 79 41 64 64 31 1A 08 0A 06 4D 79 41 64 64 32
---解釋---
0A 						//變長類型tag為1的字符串
06						//name字段6 byte長度
4D 79 4E 61 6D 65		//MyName
10						//varint編碼tag為2
12						//18
1A						//變長類型tag為3
08						//Adress 8 byte長度
0A						//變長類型tag為1
06						//add字段6 byte長度
4D 79 41 64 64 31		//MyAdd1
1A						//變長類型tag為3
08						//Adress 8 byte長度
0A						//變長類型tag為1
06						//add字段6 byte長度
4D 79 41 64 64 32		//MyAdd2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM