leveldb-ruby 0.14 → 0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/LICENSE +24 -0
  2. data/README +60 -16
  3. data/ext/leveldb/extconf.rb +1 -1
  4. data/ext/leveldb/leveldb.cc +187 -18
  5. data/leveldb/Makefile +82 -96
  6. data/leveldb/build_detect_platform +137 -51
  7. data/leveldb/db/c.cc +110 -0
  8. data/leveldb/db/db_bench.cc +105 -4
  9. data/leveldb/db/db_impl.cc +135 -45
  10. data/leveldb/db/db_impl.h +12 -10
  11. data/leveldb/db/db_test.cc +666 -431
  12. data/leveldb/db/dbformat.cc +20 -0
  13. data/leveldb/db/dbformat.h +12 -0
  14. data/leveldb/db/repair.cc +3 -1
  15. data/leveldb/db/skiplist.h +2 -1
  16. data/leveldb/db/table_cache.cc +42 -16
  17. data/leveldb/db/table_cache.h +11 -0
  18. data/leveldb/db/version_set.cc +46 -41
  19. data/leveldb/db/version_set.h +9 -0
  20. data/leveldb/db/write_batch.cc +13 -4
  21. data/leveldb/db/write_batch_internal.h +2 -0
  22. data/leveldb/db/write_batch_test.cc +31 -0
  23. data/leveldb/include/leveldb/c.h +29 -0
  24. data/leveldb/include/leveldb/db.h +2 -1
  25. data/leveldb/include/leveldb/filter_policy.h +70 -0
  26. data/leveldb/include/leveldb/options.h +8 -0
  27. data/leveldb/include/leveldb/status.h +6 -0
  28. data/leveldb/include/leveldb/table.h +15 -0
  29. data/leveldb/include/leveldb/table_builder.h +1 -0
  30. data/leveldb/port/atomic_pointer.h +13 -5
  31. data/leveldb/port/port.h +0 -2
  32. data/leveldb/port/port_example.h +10 -0
  33. data/leveldb/port/port_posix.cc +4 -0
  34. data/leveldb/port/port_posix.h +24 -9
  35. data/leveldb/table/block.cc +8 -4
  36. data/leveldb/table/block.h +3 -2
  37. data/leveldb/table/filter_block.cc +111 -0
  38. data/leveldb/table/filter_block.h +68 -0
  39. data/leveldb/table/filter_block_test.cc +128 -0
  40. data/leveldb/table/format.cc +17 -7
  41. data/leveldb/table/format.h +9 -4
  42. data/leveldb/table/table.cc +107 -6
  43. data/leveldb/table/table_builder.cc +49 -6
  44. data/leveldb/table/table_test.cc +8 -24
  45. data/leveldb/util/bloom.cc +95 -0
  46. data/leveldb/util/bloom_test.cc +159 -0
  47. data/leveldb/util/coding_test.cc +23 -0
  48. data/leveldb/util/comparator.cc +8 -3
  49. data/leveldb/util/env_posix.cc +46 -4
  50. data/leveldb/util/filter_policy.cc +11 -0
  51. data/leveldb/util/options.cc +2 -1
  52. data/lib/leveldb.rb +31 -5
  53. metadata +227 -109
  54. data/leveldb/port/port_android.cc +0 -64
  55. 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
+ }
@@ -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
- Block** block) {
70
- *block = NULL;
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
- // Copy into buf[].
104
- memcpy(buf, data, n + kBlockTrailerSize);
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
- buf = ubuf;
123
- n = ulength;
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
 
@@ -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
- // Read the block identified by "handle" from "file". On success,
87
- // store a pointer to the heap-allocated result in *block and return
88
- // OK. On failure store NULL in *block and return non-OK.
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
- Block** block);
97
+ BlockContents* result);
93
98
 
94
99
  // Implementation details follow. Clients should ignore,
95
100
 
@@ -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(), &index_block);
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, &block);
118
- if (s.ok() && options.fill_cache) {
119
- cache_handle = block_cache->Insert(
120
- key, block, block->size(), &DeleteCachedBlock);
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, &block);
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);