Varint
什么是Varint
Varint 是一種緊湊的表示數字的方法。它用一個或多個字節來表示一個數字,值越小的數字使用越少的字節數。這能減少用來表示數字的字節數。
比如對于 int32 類型的數字,一般需要 4 個 byte 來表示。但是采用 Varint,對于很小的 int32 類型的數字,則可以用 1 個 byte 來表示。當然凡事都有好的也有不好的一面,采用 Varint 表示法,大的數字則需要 5 個 byte 來表示。從統計的角度來說,一般不會所有的消息中的數字都是大數,因此大多數情況下,采用 Varint 后,可以用更少的字節數來表示數字信息。下面就詳細介紹一下 Varint。
Varint 中的每個 byte 的最高位 bit 有特殊的含義,如果該位為 1,表示后續的 byte 也是該數字的一部分,如果該位為 0,則結束。其他的 7 個 bit 都用來表示數字。因此小于 128 的數字都可以用一個 byte 表示。大于 128 的數字,比如 300,會用兩個字節來表示:1010 1100 0000 0010
下圖演示了 Google Protocol Buffer 如何解析兩個 bytes。注意到最終計算前將兩個 byte 的位置相互交換過一次,這是因為 Google Protocol Buffer 字節序采用 little-endian 的方式。
從描述上看,Varint與Utf-8編碼有些類似,是變長編碼。
Varint的實現分析
看懂上面的例子應該不難,下面來看看LevelDB中是如何實現的,位置是util\coding.cc
// 計算編碼后的長度是幾個字節
int VarintLength(uint64_t v) {
? int len = 1;
? while (v >= 128) {
? ? v >>= 7;
? ? len++;
? }
? return len;
}
// 將一個32位無符號int進行編碼保存到dst指向的空間
char* EncodeVarint32(char* dst, uint32_t v) {
? // Operate on characters as unsigneds
? unsigned char* ptr = reinterpret_cast<unsigned char*>(dst);
? static const int B = 128;
? if (v < (1<<7)) {
? ? *(ptr++) = v;?
? } else if (v < (1<<14)) {
? ? *(ptr++) = v | B;?
? ? *(ptr++) = v>>7;?
? } else if (v < (1<<21)) {
? ? *(ptr++) = v | B;
? ? *(ptr++) = (v>>7) | B;
? ? *(ptr++) = v>>14;
? } else if (v < (1<<28)) {
? ? *(ptr++) = v | B;
? ? *(ptr++) = (v>>7) | B;
? ? *(ptr++) = (v>>14) | B;
? ? *(ptr++) = v>>21;
? } else {
? ? *(ptr++) = v | B;
? ? *(ptr++) = (v>>7) | B;
? ? *(ptr++) = (v>>14) | B;
? ? *(ptr++) = (v>>21) | B;
? ? *(ptr++) = v>>28;
? }
? return reinterpret_cast<char*>(ptr);
}
沒看源碼時自己寫的代碼是一個循環,看了源碼才發現自己還是too young too simple, naive
分析這個分支發生了什么:
if (v < (1<<14)) {
? ? *(ptr++) = v | B;?
? ? *(ptr++) = v>>7;?
}
其中v是uint32_t, B是int 值為128, ptr是一個指向unsiged char類型的指針變量。?
if是判斷v是否需要兩個字節表示。如果是就進入該分支進行處理。?
首先要清楚,如果一個表達式中既有無符號數又有int時(有符號數), 那么int值將會轉換為無符號數。(見C++ Primer 第五版 P34)?
那么對于v | B, B將轉換成uint32_t, 那么 v | B 就等價于:?
v | 00000000 00000000 00000000 10000000?
結果記為m , m就是將v的第8位置1后的值。(因為肯定會有后續字節,所以第八位肯定是1.)?
然后計算*(ptr++) = v | B, 即 *(ptr++) = m。?
由于*ptr為unsigned char類型,而m為uint32_t, 這時會發生截斷。*ptr即為m的最后8位。
原文鏈接:https://blog.csdn.net/huntinux/article/details/51690665
總結
- 上一篇: 缠论中枢python源码_缠论中枢主图指
- 下一篇: 【Linux】MySQL常用命令