【leveldb】变长编码Varint(八)

在阅读Memtable处理流程时看到了Varint32、Varint64,一开始不是太理解,导致阅读Memtable出现一定障碍,所以这里就单独列出来讲下,下文用Varint特指Varint32

一、什么是变长编码
【定义】

Varint是一种通过一个或多个字节来表示整数的方法。即之前用固定字节数来表示一个正数,Varint通过可变的字节数来表示一个整数。

【范围】

对于32位(Varint32)整形数经变长编码后占用1~5个Byte,小的数字用1个字节,大的数字用5个字节。
对于64为(Varint64)整形数经变长编码后占用1~10个Byte,小的数字用1个字节,大的数字用10个字节。

【表示】

在这里插入图片描述
最高有效状态位msb:

  • 为1则表明后面的字节还是属于当前数据的;
  • 为0则表是当前字节是最后一个字节数据。

有效数据位:
每个变长编码字节用低7位来存储数字的二进制补码表示。

【举例】

leveldb中是以小端方式存储变长编码的,Varint编码是取原数据二进制的每7位为一个单元。
图中Varint编码最左边是低地址,最右边是高地址。

1:

在这里插入图片描述
因为127一个字节表示即可,后面已无字节是当前数据的,所以msb是0。

2:

在这里插入图片描述
leveldb的Varint编码是小端存储(即低字节存放在低地址处),同时128是8位二进制数,
所以Varint编码需要两个字节。因为最右边字节是属于最左边,所以最左边编码msb位是1,
而最右边已是最后一个字节,后面已无字节,所以msb位是0。

3:

在这里插入图片描述
解释参考1、2。

二、作用

通过上面的例子我们看到127需要一个字节表示,而123456需要三个字节表示,如果固定用一个字节表示整数则最大是255(无符类型),无法表示123456这种超过255的。
如果用三个字节表示,对应小于255的这种只需一个字节表示的整数则浪费了两个字节。leveldb存储为了节省空间,则用一种变长编码的方式Varint来表示整形。因为小的数字只占用1个字节,大的则会占用5个字节,而根据统计学来说,小数字使用频率大于大数字,所以利用Varint编码能够起到压缩空间的作用。

三、leveldb中的实现
【编码】
<!编码参照上述对Varint的说明看起来应该很好懂>
char* EncodeVarint32(char* dst, uint32_t v) {
  // Operate on characters as unsigneds
  uint8_t* ptr = reinterpret_cast<uint8_t*>(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);
}

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);
}
【解码】
<!解码就是编码的逆过程,这里涉及到对Slice数据的解析
  重点关注“GetVarint32Ptr()”,“GetVarint32PtrFallback()>
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, limit - q);
    return true;
  }
}

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) {
      <!128就是一个八位,最高位为1,其余位为0,128相与为0表示待解码的result<=127,所以长度就是一个字节
        直接赋值给*Value即可。
        >
      *value = result;
      return p + 1;
    }
  }
  return GetVarint32PtrFallback(p, limit, value);
}

const char* GetVarint32PtrFallback(const char* p, const char* limit,
                                   uint32_t* value) {
  <!对于超过一个字节的长度编码,解码的过程就是按小端顺序,
    每7位取出,然后移位来组装最后的实际长度,组装结束的表示就是MSB位为0>                                 
  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) {
      // More bytes are present
      result |= ((byte & 127) << shift);
    } else {
      result |= (byte << shift);
      *value = result;
      return reinterpret_cast<const char*>(p);
    }
  }
  return nullptr;
}

<!Varint64位的流程可Varint32原理一样>
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;
  }
}

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;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Python中,"import leveldb"是用于导入LevelDB的Python开发包,使得我们可以在代码中使用LevelDB的功能和方法。 当我们在代码中使用"import leveldb"时,我们可以通过创建一个LevelDB对象来打开一个数据库连接,从而可以对数据库进行读写操作。然而,如果我们尝试在已经打开的连接上再次打开连接,就会引发错误。这是因为LevelDB只允许一个进程同时持有一个数据库的锁定。如果数据库已经被其他进程锁定,就会出现"leveldb.LevelDBError: IO error: lock /var/tmp/ldb1.ldb/LOCK: already held by process"的错误提示。 此外,LevelDB还提供了一些API用法。在使用LevelDB之前,我们可以包装相关的import语句和Options对象来打开和关闭数据库连接,以及其他操作。具体的API用法可以参考LevelDB的官方文档或相关教程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [LevelDB的Python开发包 py-leveldb基本使用方法的代码](https://blog.csdn.net/weixin_43896490/article/details/121946555)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [leveldb 的部署和使用](https://blog.csdn.net/Moolight_shadow/article/details/119276763)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [leveldb:LevelDB到Java的端口](https://download.csdn.net/download/weixin_42098892/18545599)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值