前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【深入浅出leveldb】 比较器

【深入浅出leveldb】 比较器

作者头像
公众号guangcity
发布2021-07-09 15:51:33
7340
发布2021-07-09 15:51:33
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

【深入浅出leveldb】 比较器

源码版本【1.23】

代码位置【include/leveldb/comparator.h】【util/comparator.cc】

比较器里面包含了抽象类接口,同时提供了BytewiseComparatorImpl与InternalKeyComparator。

今天我们主要分析抽象类与BytewiseComparatorImpl以及相关无析构类。

1.抽象类

我们把注释删除掉之后,就只剩下下面的核心内容。

该类当中提供了4个接口,让子类去实现它,该类不可被实例化,因为该类是抽象类,同时提供的虚析构函数保证了继承之后,派生类可以调用对应析构释放内存。

在当前文件(comparator.h)中,还提供了BytewiseComparator函数。该函数的实现引申出线程安全的单例模式与无析构类如何编写。接下来看看如何实现。

代码语言:javascript
复制
class LEVELDB_EXPORT Comparator {
 public:
  virtual ~Comparator();
  virtual int Compare(const Slice& a, const Slice& b) const = 0;
  virtual const char* Name() const = 0;
  virtual void FindShortestSeparator(std::string* start,
                                     const Slice& limit) const = 0;
  virtual void FindShortSuccessor(std::string* key) const = 0;
};
LEVELDB_EXPORT const Comparator* BytewiseComparator();

2.线程安全的单例模式

BytewiseComparator在comparator.cc中实现如下:

代码语言:javascript
复制
const Comparator* BytewiseComparator() {
  static NoDestructor<BytewiseComparatorImpl> singleton;
  return singleton.get();
}

C++11保证了静态局部变量的初始化过程是线程安全的。

具体可以看:

https://www.zhihu.com/question/267013757

NoDestructor无析构类首先来看一下构造函数,位置在util/no_destructor.h

代码语言:javascript
复制
explicit NoDestructor(ConstructorArgTypes&&... constructor_args) {
  static_assert(sizeof(instance_storage_) >= sizeof(InstanceType),
                "instance_storage_ is not large enough to hold the instance");
  static_assert(
      alignof(decltype(instance_storage_)) >= alignof(InstanceType),
      "instance_storage_ does not meet the instance's alignment requirement");
  new (&instance_storage_)
      InstanceType(std::forward<ConstructorArgTypes>(constructor_args)...);

这里使用了placement new,在已经分配好的内存上构造对象,同时采用了完美转发传递参数。

禁止拷贝动作:

代码语言:javascript
复制
NoDestructor(const NoDestructor&) = delete;
NoDestructor& operator=(const NoDestructor&) = delete;

这里定义了默认的析构:

代码语言:javascript
复制
~NoDestructor() = default;

返回对象:

代码语言:javascript
复制
InstanceType* get() {
  return reinterpret_cast<InstanceType*>(&instance_storage_);
}

私有成员:

代码语言:javascript
复制
typename std::aligned_storage<sizeof(InstanceType),
                                alignof(InstanceType)>::type instance_storage_;

这里有个alignof(InstanceType)主要是取出InstanceType的字节对齐,例如:

64位操作系统gcc上,int字节对齐时4,而Test只有一个int成员,所以这里输出4。

代码语言:javascript
复制
class Test {
 private:
  int i;
};

void func() {
  cout << alignof(Test) << endl;  // 这一行输出为4
}

继续看aligned_storage:

代码语言:javascript
复制
template<std::size_t _Len, std::size_t _Align =
 __alignof__(typename __aligned_storage_msa<_Len>::__type)>
struct aligned_storage
{
  union type
  {
unsigned char __data[_Len];
struct __attribute__((__aligned__((_Align)))) { } __align;
  };
};

可以看到取出的type是个union,里面是有固定的数组,不考虑内部的__align,上述instance_storage_可以变为:

代码语言:javascript
复制
unsigned char[sizeof(InstanceType)] instance_storage_;

综上,NoDestructor构造函数会在NoDestructor的内存instance_storage_中采用placement new 构造对象,最后使用get返回单例对象。

3.BytewiseComparatorImpl

BytewiseComparatorImpl直接继承Comparator,下面是Name与Compare的实现。

代码语言:javascript
复制
class BytewiseComparatorImpl : public Comparator {
 public:
  BytewiseComparatorImpl() = default;

  const char* Name() const override { return "leveldb.BytewiseComparator"; }

  int Compare(const Slice& a, const Slice& b) const override {
    return a.compare(b);
  }
};

FindShortestSeparator(start, limit)作用是start < limit,就把start修改为*start和limit的共同前缀的ascii加1。

例如:

代码语言:javascript
复制
*start: hellow
limit: helloz
返回:*start变为hellox

看一下具体实现:首先拿出两者最小长度,从index=0开始到*start与limit的某个index值不相等结束。

如果此时index到头了 不做处理,说明两者完全一致,否则对*start的ascii加1。

代码语言:javascript
复制
void FindShortestSeparator(std::string* start,
                           const Slice& limit) const override {
  // Find length of common prefix
  size_t min_length = std::min(start->size(), limit.size());
  size_t diff_index = 0;
  while ((diff_index < min_length) &&
         ((*start)[diff_index] == limit[diff_index])) {
    diff_index++;
  }

  if (diff_index >= min_length) {
    // Do not shorten if one string is a prefix of the other
  } else {
    uint8_t diff_byte = static_cast<uint8_t>((*start)[diff_index]);
    if (diff_byte < static_cast<uint8_t>(0xff) &&
        diff_byte + 1 < static_cast<uint8_t>(limit[diff_index])) {
      (*start)[diff_index]++;
      start->resize(diff_index + 1);
      assert(Compare(*start, limit) < 0);
    }
  }
}

另一个函数是FindShortSuccessor。直接对key中第一个以uint8方式+1的字节+1,清除该位后面的数据。

代码语言:javascript
复制
void FindShortSuccessor(std::string* key) const override {
  // Find first character that can be incremented
  size_t n = key->size();
  for (size_t i = 0; i < n; i++) {
    const uint8_t byte = (*key)[i];
    if (byte != static_cast<uint8_t>(0xff)) {
      (*key)[i] = byte + 1;
      key->resize(i + 1);
      return;
    }
  }
  // *key is a run of 0xffs.  Leave it alone.
}

测试:

代码语言:javascript
复制
string s1("hellow"), s2("hellow");
bt->FindShortestSeparator(&s1, l);
cout << s1 << endl;  // hellox

bt->FindShortSuccessor(&s2);
cout << s2 << endl;  // i

本节完~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【深入浅出leveldb】 比较器
    • 1.抽象类
      • 2.线程安全的单例模式
        • 3.BytewiseComparatorImpl
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档