filiptepper-leveldb-ruby 0.14
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.
- data/LICENSE +24 -0
- data/README +72 -0
- data/ext/leveldb/extconf.rb +14 -0
- data/ext/leveldb/leveldb.cc +530 -0
- data/ext/leveldb/platform.rb +83 -0
- data/leveldb/Makefile +191 -0
- data/leveldb/build_detect_platform +160 -0
- data/leveldb/db/builder.cc +88 -0
- data/leveldb/db/builder.h +34 -0
- data/leveldb/db/c.cc +581 -0
- data/leveldb/db/corruption_test.cc +359 -0
- data/leveldb/db/db_bench.cc +970 -0
- data/leveldb/db/db_impl.cc +1448 -0
- data/leveldb/db/db_impl.h +194 -0
- data/leveldb/db/db_iter.cc +299 -0
- data/leveldb/db/db_iter.h +26 -0
- data/leveldb/db/db_test.cc +1901 -0
- data/leveldb/db/dbformat.cc +140 -0
- data/leveldb/db/dbformat.h +227 -0
- data/leveldb/db/dbformat_test.cc +112 -0
- data/leveldb/db/filename.cc +139 -0
- data/leveldb/db/filename.h +80 -0
- data/leveldb/db/filename_test.cc +122 -0
- data/leveldb/db/log_format.h +35 -0
- data/leveldb/db/log_reader.cc +259 -0
- data/leveldb/db/log_reader.h +108 -0
- data/leveldb/db/log_test.cc +500 -0
- data/leveldb/db/log_writer.cc +103 -0
- data/leveldb/db/log_writer.h +48 -0
- data/leveldb/db/memtable.cc +145 -0
- data/leveldb/db/memtable.h +91 -0
- data/leveldb/db/repair.cc +389 -0
- data/leveldb/db/skiplist.h +379 -0
- data/leveldb/db/skiplist_test.cc +378 -0
- data/leveldb/db/snapshot.h +66 -0
- data/leveldb/db/table_cache.cc +121 -0
- data/leveldb/db/table_cache.h +61 -0
- data/leveldb/db/version_edit.cc +266 -0
- data/leveldb/db/version_edit.h +107 -0
- data/leveldb/db/version_edit_test.cc +46 -0
- data/leveldb/db/version_set.cc +1402 -0
- data/leveldb/db/version_set.h +370 -0
- data/leveldb/db/version_set_test.cc +179 -0
- data/leveldb/db/write_batch.cc +147 -0
- data/leveldb/db/write_batch_internal.h +49 -0
- data/leveldb/db/write_batch_test.cc +120 -0
- data/leveldb/helpers/memenv/memenv.cc +374 -0
- data/leveldb/helpers/memenv/memenv.h +20 -0
- data/leveldb/helpers/memenv/memenv_test.cc +232 -0
- data/leveldb/include/leveldb/c.h +275 -0
- data/leveldb/include/leveldb/cache.h +99 -0
- data/leveldb/include/leveldb/comparator.h +63 -0
- data/leveldb/include/leveldb/db.h +161 -0
- data/leveldb/include/leveldb/env.h +323 -0
- data/leveldb/include/leveldb/filter_policy.h +70 -0
- data/leveldb/include/leveldb/iterator.h +100 -0
- data/leveldb/include/leveldb/options.h +195 -0
- data/leveldb/include/leveldb/slice.h +109 -0
- data/leveldb/include/leveldb/status.h +106 -0
- data/leveldb/include/leveldb/table.h +85 -0
- data/leveldb/include/leveldb/table_builder.h +92 -0
- data/leveldb/include/leveldb/write_batch.h +64 -0
- data/leveldb/port/atomic_pointer.h +144 -0
- data/leveldb/port/port.h +21 -0
- data/leveldb/port/port_android.cc +64 -0
- data/leveldb/port/port_android.h +159 -0
- data/leveldb/port/port_example.h +125 -0
- data/leveldb/port/port_posix.cc +50 -0
- data/leveldb/port/port_posix.h +129 -0
- data/leveldb/port/win/stdint.h +24 -0
- data/leveldb/table/block.cc +267 -0
- data/leveldb/table/block.h +44 -0
- data/leveldb/table/block_builder.cc +109 -0
- data/leveldb/table/block_builder.h +57 -0
- data/leveldb/table/filter_block.cc +111 -0
- data/leveldb/table/filter_block.h +68 -0
- data/leveldb/table/filter_block_test.cc +128 -0
- data/leveldb/table/format.cc +145 -0
- data/leveldb/table/format.h +108 -0
- data/leveldb/table/iterator.cc +67 -0
- data/leveldb/table/iterator_wrapper.h +63 -0
- data/leveldb/table/merger.cc +197 -0
- data/leveldb/table/merger.h +26 -0
- data/leveldb/table/table.cc +276 -0
- data/leveldb/table/table_builder.cc +270 -0
- data/leveldb/table/table_test.cc +838 -0
- data/leveldb/table/two_level_iterator.cc +182 -0
- data/leveldb/table/two_level_iterator.h +34 -0
- data/leveldb/util/arena.cc +68 -0
- data/leveldb/util/arena.h +68 -0
- data/leveldb/util/arena_test.cc +68 -0
- data/leveldb/util/bloom.cc +95 -0
- data/leveldb/util/bloom_test.cc +159 -0
- data/leveldb/util/cache.cc +328 -0
- data/leveldb/util/cache_test.cc +186 -0
- data/leveldb/util/coding.cc +194 -0
- data/leveldb/util/coding.h +104 -0
- data/leveldb/util/coding_test.cc +173 -0
- data/leveldb/util/comparator.cc +76 -0
- data/leveldb/util/crc32c.cc +332 -0
- data/leveldb/util/crc32c.h +45 -0
- data/leveldb/util/crc32c_test.cc +72 -0
- data/leveldb/util/env.cc +96 -0
- data/leveldb/util/env_posix.cc +609 -0
- data/leveldb/util/env_test.cc +104 -0
- data/leveldb/util/filter_policy.cc +11 -0
- data/leveldb/util/hash.cc +45 -0
- data/leveldb/util/hash.h +19 -0
- data/leveldb/util/histogram.cc +139 -0
- data/leveldb/util/histogram.h +42 -0
- data/leveldb/util/logging.cc +81 -0
- data/leveldb/util/logging.h +47 -0
- data/leveldb/util/mutexlock.h +39 -0
- data/leveldb/util/options.cc +29 -0
- data/leveldb/util/posix_logger.h +98 -0
- data/leveldb/util/random.h +59 -0
- data/leveldb/util/status.cc +75 -0
- data/leveldb/util/testharness.cc +77 -0
- data/leveldb/util/testharness.h +138 -0
- data/leveldb/util/testutil.cc +51 -0
- data/leveldb/util/testutil.h +53 -0
- data/lib/leveldb.rb +76 -0
- metadata +175 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Copyright (c) 2011--2012 William Morgan.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
* Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
* Neither the name of the <organization> nor the
|
|
12
|
+
names of its contributors may be used to endorse or promote products
|
|
13
|
+
derived from this software without specific prior written permission.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
LevelDB is a very fast, persistent, in-process key-value store.
|
|
2
|
+
Read more about it here: http://code.google.com/p/leveldb/.
|
|
3
|
+
|
|
4
|
+
This gem contains Ruby bindings so that you can use it from your
|
|
5
|
+
Ruby process.
|
|
6
|
+
|
|
7
|
+
INSTALLATION
|
|
8
|
+
|
|
9
|
+
gem install leveldb-ruby
|
|
10
|
+
|
|
11
|
+
SYNOPSIS
|
|
12
|
+
|
|
13
|
+
require 'rubygems' # on for ruby 1.8
|
|
14
|
+
require 'leveldb'
|
|
15
|
+
|
|
16
|
+
## make a new database
|
|
17
|
+
db = LevelDB::DB.new "/tmp/asdf"
|
|
18
|
+
|
|
19
|
+
## getting and setting
|
|
20
|
+
db.put "it", "works" # => "works"
|
|
21
|
+
db.get "it" # => "works"
|
|
22
|
+
|
|
23
|
+
db["hello"] = "there" # => "there"
|
|
24
|
+
db["hello"] # => "there"
|
|
25
|
+
|
|
26
|
+
db["nonexistent"] # => nil
|
|
27
|
+
|
|
28
|
+
## testing
|
|
29
|
+
db.includes? "hello" # => true
|
|
30
|
+
db.contains? "hello" # => true
|
|
31
|
+
|
|
32
|
+
## keys and values
|
|
33
|
+
db.keys # => "it", "hello"
|
|
34
|
+
db.values # => "there", "works"
|
|
35
|
+
|
|
36
|
+
## iterating
|
|
37
|
+
db.each { |k, v| ... }
|
|
38
|
+
db.map { |k, v| ... }
|
|
39
|
+
db.each # => LevelDB::Iterator
|
|
40
|
+
|
|
41
|
+
## ranges
|
|
42
|
+
db.each(:from => "a", :to => "b") # => LevelDB::Iterator
|
|
43
|
+
db.each(:from => "a", :to => "b").
|
|
44
|
+
map { |k, v| ... }
|
|
45
|
+
# etc...
|
|
46
|
+
|
|
47
|
+
## deleting
|
|
48
|
+
db.delete "hello" # => "there"
|
|
49
|
+
db.delete "hello" # => nil
|
|
50
|
+
|
|
51
|
+
LICENSE
|
|
52
|
+
|
|
53
|
+
Leveldb-ruby is available for your use under the terms of
|
|
54
|
+
the New BSD License. See the LICENSE file for details.
|
|
55
|
+
|
|
56
|
+
CREDIT
|
|
57
|
+
|
|
58
|
+
This gem brought to you by William Morgan <http://masanjin.net/>
|
|
59
|
+
and the following honorable contributors:
|
|
60
|
+
- Rick Olson
|
|
61
|
+
- byplayer
|
|
62
|
+
- Yukio Goto
|
|
63
|
+
- Johannes Holzfuß
|
|
64
|
+
- Steve Wilhelm
|
|
65
|
+
- Gabriel Ebner
|
|
66
|
+
and by users like you.
|
|
67
|
+
|
|
68
|
+
BUGS
|
|
69
|
+
|
|
70
|
+
Please report bugs to https://github.com/wmorgan/leveldb-ruby/issues.
|
|
71
|
+
|
|
72
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'mkmf'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require "./platform.rb"
|
|
4
|
+
|
|
5
|
+
Dir.chdir "../../leveldb"
|
|
6
|
+
system "make libleveldb.a" or abort
|
|
7
|
+
Dir.chdir "../ext/leveldb"
|
|
8
|
+
|
|
9
|
+
set_platform_specific_variables!
|
|
10
|
+
|
|
11
|
+
$CFLAGS << " -I../../leveldb/include"
|
|
12
|
+
$LIBS << " -L../../leveldb -lleveldb"
|
|
13
|
+
|
|
14
|
+
create_makefile "leveldb/leveldb"
|
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
|
|
3
|
+
#include "leveldb/db.h"
|
|
4
|
+
#include "leveldb/slice.h"
|
|
5
|
+
#include "leveldb/write_batch.h"
|
|
6
|
+
|
|
7
|
+
static VALUE m_leveldb;
|
|
8
|
+
static VALUE c_db;
|
|
9
|
+
static VALUE c_iter;
|
|
10
|
+
static VALUE c_batch;
|
|
11
|
+
static VALUE c_error;
|
|
12
|
+
static VALUE k_fill;
|
|
13
|
+
static VALUE k_verify;
|
|
14
|
+
static VALUE k_sync;
|
|
15
|
+
static VALUE k_from;
|
|
16
|
+
static VALUE k_to;
|
|
17
|
+
static VALUE k_reversed;
|
|
18
|
+
static VALUE k_class;
|
|
19
|
+
static VALUE k_name;
|
|
20
|
+
static ID to_s;
|
|
21
|
+
static leveldb::ReadOptions uncached_read_options;
|
|
22
|
+
|
|
23
|
+
// support 1.9 and 1.8
|
|
24
|
+
#ifndef RSTRING_PTR
|
|
25
|
+
#define RSTRING_PTR(v) RSTRING(v)->ptr
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
// convert status errors into exceptions
|
|
29
|
+
#define RAISE_ON_ERROR(status) do { \
|
|
30
|
+
if(!status.ok()) { \
|
|
31
|
+
VALUE exc = rb_exc_new2(c_error, status.ToString().c_str()); \
|
|
32
|
+
rb_exc_raise(exc); \
|
|
33
|
+
} \
|
|
34
|
+
} while(0)
|
|
35
|
+
|
|
36
|
+
typedef struct bound_db {
|
|
37
|
+
leveldb::DB* db;
|
|
38
|
+
} bound_db;
|
|
39
|
+
|
|
40
|
+
static void db_free(bound_db* db) {
|
|
41
|
+
if(db->db != NULL) {
|
|
42
|
+
delete db->db;
|
|
43
|
+
db->db = NULL;
|
|
44
|
+
}
|
|
45
|
+
delete db;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static VALUE db_make(VALUE klass, VALUE v_pathname, VALUE v_create_if_necessary, VALUE v_break_if_exists) {
|
|
49
|
+
Check_Type(v_pathname, T_STRING);
|
|
50
|
+
|
|
51
|
+
bound_db* db = new bound_db;
|
|
52
|
+
std::string pathname = std::string((char*)RSTRING_PTR(v_pathname));
|
|
53
|
+
|
|
54
|
+
leveldb::Options options;
|
|
55
|
+
if(RTEST(v_create_if_necessary)) options.create_if_missing = true;
|
|
56
|
+
if(RTEST(v_break_if_exists)) options.error_if_exists = true;
|
|
57
|
+
leveldb::Status status = leveldb::DB::Open(options, pathname, &db->db);
|
|
58
|
+
RAISE_ON_ERROR(status);
|
|
59
|
+
|
|
60
|
+
VALUE o_db = Data_Wrap_Struct(klass, NULL, db_free, db);
|
|
61
|
+
VALUE argv[1] = { v_pathname };
|
|
62
|
+
rb_obj_call_init(o_db, 1, argv);
|
|
63
|
+
|
|
64
|
+
return o_db;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static VALUE db_close(VALUE self) {
|
|
68
|
+
bound_db* db;
|
|
69
|
+
Data_Get_Struct(self, bound_db, db);
|
|
70
|
+
|
|
71
|
+
if(db->db != NULL) {
|
|
72
|
+
delete db->db;
|
|
73
|
+
db->db = NULL;
|
|
74
|
+
}
|
|
75
|
+
return Qtrue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static leveldb::ReadOptions parse_read_options(VALUE options) {
|
|
79
|
+
leveldb::ReadOptions readOptions;
|
|
80
|
+
|
|
81
|
+
if(!NIL_P(options)) {
|
|
82
|
+
Check_Type(options, T_HASH);
|
|
83
|
+
if(rb_hash_aref(options, k_fill) == Qfalse)
|
|
84
|
+
readOptions.fill_cache = false;
|
|
85
|
+
if(rb_hash_aref(options, k_verify) == Qtrue)
|
|
86
|
+
readOptions.verify_checksums = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return readOptions;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static leveldb::WriteOptions parse_write_options(VALUE options) {
|
|
93
|
+
leveldb::WriteOptions writeOptions;
|
|
94
|
+
|
|
95
|
+
if(!NIL_P(options)) {
|
|
96
|
+
Check_Type(options, T_HASH);
|
|
97
|
+
if(rb_hash_aref(options, k_sync) == Qtrue)
|
|
98
|
+
writeOptions.sync = true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return writeOptions;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#define RUBY_STRING_TO_SLICE(x) leveldb::Slice(RSTRING_PTR(x), RSTRING_LEN(x))
|
|
105
|
+
#define SLICE_TO_RUBY_STRING(x) rb_str_new(x.data(), x.size())
|
|
106
|
+
#define STRING_TO_RUBY_STRING(x) rb_str_new(x.data(), x.size())
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* call-seq:
|
|
110
|
+
* get(key, options = nil)
|
|
111
|
+
*
|
|
112
|
+
* get data from db
|
|
113
|
+
*
|
|
114
|
+
* [key] key you want to get
|
|
115
|
+
* [options[ :fill_cache ]] Should the data read for this iteration be cached in memory?
|
|
116
|
+
* Callers may wish to set this field to false for bulk scans.
|
|
117
|
+
*
|
|
118
|
+
* true or false
|
|
119
|
+
*
|
|
120
|
+
* Default: true
|
|
121
|
+
* [options[ :verify_checksums ]] If true, all data read from underlying storage will be
|
|
122
|
+
* verified against corresponding checksums.
|
|
123
|
+
*
|
|
124
|
+
* Default: false
|
|
125
|
+
* [return] value of stored db
|
|
126
|
+
*/
|
|
127
|
+
static VALUE db_get(int argc, VALUE* argv, VALUE self) {
|
|
128
|
+
VALUE v_key, v_options;
|
|
129
|
+
rb_scan_args(argc, argv, "11", &v_key, &v_options);
|
|
130
|
+
Check_Type(v_key, T_STRING);
|
|
131
|
+
leveldb::ReadOptions readOptions = parse_read_options(v_options);
|
|
132
|
+
|
|
133
|
+
bound_db* db;
|
|
134
|
+
Data_Get_Struct(self, bound_db, db);
|
|
135
|
+
|
|
136
|
+
leveldb::Slice key = RUBY_STRING_TO_SLICE(v_key);
|
|
137
|
+
std::string value;
|
|
138
|
+
leveldb::Status status = db->db->Get(readOptions, key, &value);
|
|
139
|
+
if(status.IsNotFound()) return Qnil;
|
|
140
|
+
|
|
141
|
+
RAISE_ON_ERROR(status);
|
|
142
|
+
return STRING_TO_RUBY_STRING(value);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static VALUE db_delete(int argc, VALUE* argv, VALUE self) {
|
|
146
|
+
VALUE v_key, v_options;
|
|
147
|
+
rb_scan_args(argc, argv, "11", &v_key, &v_options);
|
|
148
|
+
Check_Type(v_key, T_STRING);
|
|
149
|
+
leveldb::WriteOptions writeOptions = parse_write_options(v_options);
|
|
150
|
+
|
|
151
|
+
bound_db* db;
|
|
152
|
+
Data_Get_Struct(self, bound_db, db);
|
|
153
|
+
|
|
154
|
+
leveldb::Slice key = RUBY_STRING_TO_SLICE(v_key);
|
|
155
|
+
std::string value;
|
|
156
|
+
leveldb::Status status = db->db->Get(uncached_read_options, key, &value);
|
|
157
|
+
|
|
158
|
+
if(status.IsNotFound()) return Qnil;
|
|
159
|
+
|
|
160
|
+
status = db->db->Delete(writeOptions, key);
|
|
161
|
+
RAISE_ON_ERROR(status);
|
|
162
|
+
|
|
163
|
+
return STRING_TO_RUBY_STRING(value);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static VALUE db_exists(VALUE self, VALUE v_key) {
|
|
167
|
+
Check_Type(v_key, T_STRING);
|
|
168
|
+
|
|
169
|
+
bound_db* db;
|
|
170
|
+
Data_Get_Struct(self, bound_db, db);
|
|
171
|
+
|
|
172
|
+
leveldb::Slice key = RUBY_STRING_TO_SLICE(v_key);
|
|
173
|
+
std::string value;
|
|
174
|
+
leveldb::Status status = db->db->Get(leveldb::ReadOptions(), key, &value);
|
|
175
|
+
|
|
176
|
+
if(status.IsNotFound()) return Qfalse;
|
|
177
|
+
return Qtrue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/*
|
|
181
|
+
* call-seq:
|
|
182
|
+
* put(key, value, options = nil)
|
|
183
|
+
*
|
|
184
|
+
* store data into DB
|
|
185
|
+
*
|
|
186
|
+
* [key] key you want to store
|
|
187
|
+
* [value] data you want to store
|
|
188
|
+
* [options[ :sync ]] If true, the write will be flushed from the operating system
|
|
189
|
+
* buffer cache (by calling WritableFile::Sync()) before the write
|
|
190
|
+
* is considered complete. If this flag is true, writes will be
|
|
191
|
+
* slower.
|
|
192
|
+
*
|
|
193
|
+
* If this flag is false, and the machine crashes, some recent
|
|
194
|
+
* writes may be lost. Note that if it is just the process that
|
|
195
|
+
* crashes (i.e., the machine does not reboot), no writes will be
|
|
196
|
+
* lost even if sync==false.
|
|
197
|
+
*
|
|
198
|
+
* In other words, a DB write with sync==false has similar
|
|
199
|
+
* crash semantics as the "write()" system call. A DB write
|
|
200
|
+
* with sync==true has similar crash semantics to a "write()"
|
|
201
|
+
* system call followed by "fsync()".
|
|
202
|
+
*
|
|
203
|
+
* Default: false
|
|
204
|
+
* [return] stored value
|
|
205
|
+
*/
|
|
206
|
+
static VALUE db_put(int argc, VALUE* argv, VALUE self) {
|
|
207
|
+
VALUE v_key, v_value, v_options;
|
|
208
|
+
|
|
209
|
+
rb_scan_args(argc, argv, "21", &v_key, &v_value, &v_options);
|
|
210
|
+
Check_Type(v_key, T_STRING);
|
|
211
|
+
Check_Type(v_value, T_STRING);
|
|
212
|
+
leveldb::WriteOptions writeOptions = parse_write_options(v_options);
|
|
213
|
+
|
|
214
|
+
bound_db* db;
|
|
215
|
+
Data_Get_Struct(self, bound_db, db);
|
|
216
|
+
|
|
217
|
+
leveldb::Slice key = RUBY_STRING_TO_SLICE(v_key);
|
|
218
|
+
leveldb::Slice value = RUBY_STRING_TO_SLICE(v_value);
|
|
219
|
+
leveldb::Status status = db->db->Put(writeOptions, key, value);
|
|
220
|
+
|
|
221
|
+
RAISE_ON_ERROR(status);
|
|
222
|
+
|
|
223
|
+
return v_value;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static VALUE db_size(VALUE self) {
|
|
227
|
+
long count = 0;
|
|
228
|
+
|
|
229
|
+
bound_db* db;
|
|
230
|
+
Data_Get_Struct(self, bound_db, db);
|
|
231
|
+
leveldb::Iterator* it = db->db->NewIterator(uncached_read_options);
|
|
232
|
+
|
|
233
|
+
// apparently this is how we have to do it. slow and painful!
|
|
234
|
+
for (it->SeekToFirst(); it->Valid(); it->Next()) count++;
|
|
235
|
+
RAISE_ON_ERROR(it->status());
|
|
236
|
+
delete it;
|
|
237
|
+
return INT2NUM(count);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static VALUE db_init(VALUE self, VALUE v_pathname) {
|
|
241
|
+
rb_iv_set(self, "@pathname", v_pathname);
|
|
242
|
+
return self;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
typedef struct current_iteration {
|
|
246
|
+
leveldb::Iterator* iterator;
|
|
247
|
+
bool passed_limit;
|
|
248
|
+
bool check_limit;
|
|
249
|
+
bool reversed;
|
|
250
|
+
int checked_valid; // 0 = unchecked, 1 = valid, -1 = invalid
|
|
251
|
+
std::string key_to_str;
|
|
252
|
+
leveldb::Slice current_key;
|
|
253
|
+
} current_iteration;
|
|
254
|
+
|
|
255
|
+
static void current_iteration_free(current_iteration* iter) {
|
|
256
|
+
delete iter;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static VALUE iter_make(VALUE klass, VALUE db, VALUE options) {
|
|
260
|
+
if(c_db != rb_funcall(db, k_class, 0)) {
|
|
261
|
+
rb_raise(rb_eArgError, "db must be a LevelDB::DB");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
bound_db* b_db;
|
|
265
|
+
Data_Get_Struct(db, bound_db, b_db);
|
|
266
|
+
|
|
267
|
+
current_iteration* iter = new current_iteration;
|
|
268
|
+
iter->passed_limit = false;
|
|
269
|
+
iter->check_limit = false;
|
|
270
|
+
iter->checked_valid = 0;
|
|
271
|
+
iter->iterator = b_db->db->NewIterator(uncached_read_options);
|
|
272
|
+
|
|
273
|
+
VALUE o_iter = Data_Wrap_Struct(klass, NULL, current_iteration_free, iter);
|
|
274
|
+
|
|
275
|
+
VALUE argv[2];
|
|
276
|
+
argv[0] = db;
|
|
277
|
+
argv[1] = options;
|
|
278
|
+
rb_obj_call_init(o_iter, 2, argv);
|
|
279
|
+
|
|
280
|
+
return o_iter;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
static VALUE iter_init(VALUE self, VALUE db, VALUE options) {
|
|
284
|
+
if(c_db != rb_funcall(db, k_class, 0)) {
|
|
285
|
+
rb_raise(rb_eArgError, "db must be a LevelDB::DB");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
rb_iv_set(self, "@db", db);
|
|
289
|
+
current_iteration* iter;
|
|
290
|
+
Data_Get_Struct(self, current_iteration, iter);
|
|
291
|
+
|
|
292
|
+
VALUE key_from = Qnil;
|
|
293
|
+
VALUE key_to = Qnil;
|
|
294
|
+
|
|
295
|
+
if(!NIL_P(options)) {
|
|
296
|
+
Check_Type(options, T_HASH);
|
|
297
|
+
key_from = rb_hash_aref(options, k_from);
|
|
298
|
+
key_to = rb_hash_aref(options, k_to);
|
|
299
|
+
|
|
300
|
+
if(RTEST(key_to)) {
|
|
301
|
+
iter->check_limit = true;
|
|
302
|
+
iter->key_to_str = RUBY_STRING_TO_SLICE(rb_funcall(key_to, to_s, 0)).ToString();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
rb_iv_set(self, "@from", key_from);
|
|
306
|
+
rb_iv_set(self, "@to", key_to);
|
|
307
|
+
if(NIL_P(rb_hash_aref(options, k_reversed))) {
|
|
308
|
+
iter->reversed = false;
|
|
309
|
+
rb_iv_set(self, "@reversed", false);
|
|
310
|
+
} else {
|
|
311
|
+
iter->reversed = true;
|
|
312
|
+
rb_iv_set(self, "@reversed", true);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if(RTEST(key_from)) {
|
|
317
|
+
iter->iterator->Seek(RUBY_STRING_TO_SLICE(rb_funcall(key_from, to_s, 0)));
|
|
318
|
+
} else {
|
|
319
|
+
if(iter->reversed) {
|
|
320
|
+
iter->iterator->SeekToLast();
|
|
321
|
+
} else {
|
|
322
|
+
iter->iterator->SeekToFirst();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return self;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
static bool iter_valid(current_iteration* iter) {
|
|
330
|
+
if(iter->checked_valid == 0) {
|
|
331
|
+
if(iter->passed_limit) {
|
|
332
|
+
iter->checked_valid = -2;
|
|
333
|
+
} else {
|
|
334
|
+
if(iter->iterator->Valid()) {
|
|
335
|
+
iter->current_key = iter->iterator->key();
|
|
336
|
+
|
|
337
|
+
if(iter->check_limit &&
|
|
338
|
+
(iter->reversed ?
|
|
339
|
+
(iter->current_key.ToString() < iter->key_to_str) :
|
|
340
|
+
(iter->current_key.ToString() > iter->key_to_str))) {
|
|
341
|
+
iter->passed_limit = true;
|
|
342
|
+
iter->checked_valid = -2;
|
|
343
|
+
} else {
|
|
344
|
+
iter->checked_valid = 1;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
} else {
|
|
348
|
+
iter->checked_valid = -1;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if(iter->checked_valid == 1)
|
|
354
|
+
return true;
|
|
355
|
+
else
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
static VALUE iter_invalid_reason(VALUE self) {
|
|
360
|
+
current_iteration* iter;
|
|
361
|
+
Data_Get_Struct(self, current_iteration, iter);
|
|
362
|
+
if(iter_valid(iter)) {
|
|
363
|
+
return Qnil;
|
|
364
|
+
} else {
|
|
365
|
+
return INT2FIX(iter->checked_valid);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
static VALUE iter_next_value(current_iteration* iter) {
|
|
370
|
+
VALUE arr = rb_ary_new2(2);
|
|
371
|
+
rb_ary_push(arr, SLICE_TO_RUBY_STRING(iter->current_key));
|
|
372
|
+
rb_ary_push(arr, SLICE_TO_RUBY_STRING(iter->iterator->value()));
|
|
373
|
+
return arr;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
static void iter_scan_iterator(current_iteration* iter) {
|
|
377
|
+
if(iter->reversed)
|
|
378
|
+
iter->iterator->Prev();
|
|
379
|
+
else
|
|
380
|
+
iter->iterator->Next();
|
|
381
|
+
iter->checked_valid = 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
static VALUE iter_peek(VALUE self) {
|
|
385
|
+
current_iteration* iter;
|
|
386
|
+
Data_Get_Struct(self, current_iteration, iter);
|
|
387
|
+
if(iter_valid(iter)) {
|
|
388
|
+
return iter_next_value(iter);
|
|
389
|
+
} else {
|
|
390
|
+
return Qnil;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
static VALUE iter_scan(VALUE self) {
|
|
395
|
+
current_iteration* iter;
|
|
396
|
+
Data_Get_Struct(self, current_iteration, iter);
|
|
397
|
+
if(iter_valid(iter))
|
|
398
|
+
iter_scan_iterator(iter);
|
|
399
|
+
return Qnil;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
static VALUE iter_next(VALUE self) {
|
|
403
|
+
current_iteration* iter;
|
|
404
|
+
Data_Get_Struct(self, current_iteration, iter);
|
|
405
|
+
|
|
406
|
+
VALUE arr = Qnil;
|
|
407
|
+
|
|
408
|
+
if(iter_valid(iter)) {
|
|
409
|
+
arr = iter_next_value(iter);
|
|
410
|
+
iter_scan_iterator(iter);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return arr;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
static VALUE iter_each(VALUE self) {
|
|
417
|
+
current_iteration* iter;
|
|
418
|
+
Data_Get_Struct(self, current_iteration, iter);
|
|
419
|
+
|
|
420
|
+
while(iter_valid(iter)) {
|
|
421
|
+
rb_yield(iter_next_value(iter));
|
|
422
|
+
iter_scan_iterator(iter);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
RAISE_ON_ERROR(iter->iterator->status());
|
|
426
|
+
delete iter->iterator;
|
|
427
|
+
return self;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
typedef struct bound_batch {
|
|
431
|
+
leveldb::WriteBatch batch;
|
|
432
|
+
} bound_batch;
|
|
433
|
+
|
|
434
|
+
static void batch_free(bound_batch* batch) {
|
|
435
|
+
delete batch;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
static VALUE batch_make(VALUE klass) {
|
|
439
|
+
bound_batch* batch = new bound_batch;
|
|
440
|
+
batch->batch = leveldb::WriteBatch();
|
|
441
|
+
|
|
442
|
+
VALUE o_batch = Data_Wrap_Struct(klass, NULL, batch_free, batch);
|
|
443
|
+
VALUE argv[0];
|
|
444
|
+
rb_obj_call_init(o_batch, 0, argv);
|
|
445
|
+
|
|
446
|
+
return o_batch;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
static VALUE batch_put(VALUE self, VALUE v_key, VALUE v_value) {
|
|
450
|
+
Check_Type(v_key, T_STRING);
|
|
451
|
+
Check_Type(v_value, T_STRING);
|
|
452
|
+
|
|
453
|
+
bound_batch* batch;
|
|
454
|
+
Data_Get_Struct(self, bound_batch, batch);
|
|
455
|
+
batch->batch.Put(RUBY_STRING_TO_SLICE(v_key), RUBY_STRING_TO_SLICE(v_value));
|
|
456
|
+
|
|
457
|
+
return v_value;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
static VALUE batch_delete(VALUE self, VALUE v_key) {
|
|
461
|
+
Check_Type(v_key, T_STRING);
|
|
462
|
+
bound_batch* batch;
|
|
463
|
+
Data_Get_Struct(self, bound_batch, batch);
|
|
464
|
+
batch->batch.Delete(RUBY_STRING_TO_SLICE(v_key));
|
|
465
|
+
return Qtrue;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
static VALUE db_batch(int argc, VALUE* argv, VALUE self) {
|
|
469
|
+
VALUE o_batch = batch_make(c_batch);
|
|
470
|
+
|
|
471
|
+
rb_yield(o_batch);
|
|
472
|
+
|
|
473
|
+
bound_batch* batch;
|
|
474
|
+
bound_db* db;
|
|
475
|
+
Data_Get_Struct(o_batch, bound_batch, batch);
|
|
476
|
+
Data_Get_Struct(self, bound_db, db);
|
|
477
|
+
|
|
478
|
+
VALUE v_options;
|
|
479
|
+
rb_scan_args(argc, argv, "01", &v_options);
|
|
480
|
+
leveldb::WriteOptions writeOptions = parse_write_options(v_options);
|
|
481
|
+
|
|
482
|
+
leveldb::Status status = db->db->Write(writeOptions, &batch->batch);
|
|
483
|
+
RAISE_ON_ERROR(status);
|
|
484
|
+
return Qtrue;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
extern "C" {
|
|
488
|
+
void Init_leveldb() {
|
|
489
|
+
k_fill = ID2SYM(rb_intern("fill_cache"));
|
|
490
|
+
k_verify = ID2SYM(rb_intern("verify_checksums"));
|
|
491
|
+
k_sync = ID2SYM(rb_intern("sync"));
|
|
492
|
+
k_from = ID2SYM(rb_intern("from"));
|
|
493
|
+
k_to = ID2SYM(rb_intern("to"));
|
|
494
|
+
k_reversed = ID2SYM(rb_intern("reversed"));
|
|
495
|
+
k_class = rb_intern("class");
|
|
496
|
+
k_name = rb_intern("name");
|
|
497
|
+
to_s = rb_intern("to_s");
|
|
498
|
+
uncached_read_options = leveldb::ReadOptions();
|
|
499
|
+
uncached_read_options.fill_cache = false;
|
|
500
|
+
|
|
501
|
+
m_leveldb = rb_define_module("LevelDB");
|
|
502
|
+
|
|
503
|
+
c_db = rb_define_class_under(m_leveldb, "DB", rb_cObject);
|
|
504
|
+
rb_define_singleton_method(c_db, "make", RUBY_METHOD_FUNC(db_make), 3);
|
|
505
|
+
rb_define_method(c_db, "initialize", RUBY_METHOD_FUNC(db_init), 1);
|
|
506
|
+
rb_define_method(c_db, "get", RUBY_METHOD_FUNC(db_get), -1);
|
|
507
|
+
rb_define_method(c_db, "delete", RUBY_METHOD_FUNC(db_delete), -1);
|
|
508
|
+
rb_define_method(c_db, "put", RUBY_METHOD_FUNC(db_put), -1);
|
|
509
|
+
rb_define_method(c_db, "exists?", RUBY_METHOD_FUNC(db_exists), 1);
|
|
510
|
+
rb_define_method(c_db, "close", RUBY_METHOD_FUNC(db_close), 0);
|
|
511
|
+
rb_define_method(c_db, "size", RUBY_METHOD_FUNC(db_size), 0);
|
|
512
|
+
rb_define_method(c_db, "batch", RUBY_METHOD_FUNC(db_batch), -1);
|
|
513
|
+
|
|
514
|
+
c_iter = rb_define_class_under(m_leveldb, "Iterator", rb_cObject);
|
|
515
|
+
rb_define_singleton_method(c_iter, "make", RUBY_METHOD_FUNC(iter_make), 2);
|
|
516
|
+
rb_define_method(c_iter, "initialize", RUBY_METHOD_FUNC(iter_init), 2);
|
|
517
|
+
rb_define_method(c_iter, "each", RUBY_METHOD_FUNC(iter_each), 0);
|
|
518
|
+
rb_define_method(c_iter, "next", RUBY_METHOD_FUNC(iter_next), 0);
|
|
519
|
+
rb_define_method(c_iter, "scan", RUBY_METHOD_FUNC(iter_scan), 0);
|
|
520
|
+
rb_define_method(c_iter, "peek", RUBY_METHOD_FUNC(iter_peek), 0);
|
|
521
|
+
rb_define_method(c_iter, "invalid_reason", RUBY_METHOD_FUNC(iter_invalid_reason), 0);
|
|
522
|
+
|
|
523
|
+
c_batch = rb_define_class_under(m_leveldb, "WriteBatch", rb_cObject);
|
|
524
|
+
rb_define_singleton_method(c_batch, "make", RUBY_METHOD_FUNC(batch_make), 0);
|
|
525
|
+
rb_define_method(c_batch, "put", RUBY_METHOD_FUNC(batch_put), 2);
|
|
526
|
+
rb_define_method(c_batch, "delete", RUBY_METHOD_FUNC(batch_delete), 1);
|
|
527
|
+
|
|
528
|
+
c_error = rb_define_class_under(m_leveldb, "Error", rb_eStandardError);
|
|
529
|
+
}
|
|
530
|
+
}
|