leveldb 0.0.1 → 0.1.0
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.
- checksums.yaml +4 -4
- data/README.md +69 -46
- data/lib/fiddler.rb +77 -0
- data/lib/leveldb/batch.rb +60 -0
- data/lib/leveldb/db.rb +199 -0
- data/lib/leveldb/iterator.rb +126 -0
- data/lib/leveldb/snapshot.rb +19 -0
- data/lib/leveldb/version.rb +2 -2
- data/lib/leveldb.rb +4 -1004
- data/lib/native.rb +86 -0
- data/test/helper.rb +6 -0
- data/test/test_batch.rb +43 -0
- data/test/test_db.rb +58 -0
- data/test/test_iterator.rb +120 -0
- data/test/test_snapshot.rb +37 -0
- metadata +30 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d547f092bdcb30953adbf70670bc8d6342eac11
|
4
|
+
data.tar.gz: d446c8980b3ee0c230932722e73c408ab70f4660
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da8e914f735c3846b8a0c24541cc9cd5bc203f32afd61c7b287ed7142401b5b74c8990ce77f17e894a8145dd5e23eae70e55fbe3ae5fcef05c8c4b1601a24da0
|
7
|
+
data.tar.gz: 3c4770ed0ff4f84ac01af8bcaa2a217a614c33ccbfb90211b82924a98b1336e51a6b1cb4f63068639b9121222537c3213445f5a477bfd472e2ae6f6df4851425
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Leveldb
|
2
2
|
|
3
|
-
|
3
|
+
LevelDB is a database library (C++, 350 kB) written at Google. It is an
|
4
|
+
embedded database. LevelDB is a persistent ordered map.
|
4
5
|
|
5
6
|
## Documentation
|
6
7
|
|
@@ -9,83 +10,105 @@ Experimental FFI bindings for leveldb
|
|
9
10
|
|
10
11
|
## Installation
|
11
12
|
|
13
|
+
### Development
|
14
|
+
|
12
15
|
$ brew install snappy
|
13
16
|
$ git clone git://github.com/DAddYE/leveldb.git
|
14
17
|
$ cd leveldb
|
15
18
|
$ rake compile
|
16
19
|
$ rake console
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
Here a basic usage, for more advanced please see the doc.
|
21
|
-
|
22
|
-
Right now is very **low** level, I'm planning to add an higher layer api.
|
21
|
+
### Standard
|
23
22
|
|
24
|
-
|
23
|
+
$ brew install snappy
|
24
|
+
$ gem install leveldb
|
25
|
+
$ irb -r leveldb
|
25
26
|
|
26
|
-
|
27
|
+
## Usage
|
27
28
|
|
28
|
-
|
29
|
+
Here a basic usage:
|
29
30
|
|
30
31
|
```rb
|
31
|
-
|
32
|
+
db = LevelDB::DB.new '/tmp/foo'
|
32
33
|
|
33
|
-
|
34
|
+
# Writing
|
35
|
+
db.put('hello', 'world')
|
36
|
+
db['hello'] = 'world'
|
34
37
|
|
35
|
-
|
36
|
-
|
38
|
+
# Reading
|
39
|
+
db.get('hello') # => world
|
40
|
+
db['hello'] # => world
|
37
41
|
|
38
|
-
|
42
|
+
# Deleting
|
43
|
+
db.delete('hello')
|
39
44
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
db
|
45
|
+
# Iterating
|
46
|
+
db.each { |key, val| puts "Key: #{key}, Val: #{val}" }
|
47
|
+
db.reverse_each { |key, val| puts "Key: #{key}, Val: #{val}" }
|
48
|
+
db.keys
|
49
|
+
db.values
|
50
|
+
db.map { |k,v| do_some_with(k, v) }
|
51
|
+
db.reduce([]) { |memo, (k, v)| memo << k + v; memo }
|
52
|
+
db.each # => enumerator
|
53
|
+
db.reverse_each # => enumerator
|
44
54
|
|
45
|
-
|
46
|
-
|
55
|
+
# Ranges
|
56
|
+
db.range('c', 'd') { |k,v| do_some_with_only_keys_in_range }
|
57
|
+
db.reverse_range('c', 'd') # => same as above but results are in reverse order
|
58
|
+
db.range(...) # => enumerable
|
47
59
|
|
48
|
-
|
60
|
+
# Batches
|
61
|
+
db.batch do |b|
|
62
|
+
b.put 'a', 1
|
63
|
+
b.put 'b', 2
|
64
|
+
b.delete 'c'
|
65
|
+
end
|
49
66
|
|
50
|
-
|
51
|
-
|
52
|
-
put
|
67
|
+
b = db.batch
|
68
|
+
b.put 'a', 1
|
69
|
+
b.put 'b', 2
|
70
|
+
b.delete 'c'
|
71
|
+
b.write!
|
53
72
|
|
54
|
-
|
55
|
-
|
73
|
+
# Snapshots
|
74
|
+
db.put 'a', 1
|
75
|
+
db.put 'b', 2
|
76
|
+
db.put 'c', 3
|
56
77
|
|
57
|
-
|
78
|
+
snap = db.snapshot
|
58
79
|
|
59
|
-
|
60
|
-
|
61
|
-
read = get(db, read_opts, "key", 3, read_len, err)
|
80
|
+
db.delete 'a'
|
81
|
+
db.get 'a' # => nil
|
62
82
|
|
63
|
-
|
64
|
-
puts "Key is: #{read}"
|
65
|
-
```
|
83
|
+
snap.set!
|
66
84
|
|
67
|
-
|
85
|
+
db.get('a') # => 1
|
68
86
|
|
69
|
-
|
70
|
-
delete(db, write_opts, "key", 3, err)
|
87
|
+
snap.reset!
|
71
88
|
|
72
|
-
|
73
|
-
```
|
89
|
+
db.get('a') # => nil
|
74
90
|
|
75
|
-
|
91
|
+
snap.set!
|
76
92
|
|
77
|
-
|
78
|
-
db.close
|
79
|
-
```
|
93
|
+
db.get('a') # => 1
|
80
94
|
|
81
|
-
|
95
|
+
# Properties
|
96
|
+
db.read_property('leveldb.stats')
|
82
97
|
|
83
|
-
|
84
|
-
|
98
|
+
# Level Files Size(MB) Time(sec) Read(MB) Write(MB)
|
99
|
+
# --------------------------------------------------
|
100
|
+
# 0 1 0 0 0 0
|
101
|
+
# 1 1 0 0 0 0
|
85
102
|
|
86
|
-
|
103
|
+
# same of:
|
104
|
+
db.stats
|
87
105
|
```
|
88
106
|
|
107
|
+
## Todo
|
108
|
+
|
109
|
+
1. Add pluggable serializers
|
110
|
+
2. Custom comparators
|
111
|
+
|
89
112
|
## Contributing
|
90
113
|
|
91
114
|
1. Fork it
|
data/lib/fiddler.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'fiddle'
|
2
|
+
require 'fiddle/import'
|
3
|
+
require 'rbconfig'
|
4
|
+
|
5
|
+
module Fiddler
|
6
|
+
include Fiddle
|
7
|
+
include Fiddle::CParser
|
8
|
+
include Fiddle::Importer
|
9
|
+
extend Fiddle::Importer
|
10
|
+
extend self
|
11
|
+
|
12
|
+
LIBSUFFIX =
|
13
|
+
case RbConfig::CONFIG['host_os'].downcase
|
14
|
+
when /windows|cgywin/ then 'dll'
|
15
|
+
when /darwin/ then 'dylib'
|
16
|
+
else 'so'
|
17
|
+
end
|
18
|
+
|
19
|
+
LIBPATHS =
|
20
|
+
[ '/usr/local/lib',
|
21
|
+
'/opt/local/lib',
|
22
|
+
'/usr/lib64',
|
23
|
+
File.expand_path("../../ext/leveldb", __FILE__)]
|
24
|
+
|
25
|
+
def dlload(*libs)
|
26
|
+
libs.map! do |lib|
|
27
|
+
LIBPATHS.map { |l| File.join(l, "#{lib}.#{LIBSUFFIX}") }.
|
28
|
+
detect { |l| File.exist?(l) }
|
29
|
+
end
|
30
|
+
super(*libs)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Alias CTypes
|
34
|
+
::Fiddle.constants.each do |c|
|
35
|
+
next unless c.to_s =~ /^TYPE_(.+)$/
|
36
|
+
const_set $1, const_get(c)
|
37
|
+
end
|
38
|
+
|
39
|
+
ULONG = -LONG
|
40
|
+
LLONG = LONG_LONG
|
41
|
+
ULLONG = -LONG_LONG
|
42
|
+
UCHAR = -CHAR
|
43
|
+
USHORT = -SHORT
|
44
|
+
UINT = -INT
|
45
|
+
|
46
|
+
def cdef(name, ret_type, args_types={}, options={})
|
47
|
+
address = handler["#{@_prefix}#{name}"] || handler[name.to_s]
|
48
|
+
options = {name: name}.merge(options)
|
49
|
+
call_type = options.delete(:call_type) || Function::DEFAULT
|
50
|
+
params = args_types.keys.join(', ')
|
51
|
+
values = args_types.values
|
52
|
+
cdefs[name] = Function.new(address, values, ret_type, call_type, options)
|
53
|
+
|
54
|
+
module_eval <<-RB, __FILE__, __LINE__ + 1
|
55
|
+
def #{name}(#{params})
|
56
|
+
cdefs[#{name.inspect}].call(#{params})
|
57
|
+
end
|
58
|
+
module_function #{name.inspect}
|
59
|
+
RB
|
60
|
+
end
|
61
|
+
|
62
|
+
def cdefs
|
63
|
+
@_cdefs ||= {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def [](val)
|
67
|
+
cdefs[val]
|
68
|
+
end
|
69
|
+
|
70
|
+
def prefix(value)
|
71
|
+
@_prefix = value
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.included(base)
|
75
|
+
base.extend self
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module LevelDB
|
2
|
+
|
3
|
+
class Batch
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
def initialize(db, write_opts)
|
7
|
+
@_db = db
|
8
|
+
@_write_opts = write_opts
|
9
|
+
@_err = C::Pointer.malloc(C::SIZEOF_VOIDP)
|
10
|
+
@_err.free = C[:free]
|
11
|
+
@_batch = C.writebatch_create
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(key, val)
|
15
|
+
key, val = key.to_s, val.to_s
|
16
|
+
C.writebatch_put(@_batch, key, key.size, val, val.size)
|
17
|
+
|
18
|
+
val
|
19
|
+
end
|
20
|
+
alias put []=
|
21
|
+
|
22
|
+
def delete(key)
|
23
|
+
key = key.to_s
|
24
|
+
C.writebatch_delete(@_batch, key, key.size)
|
25
|
+
|
26
|
+
key
|
27
|
+
end
|
28
|
+
|
29
|
+
# def clear
|
30
|
+
# C.writebatch_clear(@_batch)
|
31
|
+
|
32
|
+
# true
|
33
|
+
# end
|
34
|
+
# alias clear! clear
|
35
|
+
|
36
|
+
def write!
|
37
|
+
C.write(@_db, @_write_opts, @_batch, @_err)
|
38
|
+
|
39
|
+
raise Error, error_message if errors?
|
40
|
+
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def errors?
|
45
|
+
return unless @_err
|
46
|
+
!@_err.ptr.null?
|
47
|
+
end
|
48
|
+
|
49
|
+
def error_message
|
50
|
+
return unless errors?
|
51
|
+
@_err.ptr.to_s
|
52
|
+
ensure
|
53
|
+
if errors?
|
54
|
+
@_err = C::Pointer.malloc(C::SIZEOF_VOIDP)
|
55
|
+
@_err.free = C[:free]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
alias clear_errors! error_message
|
59
|
+
end
|
60
|
+
end
|
data/lib/leveldb/db.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'leveldb/iterator'
|
3
|
+
require 'leveldb/batch'
|
4
|
+
require 'Leveldb/snapshot'
|
5
|
+
|
6
|
+
module LevelDB
|
7
|
+
class DB
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
class Error < StandardError; end
|
11
|
+
class ClosedError < StandardError; end
|
12
|
+
|
13
|
+
attr_reader :path
|
14
|
+
@@mutex = Mutex.new
|
15
|
+
|
16
|
+
DEFAULT = {
|
17
|
+
create_if_missing: true,
|
18
|
+
error_if_exists: false,
|
19
|
+
paranoid_checks: false,
|
20
|
+
write_buffer_size: 4 << 20,
|
21
|
+
block_size: 4096,
|
22
|
+
max_open_files: 1000,
|
23
|
+
block_cache_size: 8 * (2 << 20),
|
24
|
+
block_restart_interval: 16,
|
25
|
+
compression: false,
|
26
|
+
verify_checksums: false,
|
27
|
+
fill_cache: true
|
28
|
+
}
|
29
|
+
|
30
|
+
def initialize(path, options={})
|
31
|
+
@_db_opts = C.options_create
|
32
|
+
@_write_opts = C.writeoptions_create
|
33
|
+
@_read_opts = C.readoptions_create
|
34
|
+
@_read_len = C.value('size_t')
|
35
|
+
|
36
|
+
options = DEFAULT.merge(options)
|
37
|
+
|
38
|
+
@_cache = C.cache_create_lru(options[:block_cache_size])
|
39
|
+
|
40
|
+
C.readoptions_set_verify_checksums(@_read_opts, options[:verify_checksums] ? 1 : 0)
|
41
|
+
C.readoptions_set_fill_cache(@_read_opts, options[:fill_cache] ? 1 : 0)
|
42
|
+
|
43
|
+
C.options_set_create_if_missing(@_db_opts, options[:create_if_missing] ? 1 : 0)
|
44
|
+
C.options_set_error_if_exists(@_db_opts, options[:error_if_exists] ? 1 : 0)
|
45
|
+
C.options_set_paranoid_checks(@_db_opts, options[:paranoid_checks] ? 1 : 0)
|
46
|
+
C.options_set_write_buffer_size(@_db_opts, options[:write_buffer_size])
|
47
|
+
C.options_set_block_size(@_db_opts, options[:block_size])
|
48
|
+
C.options_set_cache(@_db_opts, @_cache)
|
49
|
+
C.options_set_max_open_files(@_db_opts, options[:max_open_files])
|
50
|
+
C.options_set_block_restart_interval(@_db_opts, options[:block_restart_interval])
|
51
|
+
C.options_set_compression(@_db_opts, options[:compression] ? 1 : 0)
|
52
|
+
|
53
|
+
@_db_opts.free = @_write_opts.free = @_read_opts.free = C[:options_destroy]
|
54
|
+
|
55
|
+
@path = path
|
56
|
+
|
57
|
+
@_err = C::Pointer.malloc(C::SIZEOF_VOIDP)
|
58
|
+
@_err.free = @_read_len.to_ptr.free = C[:free]
|
59
|
+
|
60
|
+
@_db = C.open(@_db_opts, @path, @_err)
|
61
|
+
@_db.free = C[:close]
|
62
|
+
|
63
|
+
raise Error, error_message if errors?
|
64
|
+
end
|
65
|
+
|
66
|
+
def []=(key, val)
|
67
|
+
raise ClosedError if closed?
|
68
|
+
|
69
|
+
key = key.to_s
|
70
|
+
val = val.to_s
|
71
|
+
|
72
|
+
C.put(@_db, @_write_opts, key, key.size, val, val.size, @_err)
|
73
|
+
|
74
|
+
raise Error, error_message if errors?
|
75
|
+
|
76
|
+
val
|
77
|
+
end
|
78
|
+
alias put []=
|
79
|
+
|
80
|
+
def [](key)
|
81
|
+
raise ClosedError if closed?
|
82
|
+
|
83
|
+
key = key.to_s
|
84
|
+
val = C.get(@_db, @_read_opts, key, key.size, @_read_len, @_err)
|
85
|
+
|
86
|
+
raise Error, error_message if errors?
|
87
|
+
|
88
|
+
val.null? ? nil : val.to_s(@_read_len.value)
|
89
|
+
end
|
90
|
+
alias get []
|
91
|
+
|
92
|
+
def delete(key)
|
93
|
+
raise ClosedError if closed?
|
94
|
+
|
95
|
+
key = key.to_s
|
96
|
+
val = get(key)
|
97
|
+
C.delete(@_db, @_write_opts, key, key.size, @_err)
|
98
|
+
|
99
|
+
raise Error, error_message if errors?
|
100
|
+
|
101
|
+
val
|
102
|
+
end
|
103
|
+
|
104
|
+
def snapshot
|
105
|
+
Snapshot.new(@_db, @_read_opts)
|
106
|
+
end
|
107
|
+
|
108
|
+
def batch(&block)
|
109
|
+
raise ClosedError if closed?
|
110
|
+
|
111
|
+
batch = Batch.new(@_db, @_write_opts)
|
112
|
+
|
113
|
+
if block_given?
|
114
|
+
block[batch]
|
115
|
+
batch.write!
|
116
|
+
else
|
117
|
+
batch
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def close
|
122
|
+
raise ClosedError if closed?
|
123
|
+
|
124
|
+
# Prevent double free, I can't free it since
|
125
|
+
# after this call we can still `destroy` it.
|
126
|
+
@_db.free = nil
|
127
|
+
C.close(@_db)
|
128
|
+
@@mutex.synchronize { @_closed = true }
|
129
|
+
raise Error, error_message if errors?
|
130
|
+
|
131
|
+
true
|
132
|
+
end
|
133
|
+
|
134
|
+
def each(&block)
|
135
|
+
raise ClosedError if closed?
|
136
|
+
|
137
|
+
iterator = Iterator.new(@_db, @_read_opts, @_read_len)
|
138
|
+
iterator.each(&block)
|
139
|
+
iterator
|
140
|
+
end
|
141
|
+
|
142
|
+
def reverse_each(&block)
|
143
|
+
each.reverse_each(&block)
|
144
|
+
end
|
145
|
+
|
146
|
+
def range(from, to, &block)
|
147
|
+
each.range(from, to, &block)
|
148
|
+
end
|
149
|
+
|
150
|
+
def reverse_range(from, to, &block)
|
151
|
+
reverse_each.range(from, to, &block)
|
152
|
+
end
|
153
|
+
|
154
|
+
def keys
|
155
|
+
map { |k, v| k }
|
156
|
+
end
|
157
|
+
|
158
|
+
def values
|
159
|
+
map { |k, v| v }
|
160
|
+
end
|
161
|
+
|
162
|
+
def closed?
|
163
|
+
@@mutex.synchronize { @_closed }
|
164
|
+
end
|
165
|
+
|
166
|
+
def destroy
|
167
|
+
C.destroy_db(@_db_opts, @path, @_err)
|
168
|
+
raise Error, error_message if errors?
|
169
|
+
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
def read_property(name)
|
174
|
+
raise ClosedError if closed?
|
175
|
+
|
176
|
+
C.property_value(@_db, name).to_s
|
177
|
+
end
|
178
|
+
|
179
|
+
def stats
|
180
|
+
read_property('leveldb.stats')
|
181
|
+
end
|
182
|
+
|
183
|
+
def errors?
|
184
|
+
return unless @_err
|
185
|
+
!@_err.ptr.null?
|
186
|
+
end
|
187
|
+
|
188
|
+
def error_message
|
189
|
+
return unless errors?
|
190
|
+
@_err.ptr.to_s
|
191
|
+
ensure
|
192
|
+
if errors?
|
193
|
+
@_err = C::Pointer.malloc(C::SIZEOF_VOIDP)
|
194
|
+
@_err.free = C[:free]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
alias clear_errors! error_message
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module LevelDB
|
2
|
+
class Iterator
|
3
|
+
def initialize(db, read_opts, read_len, reverse=false)
|
4
|
+
@_db = db
|
5
|
+
@_read_opts = read_opts
|
6
|
+
@_read_len = read_len
|
7
|
+
@_reverse = reverse
|
8
|
+
@_err = C::Pointer.malloc(C::SIZEOF_VOIDP)
|
9
|
+
@_err.free = C[:free]
|
10
|
+
@_iterator = C.create_iterator(@_db, @_read_opts)
|
11
|
+
# @_iterator.free = C[:iter_destroy]
|
12
|
+
rewind
|
13
|
+
end
|
14
|
+
|
15
|
+
def rewind
|
16
|
+
if reverse?
|
17
|
+
C.iter_seek_to_last(@_iterator)
|
18
|
+
else
|
19
|
+
C.iter_seek_to_first(@_iterator)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def reverse_each(&block)
|
24
|
+
@_reverse = !@_reverse
|
25
|
+
rewind
|
26
|
+
each(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
return self unless block_given?
|
31
|
+
if current = self.next
|
32
|
+
block[*current]
|
33
|
+
end while valid?
|
34
|
+
@_range = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def range(from, to, &block)
|
38
|
+
@_range = [from.to_s, to.to_s].sort
|
39
|
+
@_range = @_range.reverse if reverse?
|
40
|
+
each(&block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def next
|
44
|
+
while valid? && !in_range?
|
45
|
+
move_next
|
46
|
+
end if range?
|
47
|
+
|
48
|
+
key, val = current
|
49
|
+
|
50
|
+
return unless key
|
51
|
+
|
52
|
+
[key, val]
|
53
|
+
ensure
|
54
|
+
move_next
|
55
|
+
end
|
56
|
+
|
57
|
+
def peek
|
58
|
+
self.next
|
59
|
+
ensure
|
60
|
+
move_prev
|
61
|
+
end
|
62
|
+
|
63
|
+
def valid?
|
64
|
+
C.iter_valid(@_iterator) == 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def reverse?
|
68
|
+
@_reverse
|
69
|
+
end
|
70
|
+
|
71
|
+
def range?
|
72
|
+
!!@_range
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def current
|
77
|
+
return unless valid?
|
78
|
+
|
79
|
+
key = C.iter_key(@_iterator, @_read_len).to_s(@_read_len.value)
|
80
|
+
val = C.iter_value(@_iterator, @_read_len).to_s(@_read_len.value)
|
81
|
+
|
82
|
+
[key, val]
|
83
|
+
end
|
84
|
+
|
85
|
+
def in_range?
|
86
|
+
reverse? ? (current[0] <= @_range[0] && current[0] >= @_range[1]) :
|
87
|
+
(current[0] >= @_range[0] && current[0] <= @_range[1])
|
88
|
+
end
|
89
|
+
|
90
|
+
def move_next
|
91
|
+
return unless valid?
|
92
|
+
|
93
|
+
if reverse?
|
94
|
+
C.iter_prev(@_iterator)
|
95
|
+
else
|
96
|
+
C.iter_next(@_iterator)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def move_prev
|
101
|
+
return unless valid?
|
102
|
+
|
103
|
+
if reverse?
|
104
|
+
C.iter_next(@_iterator)
|
105
|
+
else
|
106
|
+
C.iter_prev(@_iterator)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def errors?
|
111
|
+
C.iter_get_error(@_iterator, @_err)
|
112
|
+
!@_err.ptr.null?
|
113
|
+
end
|
114
|
+
|
115
|
+
def error_message
|
116
|
+
return unless errors?
|
117
|
+
@_err.ptr.to_s
|
118
|
+
ensure
|
119
|
+
if errors?
|
120
|
+
@_err = C::Pointer.malloc(C::SIZEOF_VOIDP)
|
121
|
+
@_err.free = C[:free]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
alias clear_errors! error_message
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module LevelDB
|
2
|
+
|
3
|
+
class Snapshot
|
4
|
+
|
5
|
+
def initialize(db, read_opts)
|
6
|
+
@_db = db
|
7
|
+
@_read_opts = read_opts
|
8
|
+
@_snap = C.create_snapshot(@_db)
|
9
|
+
end
|
10
|
+
|
11
|
+
def set!
|
12
|
+
C.readoptions_set_snapshot(@_read_opts, @_snap)
|
13
|
+
end
|
14
|
+
|
15
|
+
def reset!
|
16
|
+
C.readoptions_set_snapshot(@_read_opts, nil)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/leveldb/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION =
|
1
|
+
module LevelDB
|
2
|
+
VERSION = '0.1.0'
|
3
3
|
end
|