深入淺出Google ProtoBuf中的編碼規則

2021-06-18 19:39:27 字數 2846 閱讀 5678

在開始本部分的內容之前,首先有必要介紹兩個基本概念,乙個是序列化,乙個是反序列化。這兩個概念的定義在網上搜一下都很多的,但大多都講得比較晦澀,不太好理解,在這裡我會用比較通俗的文本來解釋,盡可能讓讀都朋友們一讀就明白是怎麼回事:

序列化:是指將結構化的資料按一定的編碼規範轉成指定格式的過程

反序列化:是指將轉成指定格式的資料解析成原始的結構化資料的過程

舉個例子,person是乙個表示人的物件型別,person是乙個person型別的物件,將person存到乙個對應的xml文件中的過程就是一種序列化,而解析xml生成對應person型別物件person的過程,就是乙個反序列化的過程。在這裡結構化資料指的就是person型別的資料,一定的編碼規範指的就是xml文件的規範。xml是一種簡單的序列化方式,用xml序列化的好處是,xml的通用性比較好,另外,xml是一種文字格式,對人閱讀比較友好,但是xml方式比較佔空間,效率也不是很高。通常,比較高效的序列化都是採用二進位制方式的,將要序列化的結構化資料,按一定的編碼規範,轉成為一串二進位制的位元組流儲存下來,需要用的時候再從這串二進位制的位元組流中反序列化出對應的結構化的資料。

通過上面的介紹,我們給protobuf下乙個比較正式的定義了:google protobuf是google制定的一種用來序列化結構化資料的程式庫。

1) protobuf編碼基礎——varints, varints是一種將乙個整數序列化為乙個或者多個bytes的方法,越小的整數,使用的bytes越少。

varints的基本規則是:

(a) 每個byte的最高位(msb)是標誌位,如果該位為1,表示該byte後面還有其它byte,如果該位為0,表示該byte是最後乙個byte。

(b)每個byte的低7位是用來存數值的位

(c)varints方法用litte-endian(小端)位元組序

舉個例子:300用varints序列化的結果是1010 1100 0000 0010,運算過程如下 所示:

1010 1100 0000 0010->010 1100 000 0010(去標誌位)->

000 0010 010 1100(調整位元組序)-> 1 0010 1100 ->256+32+8+4=300(計算值)

2)protobuf中訊息的編碼規則:

(a)每條訊息(message)都是有一系列的key-value對組成的, key和value分別採用不同的編碼方式。

(b)對某一條件訊息(message)進行編碼的時候,是把該訊息中所有的key-value對序列化成二進位制位元組流;而解碼的時候,解碼程式讀入二進位制的位元組流,解析出每乙個key-value對,如果解碼過程中遇到識別不出來的型別,直接跳過。這樣的機制,保證了即使該訊息新增了新的字段,也不會影響舊的編/解碼程式正常工作。

(c)key由兩部分組成,一部分是在定義訊息時對字段的編號(field_num),另一部分是字段型別(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

(d)key的編碼方式:field_num << 3 | wire_type

(e)varint型別(wire_type=0)的編碼,與第(1)部分中介紹的方法基本一致,但是int32, int64和sint32,sint64有些特別之處:int32和int64就是簡單的按varints方法來編碼,所以像-1、-2這樣負數也會佔比較多的bytes。於是sint32和sint64採用了一種改進的方法:先採用zigzag方法將所有的整數(正數、0和負數)一一對映到所有的無符號數上,然後再採用varints編碼方法進行編碼。zigzag對映函式為:

zigzag(n) = (n << 1) ^ (n >> 31),  n為sint32時

zigzag(n) = (n << 1) ^ (n >> 63),  n為sint64時

下表是乙個比較直觀的對映表,這樣對映後再進行編碼的好處就是絕對值比較小的負數序列化後的結果佔的bytes數也會比較少。

signed original

encoded as00

-1112

-2324

-35……

2147483647

4294967294

-2147483648

4294967295

(f)64-bit(wire_type=1)和32-bit(wire_type=5)的編碼方式就比較簡單了,直接在key後面跟上64bits或32bits,採用little-endian(小端)位元組序。

(g)length-delimited(wire_type=2)的編碼方式:key+length+content, key的編碼方式是統一的,length採用varints編碼方式,content就是由length指定的長度的bytes。

(h)wire_type=3和4的現在已經不推薦使用了,因此這裡也不再做介紹。

3)protobuf編譯碼中字段順序(field order)的問題:

(a) 編碼/解碼與字段順序無關,這一點由key-value機制就能保證

(b)對於未知的字段,編碼的時候會把它寫在序列化完的已知字段後面。

深入淺出sizeof

int佔 位元組,short佔 位元組 1.0 回答下列問題 答案在文章末尾 1.sizeof char 2.sizeof a 3.sizeof a 4.strlen a 如果你答對了全部四道題,那麼你可以不用細看下面關於sizeof的論述。如果你答錯了部分題目,那麼就跟著我來一起 關於sizeof...

深入淺出ShellExecute

ipconfig c log.txt應如何處理?二樓的朋友,開啟拔號網路這樣 shellexecute null,open c windows rundll32.exe shell32.dll,control rundll c windows system telephon.cpl null,sw ...

深入淺出ShellExecute

深入淺出shellexecute譯者 徐景周 原作 nishant s q 如何開啟乙個應用程式?shellexecute this m hwnd,open calc.exe sw show 或shellexecute this m hwnd,open notepad.exe c mylog.log...