You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

111 lines
3.3 KiB

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