leveldb 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|