1.Protocol Buffer編碼
什么是Protocol Buffer(下稱pb),這邊也就不介紹了阻星,通常要求不是特別嚴(yán)格的時候Json,甚至XML就能滿足要求妥箕。
如需了解這邊傳送門:
message Test1 { required int32 a = 1; }
pb以二進(jìn)制形式存儲更舞,以Base 128 Varints
進(jìn)行編碼畦幢,Test1的存儲內(nèi)容僅為3個字節(jié):
08 96 01
這是什么意思呢缆蝉?
Varints are a method of serializing integers using one or more bytes. Smaller numbers take a smaller number of bytes.
Each byte in a varint, except the last byte, has the most significant bit (msb) set – this indicates that there are further bytes to come. The lower 7 bits of each byte are used to store the two's complement representation of the number in groups of 7 bits, least significant group first.
在Varints中,每個Byte的取低7位進(jìn)行存儲實際內(nèi)容刊头。最高位用1來表示most significant bit
,既表示下一個Byte跟當(dāng)前Byte存儲的是同一個Varint印颤;用0來表示least significant bit
。看完這里年局,相信大家知道咸产,對于一個完全的int64,其實用varints真實物理存儲會超過64位矢否。
那么接下來解釋Test1的存儲脑溢,首先先一張表
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 |
當(dāng)一個Message被encode的時候,message的key其實只存了filed number以及一個wire type,filed number被你定義在.proto文件中屑彻,wire type即上面的表格中對應(yīng)的。
每一個varint key 為 field_number << 3 | wire_type壶谒,言下之意,一個varint key的某三位存的是wire type,后4位存的是filed number。
以Test1為例陨界,我們知道第一Byte通常就是varint key,Test1的內(nèi)容是 08菌瘪,也就是:
000 1000
于是我們有filed number = 0001,wire type = 0阱当。因此我們也得到了如下信息,在filed number = 1的地方 存儲著一個varint俏扩。
然后我們接下去讀
96 01 = 1001 00110 0000 0001
基于varint的存儲表示: 000 0001 001 00110 -> 10010110 = 150
所以我們知道存儲的值為150弊添。
2.Lua PB 與int_64
情景是這樣的,我們有一個server用的是openresty油坝,語言用的是lua,lua的解釋器是luajit澈圈。至于為什么是luajit,我就不展開了窍帝,我們直奔主題,luajit是不支持int_64盯桦。但是可以通過ffi,間接的操作int_64拥峦。
lua本身也沒有官方支持的pb解釋器卖子,我們用的是下面這個庫:
https://github.com/Neopallium/lua-pb
但是之前這個庫在pack int_64的時候有一個BUG(在我們同事提了issue后,剛剛上去看了一下已修復(fù),囧rz。羽利。这弧。)
但我還是把老代碼翻出來吧(要不然就寫不下去了- -皇帮,只能怪自己手慢属拾,再他沒更新的時候?qū)懢投嗪冒渐白。?/p>
先稍微解釋一下這個方法,h存的是高32位虏劲,l則是低32位柒巫。
每次取7位堡掏,然后第八位補(bǔ)1,進(jìn)行一個遞歸(如有不懂見上部分)。
理想的是遞歸4次后扮休,再處理高位。但是有一個問題八堡,就是如果地位是一個比較小的值,于是就可以遞歸1-3三次后就開始處理高位了,于是就值就出錯了叔壤。
我們最終的實現(xiàn)是加了一個標(biāo)志位凳兵,記錄地位處理次數(shù),強(qiáng)制轉(zhuǎn)4個B形庭。不過還是推薦這個作者最后的實現(xiàn)方式,
local function varint64_next_byte_h_l(h, l) if h ~= 0 then -- encode lower 28 bits. local b1 = bor(band(l, 0x7F), 0x80) local b2 = bor(band(rshift(l, 7), 0x7F), 0x80) local b3 = bor(band(rshift(l, 14), 0x7F), 0x80) local b4 = bor(band(rshift(l, 21), 0x7F), 0x80) -- merg high 32-bits with the last 4 bits from the low 32-bits l = (h * 16) + band(rshift(l, 28), 0xF) -- Use variable length encoding of higher 36 bits. return b1, b2, b3, b4, varint_next_byte(l) end -- No high bits. Use variable length encoding of low bits. return varint_next_byte(l) end