1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
6 // Copyright (c) 2012 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
10 #include "table/block_based_filter_block.h"
12 #include "rocksdb/filter_policy.h"
13 #include "util/coding.h"
14 #include "util/hash.h"
15 #include "util/string_util.h"
16 #include "util/testharness.h"
17 #include "util/testutil.h"
21 // For testing: emit an array with one hash value per key
22 class TestHashFilter
: public FilterPolicy
{
24 virtual const char* Name() const override
{ return "TestHashFilter"; }
26 virtual void CreateFilter(const Slice
* keys
, int n
,
27 std::string
* dst
) const override
{
28 for (int i
= 0; i
< n
; i
++) {
29 uint32_t h
= Hash(keys
[i
].data(), keys
[i
].size(), 1);
34 virtual bool KeyMayMatch(const Slice
& key
,
35 const Slice
& filter
) const override
{
36 uint32_t h
= Hash(key
.data(), key
.size(), 1);
37 for (unsigned int i
= 0; i
+ 4 <= filter
.size(); i
+= 4) {
38 if (h
== DecodeFixed32(filter
.data() + i
)) {
46 class FilterBlockTest
: public testing::Test
{
48 TestHashFilter policy_
;
49 BlockBasedTableOptions table_options_
;
52 table_options_
.filter_policy
.reset(new TestHashFilter());
56 TEST_F(FilterBlockTest
, EmptyBuilder
) {
57 BlockBasedFilterBlockBuilder
builder(nullptr, table_options_
);
58 BlockContents
block(builder
.Finish(), false, kNoCompression
);
59 ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block
.data
));
60 BlockBasedFilterBlockReader
reader(nullptr, table_options_
, true,
61 std::move(block
), nullptr);
62 ASSERT_TRUE(reader
.KeyMayMatch("foo", nullptr, uint64_t{0}));
63 ASSERT_TRUE(reader
.KeyMayMatch("foo", nullptr, 100000));
66 TEST_F(FilterBlockTest
, SingleChunk
) {
67 BlockBasedFilterBlockBuilder
builder(nullptr, table_options_
);
68 ASSERT_EQ(0, builder
.NumAdded());
69 builder
.StartBlock(100);
73 builder
.StartBlock(200);
75 builder
.StartBlock(300);
77 ASSERT_EQ(5, builder
.NumAdded());
78 BlockContents
block(builder
.Finish(), false, kNoCompression
);
79 BlockBasedFilterBlockReader
reader(nullptr, table_options_
, true,
80 std::move(block
), nullptr);
81 ASSERT_TRUE(reader
.KeyMayMatch("foo", nullptr, 100));
82 ASSERT_TRUE(reader
.KeyMayMatch("bar", nullptr, 100));
83 ASSERT_TRUE(reader
.KeyMayMatch("box", nullptr, 100));
84 ASSERT_TRUE(reader
.KeyMayMatch("hello", nullptr, 100));
85 ASSERT_TRUE(reader
.KeyMayMatch("foo", nullptr, 100));
86 ASSERT_TRUE(!reader
.KeyMayMatch("missing", nullptr, 100));
87 ASSERT_TRUE(!reader
.KeyMayMatch("other", nullptr, 100));
90 TEST_F(FilterBlockTest
, MultiChunk
) {
91 BlockBasedFilterBlockBuilder
builder(nullptr, table_options_
);
94 builder
.StartBlock(0);
96 builder
.StartBlock(2000);
100 builder
.StartBlock(3100);
103 // Third filter is empty
106 builder
.StartBlock(9000);
108 builder
.Add("hello");
110 BlockContents
block(builder
.Finish(), false, kNoCompression
);
111 BlockBasedFilterBlockReader
reader(nullptr, table_options_
, true,
112 std::move(block
), nullptr);
114 // Check first filter
115 ASSERT_TRUE(reader
.KeyMayMatch("foo", nullptr, uint64_t{0}));
116 ASSERT_TRUE(reader
.KeyMayMatch("bar", nullptr, 2000));
117 ASSERT_TRUE(!reader
.KeyMayMatch("box", nullptr, uint64_t{0}));
118 ASSERT_TRUE(!reader
.KeyMayMatch("hello", nullptr, uint64_t{0}));
120 // Check second filter
121 ASSERT_TRUE(reader
.KeyMayMatch("box", nullptr, 3100));
122 ASSERT_TRUE(!reader
.KeyMayMatch("foo", nullptr, 3100));
123 ASSERT_TRUE(!reader
.KeyMayMatch("bar", nullptr, 3100));
124 ASSERT_TRUE(!reader
.KeyMayMatch("hello", nullptr, 3100));
126 // Check third filter (empty)
127 ASSERT_TRUE(!reader
.KeyMayMatch("foo", nullptr, 4100));
128 ASSERT_TRUE(!reader
.KeyMayMatch("bar", nullptr, 4100));
129 ASSERT_TRUE(!reader
.KeyMayMatch("box", nullptr, 4100));
130 ASSERT_TRUE(!reader
.KeyMayMatch("hello", nullptr, 4100));
133 ASSERT_TRUE(reader
.KeyMayMatch("box", nullptr, 9000));
134 ASSERT_TRUE(reader
.KeyMayMatch("hello", nullptr, 9000));
135 ASSERT_TRUE(!reader
.KeyMayMatch("foo", nullptr, 9000));
136 ASSERT_TRUE(!reader
.KeyMayMatch("bar", nullptr, 9000));
139 // Test for block based filter block
140 // use new interface in FilterPolicy to create filter builder/reader
141 class BlockBasedFilterBlockTest
: public testing::Test
{
143 BlockBasedTableOptions table_options_
;
145 BlockBasedFilterBlockTest() {
146 table_options_
.filter_policy
.reset(NewBloomFilterPolicy(10));
149 ~BlockBasedFilterBlockTest() {}
152 TEST_F(BlockBasedFilterBlockTest
, BlockBasedEmptyBuilder
) {
153 FilterBlockBuilder
* builder
= new BlockBasedFilterBlockBuilder(
154 nullptr, table_options_
);
155 BlockContents
block(builder
->Finish(), false, kNoCompression
);
156 ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block
.data
));
157 FilterBlockReader
* reader
= new BlockBasedFilterBlockReader(
158 nullptr, table_options_
, true, std::move(block
), nullptr);
159 ASSERT_TRUE(reader
->KeyMayMatch("foo", nullptr, uint64_t{0}));
160 ASSERT_TRUE(reader
->KeyMayMatch("foo", nullptr, 100000));
166 TEST_F(BlockBasedFilterBlockTest
, BlockBasedSingleChunk
) {
167 FilterBlockBuilder
* builder
= new BlockBasedFilterBlockBuilder(
168 nullptr, table_options_
);
169 builder
->StartBlock(100);
173 builder
->StartBlock(200);
175 builder
->StartBlock(300);
176 builder
->Add("hello");
177 BlockContents
block(builder
->Finish(), false, kNoCompression
);
178 FilterBlockReader
* reader
= new BlockBasedFilterBlockReader(
179 nullptr, table_options_
, true, std::move(block
), nullptr);
180 ASSERT_TRUE(reader
->KeyMayMatch("foo", nullptr, 100));
181 ASSERT_TRUE(reader
->KeyMayMatch("bar", nullptr, 100));
182 ASSERT_TRUE(reader
->KeyMayMatch("box", nullptr, 100));
183 ASSERT_TRUE(reader
->KeyMayMatch("hello", nullptr, 100));
184 ASSERT_TRUE(reader
->KeyMayMatch("foo", nullptr, 100));
185 ASSERT_TRUE(!reader
->KeyMayMatch("missing", nullptr, 100));
186 ASSERT_TRUE(!reader
->KeyMayMatch("other", nullptr, 100));
192 TEST_F(BlockBasedFilterBlockTest
, BlockBasedMultiChunk
) {
193 FilterBlockBuilder
* builder
= new BlockBasedFilterBlockBuilder(
194 nullptr, table_options_
);
197 builder
->StartBlock(0);
199 builder
->StartBlock(2000);
203 builder
->StartBlock(3100);
206 // Third filter is empty
209 builder
->StartBlock(9000);
211 builder
->Add("hello");
213 BlockContents
block(builder
->Finish(), false, kNoCompression
);
214 FilterBlockReader
* reader
= new BlockBasedFilterBlockReader(
215 nullptr, table_options_
, true, std::move(block
), nullptr);
217 // Check first filter
218 ASSERT_TRUE(reader
->KeyMayMatch("foo", nullptr, uint64_t{0}));
219 ASSERT_TRUE(reader
->KeyMayMatch("bar", nullptr, 2000));
220 ASSERT_TRUE(!reader
->KeyMayMatch("box", nullptr, uint64_t{0}));
221 ASSERT_TRUE(!reader
->KeyMayMatch("hello", nullptr, uint64_t{0}));
223 // Check second filter
224 ASSERT_TRUE(reader
->KeyMayMatch("box", nullptr, 3100));
225 ASSERT_TRUE(!reader
->KeyMayMatch("foo", nullptr, 3100));
226 ASSERT_TRUE(!reader
->KeyMayMatch("bar", nullptr, 3100));
227 ASSERT_TRUE(!reader
->KeyMayMatch("hello", nullptr, 3100));
229 // Check third filter (empty)
230 ASSERT_TRUE(!reader
->KeyMayMatch("foo", nullptr, 4100));
231 ASSERT_TRUE(!reader
->KeyMayMatch("bar", nullptr, 4100));
232 ASSERT_TRUE(!reader
->KeyMayMatch("box", nullptr, 4100));
233 ASSERT_TRUE(!reader
->KeyMayMatch("hello", nullptr, 4100));
236 ASSERT_TRUE(reader
->KeyMayMatch("box", nullptr, 9000));
237 ASSERT_TRUE(reader
->KeyMayMatch("hello", nullptr, 9000));
238 ASSERT_TRUE(!reader
->KeyMayMatch("foo", nullptr, 9000));
239 ASSERT_TRUE(!reader
->KeyMayMatch("bar", nullptr, 9000));
245 } // namespace rocksdb
247 int main(int argc
, char** argv
) {
248 ::testing::InitGoogleTest(&argc
, argv
);
249 return RUN_ALL_TESTS();