leveldb-ruby 0.14 → 0.15
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +24 -0
- data/README +60 -16
- data/ext/leveldb/extconf.rb +1 -1
- data/ext/leveldb/leveldb.cc +187 -18
- data/leveldb/Makefile +82 -96
- data/leveldb/build_detect_platform +137 -51
- data/leveldb/db/c.cc +110 -0
- data/leveldb/db/db_bench.cc +105 -4
- data/leveldb/db/db_impl.cc +135 -45
- data/leveldb/db/db_impl.h +12 -10
- data/leveldb/db/db_test.cc +666 -431
- data/leveldb/db/dbformat.cc +20 -0
- data/leveldb/db/dbformat.h +12 -0
- data/leveldb/db/repair.cc +3 -1
- data/leveldb/db/skiplist.h +2 -1
- data/leveldb/db/table_cache.cc +42 -16
- data/leveldb/db/table_cache.h +11 -0
- data/leveldb/db/version_set.cc +46 -41
- data/leveldb/db/version_set.h +9 -0
- data/leveldb/db/write_batch.cc +13 -4
- data/leveldb/db/write_batch_internal.h +2 -0
- data/leveldb/db/write_batch_test.cc +31 -0
- data/leveldb/include/leveldb/c.h +29 -0
- data/leveldb/include/leveldb/db.h +2 -1
- data/leveldb/include/leveldb/filter_policy.h +70 -0
- data/leveldb/include/leveldb/options.h +8 -0
- data/leveldb/include/leveldb/status.h +6 -0
- data/leveldb/include/leveldb/table.h +15 -0
- data/leveldb/include/leveldb/table_builder.h +1 -0
- data/leveldb/port/atomic_pointer.h +13 -5
- data/leveldb/port/port.h +0 -2
- data/leveldb/port/port_example.h +10 -0
- data/leveldb/port/port_posix.cc +4 -0
- data/leveldb/port/port_posix.h +24 -9
- data/leveldb/table/block.cc +8 -4
- data/leveldb/table/block.h +3 -2
- data/leveldb/table/filter_block.cc +111 -0
- data/leveldb/table/filter_block.h +68 -0
- data/leveldb/table/filter_block_test.cc +128 -0
- data/leveldb/table/format.cc +17 -7
- data/leveldb/table/format.h +9 -4
- data/leveldb/table/table.cc +107 -6
- data/leveldb/table/table_builder.cc +49 -6
- data/leveldb/table/table_test.cc +8 -24
- data/leveldb/util/bloom.cc +95 -0
- data/leveldb/util/bloom_test.cc +159 -0
- data/leveldb/util/coding_test.cc +23 -0
- data/leveldb/util/comparator.cc +8 -3
- data/leveldb/util/env_posix.cc +46 -4
- data/leveldb/util/filter_policy.cc +11 -0
- data/leveldb/util/options.cc +2 -1
- data/lib/leveldb.rb +31 -5
- metadata +227 -109
- data/leveldb/port/port_android.cc +0 -64
- data/leveldb/port/port_android.h +0 -156
@@ -0,0 +1,68 @@
|
|
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
|
+
// A filter block is stored near the end of a Table file. It contains
|
6
|
+
// filters (e.g., bloom filters) for all data blocks in the table combined
|
7
|
+
// into a single filter block.
|
8
|
+
|
9
|
+
#ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
|
10
|
+
#define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
|
11
|
+
|
12
|
+
#include <stddef.h>
|
13
|
+
#include <stdint.h>
|
14
|
+
#include <string>
|
15
|
+
#include <vector>
|
16
|
+
#include "leveldb/slice.h"
|
17
|
+
#include "util/hash.h"
|
18
|
+
|
19
|
+
namespace leveldb {
|
20
|
+
|
21
|
+
class FilterPolicy;
|
22
|
+
|
23
|
+
// A FilterBlockBuilder is used to construct all of the filters for a
|
24
|
+
// particular Table. It generates a single string which is stored as
|
25
|
+
// a special block in the Table.
|
26
|
+
//
|
27
|
+
// The sequence of calls to FilterBlockBuilder must match the regexp:
|
28
|
+
// (StartBlock AddKey*)* Finish
|
29
|
+
class FilterBlockBuilder {
|
30
|
+
public:
|
31
|
+
explicit FilterBlockBuilder(const FilterPolicy*);
|
32
|
+
|
33
|
+
void StartBlock(uint64_t block_offset);
|
34
|
+
void AddKey(const Slice& key);
|
35
|
+
Slice Finish();
|
36
|
+
|
37
|
+
private:
|
38
|
+
void GenerateFilter();
|
39
|
+
|
40
|
+
const FilterPolicy* policy_;
|
41
|
+
std::string keys_; // Flattened key contents
|
42
|
+
std::vector<size_t> start_; // Starting index in keys_ of each key
|
43
|
+
std::string result_; // Filter data computed so far
|
44
|
+
std::vector<Slice> tmp_keys_; // policy_->CreateFilter() argument
|
45
|
+
std::vector<uint32_t> filter_offsets_;
|
46
|
+
|
47
|
+
// No copying allowed
|
48
|
+
FilterBlockBuilder(const FilterBlockBuilder&);
|
49
|
+
void operator=(const FilterBlockBuilder&);
|
50
|
+
};
|
51
|
+
|
52
|
+
class FilterBlockReader {
|
53
|
+
public:
|
54
|
+
// REQUIRES: "contents" and *policy must stay live while *this is live.
|
55
|
+
FilterBlockReader(const FilterPolicy* policy, const Slice& contents);
|
56
|
+
bool KeyMayMatch(uint64_t block_offset, const Slice& key);
|
57
|
+
|
58
|
+
private:
|
59
|
+
const FilterPolicy* policy_;
|
60
|
+
const char* data_; // Pointer to filter data (at block-start)
|
61
|
+
const char* offset_; // Pointer to beginning of offset array (at block-end)
|
62
|
+
size_t num_; // Number of entries in offset array
|
63
|
+
size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file)
|
64
|
+
};
|
65
|
+
|
66
|
+
}
|
67
|
+
|
68
|
+
#endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
|
@@ -0,0 +1,128 @@
|
|
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
|
+
#include "util/hash.h"
|
10
|
+
#include "util/logging.h"
|
11
|
+
#include "util/testharness.h"
|
12
|
+
#include "util/testutil.h"
|
13
|
+
|
14
|
+
namespace leveldb {
|
15
|
+
|
16
|
+
// For testing: emit an array with one hash value per key
|
17
|
+
class TestHashFilter : public FilterPolicy {
|
18
|
+
public:
|
19
|
+
virtual const char* Name() const {
|
20
|
+
return "TestHashFilter";
|
21
|
+
}
|
22
|
+
|
23
|
+
virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const {
|
24
|
+
for (int i = 0; i < n; i++) {
|
25
|
+
uint32_t h = Hash(keys[i].data(), keys[i].size(), 1);
|
26
|
+
PutFixed32(dst, h);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const {
|
31
|
+
uint32_t h = Hash(key.data(), key.size(), 1);
|
32
|
+
for (int i = 0; i + 4 <= filter.size(); i += 4) {
|
33
|
+
if (h == DecodeFixed32(filter.data() + i)) {
|
34
|
+
return true;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
};
|
40
|
+
|
41
|
+
class FilterBlockTest {
|
42
|
+
public:
|
43
|
+
TestHashFilter policy_;
|
44
|
+
};
|
45
|
+
|
46
|
+
TEST(FilterBlockTest, EmptyBuilder) {
|
47
|
+
FilterBlockBuilder builder(&policy_);
|
48
|
+
Slice block = builder.Finish();
|
49
|
+
ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block));
|
50
|
+
FilterBlockReader reader(&policy_, block);
|
51
|
+
ASSERT_TRUE(reader.KeyMayMatch(0, "foo"));
|
52
|
+
ASSERT_TRUE(reader.KeyMayMatch(100000, "foo"));
|
53
|
+
}
|
54
|
+
|
55
|
+
TEST(FilterBlockTest, SingleChunk) {
|
56
|
+
FilterBlockBuilder builder(&policy_);
|
57
|
+
builder.StartBlock(100);
|
58
|
+
builder.AddKey("foo");
|
59
|
+
builder.AddKey("bar");
|
60
|
+
builder.AddKey("box");
|
61
|
+
builder.StartBlock(200);
|
62
|
+
builder.AddKey("box");
|
63
|
+
builder.StartBlock(300);
|
64
|
+
builder.AddKey("hello");
|
65
|
+
Slice block = builder.Finish();
|
66
|
+
FilterBlockReader reader(&policy_, block);
|
67
|
+
ASSERT_TRUE(reader.KeyMayMatch(100, "foo"));
|
68
|
+
ASSERT_TRUE(reader.KeyMayMatch(100, "bar"));
|
69
|
+
ASSERT_TRUE(reader.KeyMayMatch(100, "box"));
|
70
|
+
ASSERT_TRUE(reader.KeyMayMatch(100, "hello"));
|
71
|
+
ASSERT_TRUE(reader.KeyMayMatch(100, "foo"));
|
72
|
+
ASSERT_TRUE(! reader.KeyMayMatch(100, "missing"));
|
73
|
+
ASSERT_TRUE(! reader.KeyMayMatch(100, "other"));
|
74
|
+
}
|
75
|
+
|
76
|
+
TEST(FilterBlockTest, MultiChunk) {
|
77
|
+
FilterBlockBuilder builder(&policy_);
|
78
|
+
|
79
|
+
// First filter
|
80
|
+
builder.StartBlock(0);
|
81
|
+
builder.AddKey("foo");
|
82
|
+
builder.StartBlock(2000);
|
83
|
+
builder.AddKey("bar");
|
84
|
+
|
85
|
+
// Second filter
|
86
|
+
builder.StartBlock(3100);
|
87
|
+
builder.AddKey("box");
|
88
|
+
|
89
|
+
// Third filter is empty
|
90
|
+
|
91
|
+
// Last filter
|
92
|
+
builder.StartBlock(9000);
|
93
|
+
builder.AddKey("box");
|
94
|
+
builder.AddKey("hello");
|
95
|
+
|
96
|
+
Slice block = builder.Finish();
|
97
|
+
FilterBlockReader reader(&policy_, block);
|
98
|
+
|
99
|
+
// Check first filter
|
100
|
+
ASSERT_TRUE(reader.KeyMayMatch(0, "foo"));
|
101
|
+
ASSERT_TRUE(reader.KeyMayMatch(2000, "bar"));
|
102
|
+
ASSERT_TRUE(! reader.KeyMayMatch(0, "box"));
|
103
|
+
ASSERT_TRUE(! reader.KeyMayMatch(0, "hello"));
|
104
|
+
|
105
|
+
// Check second filter
|
106
|
+
ASSERT_TRUE(reader.KeyMayMatch(3100, "box"));
|
107
|
+
ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo"));
|
108
|
+
ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar"));
|
109
|
+
ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello"));
|
110
|
+
|
111
|
+
// Check third filter (empty)
|
112
|
+
ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo"));
|
113
|
+
ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar"));
|
114
|
+
ASSERT_TRUE(! reader.KeyMayMatch(4100, "box"));
|
115
|
+
ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello"));
|
116
|
+
|
117
|
+
// Check last filter
|
118
|
+
ASSERT_TRUE(reader.KeyMayMatch(9000, "box"));
|
119
|
+
ASSERT_TRUE(reader.KeyMayMatch(9000, "hello"));
|
120
|
+
ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo"));
|
121
|
+
ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar"));
|
122
|
+
}
|
123
|
+
|
124
|
+
} // namespace leveldb
|
125
|
+
|
126
|
+
int main(int argc, char** argv) {
|
127
|
+
return leveldb::test::RunAllTests();
|
128
|
+
}
|
data/leveldb/table/format.cc
CHANGED
@@ -66,8 +66,10 @@ Status Footer::DecodeFrom(Slice* input) {
|
|
66
66
|
Status ReadBlock(RandomAccessFile* file,
|
67
67
|
const ReadOptions& options,
|
68
68
|
const BlockHandle& handle,
|
69
|
-
|
70
|
-
|
69
|
+
BlockContents* result) {
|
70
|
+
result->data = Slice();
|
71
|
+
result->cachable = false;
|
72
|
+
result->heap_allocated = false;
|
71
73
|
|
72
74
|
// Read the block contents as well as the type/crc footer.
|
73
75
|
// See table_builder.cc for the code that built this structure.
|
@@ -100,8 +102,16 @@ Status ReadBlock(RandomAccessFile* file,
|
|
100
102
|
case kNoCompression:
|
101
103
|
if (data != buf) {
|
102
104
|
// File implementation gave us pointer to some other data.
|
103
|
-
//
|
104
|
-
|
105
|
+
// Use it directly under the assumption that it will be live
|
106
|
+
// while the file is open.
|
107
|
+
delete[] buf;
|
108
|
+
result->data = Slice(data, n);
|
109
|
+
result->heap_allocated = false;
|
110
|
+
result->cachable = false; // Do not double-cache
|
111
|
+
} else {
|
112
|
+
result->data = Slice(buf, n);
|
113
|
+
result->heap_allocated = true;
|
114
|
+
result->cachable = true;
|
105
115
|
}
|
106
116
|
|
107
117
|
// Ok
|
@@ -119,8 +129,9 @@ Status ReadBlock(RandomAccessFile* file,
|
|
119
129
|
return Status::Corruption("corrupted compressed block contents");
|
120
130
|
}
|
121
131
|
delete[] buf;
|
122
|
-
|
123
|
-
|
132
|
+
result->data = Slice(ubuf, ulength);
|
133
|
+
result->heap_allocated = true;
|
134
|
+
result->cachable = true;
|
124
135
|
break;
|
125
136
|
}
|
126
137
|
default:
|
@@ -128,7 +139,6 @@ Status ReadBlock(RandomAccessFile* file,
|
|
128
139
|
return Status::Corruption("bad block type");
|
129
140
|
}
|
130
141
|
|
131
|
-
*block = new Block(buf, n); // Block takes ownership of buf[]
|
132
142
|
return Status::OK();
|
133
143
|
}
|
134
144
|
|
data/leveldb/table/format.h
CHANGED
@@ -83,13 +83,18 @@ static const uint64_t kTableMagicNumber = 0xdb4775248b80fb57ull;
|
|
83
83
|
// 1-byte type + 32-bit crc
|
84
84
|
static const size_t kBlockTrailerSize = 5;
|
85
85
|
|
86
|
-
|
87
|
-
//
|
88
|
-
|
86
|
+
struct BlockContents {
|
87
|
+
Slice data; // Actual contents of data
|
88
|
+
bool cachable; // True iff data can be cached
|
89
|
+
bool heap_allocated; // True iff caller should delete[] data.data()
|
90
|
+
};
|
91
|
+
|
92
|
+
// Read the block identified by "handle" from "file". On failure
|
93
|
+
// return non-OK. On success fill *result and return OK.
|
89
94
|
extern Status ReadBlock(RandomAccessFile* file,
|
90
95
|
const ReadOptions& options,
|
91
96
|
const BlockHandle& handle,
|
92
|
-
|
97
|
+
BlockContents* result);
|
93
98
|
|
94
99
|
// Implementation details follow. Clients should ignore,
|
95
100
|
|
data/leveldb/table/table.cc
CHANGED
@@ -5,8 +5,12 @@
|
|
5
5
|
#include "leveldb/table.h"
|
6
6
|
|
7
7
|
#include "leveldb/cache.h"
|
8
|
+
#include "leveldb/comparator.h"
|
8
9
|
#include "leveldb/env.h"
|
10
|
+
#include "leveldb/filter_policy.h"
|
11
|
+
#include "leveldb/options.h"
|
9
12
|
#include "table/block.h"
|
13
|
+
#include "table/filter_block.h"
|
10
14
|
#include "table/format.h"
|
11
15
|
#include "table/two_level_iterator.h"
|
12
16
|
#include "util/coding.h"
|
@@ -15,6 +19,8 @@ namespace leveldb {
|
|
15
19
|
|
16
20
|
struct Table::Rep {
|
17
21
|
~Rep() {
|
22
|
+
delete filter;
|
23
|
+
delete [] filter_data;
|
18
24
|
delete index_block;
|
19
25
|
}
|
20
26
|
|
@@ -22,6 +28,8 @@ struct Table::Rep {
|
|
22
28
|
Status status;
|
23
29
|
RandomAccessFile* file;
|
24
30
|
uint64_t cache_id;
|
31
|
+
FilterBlockReader* filter;
|
32
|
+
const char* filter_data;
|
25
33
|
|
26
34
|
BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer
|
27
35
|
Block* index_block;
|
@@ -47,9 +55,13 @@ Status Table::Open(const Options& options,
|
|
47
55
|
if (!s.ok()) return s;
|
48
56
|
|
49
57
|
// Read the index block
|
58
|
+
BlockContents contents;
|
50
59
|
Block* index_block = NULL;
|
51
60
|
if (s.ok()) {
|
52
|
-
s = ReadBlock(file, ReadOptions(), footer.index_handle(), &
|
61
|
+
s = ReadBlock(file, ReadOptions(), footer.index_handle(), &contents);
|
62
|
+
if (s.ok()) {
|
63
|
+
index_block = new Block(contents);
|
64
|
+
}
|
53
65
|
}
|
54
66
|
|
55
67
|
if (s.ok()) {
|
@@ -61,7 +73,10 @@ Status Table::Open(const Options& options,
|
|
61
73
|
rep->metaindex_handle = footer.metaindex_handle();
|
62
74
|
rep->index_block = index_block;
|
63
75
|
rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0);
|
76
|
+
rep->filter_data = NULL;
|
77
|
+
rep->filter = NULL;
|
64
78
|
*table = new Table(rep);
|
79
|
+
(*table)->ReadMeta(footer);
|
65
80
|
} else {
|
66
81
|
if (index_block) delete index_block;
|
67
82
|
}
|
@@ -69,6 +84,52 @@ Status Table::Open(const Options& options,
|
|
69
84
|
return s;
|
70
85
|
}
|
71
86
|
|
87
|
+
void Table::ReadMeta(const Footer& footer) {
|
88
|
+
if (rep_->options.filter_policy == NULL) {
|
89
|
+
return; // Do not need any metadata
|
90
|
+
}
|
91
|
+
|
92
|
+
// TODO(sanjay): Skip this if footer.metaindex_handle() size indicates
|
93
|
+
// it is an empty block.
|
94
|
+
ReadOptions opt;
|
95
|
+
BlockContents contents;
|
96
|
+
if (!ReadBlock(rep_->file, opt, footer.metaindex_handle(), &contents).ok()) {
|
97
|
+
// Do not propagate errors since meta info is not needed for operation
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
Block* meta = new Block(contents);
|
101
|
+
|
102
|
+
Iterator* iter = meta->NewIterator(BytewiseComparator());
|
103
|
+
std::string key = "filter.";
|
104
|
+
key.append(rep_->options.filter_policy->Name());
|
105
|
+
iter->Seek(key);
|
106
|
+
if (iter->Valid() && iter->key() == Slice(key)) {
|
107
|
+
ReadFilter(iter->value());
|
108
|
+
}
|
109
|
+
delete iter;
|
110
|
+
delete meta;
|
111
|
+
}
|
112
|
+
|
113
|
+
void Table::ReadFilter(const Slice& filter_handle_value) {
|
114
|
+
Slice v = filter_handle_value;
|
115
|
+
BlockHandle filter_handle;
|
116
|
+
if (!filter_handle.DecodeFrom(&v).ok()) {
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
|
120
|
+
// We might want to unify with ReadBlock() if we start
|
121
|
+
// requiring checksum verification in Table::Open.
|
122
|
+
ReadOptions opt;
|
123
|
+
BlockContents block;
|
124
|
+
if (!ReadBlock(rep_->file, opt, filter_handle, &block).ok()) {
|
125
|
+
return;
|
126
|
+
}
|
127
|
+
if (block.heap_allocated) {
|
128
|
+
rep_->filter_data = block.data.data(); // Will need to delete later
|
129
|
+
}
|
130
|
+
rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data);
|
131
|
+
}
|
132
|
+
|
72
133
|
Table::~Table() {
|
73
134
|
delete rep_;
|
74
135
|
}
|
@@ -105,6 +166,7 @@ Iterator* Table::BlockReader(void* arg,
|
|
105
166
|
// can add more features in the future.
|
106
167
|
|
107
168
|
if (s.ok()) {
|
169
|
+
BlockContents contents;
|
108
170
|
if (block_cache != NULL) {
|
109
171
|
char cache_key_buffer[16];
|
110
172
|
EncodeFixed64(cache_key_buffer, table->rep_->cache_id);
|
@@ -114,14 +176,20 @@ Iterator* Table::BlockReader(void* arg,
|
|
114
176
|
if (cache_handle != NULL) {
|
115
177
|
block = reinterpret_cast<Block*>(block_cache->Value(cache_handle));
|
116
178
|
} else {
|
117
|
-
s = ReadBlock(table->rep_->file, options, handle, &
|
118
|
-
if (s.ok()
|
119
|
-
|
120
|
-
|
179
|
+
s = ReadBlock(table->rep_->file, options, handle, &contents);
|
180
|
+
if (s.ok()) {
|
181
|
+
block = new Block(contents);
|
182
|
+
if (contents.cachable && options.fill_cache) {
|
183
|
+
cache_handle = block_cache->Insert(
|
184
|
+
key, block, block->size(), &DeleteCachedBlock);
|
185
|
+
}
|
121
186
|
}
|
122
187
|
}
|
123
188
|
} else {
|
124
|
-
s = ReadBlock(table->rep_->file, options, handle, &
|
189
|
+
s = ReadBlock(table->rep_->file, options, handle, &contents);
|
190
|
+
if (s.ok()) {
|
191
|
+
block = new Block(contents);
|
192
|
+
}
|
125
193
|
}
|
126
194
|
}
|
127
195
|
|
@@ -145,6 +213,39 @@ Iterator* Table::NewIterator(const ReadOptions& options) const {
|
|
145
213
|
&Table::BlockReader, const_cast<Table*>(this), options);
|
146
214
|
}
|
147
215
|
|
216
|
+
Status Table::InternalGet(const ReadOptions& options, const Slice& k,
|
217
|
+
void* arg,
|
218
|
+
void (*saver)(void*, const Slice&, const Slice&)) {
|
219
|
+
Status s;
|
220
|
+
Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator);
|
221
|
+
iiter->Seek(k);
|
222
|
+
if (iiter->Valid()) {
|
223
|
+
Slice handle_value = iiter->value();
|
224
|
+
FilterBlockReader* filter = rep_->filter;
|
225
|
+
BlockHandle handle;
|
226
|
+
if (filter != NULL &&
|
227
|
+
handle.DecodeFrom(&handle_value).ok() &&
|
228
|
+
!filter->KeyMayMatch(handle.offset(), k)) {
|
229
|
+
// Not found
|
230
|
+
} else {
|
231
|
+
Slice handle = iiter->value();
|
232
|
+
Iterator* block_iter = BlockReader(this, options, iiter->value());
|
233
|
+
block_iter->Seek(k);
|
234
|
+
if (block_iter->Valid()) {
|
235
|
+
(*saver)(arg, block_iter->key(), block_iter->value());
|
236
|
+
}
|
237
|
+
s = block_iter->status();
|
238
|
+
delete block_iter;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
if (s.ok()) {
|
242
|
+
s = iiter->status();
|
243
|
+
}
|
244
|
+
delete iiter;
|
245
|
+
return s;
|
246
|
+
}
|
247
|
+
|
248
|
+
|
148
249
|
uint64_t Table::ApproximateOffsetOf(const Slice& key) const {
|
149
250
|
Iterator* index_iter =
|
150
251
|
rep_->index_block->NewIterator(rep_->options.comparator);
|