leveldb-ruby 0.14 → 0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);