Leveldb varint 原理解析

原创 2018年04月17日 19:59:20

varint 介绍

我们知道 uint32_t 类型占用4个byte,uint64_t 占用8个byte, 但是对于比较小的数字来说,使用uint32_t 或者uint64_t 存储会比较浪费,varint 的思想是根据数字所需大小使用unsigned char* 指针存储数据,节约内存。

leveldb 实现

leveldb 中的varint实现原理简单,每个byte 使用最高bit的 0/1值代表此整数值是否结束,用剩下的7个bit 存储实际的数值。知道最后一个byte 的最高bit 是0 表示整数结束。因此小于128的数据都可以用一个byte 来表示,大于128的,比如说300,使用varint 编码的话需要两个字节10101100 0000 0010

leveldb 32位int变长编码实现

正常情况下,32位int占用4个byte, varint 编码中每个byte 中的最高bit 用来表示该byte 是不是最后一个byte,所以针对大的数varint 编码可能需要5个byte才能表示。leveldb 实现中5个if 分支对应varint占用1到5个byte 的情况。

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);
}

对应的varint 解码的思路是从低byte 到高byte遍历,直到找到最后一个表示编码结束的byte(判断条件是 byte & 128 == 1),代码如下

// 针对一个byte情况直接处理,大于一个byte 时,
// 使用 GetVarint32PtrFallback 处理
inline const char* GetVarint32Ptr(const char* p,
                                  const char* limit,
                                  uint32_t* value) {
  if (p < limit) {
    uint32_t result = *(reinterpret_cast<const unsigned char*>(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 unsigned char*>(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 NULL;
}

64位变长编码的实现

64位整形最多需要(10 * 8 - 10 > 64) 10个byte 来保存,类似于32位int的编码,需要写10个if-else 分支,针对64位整形的编码,leveldb 给出了更优雅的解决方案。

char* EncodeVarint64(char* dst, uint64_t v) {
  static const int B = 128;
  unsigned char* ptr = reinterpret_cast<unsigned char*>(dst);
  while (v >= B) {
    *(ptr++) = (v & (B-1)) | B;
    v >>= 7;
  }
  *(ptr++) = static_cast<unsigned char>(v);
  return reinterpret_cast<char*>(ptr);
}

针对64位整形的解码,leveldb 实现同样是类似的逻辑。也是从低位byte 开始向高位byte遍历判断编码是否结束。代码实现如下

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 unsigned char*>(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 NULL;
}

总结

varint 的思想是针对32位或者64位整形类型来说存储的大部分数据都是多余的,因此使用每个byte的最高位来标识编码是否结束,使用一个char 型指针存储编码的结果,针对很多情况可以节约存储空间。

图标绘制原理与案例解析

-
  • 1970年01月01日 08:00

LevelDB : Varint

参考: 1. http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ 2. http://blog.csdn.net/sparkliang...
  • chj90220
  • chj90220
  • 2016-06-16 11:30:26
  • 644

leveldb学习:Memtable和Varint

Memtable:Memtable是leveldb数据在内存中的存储形式,写操作的数据都会先写到memtable中,memtable的size有限制最大值(write_buffer_size)。mem...
  • tmshasha
  • tmshasha
  • 2015-08-24 19:28:51
  • 925

leveldb学习开篇

打算研究生在读期间好好学习下leveldb的源码,期望能够从中感悟一下现代软件的开发流程和设计模式,体验一下google编程规范,不得不承认leveldb源码写得很有美感,最后最重要的当然是希望从中进...
  • tmshasha
  • tmshasha
  • 2015-06-16 22:06:03
  • 851

LevelDB原理探究和代码分析(下)

3.文件结构 3.1 文件组成 Level DB包含一下几种文件: 文件类型 说明 dbname/MANIFEST-[0-9]+   清单文件             ...
  • u012432778
  • u012432778
  • 2015-06-14 21:31:51
  • 809

LevelDB原理探究与代码分析(上)

1.概述 LevelDB(http://code.google.com/p/leveldb/)是google开源的Key/Value存储系统,它的committer阵容相当强大,基本上是bigta...
  • u012432778
  • u012432778
  • 2015-06-14 20:34:04
  • 1827

leveldb实现解析

  • 2012年03月07日 13:27
  • 663KB
  • 下载

Leveldb源码解析第一篇【Data Block】

版权声明:本文为博主原创文章,未经博主允许不得转载。 leveldb 作为一个 key-value 数据库,它和 redis 的区别在于不仅没有把所有的数据放在内存中,而是把大部分数据放在了磁盘中le...
  • xuxuan_csd
  • xuxuan_csd
  • 2017-06-09 17:17:41
  • 3893

leveldb-1.18 源码及 leveldb实现解析.PDF

  • 2015年05月23日 15:53
  • 844KB
  • 下载

【神经网络与深度学习】leveldb的实现原理

郑重声明:本篇博客是自己学习 Leveldb 实现原理时参考了郎格科技系列博客整理的,原文地址:http://www.samecity.com/blog/Index.asp?SortID=12,只是为...
  • LG1259156776
  • LG1259156776
  • 2016-09-18 21:09:51
  • 664
收藏助手
不良信息举报
您举报文章:Leveldb varint 原理解析
举报原因:
原因补充:

(最多只允许输入30个字)