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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81b89ea5959e4b60215fed39fadbddf1a75bac83
4
- data.tar.gz: 03b75e199e7b944525d611a5dc402dc5bea3334d
3
+ metadata.gz: 5d547f092bdcb30953adbf70670bc8d6342eac11
4
+ data.tar.gz: d446c8980b3ee0c230932722e73c408ab70f4660
5
5
  SHA512:
6
- metadata.gz: bd7ee6bc335740240553b41c422a7ea8fb5591a4b5cdbf5cda65e9cd8bcfc841264ddb3c8687674279d5e10e03a7b49b4cc5653c80621729aa20d3f6a377e54c
7
- data.tar.gz: 038d6bf18789e79919113430dde4d34f796ee96e4843c6cffc01ee6c04fff9b51c6f345e7fc6d8960f2469b9ecb35925c39ddd57914c8a2c725c085780bac830
6
+ metadata.gz: da8e914f735c3846b8a0c24541cc9cd5bc203f32afd61c7b287ed7142401b5b74c8990ce77f17e894a8145dd5e23eae70e55fbe3ae5fcef05c8c4b1601a24da0
7
+ data.tar.gz: 3c4770ed0ff4f84ac01af8bcaa2a217a614c33ccbfb90211b82924a98b1336e51a6b1cb4f63068639b9121222537c3213445f5a477bfd472e2ae6f6df4851425
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Leveldb
2
2
 
3
- Experimental FFI bindings for leveldb
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
- ## Usage
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
- ### Prepare
23
+ $ brew install snappy
24
+ $ gem install leveldb
25
+ $ irb -r leveldb
25
26
 
26
- If you plan to hack in a console ...
27
+ ## Usage
27
28
 
28
- Remember that `err` is important, we are going to use this var in all our calls
29
+ Here a basic usage:
29
30
 
30
31
  ```rb
31
- require 'leveldb'
32
+ db = LevelDB::DB.new '/tmp/foo'
32
33
 
33
- include Leveldb
34
+ # Writing
35
+ db.put('hello', 'world')
36
+ db['hello'] = 'world'
34
37
 
35
- err = err_create
36
- ```
38
+ # Reading
39
+ db.get('hello') # => world
40
+ db['hello'] # => world
37
41
 
38
- ### Open
42
+ # Deleting
43
+ db.delete('hello')
39
44
 
40
- ```ruby
41
- options = options_create
42
- options.set_create_if_missing 1
43
- db = open options, './tmp/testdb', err
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
- abort err.message unless err.null?
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
- ### Write
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
- ```ruby
51
- write_opts = writeoptions_create
52
- put(db, write_opts, "key", "value", err)
67
+ b = db.batch
68
+ b.put 'a', 1
69
+ b.put 'b', 2
70
+ b.delete 'c'
71
+ b.write!
53
72
 
54
- abort err.message unless err.null?
55
- ```
73
+ # Snapshots
74
+ db.put 'a', 1
75
+ db.put 'b', 2
76
+ db.put 'c', 3
56
77
 
57
- ### Read
78
+ snap = db.snapshot
58
79
 
59
- ```ruby
60
- read_opts = readoptions_create
61
- read = get(db, read_opts, "key", 3, read_len, err)
80
+ db.delete 'a'
81
+ db.get 'a' # => nil
62
82
 
63
- abort err.message unless err.null?
64
- puts "Key is: #{read}"
65
- ```
83
+ snap.set!
66
84
 
67
- ### Delete
85
+ db.get('a') # => 1
68
86
 
69
- ```ruby
70
- delete(db, write_opts, "key", 3, err)
87
+ snap.reset!
71
88
 
72
- abort err.message unless err.null?
73
- ```
89
+ db.get('a') # => nil
74
90
 
75
- ### Close (connection)
91
+ snap.set!
76
92
 
77
- ```ruby
78
- db.close
79
- ```
93
+ db.get('a') # => 1
80
94
 
81
- ### Destroy (database)
95
+ # Properties
96
+ db.read_property('leveldb.stats')
82
97
 
83
- ```ruby
84
- destroy_db(options, './tmp/testdb', err)
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
- abort err.message unless err.null?
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
@@ -1,3 +1,3 @@
1
- module Leveldb
2
- VERSION = "0.0.1"
1
+ module LevelDB
2
+ VERSION = '0.1.0'
3
3
  end