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
@@ -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
+ }