Home | History | Annotate | Download | only in table
      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.txt 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 
     22 void FilterBlockBuilder::StartBlock(uint64_t block_offset) {
     23   uint64_t filter_index = (block_offset / kFilterBase);
     24   assert(filter_index >= filter_offsets_.size());
     25   while (filter_index > filter_offsets_.size()) {
     26     GenerateFilter();
     27   }
     28 }
     29 
     30 void FilterBlockBuilder::AddKey(const Slice& key) {
     31   Slice k = key;
     32   start_.push_back(keys_.size());
     33   keys_.append(k.data(), k.size());
     34 }
     35 
     36 Slice FilterBlockBuilder::Finish() {
     37   if (!start_.empty()) {
     38     GenerateFilter();
     39   }
     40 
     41   // Append array of per-filter offsets
     42   const uint32_t array_offset = result_.size();
     43   for (size_t i = 0; i < filter_offsets_.size(); i++) {
     44     PutFixed32(&result_, filter_offsets_[i]);
     45   }
     46 
     47   PutFixed32(&result_, array_offset);
     48   result_.push_back(kFilterBaseLg);  // Save encoding parameter in result
     49   return Slice(result_);
     50 }
     51 
     52 void FilterBlockBuilder::GenerateFilter() {
     53   const size_t num_keys = start_.size();
     54   if (num_keys == 0) {
     55     // Fast path if there are no keys for this filter
     56     filter_offsets_.push_back(result_.size());
     57     return;
     58   }
     59 
     60   // Make list of keys from flattened key structure
     61   start_.push_back(keys_.size());  // Simplify length computation
     62   tmp_keys_.resize(num_keys);
     63   for (size_t i = 0; i < num_keys; i++) {
     64     const char* base = keys_.data() + start_[i];
     65     size_t length = start_[i+1] - start_[i];
     66     tmp_keys_[i] = Slice(base, length);
     67   }
     68 
     69   // Generate filter for current set of keys and append to result_.
     70   filter_offsets_.push_back(result_.size());
     71   policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_);
     72 
     73   tmp_keys_.clear();
     74   keys_.clear();
     75   start_.clear();
     76 }
     77 
     78 FilterBlockReader::FilterBlockReader(const FilterPolicy* policy,
     79                                      const Slice& contents)
     80     : policy_(policy),
     81       data_(NULL),
     82       offset_(NULL),
     83       num_(0),
     84       base_lg_(0) {
     85   size_t n = contents.size();
     86   if (n < 5) return;  // 1 byte for base_lg_ and 4 for start of offset array
     87   base_lg_ = contents[n-1];
     88   uint32_t last_word = DecodeFixed32(contents.data() + n - 5);
     89   if (last_word > n - 5) return;
     90   data_ = contents.data();
     91   offset_ = data_ + last_word;
     92   num_ = (n - 5 - last_word) / 4;
     93 }
     94 
     95 bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) {
     96   uint64_t index = block_offset >> base_lg_;
     97   if (index < num_) {
     98     uint32_t start = DecodeFixed32(offset_ + index*4);
     99     uint32_t limit = DecodeFixed32(offset_ + index*4 + 4);
    100     if (start <= limit && limit <= (offset_ - data_)) {
    101       Slice filter = Slice(data_ + start, limit - start);
    102       return policy_->KeyMayMatch(key, filter);
    103     } else if (start == limit) {
    104       // Empty filters do not match any keys
    105       return false;
    106     }
    107   }
    108   return true;  // Errors are treated as potential matches
    109 }
    110 
    111 }
    112