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 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