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.
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
+ }