RocksDB源码-0x0D-协议设计TLV说过TLV的协议设计,编解码也是一种协议设计,它是字节层面的而已。
在TLV协议设计的时候最多的就两个场景
- 1 只有tag,隐式length,在RocksDB里面tag就是枚举
enum Tag : uint32_t
- 2 有tag/length/value,这个length约定的是32位数字
所以可以看得出来,高频使用的就是数字和字符串,而数字的使用又有特点
- 1 32位长度的数字覆盖面最广
- 2 会有特定场景对64位长度数字有需要
- 3 数字大部分是小数字,但上限高,比如wal日志文件编号或者sst文件编号
以32位数字为例,声明为int占4字节,这样不管数字大小都占4字节。但是因为实际使用大部分都是小数字,所以并不需要占满4字节就可以表达出来,另一方面上限高,得满足数字上限的要求。所以综合来看,自定义编码用变长数字替代定长数字可以省下来很大的空间。
1 32位数字的编解码
1.1 编码
1 2 3 4 5 6 7 8 9 10 11 12 13
|
inline void PutVarint32(std::string* dst, uint32_t v) { char buf[5]; char* ptr = EncodeVarint32(buf, v); dst->append(buf, static_cast<size_t>(ptr - buf)); }
|
处理方式很简单,就是把定长数字每7位编到变长数字的8位里面
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
char* EncodeVarint32(char* dst, uint32_t v) { 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); }
|
1.2 解码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
inline bool GetVarint32(Slice* input, uint32_t* value) { const char* p = input->data(); const char* limit = p + input->size(); const char* q = GetVarint32Ptr(p, limit, value); if (q == nullptr) { return false; } else { *input = Slice(q, static_cast<size_t>(limit - q)); return true; } }
|
本质就是编码的逆向,编的时候7位编,解的时候就每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 29 30
|
const char* GetVarint32PtrFallback(const char* p, const char* limit, uint32_t* value) { uint32_t result = 0; for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { uint32_t byte = *(reinterpret_cast<const unsigned char*>(p)); p++; if (byte & 128) { result |= ((byte & 127) << shift); } else { result |= (byte << shift); *value = result; return reinterpret_cast<const char*>(p); } } return nullptr; }
|
2 显式length的编解码
2.1 编码
2.2 解码
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
|
inline bool GetLengthPrefixedSlice(Slice* input, Slice* result) { uint32_t len = 0; if (GetVarint32(input, &len) && input->size() >= len) {
*result = Slice(input->data(), len); input->remove_prefix(len); return true; } else { return false; } }
|