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
@@ -12,8 +12,9 @@
12
12
 
13
13
  namespace leveldb {
14
14
 
15
+ // Update Makefile if you change these
15
16
  static const int kMajorVersion = 1;
16
- static const int kMinorVersion = 2;
17
+ static const int kMinorVersion = 5;
17
18
 
18
19
  struct Options;
19
20
  struct ReadOptions;
@@ -0,0 +1,70 @@
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 database can be configured with a custom FilterPolicy object.
6
+ // This object is responsible for creating a small filter from a set
7
+ // of keys. These filters are stored in leveldb and are consulted
8
+ // automatically by leveldb to decide whether or not to read some
9
+ // information from disk. In many cases, a filter can cut down the
10
+ // number of disk seeks form a handful to a single disk seek per
11
+ // DB::Get() call.
12
+ //
13
+ // Most people will want to use the builtin bloom filter support (see
14
+ // NewBloomFilterPolicy() below).
15
+
16
+ #ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_
17
+ #define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_
18
+
19
+ #include <string>
20
+
21
+ namespace leveldb {
22
+
23
+ class Slice;
24
+
25
+ class FilterPolicy {
26
+ public:
27
+ virtual ~FilterPolicy();
28
+
29
+ // Return the name of this policy. Note that if the filter encoding
30
+ // changes in an incompatible way, the name returned by this method
31
+ // must be changed. Otherwise, old incompatible filters may be
32
+ // passed to methods of this type.
33
+ virtual const char* Name() const = 0;
34
+
35
+ // keys[0,n-1] contains a list of keys (potentially with duplicates)
36
+ // that are ordered according to the user supplied comparator.
37
+ // Append a filter that summarizes keys[0,n-1] to *dst.
38
+ //
39
+ // Warning: do not change the initial contents of *dst. Instead,
40
+ // append the newly constructed filter to *dst.
41
+ virtual void CreateFilter(const Slice* keys, int n, std::string* dst)
42
+ const = 0;
43
+
44
+ // "filter" contains the data appended by a preceding call to
45
+ // CreateFilter() on this class. This method must return true if
46
+ // the key was in the list of keys passed to CreateFilter().
47
+ // This method may return true or false if the key was not on the
48
+ // list, but it should aim to return false with a high probability.
49
+ virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0;
50
+ };
51
+
52
+ // Return a new filter policy that uses a bloom filter with approximately
53
+ // the specified number of bits per key. A good value for bits_per_key
54
+ // is 10, which yields a filter with ~ 1% false positive rate.
55
+ //
56
+ // Callers must delete the result after any database that is using the
57
+ // result has been closed.
58
+ //
59
+ // Note: if you are using a custom comparator that ignores some parts
60
+ // of the keys being compared, you must not use NewBloomFilterPolicy()
61
+ // and must provide your own FilterPolicy that also ignores the
62
+ // corresponding parts of the keys. For example, if the comparator
63
+ // ignores trailing spaces, it would be incorrect to use a
64
+ // FilterPolicy (like NewBloomFilterPolicy) that does not ignore
65
+ // trailing spaces in keys.
66
+ extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key);
67
+
68
+ }
69
+
70
+ #endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_
@@ -12,6 +12,7 @@ namespace leveldb {
12
12
  class Cache;
13
13
  class Comparator;
14
14
  class Env;
15
+ class FilterPolicy;
15
16
  class Logger;
16
17
  class Snapshot;
17
18
 
@@ -127,6 +128,13 @@ struct Options {
127
128
  // efficiently detect that and will switch to uncompressed mode.
128
129
  CompressionType compression;
129
130
 
131
+ // If non-NULL, use the specified filter policy to reduce disk reads.
132
+ // Many applications will benefit from passing the result of
133
+ // NewBloomFilterPolicy() here.
134
+ //
135
+ // Default: NULL
136
+ const FilterPolicy* filter_policy;
137
+
130
138
  // Create an Options object with default values for all fields.
131
139
  Options();
132
140
  };
@@ -54,6 +54,12 @@ class Status {
54
54
  // Returns true iff the status indicates a NotFound error.
55
55
  bool IsNotFound() const { return code() == kNotFound; }
56
56
 
57
+ // Returns true iff the status indicates a Corruption error.
58
+ bool IsCorruption() const { return code() == kCorruption; }
59
+
60
+ // Returns true iff the status indicates an IOError.
61
+ bool IsIOError() const { return code() == kIOError; }
62
+
57
63
  // Return a string representation of this status suitable for printing.
58
64
  // Returns the string "OK" for success.
59
65
  std::string ToString() const;
@@ -12,9 +12,11 @@ namespace leveldb {
12
12
 
13
13
  class Block;
14
14
  class BlockHandle;
15
+ class Footer;
15
16
  struct Options;
16
17
  class RandomAccessFile;
17
18
  struct ReadOptions;
19
+ class TableCache;
18
20
 
19
21
  // A Table is a sorted map from strings to strings. Tables are
20
22
  // immutable and persistent. A Table may be safely accessed from
@@ -60,6 +62,19 @@ class Table {
60
62
  explicit Table(Rep* rep) { rep_ = rep; }
61
63
  static Iterator* BlockReader(void*, const ReadOptions&, const Slice&);
62
64
 
65
+ // Calls (*handle_result)(arg, ...) with the entry found after a call
66
+ // to Seek(key). May not make such a call if filter policy says
67
+ // that key is not present.
68
+ friend class TableCache;
69
+ Status InternalGet(
70
+ const ReadOptions&, const Slice& key,
71
+ void* arg,
72
+ void (*handle_result)(void* arg, const Slice& k, const Slice& v));
73
+
74
+
75
+ void ReadMeta(const Footer& footer);
76
+ void ReadFilter(const Slice& filter_handle_value);
77
+
63
78
  // No copying allowed
64
79
  Table(const Table&);
65
80
  void operator=(const Table&);
@@ -77,6 +77,7 @@ class TableBuilder {
77
77
  private:
78
78
  bool ok() const { return status().ok(); }
79
79
  void WriteBlock(BlockBuilder* block, BlockHandle* handle);
80
+ void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle);
80
81
 
81
82
  struct Rep;
82
83
  Rep* rep_;
@@ -73,13 +73,21 @@ inline void MemoryBarrier() {
73
73
  }
74
74
  #define LEVELDB_HAVE_MEMORY_BARRIER
75
75
 
76
- // ARM
77
- #elif defined(ARCH_CPU_ARM_FAMILY)
76
+ // ARM Linux
77
+ #elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__)
78
78
  typedef void (*LinuxKernelMemoryBarrierFunc)(void);
79
- LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
80
- (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
79
+ // The Linux ARM kernel provides a highly optimized device-specific memory
80
+ // barrier function at a fixed memory address that is mapped in every
81
+ // user-level process.
82
+ //
83
+ // This beats using CPU-specific instructions which are, on single-core
84
+ // devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more
85
+ // than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking
86
+ // shows that the extra function call cost is completely negligible on
87
+ // multi-core devices.
88
+ //
81
89
  inline void MemoryBarrier() {
82
- pLinuxKernelMemoryBarrier();
90
+ (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)();
83
91
  }
84
92
  #define LEVELDB_HAVE_MEMORY_BARRIER
85
93
 
@@ -14,8 +14,6 @@
14
14
  # include "port/port_posix.h"
15
15
  #elif defined(LEVELDB_PLATFORM_CHROMIUM)
16
16
  # include "port/port_chromium.h"
17
- #elif defined(LEVELDB_PLATFORM_ANDROID)
18
- # include "port/port_android.h"
19
17
  #endif
20
18
 
21
19
  #endif // STORAGE_LEVELDB_PORT_PORT_H_
@@ -60,6 +60,16 @@ class CondVar {
60
60
  void SignallAll();
61
61
  };
62
62
 
63
+ // Thread-safe initialization.
64
+ // Used as follows:
65
+ // static port::OnceType init_control = LEVELDB_ONCE_INIT;
66
+ // static void Initializer() { ... do something ...; }
67
+ // ...
68
+ // port::InitOnce(&init_control, &Initializer);
69
+ typedef intptr_t OnceType;
70
+ #define LEVELDB_ONCE_INIT 0
71
+ extern void InitOnce(port::OnceType*, void (*initializer)());
72
+
63
73
  // A type that holds a pointer that can be read or written atomically
64
74
  // (i.e., without word-tearing.)
65
75
  class AtomicPointer {
@@ -46,5 +46,9 @@ void CondVar::SignalAll() {
46
46
  PthreadCall("broadcast", pthread_cond_broadcast(&cv_));
47
47
  }
48
48
 
49
+ void InitOnce(OnceType* once, void (*initializer)()) {
50
+ PthreadCall("once", pthread_once(once, initializer));
51
+ }
52
+
49
53
  } // namespace port
50
54
  } // namespace leveldb
@@ -7,17 +7,22 @@
7
7
  #ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_
8
8
  #define STORAGE_LEVELDB_PORT_PORT_POSIX_H_
9
9
 
10
+ #undef PLATFORM_IS_LITTLE_ENDIAN
10
11
  #if defined(OS_MACOSX)
11
12
  #include <machine/endian.h>
13
+ #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER)
14
+ #define PLATFORM_IS_LITTLE_ENDIAN \
15
+ (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN)
16
+ #endif
12
17
  #elif defined(OS_SOLARIS)
13
18
  #include <sys/isa_defs.h>
14
19
  #ifdef _LITTLE_ENDIAN
15
- #define LITTLE_ENDIAN
20
+ #define PLATFORM_IS_LITTLE_ENDIAN true
16
21
  #else
17
- #define BIG_ENDIAN
22
+ #define PLATFORM_IS_LITTLE_ENDIAN false
18
23
  #endif
19
24
  #elif defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\
20
- defined(OS_DRAGONFLYBSD)
25
+ defined(OS_DRAGONFLYBSD) || defined(OS_ANDROID)
21
26
  #include <sys/types.h>
22
27
  #include <sys/endian.h>
23
28
  #else
@@ -31,14 +36,13 @@
31
36
  #include <string>
32
37
  #include "port/atomic_pointer.h"
33
38
 
34
- #ifdef LITTLE_ENDIAN
35
- #define IS_LITTLE_ENDIAN true
36
- #else
37
- #define IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
39
+ #ifndef PLATFORM_IS_LITTLE_ENDIAN
40
+ #define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
38
41
  #endif
39
42
 
40
43
  #if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\
41
- defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD)
44
+ defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\
45
+ defined(OS_ANDROID)
42
46
  // Use fread/fwrite/fflush on platforms without _unlocked variants
43
47
  #define fread_unlocked fread
44
48
  #define fwrite_unlocked fwrite
@@ -51,10 +55,17 @@
51
55
  #define fdatasync fsync
52
56
  #endif
53
57
 
58
+ #if defined(OS_ANDROID) && __ANDROID_API__ < 9
59
+ // fdatasync() was only introduced in API level 9 on Android. Use fsync()
60
+ // when targetting older platforms.
61
+ #define fdatasync fsync
62
+ #endif
63
+
54
64
  namespace leveldb {
55
65
  namespace port {
56
66
 
57
- static const bool kLittleEndian = IS_LITTLE_ENDIAN;
67
+ static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN;
68
+ #undef PLATFORM_IS_LITTLE_ENDIAN
58
69
 
59
70
  class CondVar;
60
71
 
@@ -88,6 +99,10 @@ class CondVar {
88
99
  Mutex* mu_;
89
100
  };
90
101
 
102
+ typedef pthread_once_t OnceType;
103
+ #define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT
104
+ extern void InitOnce(OnceType* once, void (*initializer)());
105
+
91
106
  inline bool Snappy_Compress(const char* input, size_t length,
92
107
  ::std::string* output) {
93
108
  #ifdef SNAPPY
@@ -9,6 +9,7 @@
9
9
  #include <vector>
10
10
  #include <algorithm>
11
11
  #include "leveldb/comparator.h"
12
+ #include "table/format.h"
12
13
  #include "util/coding.h"
13
14
  #include "util/logging.h"
14
15
 
@@ -19,9 +20,10 @@ inline uint32_t Block::NumRestarts() const {
19
20
  return DecodeFixed32(data_ + size_ - sizeof(uint32_t));
20
21
  }
21
22
 
22
- Block::Block(const char* data, size_t size)
23
- : data_(data),
24
- size_(size) {
23
+ Block::Block(const BlockContents& contents)
24
+ : data_(contents.data.data()),
25
+ size_(contents.data.size()),
26
+ owned_(contents.heap_allocated) {
25
27
  if (size_ < sizeof(uint32_t)) {
26
28
  size_ = 0; // Error marker
27
29
  } else {
@@ -35,7 +37,9 @@ Block::Block(const char* data, size_t size)
35
37
  }
36
38
 
37
39
  Block::~Block() {
38
- delete[] data_;
40
+ if (owned_) {
41
+ delete[] data_;
42
+ }
39
43
  }
40
44
 
41
45
  // Helper routine: decode the next block entry starting at "p",
@@ -11,13 +11,13 @@
11
11
 
12
12
  namespace leveldb {
13
13
 
14
+ struct BlockContents;
14
15
  class Comparator;
15
16
 
16
17
  class Block {
17
18
  public:
18
19
  // Initialize the block with the specified contents.
19
- // Takes ownership of data[] and will delete[] it when done.
20
- Block(const char* data, size_t size);
20
+ explicit Block(const BlockContents& contents);
21
21
 
22
22
  ~Block();
23
23
 
@@ -30,6 +30,7 @@ class Block {
30
30
  const char* data_;
31
31
  size_t size_;
32
32
  uint32_t restart_offset_; // Offset in data_ of restart array
33
+ bool owned_; // Block owns data_[]
33
34
 
34
35
  // No copying allowed
35
36
  Block(const Block&);
@@ -0,0 +1,111 @@
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
+
10
+ namespace leveldb {
11
+
12
+ // See doc/table_format.txt for an explanation of the filter block format.
13
+
14
+ // Generate new filter every 2KB of data
15
+ static const size_t kFilterBaseLg = 11;
16
+ static const size_t kFilterBase = 1 << kFilterBaseLg;
17
+
18
+ FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy)
19
+ : policy_(policy) {
20
+ }
21
+
22
+ void FilterBlockBuilder::StartBlock(uint64_t block_offset) {
23
+ uint64_t filter_index = (block_offset / kFilterBase);
24
+ assert(filter_index >= filter_offsets_.size());
25
+ while (filter_index > filter_offsets_.size()) {
26
+ GenerateFilter();
27
+ }
28
+ }
29
+
30
+ void FilterBlockBuilder::AddKey(const Slice& key) {
31
+ Slice k = key;
32
+ start_.push_back(keys_.size());
33
+ keys_.append(k.data(), k.size());
34
+ }
35
+
36
+ Slice FilterBlockBuilder::Finish() {
37
+ if (!start_.empty()) {
38
+ GenerateFilter();
39
+ }
40
+
41
+ // Append array of per-filter offsets
42
+ const uint32_t array_offset = result_.size();
43
+ for (size_t i = 0; i < filter_offsets_.size(); i++) {
44
+ PutFixed32(&result_, filter_offsets_[i]);
45
+ }
46
+
47
+ PutFixed32(&result_, array_offset);
48
+ result_.push_back(kFilterBaseLg); // Save encoding parameter in result
49
+ return Slice(result_);
50
+ }
51
+
52
+ void FilterBlockBuilder::GenerateFilter() {
53
+ const size_t num_keys = start_.size();
54
+ if (num_keys == 0) {
55
+ // Fast path if there are no keys for this filter
56
+ filter_offsets_.push_back(result_.size());
57
+ return;
58
+ }
59
+
60
+ // Make list of keys from flattened key structure
61
+ start_.push_back(keys_.size()); // Simplify length computation
62
+ tmp_keys_.resize(num_keys);
63
+ for (size_t i = 0; i < num_keys; i++) {
64
+ const char* base = keys_.data() + start_[i];
65
+ size_t length = start_[i+1] - start_[i];
66
+ tmp_keys_[i] = Slice(base, length);
67
+ }
68
+
69
+ // Generate filter for current set of keys and append to result_.
70
+ filter_offsets_.push_back(result_.size());
71
+ policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_);
72
+
73
+ tmp_keys_.clear();
74
+ keys_.clear();
75
+ start_.clear();
76
+ }
77
+
78
+ FilterBlockReader::FilterBlockReader(const FilterPolicy* policy,
79
+ const Slice& contents)
80
+ : policy_(policy),
81
+ data_(NULL),
82
+ offset_(NULL),
83
+ num_(0),
84
+ base_lg_(0) {
85
+ size_t n = contents.size();
86
+ if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array
87
+ base_lg_ = contents[n-1];
88
+ uint32_t last_word = DecodeFixed32(contents.data() + n - 5);
89
+ if (last_word > n - 5) return;
90
+ data_ = contents.data();
91
+ offset_ = data_ + last_word;
92
+ num_ = (n - 5 - last_word) / 4;
93
+ }
94
+
95
+ bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) {
96
+ uint64_t index = block_offset >> base_lg_;
97
+ if (index < num_) {
98
+ uint32_t start = DecodeFixed32(offset_ + index*4);
99
+ uint32_t limit = DecodeFixed32(offset_ + index*4 + 4);
100
+ if (start <= limit && limit <= (offset_ - data_)) {
101
+ Slice filter = Slice(data_ + start, limit - start);
102
+ return policy_->KeyMayMatch(key, filter);
103
+ } else if (start == limit) {
104
+ // Empty filters do not match any keys
105
+ return false;
106
+ }
107
+ }
108
+ return true; // Errors are treated as potential matches
109
+ }
110
+
111
+ }