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 "leveldb/filter_policy.h" 6 7 #include "util/coding.h" 8 #include "util/logging.h" 9 #include "util/testharness.h" 10 #include "util/testutil.h" 11 12 namespace leveldb { 13 14 static const int kVerbose = 1; 15 16 static Slice Key(int i, char* buffer) { 17 EncodeFixed32(buffer, i); 18 return Slice(buffer, sizeof(uint32_t)); 19 } 20 21 class BloomTest { 22 private: 23 const FilterPolicy* policy_; 24 std::string filter_; 25 std::vector<std::string> keys_; 26 27 public: 28 BloomTest() : policy_(NewBloomFilterPolicy(10)) { } 29 30 ~BloomTest() { 31 delete policy_; 32 } 33 34 void Reset() { 35 keys_.clear(); 36 filter_.clear(); 37 } 38 39 void Add(const Slice& s) { 40 keys_.push_back(s.ToString()); 41 } 42 43 void Build() { 44 std::vector<Slice> key_slices; 45 for (size_t i = 0; i < keys_.size(); i++) { 46 key_slices.push_back(Slice(keys_[i])); 47 } 48 filter_.clear(); 49 policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_); 50 keys_.clear(); 51 if (kVerbose >= 2) DumpFilter(); 52 } 53 54 size_t FilterSize() const { 55 return filter_.size(); 56 } 57 58 void DumpFilter() { 59 fprintf(stderr, "F("); 60 for (size_t i = 0; i+1 < filter_.size(); i++) { 61 const unsigned int c = static_cast<unsigned int>(filter_[i]); 62 for (int j = 0; j < 8; j++) { 63 fprintf(stderr, "%c", (c & (1 <<j)) ? '1' : '.'); 64 } 65 } 66 fprintf(stderr, ")\n"); 67 } 68 69 bool Matches(const Slice& s) { 70 if (!keys_.empty()) { 71 Build(); 72 } 73 return policy_->KeyMayMatch(s, filter_); 74 } 75 76 double FalsePositiveRate() { 77 char buffer[sizeof(int)]; 78 int result = 0; 79 for (int i = 0; i < 10000; i++) { 80 if (Matches(Key(i + 1000000000, buffer))) { 81 result++; 82 } 83 } 84 return result / 10000.0; 85 } 86 }; 87 88 TEST(BloomTest, EmptyFilter) { 89 ASSERT_TRUE(! Matches("hello")); 90 ASSERT_TRUE(! Matches("world")); 91 } 92 93 TEST(BloomTest, Small) { 94 Add("hello"); 95 Add("world"); 96 ASSERT_TRUE(Matches("hello")); 97 ASSERT_TRUE(Matches("world")); 98 ASSERT_TRUE(! Matches("x")); 99 ASSERT_TRUE(! Matches("foo")); 100 } 101 102 static int NextLength(int length) { 103 if (length < 10) { 104 length += 1; 105 } else if (length < 100) { 106 length += 10; 107 } else if (length < 1000) { 108 length += 100; 109 } else { 110 length += 1000; 111 } 112 return length; 113 } 114 115 TEST(BloomTest, VaryingLengths) { 116 char buffer[sizeof(int)]; 117 118 // Count number of filters that significantly exceed the false positive rate 119 int mediocre_filters = 0; 120 int good_filters = 0; 121 122 for (int length = 1; length <= 10000; length = NextLength(length)) { 123 Reset(); 124 for (int i = 0; i < length; i++) { 125 Add(Key(i, buffer)); 126 } 127 Build(); 128 129 ASSERT_LE(FilterSize(), static_cast<size_t>((length * 10 / 8) + 40)) 130 << length; 131 132 // All added keys must match 133 for (int i = 0; i < length; i++) { 134 ASSERT_TRUE(Matches(Key(i, buffer))) 135 << "Length " << length << "; key " << i; 136 } 137 138 // Check false positive rate 139 double rate = FalsePositiveRate(); 140 if (kVerbose >= 1) { 141 fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", 142 rate*100.0, length, static_cast<int>(FilterSize())); 143 } 144 ASSERT_LE(rate, 0.02); // Must not be over 2% 145 if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often 146 else good_filters++; 147 } 148 if (kVerbose >= 1) { 149 fprintf(stderr, "Filters: %d good, %d mediocre\n", 150 good_filters, mediocre_filters); 151 } 152 ASSERT_LE(mediocre_filters, good_filters/5); 153 } 154 155 // Different bits-per-byte 156 157 } // namespace leveldb 158 159 int main(int argc, char** argv) { 160 return leveldb::test::RunAllTests(); 161 } 162