使用Thrift格式進行數據序列化反序列化,thrift的存儲格式,主要使用thrift的TCompactProtocol。
發現該序列化方式主要使用了TLV格式式來存儲每個字段,使用VARINT來表示其中的L。
1. TLV 格式
很簡單,Type-length-value(類型-長度-值)。在一串字節中,使用該方式標示出一個自定義的字段。
三個域的表示方式均可自定義。如使用1個字節標示數據類型T,使用4個字節標示數據長度L,之後使用L個字節來表示數據的值。(其實Thrift的TBinaryProtocol就是這種方式)
2. VARINT 數值壓縮存儲
c中int使用4個固定字節表式,即使數字很小。假如使用int16,則不能表示較大的數字。
而VARINT是一種可變長度的表示數字的方法,當數字較小時可以使用1個字節,如果比較大需要利用5個。
3.java實現
int轉爲varint,TCompact 的java實現如下:
private void writeVarint32(int n) {
int idx = 0;
while (true) {
if ((n & ~0x7F) == 0) {
i32buf[idx++] = (byte)n;
break;
} else {
i32buf[idx++] = (byte)((n & 0x7F) | 0x80);
n >>>= 7;
}
}
trans_.write(i32buf, 0, idx); // trans_不用管,可以看作字節流
}
其實就是,依次從字節串的末尾選取7位,最高位添加1或0構造出1個字節。並且,只有最後一次選取時最高位置爲0,此時剩餘的位構成到數字是小於 1000 0000,的所以添加0並不會影響其大小。如下:
10進制: 296
16進制: x01 x28
2進制: 0000 0001 0010 1000
轉換:a --- ---- ==> 1010 1000 選擇末7位,最高位置爲1
b -- ---- - ==> 1010 1000 0000 0010 右移7位,選擇末7位,最高位置爲0
VARINT: 1010 1000 0000 0010
16進制:xa8 x02
很明顯,通過這種方法,我們得到的VARINT,依次讀取每個字節,只有表示該VARINT的最後一個字節,最高位爲0。所以,當我們讀取VARINT時,只要讀取到最高位爲0的字節時,就表示已經是VARINT的最後一個字節了。
結合TLV就是:先讀取1個字節得到類型T;然後讀取n個字節計算出長度L,其中第n個字節的最高位爲0;然後讀取L個字節,表示我們的V。
4.python實現
最後,我們看下讀取VARINT時,python的處理方法:
def readVarint(trans):
result = 0
shift = 0
while True:
x = trans.readAll(1) // 讀取下一個字符
byte = ord(x) // 轉成整數表示
result |= (byte & 0x7f) << shift // 將該字節去掉最高位放在已有結果的左側
if byte >> 7 == 0: // 如果該字節最高位是0,結束
return result
shift += 7