/ src / leveldb / table / filter_block.cc
filter_block.cc
  1  // Copyright (c) 2012 The LevelDB Authors. All rights reserved.
  2  // Use of this source code is governed by a BSD-style license that can be
  3  // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4  
  5  #include "table/filter_block.h"
  6  
  7  #include "leveldb/filter_policy.h"
  8  #include "util/coding.h"
  9  
 10  namespace leveldb {
 11  
 12  // See doc/table_format.md for an explanation of the filter block format.
 13  
 14  // Generate new filter every 2KB of data
 15  static const size_t kFilterBaseLg = 11;
 16  static const size_t kFilterBase = 1 << kFilterBaseLg;
 17  
 18  FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy)
 19      : policy_(policy) {}
 20  
 21  void FilterBlockBuilder::StartBlock(uint64_t block_offset) {
 22    uint64_t filter_index = (block_offset / kFilterBase);
 23    assert(filter_index >= filter_offsets_.size());
 24    while (filter_index > filter_offsets_.size()) {
 25      GenerateFilter();
 26    }
 27  }
 28  
 29  void FilterBlockBuilder::AddKey(const Slice& key) {
 30    Slice k = key;
 31    start_.push_back(keys_.size());
 32    keys_.append(k.data(), k.size());
 33  }
 34  
 35  Slice FilterBlockBuilder::Finish() {
 36    if (!start_.empty()) {
 37      GenerateFilter();
 38    }
 39  
 40    // Append array of per-filter offsets
 41    const uint32_t array_offset = result_.size();
 42    for (size_t i = 0; i < filter_offsets_.size(); i++) {
 43      PutFixed32(&result_, filter_offsets_[i]);
 44    }
 45  
 46    PutFixed32(&result_, array_offset);
 47    result_.push_back(kFilterBaseLg);  // Save encoding parameter in result
 48    return Slice(result_);
 49  }
 50  
 51  void FilterBlockBuilder::GenerateFilter() {
 52    const size_t num_keys = start_.size();
 53    if (num_keys == 0) {
 54      // Fast path if there are no keys for this filter
 55      filter_offsets_.push_back(result_.size());
 56      return;
 57    }
 58  
 59    // Make list of keys from flattened key structure
 60    start_.push_back(keys_.size());  // Simplify length computation
 61    tmp_keys_.resize(num_keys);
 62    for (size_t i = 0; i < num_keys; i++) {
 63      const char* base = keys_.data() + start_[i];
 64      size_t length = start_[i + 1] - start_[i];
 65      tmp_keys_[i] = Slice(base, length);
 66    }
 67  
 68    // Generate filter for current set of keys and append to result_.
 69    filter_offsets_.push_back(result_.size());
 70    policy_->CreateFilter(&tmp_keys_[0], static_cast<int>(num_keys), &result_);
 71  
 72    tmp_keys_.clear();
 73    keys_.clear();
 74    start_.clear();
 75  }
 76  
 77  FilterBlockReader::FilterBlockReader(const FilterPolicy* policy,
 78                                       const Slice& contents)
 79      : policy_(policy), data_(nullptr), offset_(nullptr), num_(0), base_lg_(0) {
 80    size_t n = contents.size();
 81    if (n < 5) return;  // 1 byte for base_lg_ and 4 for start of offset array
 82    base_lg_ = contents[n - 1];
 83    uint32_t last_word = DecodeFixed32(contents.data() + n - 5);
 84    if (last_word > n - 5) return;
 85    data_ = contents.data();
 86    offset_ = data_ + last_word;
 87    num_ = (n - 5 - last_word) / 4;
 88  }
 89  
 90  bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) {
 91    uint64_t index = block_offset >> base_lg_;
 92    if (index < num_) {
 93      uint32_t start = DecodeFixed32(offset_ + index * 4);
 94      uint32_t limit = DecodeFixed32(offset_ + index * 4 + 4);
 95      if (start <= limit && limit <= static_cast<size_t>(offset_ - data_)) {
 96        Slice filter = Slice(data_ + start, limit - start);
 97        return policy_->KeyMayMatch(key, filter);
 98      } else if (start == limit) {
 99        // Empty filters do not match any keys
100        return false;
101      }
102    }
103    return true;  // Errors are treated as potential matches
104  }
105  
106  }  // namespace leveldb