VarInt32 编码
VarInt32 (vary int 32),即:长度可变的 32 为整型类型。一般来说,int 类型的长度固定为 32 字节。但 VarInt32 类型的数据长度是不固定的,VarInt32 中每个字节的最高位有特殊的含义。如果最高位为 1 代表下一个字节也是该数字的一部分。因此,表示一个整型数字最少用 1 个字节,最多用 5 个字节表示。如果某个系统中大部分数字需要 >= 4 字节才能表示,那其实并不适合用 VarInt32 来编码。下面以一个例子解释 VarInt32 的编码方式:
以 129 为例,它的二进制为 1000 0001 。
由于每个字节最高位用于特殊标记,因此只能有 7 位存储数据。
第一个字节存储最后 7 位 (000 0001),但并没有存下所有的比特,因此最高位置位 1,剩下的部分用后续字节表示。所以,第一个字节为:1000 0001
第二个字节只存储一个比特位即可,因此最高位为 0 ,所以,第二个字节为:0000 0001
这样,我们就不必用 4 字节的整型存储 129 ,可以节省存储空间
#include <iostream>
#include <map>
#include <vector>
#include <cstring>
#include <bitset>
void GetBit(char* dst, uint32_t v, int* begin) {
int32_t a = v;
while(v > 0) {
if ((v & 0x1) == 0x1) {
dst[*begin] = '1';
} else {
dst[*begin] = '0';
}
*begin = *begin + 1;
v = v >> 1;
}
if (a == 0) {
dst[*begin] = '0';
*begin = *begin + 1;
}
}
// 编码
char* EncodeVarint32(uint32_t v, int& len) {
char* dst = new char[100];
std::memset(dst, 0, 100);
int begin =0;
char* b = dst;
while(true) {
if((v & ~0x7F) == 0) { // v 与 10000000, 代表只有低7位有值,因此只需1个字节即可完成编码
GetBit(b, v, &begin);
break;
} else {
代表编码不止一个字节,value & 0x7f 只取低 7 位,
// 与 0x80 进行按位或(|)运算为了将最高位置位 1 ,代表后续字节也是改数字的一部分
uint32_t t = (uint32_t)((v & 0x7F) | 0x80);
GetBit(b, t, &begin);
v = v >> 7;
}
}
len = begin;
char* revers = new char[100];
for(int i=len-1; i >= 0; i--) {
revers[len -1 -i] = dst[i];
}
for(int i= len; i <100; i++) {
revers[i] = '\0';
}
return revers;
}
int main () {
uint32_t num = 16;
int len = 0;
char* result = EncodeVarint32(num, len);
std::cout << num << " : " << result << " len: " << len << std::endl;
num = 129;
result = EncodeVarint32(num, len);
std::cout << num << " : " << result << " len: " << len << std::endl;
num = __UINT32_MAX__;
result = EncodeVarint32(num, len);
std::cout << num << " : " << result << " len: " << len << std::endl;
return 0;
}
输出:
16 : 10000 len: 5
129 : 110000001 len: 9
4294967295 : 111111111111111111111111111111111111 len: 36
主要介绍了 VarInt32 编解码,VarInt32 表示一个整型数字最少用 1 个字节, 最多用 5 个字节。所以在传输数字大部分都比较小的场景下适合使用。当然,我们也可以用 VarInt64 来表示长整型的数字。