protobuf 變長64位無符號整數 為什麼最多需要消耗10位元組而不是9位元組?

我們知道protobuf使用了一種「位元組鏈表」的方式來表示變長整數,通過每一個位元組最高位是否為1來表示後續是否還有更多數據位元組。所以每一個位元組只有7個有效數據位。

通常的說法即是 64=9*7+1 所以最多需要10位元組來標識一個64位元組無符號變長整數。

這是解碼https://github.com/google/protobuf/blob/fa5a69e73b0dd667ff15062adbc170310d440ee9/src/google/protobuf/io/coded_stream.cc#L358

這是編碼

https://github.com/google/protobuf/blob/fa5a69e73b0dd667ff15062adbc170310d440ee9/src/google/protobuf/io/coded_stream.h#L1168

我們可以看到相關的實現代碼都把他實現為了最多消耗10位元組的。

但是64位整數最大也就64位,所以其實最後一個位元組的最高位是可以用來表示數據位而不是用來標識後續是否還有數據。

所以64=8*7+1*8 總共最多只需要9個位元組就可以標識一個64位元組無符號變長整數了(把第9個位元組的最高位當數據位來使用)。

不知道為何不這樣去節省這1個位元組呢?沒有看出來任何實現上的困難或者複雜度的增加。難道這是一個歷史問題?


你在題述里是「先驗」地知道了要存64位整數呀。但是這是個變長的數據結構,如果我解碼的時候事先不知道存了幾位元組,我又怎麼區分哪個是最後一位元組呢~


謝邀。以下只是個人見解。

如果這麼做,那麼意味著解碼邏輯依賴pb描述或者強行限定varint最大int64。

如果是前者,那麼如果pb描述變化,豈不是向前/向後不兼容?出現大改小會破壞整個message而不只是一個欄位被裁剪。

如果是後者,以後出現內置int128怎麼辦?怎麼無損過度?

所以還是不要捨不得那麼1個位元組。仔細算下來每個field必須是key-value。key你不會用那麼大的數字吧?value就算是10位元組加起來那一個位元組佔用也不到10%。

ps: 其實我現在更傾向於贊同capn"s proto或者flatbuffer那種思路,協議層就干好協議層的事兒就好了,別搞什麼壓縮。壓縮用另外的壓縮演算法。這樣一方面可以對數據中的字元串做字典,壓縮率更高,還可以根據時代演進換成更好或更快的演算法。還能保持正交性


其實主要原因就是@歐文韜說的,你這樣一搞128bit就炸了..

可以看go的varint的設計:

https://golang.org/src/encoding/binary/varint.go

專門解釋了為什麼不像你想的那樣做

// Design note:

// At most 10 bytes are needed for 64-bit values. The encoding could

// be more dense: a full 64-bit value needs an extra byte just to hold bit 63.

// Instead, the msb of the previous byte could be used to hold bit 63 since we

// know there can"t be more than 64 bits. This is a trivial improvement and

// would reduce the maximum encoding length to 9 bytes. However, it breaks the

// invariant that the msb is always the "continuation bit" and thus makes the

// format incompatible with a varint encoding for larger numbers (say 128-bit).


推薦閱讀:

C++ type_traits里哪些東西通過標準語言沒有可能實現而必須尋求編譯器內建支持?
看完 C++ 課本能否直接上手 Qt?
C++IO標準庫 能否非入侵的修改<< >>操作符的行為?
為什麼用C或C++表達Windows COM技術那麼複雜呢?是C或C++缺少什麼嗎~

TAG:protobuf | 字元編碼 | C | 遠程過程調用協議RPCRemoteProcedureCallProtocol |