filiptepper-leveldb-ruby 0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/LICENSE +24 -0
  2. data/README +72 -0
  3. data/ext/leveldb/extconf.rb +14 -0
  4. data/ext/leveldb/leveldb.cc +530 -0
  5. data/ext/leveldb/platform.rb +83 -0
  6. data/leveldb/Makefile +191 -0
  7. data/leveldb/build_detect_platform +160 -0
  8. data/leveldb/db/builder.cc +88 -0
  9. data/leveldb/db/builder.h +34 -0
  10. data/leveldb/db/c.cc +581 -0
  11. data/leveldb/db/corruption_test.cc +359 -0
  12. data/leveldb/db/db_bench.cc +970 -0
  13. data/leveldb/db/db_impl.cc +1448 -0
  14. data/leveldb/db/db_impl.h +194 -0
  15. data/leveldb/db/db_iter.cc +299 -0
  16. data/leveldb/db/db_iter.h +26 -0
  17. data/leveldb/db/db_test.cc +1901 -0
  18. data/leveldb/db/dbformat.cc +140 -0
  19. data/leveldb/db/dbformat.h +227 -0
  20. data/leveldb/db/dbformat_test.cc +112 -0
  21. data/leveldb/db/filename.cc +139 -0
  22. data/leveldb/db/filename.h +80 -0
  23. data/leveldb/db/filename_test.cc +122 -0
  24. data/leveldb/db/log_format.h +35 -0
  25. data/leveldb/db/log_reader.cc +259 -0
  26. data/leveldb/db/log_reader.h +108 -0
  27. data/leveldb/db/log_test.cc +500 -0
  28. data/leveldb/db/log_writer.cc +103 -0
  29. data/leveldb/db/log_writer.h +48 -0
  30. data/leveldb/db/memtable.cc +145 -0
  31. data/leveldb/db/memtable.h +91 -0
  32. data/leveldb/db/repair.cc +389 -0
  33. data/leveldb/db/skiplist.h +379 -0
  34. data/leveldb/db/skiplist_test.cc +378 -0
  35. data/leveldb/db/snapshot.h +66 -0
  36. data/leveldb/db/table_cache.cc +121 -0
  37. data/leveldb/db/table_cache.h +61 -0
  38. data/leveldb/db/version_edit.cc +266 -0
  39. data/leveldb/db/version_edit.h +107 -0
  40. data/leveldb/db/version_edit_test.cc +46 -0
  41. data/leveldb/db/version_set.cc +1402 -0
  42. data/leveldb/db/version_set.h +370 -0
  43. data/leveldb/db/version_set_test.cc +179 -0
  44. data/leveldb/db/write_batch.cc +147 -0
  45. data/leveldb/db/write_batch_internal.h +49 -0
  46. data/leveldb/db/write_batch_test.cc +120 -0
  47. data/leveldb/helpers/memenv/memenv.cc +374 -0
  48. data/leveldb/helpers/memenv/memenv.h +20 -0
  49. data/leveldb/helpers/memenv/memenv_test.cc +232 -0
  50. data/leveldb/include/leveldb/c.h +275 -0
  51. data/leveldb/include/leveldb/cache.h +99 -0
  52. data/leveldb/include/leveldb/comparator.h +63 -0
  53. data/leveldb/include/leveldb/db.h +161 -0
  54. data/leveldb/include/leveldb/env.h +323 -0
  55. data/leveldb/include/leveldb/filter_policy.h +70 -0
  56. data/leveldb/include/leveldb/iterator.h +100 -0
  57. data/leveldb/include/leveldb/options.h +195 -0
  58. data/leveldb/include/leveldb/slice.h +109 -0
  59. data/leveldb/include/leveldb/status.h +106 -0
  60. data/leveldb/include/leveldb/table.h +85 -0
  61. data/leveldb/include/leveldb/table_builder.h +92 -0
  62. data/leveldb/include/leveldb/write_batch.h +64 -0
  63. data/leveldb/port/atomic_pointer.h +144 -0
  64. data/leveldb/port/port.h +21 -0
  65. data/leveldb/port/port_android.cc +64 -0
  66. data/leveldb/port/port_android.h +159 -0
  67. data/leveldb/port/port_example.h +125 -0
  68. data/leveldb/port/port_posix.cc +50 -0
  69. data/leveldb/port/port_posix.h +129 -0
  70. data/leveldb/port/win/stdint.h +24 -0
  71. data/leveldb/table/block.cc +267 -0
  72. data/leveldb/table/block.h +44 -0
  73. data/leveldb/table/block_builder.cc +109 -0
  74. data/leveldb/table/block_builder.h +57 -0
  75. data/leveldb/table/filter_block.cc +111 -0
  76. data/leveldb/table/filter_block.h +68 -0
  77. data/leveldb/table/filter_block_test.cc +128 -0
  78. data/leveldb/table/format.cc +145 -0
  79. data/leveldb/table/format.h +108 -0
  80. data/leveldb/table/iterator.cc +67 -0
  81. data/leveldb/table/iterator_wrapper.h +63 -0
  82. data/leveldb/table/merger.cc +197 -0
  83. data/leveldb/table/merger.h +26 -0
  84. data/leveldb/table/table.cc +276 -0
  85. data/leveldb/table/table_builder.cc +270 -0
  86. data/leveldb/table/table_test.cc +838 -0
  87. data/leveldb/table/two_level_iterator.cc +182 -0
  88. data/leveldb/table/two_level_iterator.h +34 -0
  89. data/leveldb/util/arena.cc +68 -0
  90. data/leveldb/util/arena.h +68 -0
  91. data/leveldb/util/arena_test.cc +68 -0
  92. data/leveldb/util/bloom.cc +95 -0
  93. data/leveldb/util/bloom_test.cc +159 -0
  94. data/leveldb/util/cache.cc +328 -0
  95. data/leveldb/util/cache_test.cc +186 -0
  96. data/leveldb/util/coding.cc +194 -0
  97. data/leveldb/util/coding.h +104 -0
  98. data/leveldb/util/coding_test.cc +173 -0
  99. data/leveldb/util/comparator.cc +76 -0
  100. data/leveldb/util/crc32c.cc +332 -0
  101. data/leveldb/util/crc32c.h +45 -0
  102. data/leveldb/util/crc32c_test.cc +72 -0
  103. data/leveldb/util/env.cc +96 -0
  104. data/leveldb/util/env_posix.cc +609 -0
  105. data/leveldb/util/env_test.cc +104 -0
  106. data/leveldb/util/filter_policy.cc +11 -0
  107. data/leveldb/util/hash.cc +45 -0
  108. data/leveldb/util/hash.h +19 -0
  109. data/leveldb/util/histogram.cc +139 -0
  110. data/leveldb/util/histogram.h +42 -0
  111. data/leveldb/util/logging.cc +81 -0
  112. data/leveldb/util/logging.h +47 -0
  113. data/leveldb/util/mutexlock.h +39 -0
  114. data/leveldb/util/options.cc +29 -0
  115. data/leveldb/util/posix_logger.h +98 -0
  116. data/leveldb/util/random.h +59 -0
  117. data/leveldb/util/status.cc +75 -0
  118. data/leveldb/util/testharness.cc +77 -0
  119. data/leveldb/util/testharness.h +138 -0
  120. data/leveldb/util/testutil.cc +51 -0
  121. data/leveldb/util/testutil.h +53 -0
  122. data/lib/leveldb.rb +76 -0
  123. 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
+ }