Post

varint编码

在leveldb的memtable里看到了存varint的字段,记一下这个。

在protobuf里其实也见到过

1. 原理

简单来讲,如果是32位数,会用1-5字节来表示这个数值,其中每个byte的most significant位,即数子表征的最大bit会作为是否需要继续往后扩充的依据

那么表征64位数的话,最大就要64 + 7 - 1 / 7 = 10字节来表示了

即每个byte,只有7bit是能用来有效表征数据的

2. 编码实现

直接看leveldb里一个64位的实现

1
2
3
4
5
6
7
8
9
10
char* EncodeVarint64(char* dst, uint64_t v) {
  static const int B = 128;
  uint8_t* ptr = reinterpret_cast<uint8_t*>(dst);
  while (v >= B) {
    *(ptr++) = v | B;
    v >>= 7;
  }
  *(ptr++) = static_cast<uint8_t>(v);
  return reinterpret_cast<char*>(ptr);
}

这个B是128=1«7, 只要比这个大就表示需要进位了,把v的低7位先写入

解码的就是凡过程来一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) {
  uint64_t result = 0;
  for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) {
    uint64_t byte = *(reinterpret_cast<const uint8_t*>(p));
    p++;
    if (byte & 128) {
      // More bytes are present
      result |= ((byte & 127) << shift);
    } else {
      result |= (byte << shift);
      *value = result;
      return reinterpret_cast<const char*>(p);
    }
  }
  return nullptr;
}

bool GetVarint64(Slice* input, uint64_t* value) {
  const char* p = input->data();
  const char* limit = p + input->size();
  const char* q = GetVarint64Ptr(p, limit, value);
  if (q == nullptr) {
    return false;
  } else {
    *input = Slice(q, limit - q);
    return true;
  }
}

这里主要解码的步骤在GetVarint64Ptr, 因为最多10字节表征,所以左shift的最大是9,然后对每个byte值做判断要不要继续扩

最后返回终点右侧的指针

3. 优势

这个优势就是在数据如果大多数都是小数据的时候,是能够做压缩的,比如只用1-3字节来表示的话,就是赚的,这个上限是2097151,大概是

This post is licensed under CC BY 4.0 by the author.

Trending Tags