// Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "table/two_level_iterator.h" #include "leveldb/table.h" #include "table/block.h" #include "table/format.h" #include "table/iterator_wrapper.h" namespace leveldb { namespace { typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const EnvOptions& soptions, const Slice&); class TwoLevelIterator: public Iterator { public: TwoLevelIterator( Iterator* index_iter, BlockFunction block_function, void* arg, const ReadOptions& options, const EnvOptions& soptions); 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() != nullptr && !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_; void* arg_; const ReadOptions options_; const EnvOptions& soptions_; Status status_; IteratorWrapper index_iter_; IteratorWrapper data_iter_; // May be nullptr // If data_iter_ is non-nullptr, then "data_block_handle_" holds the // "index_value" passed to block_function_ to create the data_iter_. std::string data_block_handle_; }; TwoLevelIterator::TwoLevelIterator( Iterator* index_iter, BlockFunction block_function, void* arg, const ReadOptions& options, const EnvOptions& soptions) : block_function_(block_function), arg_(arg), options_(options), soptions_(soptions), index_iter_(index_iter), data_iter_(nullptr) { } TwoLevelIterator::~TwoLevelIterator() { } void TwoLevelIterator::Seek(const Slice& target) { index_iter_.Seek(target); InitDataBlock(); if (data_iter_.iter() != nullptr) data_iter_.Seek(target); SkipEmptyDataBlocksForward(); } void TwoLevelIterator::SeekToFirst() { index_iter_.SeekToFirst(); InitDataBlock(); if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst(); SkipEmptyDataBlocksForward(); } void TwoLevelIterator::SeekToLast() { index_iter_.SeekToLast(); InitDataBlock(); if (data_iter_.iter() != nullptr) 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() { while (data_iter_.iter() == nullptr || !data_iter_.Valid()) { // Move to next block if (!index_iter_.Valid()) { SetDataIterator(nullptr); return; } index_iter_.Next(); InitDataBlock(); if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst(); } } void TwoLevelIterator::SkipEmptyDataBlocksBackward() { while (data_iter_.iter() == nullptr || !data_iter_.Valid()) { // Move to next block if (!index_iter_.Valid()) { SetDataIterator(nullptr); return; } index_iter_.Prev(); InitDataBlock(); if (data_iter_.iter() != nullptr) data_iter_.SeekToLast(); } } void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { if (data_iter_.iter() != nullptr) SaveError(data_iter_.status()); data_iter_.Set(data_iter); } void TwoLevelIterator::InitDataBlock() { if (!index_iter_.Valid()) { SetDataIterator(nullptr); } else { Slice handle = index_iter_.value(); if (data_iter_.iter() != nullptr && handle.compare(data_block_handle_) == 0) { // data_iter_ is already constructed with this iterator, so // no need to change anything } else { Iterator* iter = (*block_function_)(arg_, options_, soptions_, handle); data_block_handle_.assign(handle.data(), handle.size()); SetDataIterator(iter); } } } } // namespace Iterator* NewTwoLevelIterator( Iterator* index_iter, BlockFunction block_function, void* arg, const ReadOptions& options, const EnvOptions& soptions) { return new TwoLevelIterator(index_iter, block_function, arg, options, soptions); } } // namespace leveldb