GitHub: https://github.com/storagezhang

Emai: debugzhang@163.com

华为云社区: https://bbs.huaweicloud.com/blogs/253047

LevelDB: https://github.com/google/leveldb

Varint 编码

LevelDB 内部采用变长编码,对数据进行压缩,减少存储空间,再采用 CRC 校验数据。

整型数据是以 32(64) 位来表示的,以 32 位为例,存储需要 4 个字节。

如果一个整数的大小在 256 以内,那么只需要一个字节就可以存储这个整数,可以节省 3 个字节。

Varint 就是根据这种思想来序列化整数的,它是一种使用一个或多个字节序列化整数的方法,会把整型数据编码为变长字节。

Varint 中的每个字节都设置为最高有效位:

  • 如果该位为 0,表示结束,当前字节的剩余 7 位就是该数据的表示。

    • 表示整数 1,需要一个字节:0000 0001
  • 如果该位为 1,表示后续的字节也是该整型数据的一部分;

    • 表示整数 300,需要两个字节:1010 1100 0000 0010

这也表示 Varint 编码后是按小端排序的。

字节顺序,又称端序或尾序(英语:Endianness),在计算机科学领域中,指电脑内存中或在数字通信链路中,组成多字节的字的字节的排列顺序。

字节的排列方式有两个通用规则。例如,将一个多位数的低位放在较小的地址处,高位放在较大的地址处,则称小端序;反之则称大端序。在网络应用中,字节序是一个必须被考虑的因素,因为不同机器类型可能采用不同标准的字节序,所以均按照网络标准转化。

因此,32 位整型数据经过 Varint 编码后占用 1~5 个字节(5 * 8 - 5 > 32),64 位整型数据编码后占用 1~10 个字节(10 * 8 - 10 > 64)。

在实际场景中,由于小数字的使用率远远高于大数字,所以在大部分场景中,通过 Varint 编码的数据都可以起到很好的压缩效果。

编码实现

EncodeVarint64 将 uint64_t 编码为 Varint 类型的字节流:

char* EncodeVarint64(char* dst, uint64_t v) {
static const int B = 128;
uint8_t* ptr = reinterpret_cast<uint8_t*>(dst); while (v >= B) {
// B=128=0x80, v|B 表示在最高位上加 1
// *ptr 是 uint8_t 类型的,即每次取下 7 位数据
*(ptr++) = v | B; // 右移 7 位, 继续处理后面的数据
v >>= 7;
} // 处理最后一个字节的小于 128 的数据
*(ptr++) = static_cast<uint8_t>(v);
return reinterpret_cast<char*>(ptr);
}

EncodeVarint32uint32_t 编码为 Varint 类型的字节流,其实现与 EncodeVarint64 类似,但是可能因为最多 5 个字节,所以是硬编码的:

char* EncodeVarint32(char* dst, uint32_t v) {
uint8_t* ptr = reinterpret_cast<uint8_t*>(dst);
static const int B = 128; if (v < (1 << 7)) {
// v < 0x80,可以用 7 位表示,占一个字节
*(ptr++) = v;
} else if (v < (1 << 14)) {
// 0x80 <= v < 0x4000,可以用 14 位表示,占两个字节
*(ptr++) = v | B;
*(ptr++) = v >> 7;
} else if (v < (1 << 21)) {
// 0x4000 <= v < 0x200000,可以用 21 位表示,占三个字节
*(ptr++) = v | B;
*(ptr++) = (v >> 7) | B;
*(ptr++) = v >> 14;
} else if (v < (1 << 28)) {
// 0x200000 <= v < 0x10000000,可以用 28 位表示,占四个字节
*(ptr++) = v | B;
*(ptr++) = (v >> 7) | B;
*(ptr++) = (v >> 14) | B;
*(ptr++) = v >> 21;
} else {
// 0x10000000 <= v < 0x100000000,可以用 35 位表示,占五个字节
*(ptr++) = v | B;
*(ptr++) = (v >> 7) | B;
*(ptr++) = (v >> 14) | B;
*(ptr++) = (v >> 21) | B;
*(ptr++) = v >> 28;
}
return reinterpret_cast<char*>(ptr);
}

解码实现

解码就是编码的逆过程,同样是利用位运算进行。

GetVarint64Ptr 将输入的 Varint 类型字节流转换成 uint64_t 整型数据:

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) {
// byte & 0x80 判断最高有效位为 1 // byte & 0x7f:获取 7 位有效数据
// (b & 0x7F) << shift:Varint 编码是小端排序,每处理一个数据,都需要向高位移动 7 位
// result | ((byte & 127) << shift):连接高位数据和低位数据
result |= ((byte & 127) << shift);
} else {
// byte & 0x80 判断最高有效位为 0,最后 7 位数据
result |= (byte << shift);
*value = result;
return reinterpret_cast<const char*>(p);
}
}
return nullptr;
}

GetVarint32Ptr 与GetVarint64Ptr 算法相同,唯一的区别在于对小于 128 的数据进行特判,如果小于则直接返回结果,这样设计的原因是大部分数字都比 128 小,可以通过内联函数提高计算效率。

inline const char* GetVarint32Ptr(const char* p, const char* limit,
uint32_t* value) {
if (p < limit) {
uint32_t result = *(reinterpret_cast<const uint8_t*>(p));
if ((result & 128) == 0) {
*value = result;
return p + 1;
}
}
return GetVarint32PtrFallback(p, limit, value);
} 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 uint8_t*>(p));
p++;
if (byte & 128) {
result |= ((byte & 127) << shift);
} else {
result |= (byte << shift);
*value = result;
return reinterpret_cast<const char*>(p);
}
}
return nullptr;
}

LevelDB 源码解析之 Varint 编码的更多相关文章

  1. LevelDB 源码解析之 Random 随机数

    GitHub: https://github.com/storagezhang Emai: debugzhang@163.com 华为云社区: https://bbs.huaweicloud.com/ ...

  2. Leveldb源码解析之Bloom Filter

    Bloom Filter,即布隆过滤器,是一种空间效率很高的随机数据结构. 原理:开辟m个bit位数组的空间,并全部置零,使用k个哈希函数将元素映射到数组中,相应位置1.如下图,元素K通过哈希函数h1 ...

  3. LevelDB 源码解析之 Arena

    GitHub: https://github.com/storagezhang Emai: debugzhang@163.com 华为云社区: https://bbs.huaweicloud.com/ ...

  4. Alink漫谈(十八) :源码解析 之 多列字符串编码MultiStringIndexer

    Alink漫谈(十八) :源码解析 之 多列字符串编码MultiStringIndexer 目录 Alink漫谈(十八) :源码解析 之 多列字符串编码MultiStringIndexer 0x00 ...

  5. 源码解析-Volley(转自codeKK)

    Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon ...

  6. 实战录 | Kafka-0.10 Consumer源码解析

    <实战录>导语 前方高能!请注意本期攻城狮幽默细胞爆表,坐地铁的拉好把手,喝水的就建议暂时先别喝了:)本期分享人为云端卫士大数据工程师韩宝君,将带来Kafka-0.10 Consumer源 ...

  7. Android AsyncTask 源码解析

    1. 官方介绍 public abstract class AsyncTask extends Object  java.lang.Object    ↳ android.os.AsyncTask&l ...

  8. LevelDB源码剖析

    LevelDB的公共部件并不复杂,但为了更好的理解其各个核心模块的实现,此处挑几个关键的部件先行备忘. Arena(内存领地) Arena类用于内存管理,其存在的价值在于: 提高程序性能,减少Heap ...

  9. Volley 源码解析

    Volley 源码解析 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. ...

随机推荐

  1. Serverless & FaaS

    Serverless & FaaS Function as a Service 通过 Functions(一个事件驱动型无服务器计算平台,还可以解决复杂的业务流程问题)更加高效地进行开发; 在 ...

  2. Vue SSR in Action

    Vue SSR in Action https://ssr.vuejs.org/ https://ssr.vuejs.org/api/ https://ssr.vuejs.org/guide/data ...

  3. dart2native 使用Dart 在macOS,Windows或Linux上创建命令行工具

    下载dart2.6以上 >dart2native --help 编写源文件 // bin\main.dart main(List<String> args) { print('hel ...

  4. Dart 处理json,built_value库

    原文链接 文档 import 'dart:convert'; main() async { // json 转化为 map String jsonStr = ''' [ {"name&quo ...

  5. 为什么Linux需要虚拟内存

    本文转载自为什么 Linux 需要虚拟内存 导语 操作系统中的 CPU 和主内存(Main memory)都是稀缺资源,所有运行在当前操作系统的进程会共享系统中的 CPU 和内存资源,操作系统会使用 ...

  6. 使用 Tye 辅助开发 k8s 应用竟如此简单(四)

    续上篇,这篇我们来进一步探索 Tye 更多的使用方法.本篇我们来了解一下如何在 Tye 中如何进行日志的统一管理. Newbe.Claptrap 是一个用于轻松应对并发问题的分布式开发框架.如果您是首 ...

  7. Why GraphQL? 6个问题

    Why GraphQL? 6个问题 GraphQL, 是一个API的标准: specification. 对于每个新技术, 要搞清楚的6个问题: 1.这个技术出现的背景, 初衷, 要达到什么样的目标或 ...

  8. SpringBoot读取配置文件的内容

    1.@Value读取 在springboot项目中,如果要读取配置文件application.properties或application.yml文件的内容,可以使用自带的注解@Value.以prop ...

  9. 力扣541. 反转字符串 II

    原题 1 class Solution: 2 def reverseStr(self, s: str, k: int) -> str: 3 begin,lens,ans = 0,len(s),' ...

  10. Shiro反序列化漏洞检测、dnslog

    目录 信息收集 poc 参考 信息收集 poc # pip install pycrypto import sys import base64 import uuid from random impo ...