当前位置:首页 > 芯闻号 > 充电吧
[导读]一.原理先看一个例子,我们为书店写一个管理图书的程序,书店里有许多书Book,每个书架(BookShelf)上有多本书。类结构如下所示:class Book { private:  string bo

一.原理

先看一个例子,我们为书店写一个管理图书的程序,书店里有许多书Book,每个书架(BookShelf)上有多本书。
类结构如下所示:

class Book {
private:
 string book_name_;
};
class Shelf {
 private:
  vectorbooks_;
};

如何遍历书架上所有的书呢?一种实现方法是:

vector& GetBooks() const {
  return books_;
}

这样的实现暴漏了内部太多的细节,调用者根本就不需要知道Shelf存储Book的方式,仅仅需要遍历所有的数据即可。而且这样当我们换用另外一种数据结构存储Book时,客户端的代码就需要进行修改。但是如果使用Iterator模式则没有这个问题。

具体的我们需要遍历书店中所有的书,现在应该如何实现呢?
一种实现方式是,由BookStore负责保存中间状态,包括当前遍历到了哪个书架,遍历到了书架上的哪本书。

class BookStore {
 Iterator* NewIterator() const;
 private:
  vectorshelf_;
  vector::iterator shelf_iter_;
  vector::iterator book_iter_;
};

这种实现方法对外是干净的,但是对于BookStore的维护者来说却是不友好的,Iterator的中间状态不是BookStore的成员,逻辑上不应该由BookStore维护。而且当两个甚至多个用户同时遍历书店时BookStore得同时维护多个中间状态,极其容易出错。更好的一种实现方式是,把遍历Iterator相关的代码和状态封装成一个类,有两个层级Shelf 和 Book,这个类的名字我们叫做TwoLevelIteator。

在双层迭代器中,level1中的迭代器指向的是一个容器,level2中的迭代器才指向真正的元素。对应到书店,level1指向书架(对图书进行分类),level2指向图书。当要查找某本书时,先要定位到书架,再在该书架中根据书的编号找到具体的书。


二.LevelDB中的实现

1.头文件


class TwoLevelIterator: public Iterator {
 public:
  TwoLevelIterator(
    Iterator* index_iter,
    BlockFunction block_function,
    void* arg,
    const ReadOptions& options);

  virtual ~TwoLevelIterator();

  virtual void Seek(const Slice& target);
  virtual void SeekToFirst();
  virtual void SeekToLast();
  virtual void Next();
  virtual void Prev();

  virtual bool Valid() const {
    return data_iter_.Valid();
  }
  virtual Slice key() const {
    assert(Valid());
    return data_iter_.key();
  }
  virtual Slice value() const {
    assert(Valid());
    return data_iter_.value();
  }
  virtual Status status() const {
    // It'd be nice if status() returned a const Status& instead of a Status
    if (!index_iter_.status().ok()) {
      return index_iter_.status();
    } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) {
      return data_iter_.status();
    } else {
      return status_;
    }
  }

 private:
  void SaveError(const Status& s) {
    if (status_.ok() && !s.ok()) status_ = s;
  }
  void SkipEmptyDataBlocksForward();
  void SkipEmptyDataBlocksBackward();
  void SetDataIterator(Iterator* data_iter);
  void InitDataBlock();

  BlockFunction block_function_;//生成Data Block中block_data字段的迭代器
  void* arg_;
  const ReadOptions options_;
  Status status_;
  IteratorWrapper index_iter_;//第一层迭代器,Index Block的block_data字段迭代器的代理
  IteratorWrapper data_iter_; //第二层迭代器,Data Block的block_data字段迭代器的代理
  // If data_iter_ is non-NULL, then "data_block_handle_" holds the
  // "index_value" passed to block_function_ to create the data_iter_.
  std::string data_block_handle_;//handle中间变量
};

这里需要注意的是,两层迭代器都是IteratorWrapper类型而不是iter,主要是为了缓存key和valid,避免每次都要调用iterator->key()和iterator->valid(),因为虚函数调的频繁调用,有一定的性能消耗。至于为何有性能损耗,可参考:

C++中虚函数(virtual function)到底有多慢

为什么 C++ 中使用虚函数时会影响效率?

2.迭代器的初始化


void TwoLevelIterator::InitDataBlock() {
  if (!index_iter_.Valid()) {
	// 当index_iter_无效时,让data_iter_也无效
    SetDataIterator(NULL);
  } else {
    // index_iter_是Index Block中block_data字段迭代器的代理
    // handle是对应的Data Block的偏移和该Data Block的block_data字段大小编码后的结果
    Slice handle = index_iter_.value();
    if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) {
    // 如果data_iter_已经创建了,什么都不用干,这可以防止InitDataBlock被多次调用
    } else {
      // 创建Data Block中block_data字段的迭代器
      Iterator* iter = (*block_function_)(arg_, options_, handle);
      // 将handle转化为data_block_handle_
      data_block_handle_.assign(handle.data(), handle.size());
      // 将iter传给其代理data_inter_
      SetDataIterator(iter);
    }
  }
}

3.迭代器的各种操作

// Index Block的block_data字段中,每一条记录的key都满足:
// 大于上一个Data Block的所有key,并且小于后面所有Data Block的key
// 因为Seek是查找key>=target的第一条记录,所以当index_iter_找到时,
// 该index_inter_对应的data_iter_所管理的Data Block中所有记录的
// key都小于target,需要在下一个Data Block中seek,而下一个Data Block
// 中的第一条记录就满足key>=target
void TwoLevelIterator::Seek(const Slice& target) {
  index_iter_.Seek(target);
  InitDataBlock();
  // data_iter_.Seek(target)必然会找不到,此时data_iter_.Valid()为false
  // 然后调用SkipEmptyDataBlocksForward定位到下一个Data Block,并定位到
  // 该Data Block的第一条记录,这条记录刚好就是要查找的那条记录
  if (data_iter_.iter() != NULL) data_iter_.Seek(target);
  SkipEmptyDataBlocksForward();
}
// 因为index_block_options.block_restart_interval = 1
// 所以这里是解析第一个Block Data的第一条记录
void TwoLevelIterator::SeekToFirst() {
  index_iter_.SeekToFirst();
  InitDataBlock();
  if (data_iter_.iter() != NULL) data_iter_.SeekToFirst();
  SkipEmptyDataBlocksForward();
}
// 因为index_block_options.block_restart_interval = 1
// 所以这里是解析最后一个Block Data的最后一条记录
void TwoLevelIterator::SeekToLast() {
  index_iter_.SeekToLast();
  InitDataBlock();
  if (data_iter_.iter() != NULL) data_iter_.SeekToLast();
  SkipEmptyDataBlocksBackward();
}

void TwoLevelIterator::Next() {
  assert(Valid());
  data_iter_.Next();
  SkipEmptyDataBlocksForward();
}

void TwoLevelIterator::Prev() {
  assert(Valid());
  data_iter_.Prev();
  SkipEmptyDataBlocksBackward();
}


void TwoLevelIterator::SkipEmptyDataBlocksForward() {
  // 1.如果data_iter_.iter()为NULL,说明index_iter_.Valid()为为NULL时调用了
  //   SetDataIterator(NULL),此时直接返回,因为没数据可读啦
  // 2.如果data_iter_.Valid()为false,说明当前Data Block的block_data字段读完啦
  //   开始读下一个Data Block的block_data字段(从block_data第一条记录开始读)
  while (data_iter_.iter() == NULL || !data_iter_.Valid()) {
    // Move to next block
    if (!index_iter_.Valid()) {
      SetDataIterator(NULL);
      return;
    }
    index_iter_.Next();
    InitDataBlock();
    if (data_iter_.iter() != NULL) data_iter_.SeekToFirst();
  }
}

void TwoLevelIterator::SkipEmptyDataBlocksBackward() {
  while (data_iter_.iter() == NULL || !data_iter_.Valid()) {
    // Move to next block
    if (!index_iter_.Valid()) {
      SetDataIterator(NULL);
      return;
    }
    index_iter_.Prev();
    InitDataBlock();
    if (data_iter_.iter() != NULL) data_iter_.SeekToLast();
  }
}

注释还是写的比较详细的,备忘足矣。block_function_是BlockFunction类型的函数指针,实参在Table类中,名为BlockReader。关于Table,详见:LevelDB源码分析之十三:table



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

May 20, 2024 ---- 据TrendForce集邦咨询研究,三大原厂开始提高先进制程的投片,继存储器合约价翻扬后,公司资金投入开始增加,产能提升将集中在今年下半年,预期1alpha nm(含)以上投片至年底将...

关键字: 晶圆 HBM 存储器

2024年5月18日,强国机械制造有限公司正式宣布,全力支持国家提出的“中国制造2050”战略。公司将把智能制造作为未来发展的核心方向,致力于在这一领域实现重大突破,提升中国制造业的全球竞争力。

关键字: 智能制造 物联网

每次与老友见面时总是免不了谈论以前美好的回忆,但时间渐长,我们的记忆也渐渐模糊,还有电子设备帮助我们留下痕迹,只是翻找起来有些许麻烦。不过我们倒是没有这样的困扰,那是因为我有铁威马NAS,无论什么时间的照片都能放在nas...

关键字: 数据中心 数据存储

合肥2024年5月18日 /美通社/ -- 5月17日,以"致新世界"为主题,国轩高科第13届科技大会在包河总部隆重启幕,瞄准用户最为关切的高安全性、长续航、快速充电等核心需求和痛点问题,重磅发布三大...

关键字: 国轩高科 快充 电芯 能量密度

上海2024年5月19日 /美通社/ -- 5月18日,一年一度佳通商用车胎产品日如期而至。结合新市场、新机遇、新挑战,佳通轮胎召开"数智赋能 佳境无限"为主题的2024年度商用车胎技术暨产品发布会,...

关键字: 轮胎 数字化 零部件 TPMS

NRT14 于 2025 年底竣工后,园区容量将提高到104 兆瓦, 以满足日本对下一代基础设施和无缝接入互联数据社区日益增长的需求 北京2024年5月20日 /美通社/ -- 世界领先的...

关键字: DIGITAL 人工智能 数字化 数据中心

北京2024年5月20日 /美通社/ -- 过去五年里,支付和收款方式日新月异,其发展和变化比过去五十年都要迅猛。从嵌入式数字商务的出现,到"一拍即付"的...

关键字: VI BSP PAY COM

杭州2024年5月20日 /美通社/ -- 5月20日,百世供应链旗下百世云仓在2024年全国网络大会上,宣布了其全面出海战略。聚焦于东南亚市场的新机遇,并积极推动品牌走向国际市场。 百世供应链召开2024年百世云仓全...

关键字: 供应链 网络 触点 软件

上海2024年5月20日 /美通社/ -- 仲夏伊始,光芒新生,5月17日,由上海工业商务展览有限公司主办的、以"拥抱新质生产力,助力新型工业化"为主题的第九届广东国际机器人及智能装备博览会(以下简称...

关键字: IAR 机器人 自动化 RS

开幕在即!SEMI-e第六届深圳国际半导体展将在深圳国际会展中心(宝安)4/6/8号馆拉开精彩帷幕!

关键字: 半导体
关闭
关闭