tkrzw-unofficial 0.1.3

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 (4) hide show
  1. checksums.yaml +7 -0
  2. data/extconf.rb +48 -0
  3. data/tkrzw.cc +3167 -0
  4. metadata +45 -0
data/tkrzw.cc ADDED
@@ -0,0 +1,3167 @@
1
+ /*************************************************************************************************
2
+ * Ruby binding of Tkrzw
3
+ *
4
+ * Copyright 2020 Google LLC
5
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
6
+ * except in compliance with the License. You may obtain a copy of the License at
7
+ * https://www.apache.org/licenses/LICENSE-2.0
8
+ * Unless required by applicable law or agreed to in writing, software distributed under the
9
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ * either express or implied. See the License for the specific language governing permissions
11
+ * and limitations under the License.
12
+ *************************************************************************************************/
13
+
14
+ #include <functional>
15
+ #include <string>
16
+ #include <string_view>
17
+ #include <map>
18
+ #include <memory>
19
+ #include <utility>
20
+ #include <vector>
21
+
22
+ #include <cstddef>
23
+ #include <cstdint>
24
+
25
+ #include "tkrzw_cmd_util.h"
26
+ #include "tkrzw_dbm.h"
27
+ #include "tkrzw_dbm_common_impl.h"
28
+ #include "tkrzw_dbm_poly.h"
29
+ #include "tkrzw_dbm_shard.h"
30
+ #include "tkrzw_file.h"
31
+ #include "tkrzw_file_mmap.h"
32
+ #include "tkrzw_file_poly.h"
33
+ #include "tkrzw_file_util.h"
34
+ #include "tkrzw_key_comparators.h"
35
+ #include "tkrzw_lib_common.h"
36
+ #include "tkrzw_str_util.h"
37
+
38
+ #include "ruby.h"
39
+ #include "ruby/thread.h"
40
+
41
+ extern "C" {
42
+
43
+ typedef VALUE (*METHOD)(...);
44
+
45
+ // Global variables.
46
+ volatile VALUE mod_tkrzw;
47
+ ID id_obj_to_str;
48
+ ID id_obj_to_s;
49
+ ID id_obj_to_i;
50
+ ID id_obj_to_f;
51
+ ID id_str_force_encoding;
52
+
53
+ volatile VALUE cls_util;
54
+ volatile VALUE cls_status;
55
+ volatile VALUE cls_future;
56
+ volatile VALUE cls_expt;
57
+ ID id_expt_status;
58
+ volatile VALUE cls_dbm;
59
+ volatile VALUE cls_iter;
60
+ volatile VALUE cls_asyncdbm;
61
+ volatile VALUE cls_file;
62
+ volatile VALUE obj_dbm_any_data;
63
+
64
+ // Generates a string expression of an arbitrary object.
65
+ static VALUE StringValueEx(VALUE vobj) {
66
+ switch (TYPE(vobj)) {
67
+ case T_FIXNUM: {
68
+ char kbuf[32];
69
+ size_t ksiz = std::sprintf(kbuf, "%d", (int)FIX2INT(vobj));
70
+ return rb_str_new(kbuf, ksiz);
71
+ }
72
+ case T_BIGNUM: {
73
+ char kbuf[32];
74
+ size_t ksiz = std::sprintf(kbuf, "%lld", (long long)NUM2LL(vobj));
75
+ return rb_str_new(kbuf, ksiz);
76
+ }
77
+ case T_FLOAT: {
78
+ const std::string& str = tkrzw::ToString(NUM2DBL(vobj));
79
+ return rb_str_new(str.data(), str.size());
80
+ }
81
+ case T_NIL: {
82
+ return rb_str_new("", 0);
83
+ }
84
+ case T_TRUE: {
85
+ return rb_str_new("true", 4);
86
+ }
87
+ case T_FALSE: {
88
+ return rb_str_new("false", 5);
89
+ }
90
+ }
91
+ if (rb_respond_to(vobj, id_obj_to_str)) {
92
+ return StringValue(vobj);
93
+ }
94
+ if (rb_respond_to(vobj, id_obj_to_s)) {
95
+ return rb_funcall(vobj, id_obj_to_s, 0);
96
+ }
97
+ char kbuf[64];
98
+ std::sprintf(kbuf, "#<Object:0x%llx>", (long long)rb_obj_id(vobj));
99
+ return rb_str_new2(kbuf);
100
+ }
101
+
102
+ // Gets a string view of a Ruby string. */
103
+ static std::string_view GetStringView(VALUE vstr) {
104
+ return std::string_view(RSTRING_PTR(vstr), RSTRING_LEN(vstr));
105
+ }
106
+
107
+ // Gets a integer value of a Ruby object. */
108
+ static int64_t GetInteger(VALUE vobj) {
109
+ switch (TYPE(vobj)) {
110
+ case T_FIXNUM: {
111
+ return FIX2LONG(vobj);
112
+ }
113
+ case T_BIGNUM: {
114
+ return NUM2LL(vobj);
115
+ }
116
+ case T_FLOAT: {
117
+ return NUM2DBL(vobj);
118
+ }
119
+ case T_NIL: {
120
+ return 0;
121
+ }
122
+ case T_TRUE: {
123
+ return 1;
124
+ }
125
+ case T_FALSE: {
126
+ return 0;
127
+ }
128
+ }
129
+ if (rb_respond_to(vobj, id_obj_to_i)) {
130
+ return rb_funcall(vobj, id_obj_to_i, 0);
131
+ }
132
+ return 0;
133
+ }
134
+
135
+ // Gets an real number value of a Ruby object. */
136
+ static double GetFloat(VALUE vobj) {
137
+ switch (TYPE(vobj)) {
138
+ case T_FIXNUM: {
139
+ return FIX2LONG(vobj);
140
+ }
141
+ case T_BIGNUM: {
142
+ return NUM2LL(vobj);
143
+ }
144
+ case T_FLOAT: {
145
+ return NUM2DBL(vobj);
146
+ }
147
+ case T_NIL: {
148
+ return 0;
149
+ }
150
+ case T_TRUE: {
151
+ return 1;
152
+ }
153
+ case T_FALSE: {
154
+ return 0;
155
+ }
156
+ }
157
+ if (rb_respond_to(vobj, id_obj_to_f)) {
158
+ return rb_funcall(vobj, id_obj_to_f, 0);
159
+ }
160
+ return 0;
161
+ }
162
+
163
+ // Maps a Ruby hash object into a C++ map.
164
+ static std::map<std::string, std::string> HashToMap(VALUE vhash) {
165
+ std::map<std::string, std::string> map;
166
+ if (TYPE(vhash) == T_HASH) {
167
+ ID id_hash_keys = rb_intern("keys");
168
+ volatile VALUE vkeys = rb_funcall(vhash, id_hash_keys, 0);
169
+ const int32_t num_keys = RARRAY_LEN(vkeys);
170
+ for (int32_t i = 0; i < num_keys; i++) {
171
+ volatile VALUE vkey = rb_ary_entry(vkeys, i);
172
+ volatile VALUE vvalue = rb_hash_aref(vhash, vkey);
173
+ vkey = StringValueEx(vkey);
174
+ vvalue = StringValueEx(vvalue);
175
+ map.emplace(std::string(RSTRING_PTR(vkey), RSTRING_LEN(vkey)),
176
+ std::string(RSTRING_PTR(vvalue), RSTRING_LEN(vvalue)));
177
+ }
178
+ }
179
+ return map;
180
+ }
181
+
182
+ // Extracts a list of pairs of string views from an array object.
183
+ static std::vector<std::pair<std::string_view, std::string_view>> ExtractSVPairs(VALUE varray) {
184
+ std::vector<std::pair<std::string_view, std::string_view>> result;
185
+ const size_t size = RARRAY_LEN(varray);
186
+ result.reserve(size);
187
+ for (size_t i = 0; i < size; i++) {
188
+ volatile VALUE vpair = rb_ary_entry(varray, i);
189
+ if (TYPE(vpair) == T_ARRAY && RARRAY_LEN(vpair) >= 2) {
190
+ volatile VALUE vkey = rb_ary_entry(vpair, 0);
191
+ volatile VALUE vvalue = rb_ary_entry(vpair, 1);
192
+ vkey = StringValueEx(vkey);
193
+ std::string_view key_view(RSTRING_PTR(vkey), RSTRING_LEN(vkey));
194
+ std::string_view value_view;
195
+ if (vvalue != Qnil) {
196
+ if (vvalue == obj_dbm_any_data) {
197
+ value_view = tkrzw::DBM::ANY_DATA;
198
+ } else {
199
+ vvalue = StringValueEx(vvalue);
200
+ value_view = std::string_view(RSTRING_PTR(vvalue), RSTRING_LEN(vvalue));
201
+ }
202
+ }
203
+ result.emplace_back(std::make_pair(key_view, value_view));
204
+ }
205
+ }
206
+ return result;
207
+ }
208
+
209
+ // Define the module.
210
+ static void DefineModule() {
211
+ mod_tkrzw = rb_define_module("Tkrzw");
212
+ id_obj_to_str = rb_intern("to_str");
213
+ id_obj_to_s = rb_intern("to_s");
214
+ id_obj_to_i = rb_intern("to_i");
215
+ id_obj_to_f = rb_intern("to_f");
216
+ id_str_force_encoding = rb_intern("force_encoding");
217
+ }
218
+
219
+ // Makes a string object in the internal encoding of the database.
220
+ static VALUE MakeString(std::string_view str, VALUE venc) {
221
+ volatile VALUE vstr = rb_str_new(str.data(), str.size());
222
+ if (venc != Qnil) {
223
+ rb_funcall(vstr, id_str_force_encoding, 1, venc);
224
+ }
225
+ return vstr;
226
+ }
227
+
228
+ // Wrapper of a native function.
229
+ class NativeFunction {
230
+ public:
231
+ NativeFunction(bool concurrent, std::function<void(void)> func)
232
+ : func_(std::move(func)) {
233
+ if (concurrent) {
234
+ rb_thread_call_without_gvl(Run, this, RUBY_UBF_IO, nullptr);
235
+ } else {
236
+ func_();
237
+ }
238
+ }
239
+
240
+ static void* Run(void* param) {
241
+ ((NativeFunction*)param)->func_();
242
+ return nullptr;
243
+ }
244
+
245
+ private:
246
+ std::function<void(void)> func_;
247
+ };
248
+
249
+ // Yields the process to the given block.
250
+ static VALUE YieldToBlock(VALUE args) {
251
+ return rb_yield(args);
252
+ }
253
+
254
+ // Gets an Encoding object of a name.
255
+ static VALUE GetEncoding(std::string_view name) {
256
+ ID id_enc_find = rb_intern("find");
257
+ return rb_funcall(rb_cEncoding, id_enc_find, 1, rb_str_new(name.data(), name.size()));
258
+ }
259
+
260
+ // Ruby wrapper of the Future object.
261
+ struct StructFuture {
262
+ std::unique_ptr<tkrzw::StatusFuture> future;
263
+ bool concurrent = false;
264
+ volatile VALUE venc = Qnil;
265
+ };
266
+
267
+ // Ruby wrapper of the DBM object.
268
+ struct StructDBM {
269
+ std::unique_ptr<tkrzw::ParamDBM> dbm;
270
+ bool concurrent = false;
271
+ volatile VALUE venc = Qnil;
272
+ };
273
+
274
+ // Ruby wrapper of the Iterator object.
275
+ struct StructIter {
276
+ std::unique_ptr<tkrzw::DBM::Iterator> iter;
277
+ bool concurrent = false;
278
+ volatile VALUE venc = Qnil;
279
+ };
280
+
281
+ // Ruby wrapper of the AsyncDBM object.
282
+ struct StructAsyncDBM {
283
+ std::unique_ptr<tkrzw::AsyncDBM> async;
284
+ bool concurrent = false;
285
+ volatile VALUE venc = Qnil;
286
+ };
287
+
288
+ // Ruby wrapper of the File object.
289
+ struct StructFile {
290
+ std::unique_ptr<tkrzw::PolyFile> file;
291
+ bool concurrent = false;
292
+ volatile VALUE venc = Qnil;
293
+ explicit StructFile(tkrzw::PolyFile* file) : file(file) {}
294
+ };
295
+
296
+ // Implementation of Utility.get_memory_capacity.
297
+ static VALUE util_get_memory_capacity(VALUE vself) {
298
+ return LL2NUM(tkrzw::GetMemoryCapacity());
299
+ }
300
+
301
+ // Implementation of Utility.get_memory_usage.
302
+ static VALUE util_get_memory_usage(VALUE vself) {
303
+ return LL2NUM(tkrzw::GetMemoryUsage());
304
+ }
305
+
306
+ // Implementation of Utility.primary_hash.
307
+ static VALUE util_primary_hash(int argc, VALUE* argv, VALUE vself) {
308
+ volatile VALUE vdata, vnum;
309
+ rb_scan_args(argc, argv, "11", &vdata, &vnum);
310
+ vdata = StringValueEx(vdata);
311
+ const std::string_view data = GetStringView(vdata);
312
+ uint64_t num_buckets = vnum == Qnil ? 0 : NUM2ULL(vnum);
313
+ if (num_buckets == 0) {
314
+ num_buckets = tkrzw::UINT64MAX;
315
+ }
316
+ return ULL2NUM(tkrzw::PrimaryHash(data, num_buckets));
317
+ }
318
+
319
+ // Implementation of Utility.secondary_hash.
320
+ static VALUE util_secondary_hash(int argc, VALUE* argv, VALUE vself) {
321
+ volatile VALUE vdata, vnum;
322
+ rb_scan_args(argc, argv, "11", &vdata, &vnum);
323
+ vdata = StringValueEx(vdata);
324
+ const std::string_view data = GetStringView(vdata);
325
+ uint64_t num_buckets = vnum == Qnil ? 0 : NUM2ULL(vnum);
326
+ if (num_buckets == 0) {
327
+ num_buckets = tkrzw::UINT64MAX;
328
+ }
329
+ return ULL2NUM(tkrzw::SecondaryHash(data, num_buckets));
330
+ }
331
+
332
+ // Implementation of Utility.edit_distance_lev.
333
+ static VALUE util_edit_distance_lev(int argc, VALUE* argv, VALUE vself) {
334
+ volatile VALUE vstra, vstrb, vutf;
335
+ rb_scan_args(argc, argv, "21", &vstra, &vstrb, &vutf);
336
+ vstra = StringValueEx(vstra);
337
+ const std::string_view stra = GetStringView(vstra);
338
+ vstrb = StringValueEx(vstrb);
339
+ const std::string_view strb = GetStringView(vstrb);
340
+ const bool utf = RTEST(vutf);
341
+ int32_t rv = 0;
342
+ if (utf) {
343
+ const std::vector<uint32_t> ucsa = tkrzw::ConvertUTF8ToUCS4(stra);
344
+ const std::vector<uint32_t> ucsb = tkrzw::ConvertUTF8ToUCS4(strb);
345
+ rv = tkrzw::EditDistanceLev<std::vector<uint32_t>>(ucsa, ucsb);
346
+ } else {
347
+ rv = tkrzw::EditDistanceLev(stra, strb);
348
+ }
349
+ return INT2FIX(rv);
350
+ }
351
+
352
+ // Defines the Utility class.
353
+ static void DefineUtility() {
354
+ cls_util = rb_define_class_under(mod_tkrzw, "Utility", rb_cObject);
355
+ rb_define_const(cls_util, "VERSION", rb_str_new2(tkrzw::PACKAGE_VERSION));
356
+ rb_define_const(cls_util, "OS_NAME", rb_str_new2(tkrzw::OS_NAME));
357
+ rb_define_const(cls_util, "PAGE_SIZE", INT2FIX(tkrzw::PAGE_SIZE));
358
+ rb_define_const(cls_util, "INT32MIN", LL2NUM(tkrzw::INT32MIN));
359
+ rb_define_const(cls_util, "INT32MAX", LL2NUM(tkrzw::INT32MAX));
360
+ rb_define_const(cls_util, "UINT32MAX", ULL2NUM(tkrzw::UINT32MAX));
361
+ rb_define_const(cls_util, "INT64MIN", LL2NUM(tkrzw::INT64MIN));
362
+ rb_define_const(cls_util, "INT64MAX", LL2NUM(tkrzw::INT64MAX));
363
+ rb_define_const(cls_util, "UINT64MAX", ULL2NUM(tkrzw::UINT64MAX));
364
+ rb_define_singleton_method(cls_util, "get_memory_capacity",
365
+ (METHOD)util_get_memory_capacity, 0);
366
+ rb_define_singleton_method(cls_util, "get_memory_usage",
367
+ (METHOD)util_get_memory_usage, 0);
368
+ rb_define_singleton_method(cls_util, "primary_hash", (METHOD)util_primary_hash, -1);
369
+ rb_define_singleton_method(cls_util, "secondary_hash", (METHOD)util_secondary_hash, -1);
370
+ rb_define_singleton_method(cls_util, "edit_distance_lev", (METHOD)util_edit_distance_lev, -1);
371
+ }
372
+
373
+ // Implementation of Status#del.
374
+ static void status_del(void* ptr) {
375
+ delete (tkrzw::Status*)ptr;
376
+ }
377
+
378
+ // Implementation of Status.new.
379
+ static VALUE status_new(VALUE cls) {
380
+ tkrzw::Status* status = new tkrzw::Status(tkrzw::Status::SUCCESS);
381
+ return Data_Wrap_Struct(cls_status, 0, status_del, status);
382
+ }
383
+
384
+ // Implementation of Status#initialize.
385
+ static VALUE status_initialize(int argc, VALUE* argv, VALUE vself) {
386
+ volatile VALUE vcode, vmessage;
387
+ rb_scan_args(argc, argv, "02", &vcode, &vmessage);
388
+ if (vcode == Qnil) {
389
+ vcode = INT2FIX(tkrzw::Status::SUCCESS);
390
+ }
391
+ if (vmessage == Qnil) {
392
+ vmessage = rb_str_new2("");
393
+ }
394
+ vmessage = StringValueEx(vmessage);
395
+ tkrzw::Status* status = nullptr;
396
+ Data_Get_Struct(vself, tkrzw::Status, status);
397
+ status->Set((tkrzw::Status::Code)NUM2INT(vcode), GetStringView(vmessage));
398
+ return Qnil;
399
+ }
400
+
401
+ // Implementation of Status#set.
402
+ static VALUE status_set(int argc, VALUE* argv, VALUE vself) {
403
+ volatile VALUE vcode, vmessage;
404
+ rb_scan_args(argc, argv, "02", &vcode, &vmessage);
405
+ if (vcode == Qnil) {
406
+ vcode = INT2FIX(tkrzw::Status::SUCCESS);
407
+ }
408
+ if (vmessage == Qnil) {
409
+ vmessage = rb_str_new2("");
410
+ }
411
+ vmessage = StringValueEx(vmessage);
412
+ tkrzw::Status* status = nullptr;
413
+ Data_Get_Struct(vself, tkrzw::Status, status);
414
+ status->Set((tkrzw::Status::Code)NUM2INT(vcode), GetStringView(vmessage));
415
+ return Qnil;
416
+ }
417
+
418
+ // Implementation of Status#join.
419
+ static VALUE status_join(VALUE vself, VALUE vrht) {
420
+ tkrzw::Status* status = nullptr;
421
+ Data_Get_Struct(vself, tkrzw::Status, status);
422
+ if (!rb_obj_is_instance_of(vrht, cls_status)) {
423
+ rb_raise(rb_eRuntimeError, "not a status");
424
+ }
425
+ tkrzw::Status* rht = nullptr;
426
+ Data_Get_Struct(vrht, tkrzw::Status, rht);
427
+ *status |= *rht;
428
+ return Qnil;
429
+ }
430
+
431
+ // Implementation of Status#code.
432
+ static VALUE status_code(VALUE vself) {
433
+ tkrzw::Status* status = nullptr;
434
+ Data_Get_Struct(vself, tkrzw::Status, status);
435
+ return INT2FIX(status->GetCode());
436
+ }
437
+
438
+ // Implementation of Status#get_message.
439
+ static VALUE status_message(VALUE vself) {
440
+ tkrzw::Status* status = nullptr;
441
+ Data_Get_Struct(vself, tkrzw::Status, status);
442
+ const std::string_view message = status->GetMessage();
443
+ return rb_str_new(message.data(), message.size());
444
+ }
445
+
446
+ // Implementation of Status#ok?.
447
+ static VALUE status_ok(VALUE vself) {
448
+ tkrzw::Status* status = nullptr;
449
+ Data_Get_Struct(vself, tkrzw::Status, status);
450
+ return status->IsOK() ? Qtrue : Qfalse;
451
+ }
452
+
453
+ // Implementation of Status#or_die.
454
+ static VALUE status_or_die(VALUE vself) {
455
+ tkrzw::Status* status = nullptr;
456
+ Data_Get_Struct(vself, tkrzw::Status, status);
457
+ if (!status->IsOK()) {
458
+ // TODO: The thrown exception should be initialized with the status object.
459
+ const std::string& message = tkrzw::ToString(*status);
460
+ rb_raise(cls_expt, "%s", message.c_str());
461
+ }
462
+ return Qnil;
463
+ }
464
+
465
+ // Implementation of Status#to_s.
466
+ static VALUE status_to_s(VALUE vself) {
467
+ tkrzw::Status* status = nullptr;
468
+ Data_Get_Struct(vself, tkrzw::Status, status);
469
+ const std::string str = tkrzw::ToString(*status);
470
+ return rb_str_new(str.data(), str.size());
471
+ }
472
+
473
+ // Implementation of Status#inspect.
474
+ static VALUE status_inspect(VALUE vself) {
475
+ tkrzw::Status* status = nullptr;
476
+ Data_Get_Struct(vself, tkrzw::Status, status);
477
+ const std::string str = tkrzw::StrCat("#<Tkrzw::Status:", *status, ">");
478
+ return rb_str_new(str.data(), str.size());
479
+ }
480
+
481
+ // Implementation of Status#op_eq.
482
+ static VALUE status_op_eq(VALUE vself, VALUE vrhs) {
483
+ tkrzw::Status* status = nullptr;
484
+ Data_Get_Struct(vself, tkrzw::Status, status);
485
+ int32_t rcode = 0;
486
+ if (rb_obj_is_instance_of(vrhs, cls_status)) {
487
+ tkrzw::Status* rstatus = nullptr;
488
+ Data_Get_Struct(vrhs, tkrzw::Status, rstatus);
489
+ rcode = rstatus->GetCode();
490
+ } else if (TYPE(vrhs) == T_FIXNUM) {
491
+ rcode = FIX2INT(vrhs);
492
+ } else {
493
+ return Qfalse;
494
+ }
495
+ return status->GetCode() == rcode ? Qtrue : Qfalse;
496
+ }
497
+
498
+ // Implementation of Status#op_ne.
499
+ static VALUE status_op_ne(VALUE vself, VALUE vrhs) {
500
+ tkrzw::Status* status = nullptr;
501
+ Data_Get_Struct(vself, tkrzw::Status, status);
502
+ int32_t rcode = 0;
503
+ if (rb_obj_is_instance_of(vrhs, cls_status)) {
504
+ tkrzw::Status* rstatus = nullptr;
505
+ Data_Get_Struct(vrhs, tkrzw::Status, rstatus);
506
+ rcode = rstatus->GetCode();
507
+ } else if (TYPE(vrhs) == T_FIXNUM) {
508
+ rcode = FIX2INT(vrhs);
509
+ } else {
510
+ return Qfalse;
511
+ }
512
+ return status->GetCode() == rcode ? Qfalse : Qtrue;
513
+ }
514
+
515
+ // Implementation of Status.code_name.
516
+ static VALUE status_code_name(VALUE vself, VALUE vcode) {
517
+ return rb_str_new2(tkrzw::Status::CodeName((tkrzw::Status::Code)NUM2INT(vcode)));
518
+ }
519
+
520
+ // Creates a status object.
521
+ static VALUE MakeStatusValue(tkrzw::Status&& status) {
522
+ tkrzw::Status* new_status = new tkrzw::Status(status);
523
+ return Data_Wrap_Struct(cls_status, 0, status_del, new_status);
524
+ }
525
+
526
+ // Sets a status object.
527
+ static void SetStatusValue(VALUE vstatus, const tkrzw::Status& rhs) {
528
+ tkrzw::Status* status = nullptr;
529
+ Data_Get_Struct(vstatus, tkrzw::Status, status);
530
+ *status = rhs;
531
+ }
532
+
533
+ // Defines the Status class.
534
+ static void DefineStatus() {
535
+ cls_status = rb_define_class_under(mod_tkrzw, "Status", rb_cObject);
536
+ rb_define_alloc_func(cls_status, status_new);
537
+ rb_define_const(cls_status, "SUCCESS",
538
+ INT2FIX(tkrzw::Status::SUCCESS));
539
+ rb_define_const(cls_status, "UNKNOWN_ERROR",
540
+ INT2FIX(tkrzw::Status::UNKNOWN_ERROR));
541
+ rb_define_const(cls_status, "SYSTEM_ERROR",
542
+ INT2FIX(tkrzw::Status::SYSTEM_ERROR));
543
+ rb_define_const(cls_status, "NOT_IMPLEMENTED_ERROR",
544
+ INT2FIX(tkrzw::Status::NOT_IMPLEMENTED_ERROR));
545
+ rb_define_const(cls_status, "PRECONDITION_ERROR",
546
+ INT2FIX(tkrzw::Status::PRECONDITION_ERROR));
547
+ rb_define_const(cls_status, "INVALID_ARGUMENT_ERROR",
548
+ INT2FIX(tkrzw::Status::INVALID_ARGUMENT_ERROR));
549
+ rb_define_const(cls_status, "CANCELED_ERROR",
550
+ INT2FIX(tkrzw::Status::CANCELED_ERROR));
551
+ rb_define_const(cls_status, "NOT_FOUND_ERROR",
552
+ INT2FIX(tkrzw::Status::NOT_FOUND_ERROR));
553
+ rb_define_const(cls_status, "PERMISSION_ERROR",
554
+ INT2FIX(tkrzw::Status::PERMISSION_ERROR));
555
+ rb_define_const(cls_status, "INFEASIBLE_ERROR",
556
+ INT2FIX(tkrzw::Status::INFEASIBLE_ERROR));
557
+ rb_define_const(cls_status, "DUPLICATION_ERROR",
558
+ INT2FIX(tkrzw::Status::DUPLICATION_ERROR));
559
+ rb_define_const(cls_status, "BROKEN_DATA_ERROR",
560
+ INT2FIX(tkrzw::Status::BROKEN_DATA_ERROR));
561
+ rb_define_const(cls_status, "NETWORK_ERROR",
562
+ INT2FIX(tkrzw::Status::NETWORK_ERROR));
563
+ rb_define_const(cls_status, "APPLICATION_ERROR",
564
+ INT2FIX(tkrzw::Status::APPLICATION_ERROR));
565
+ rb_define_private_method(cls_status, "initialize", (METHOD)status_initialize, -1);
566
+ rb_define_method(cls_status, "set", (METHOD)status_set, -1);
567
+ rb_define_method(cls_status, "join", (METHOD)status_join, 1);
568
+ rb_define_method(cls_status, "code", (METHOD)status_code, 0);
569
+ rb_define_method(cls_status, "message", (METHOD)status_message, 0);
570
+ rb_define_method(cls_status, "ok?", (METHOD)status_ok, 0);
571
+ rb_define_method(cls_status, "or_die", (METHOD)status_or_die, 0);
572
+ rb_define_method(cls_status, "to_s", (METHOD)status_to_s, 0);
573
+ rb_define_method(cls_status, "to_i", (METHOD)status_code, 0);
574
+ rb_define_method(cls_status, "inspect", (METHOD)status_inspect, 0);
575
+ rb_define_method(cls_status, "==", (METHOD)status_op_eq, 1);
576
+ rb_define_method(cls_status, "!=", (METHOD)status_op_ne, 1);
577
+ rb_define_singleton_method(cls_status, "code_name", (METHOD)status_code_name, 1);
578
+ }
579
+
580
+ // Implementation of Future#del.
581
+ static void future_del(void* ptr) {
582
+ StructFuture* sfuture = (StructFuture*)ptr;
583
+ sfuture->future.reset(nullptr);
584
+ delete sfuture;
585
+ }
586
+
587
+ // Implementation of Future.new.
588
+ static VALUE future_new(VALUE cls) {
589
+ StructFuture* sfuture = new StructFuture;
590
+ return Data_Wrap_Struct(cls_future, 0, future_del, sfuture);
591
+ }
592
+
593
+ // Implementation of Future#initialize.
594
+ static VALUE future_initialize(int argc, VALUE* argv, VALUE vself) {
595
+ rb_raise(rb_eRuntimeError, "Future cannot be created itself");
596
+ return Qnil;
597
+ }
598
+
599
+ // Implementation of Future#destruct.
600
+ static VALUE future_destruct(VALUE vself) {
601
+ StructFuture* sfuture = nullptr;
602
+ Data_Get_Struct(vself, StructFuture, sfuture);
603
+ sfuture->future.reset(nullptr);
604
+ return Qnil;
605
+ }
606
+
607
+ // Implementation of Future#wait.
608
+ static VALUE future_wait(int argc, VALUE* argv, VALUE vself) {
609
+ StructFuture* sfuture = nullptr;
610
+ Data_Get_Struct(vself, StructFuture, sfuture);
611
+ if (sfuture->future == nullptr) {
612
+ rb_raise(rb_eRuntimeError, "destructed object");
613
+ }
614
+ volatile VALUE vtimeout;
615
+ rb_scan_args(argc, argv, "01", &vtimeout);
616
+ const double timeout = vtimeout == Qnil ? -1.0 : GetFloat(vtimeout);
617
+ bool ok = false;
618
+ NativeFunction(sfuture->concurrent, [&]() {
619
+ ok = sfuture->future->Wait(timeout);
620
+ });
621
+ if (ok) {
622
+ sfuture->concurrent = false;
623
+ return Qtrue;
624
+ }
625
+ return Qfalse;
626
+ }
627
+
628
+ // Implementation of Future#get.
629
+ static VALUE future_get(VALUE vself) {
630
+ StructFuture* sfuture = nullptr;
631
+ Data_Get_Struct(vself, StructFuture, sfuture);
632
+ if (sfuture->future == nullptr) {
633
+ rb_raise(rb_eRuntimeError, "destructed object");
634
+ }
635
+ const auto& type = sfuture->future->GetExtraType();
636
+ if (type == typeid(tkrzw::Status)) {
637
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
638
+ NativeFunction(sfuture->concurrent, [&]() {
639
+ status = sfuture->future->Get();
640
+ });
641
+ sfuture->future.reset(nullptr);
642
+ return MakeStatusValue(std::move(status));
643
+ }
644
+ if (type == typeid(std::pair<tkrzw::Status, std::string>)) {
645
+ std::pair<tkrzw::Status, std::string> result;
646
+ NativeFunction(sfuture->concurrent, [&]() {
647
+ result = sfuture->future->GetString();
648
+ });
649
+ sfuture->future.reset(nullptr);
650
+ volatile VALUE vpair = rb_ary_new2(2);
651
+ rb_ary_push(vpair, MakeStatusValue(std::move(result.first)));
652
+ rb_ary_push(vpair, MakeString(result.second, sfuture->venc));
653
+ return vpair;
654
+ }
655
+ if (type == typeid(std::pair<tkrzw::Status, std::pair<std::string, std::string>>)) {
656
+ std::pair<tkrzw::Status, std::pair<std::string, std::string>> result;
657
+ NativeFunction(sfuture->concurrent, [&]() {
658
+ result = sfuture->future->GetStringPair();
659
+ });
660
+ sfuture->future.reset(nullptr);
661
+ volatile VALUE vtuple = rb_ary_new2(3);
662
+ rb_ary_push(vtuple, MakeStatusValue(std::move(result.first)));
663
+ rb_ary_push(vtuple, MakeString(result.second.first, sfuture->venc));
664
+ rb_ary_push(vtuple, MakeString(result.second.second, sfuture->venc));
665
+ return vtuple;
666
+ }
667
+ if (type == typeid(std::pair<tkrzw::Status, std::vector<std::string>>)) {
668
+ std::pair<tkrzw::Status, std::vector<std::string>> result;
669
+ NativeFunction(sfuture->concurrent, [&]() {
670
+ result = sfuture->future->GetStringVector();
671
+ });
672
+ sfuture->future.reset(nullptr);
673
+ volatile VALUE vlist = rb_ary_new2(result.second.size());
674
+ for (const auto& value : result.second) {
675
+ rb_ary_push(vlist, MakeString(value, sfuture->venc));
676
+ }
677
+ volatile VALUE vpair = rb_ary_new2(2);
678
+ rb_ary_push(vpair, MakeStatusValue(std::move(result.first)));
679
+ rb_ary_push(vpair, vlist);
680
+ return vpair;
681
+ }
682
+ if (type == typeid(std::pair<tkrzw::Status, std::map<std::string, std::string>>)) {
683
+ std::pair<tkrzw::Status, std::map<std::string, std::string>> result;
684
+ NativeFunction(sfuture->concurrent, [&]() {
685
+ result = sfuture->future->GetStringMap();
686
+ });
687
+ sfuture->future.reset(nullptr);
688
+ volatile VALUE vhash = rb_hash_new();
689
+ for (const auto& record : result.second) {
690
+ volatile VALUE vkey = MakeString(record.first, sfuture->venc);
691
+ volatile VALUE vvalue = MakeString(record.second, sfuture->venc);
692
+ rb_hash_aset(vhash, vkey, vvalue);
693
+ }
694
+ volatile VALUE vpair = rb_ary_new2(2);
695
+ rb_ary_push(vpair, MakeStatusValue(std::move(result.first)));
696
+ rb_ary_push(vpair, vhash);
697
+ return vpair;
698
+ }
699
+ if (type == typeid(std::pair<tkrzw::Status, int64_t>)) {
700
+ std::pair<tkrzw::Status, int64_t> result;
701
+ NativeFunction(sfuture->concurrent, [&]() {
702
+ result = sfuture->future->GetInteger();
703
+ });
704
+ sfuture->future.reset(nullptr);
705
+ volatile VALUE vpair = rb_ary_new2(2);
706
+ rb_ary_push(vpair, MakeStatusValue(std::move(result.first)));
707
+ rb_ary_push(vpair, LL2NUM(result.second));
708
+ return vpair;
709
+ }
710
+ rb_raise(rb_eRuntimeError, "unimplemented type");
711
+ return Qnil;
712
+ }
713
+
714
+ // Implementation of Future#to_s.
715
+ static VALUE future_to_s(VALUE vself) {
716
+ StructFuture* sfuture = nullptr;
717
+ Data_Get_Struct(vself, StructFuture, sfuture);
718
+ if (sfuture->future == nullptr) {
719
+ rb_raise(rb_eRuntimeError, "destructed object");
720
+ }
721
+ const std::string str = tkrzw::SPrintF("Future:%p", (void*)sfuture->future.get());
722
+ return rb_str_new(str.data(), str.size());
723
+ }
724
+
725
+ // Implementation of Future#inspect.
726
+ static VALUE future_inspect(VALUE vself) {
727
+ StructFuture* sfuture = nullptr;
728
+ Data_Get_Struct(vself, StructFuture, sfuture);
729
+ if (sfuture->future == nullptr) {
730
+ rb_raise(rb_eRuntimeError, "destructed object");
731
+ }
732
+ const std::string str = tkrzw::SPrintF("#<Tkrzw::Future: %p>", (void*)sfuture->future.get());
733
+ return rb_str_new(str.data(), str.size());
734
+ }
735
+
736
+ // Creates a future object.
737
+ static VALUE MakeFutureValue(tkrzw::StatusFuture&& future, bool concurrent, VALUE venc) {
738
+ StructFuture* sfuture = new StructFuture;
739
+ sfuture->future = std::make_unique<tkrzw::StatusFuture>(std::move(future));
740
+ sfuture->concurrent = false;
741
+ sfuture->venc = Qnil;
742
+ return Data_Wrap_Struct(cls_future, 0, future_del, sfuture);
743
+ }
744
+
745
+ // Defines the Future class.
746
+ static void DefineFuture() {
747
+ cls_future = rb_define_class_under(mod_tkrzw, "Future", rb_cObject);
748
+ rb_define_alloc_func(cls_future, future_new);
749
+ rb_define_private_method(cls_future, "initialize", (METHOD)future_initialize, -1);
750
+ rb_define_method(cls_future, "destruct", (METHOD)future_destruct, 0);
751
+ rb_define_method(cls_future, "wait", (METHOD)future_wait, -1);
752
+ rb_define_method(cls_future, "get", (METHOD)future_get, 0);
753
+ rb_define_method(cls_future, "to_s", (METHOD)future_to_s, 0);
754
+ rb_define_method(cls_future, "inspect", (METHOD)future_inspect, 0);
755
+ }
756
+
757
+ // Implementation of StatusException#initialize.
758
+ static VALUE expt_initialize(VALUE vself, VALUE vstatus) {
759
+ volatile VALUE vmessage = StringValueEx(vstatus);
760
+ volatile VALUE vsuperargs[] = {vmessage};
761
+ rb_call_super(1, (const VALUE*)vsuperargs);
762
+ if (!rb_obj_is_instance_of(vstatus, cls_status)) {
763
+ tkrzw::Status::Code code = tkrzw::Status::SYSTEM_ERROR;
764
+ std::string_view message = GetStringView(vmessage);
765
+ size_t pos = message.find(':');
766
+ if (pos != std::string::npos) {
767
+ const std::string_view name = message.substr(0, pos);
768
+ pos++;
769
+ while (pos < message.size() && message[pos] == ' ') {
770
+ pos++;
771
+ }
772
+ const std::string_view sub_message = message.substr(pos);
773
+ int32_t num = 0;
774
+ while (num <= (int)tkrzw::Status::APPLICATION_ERROR) {
775
+ if (name == tkrzw::Status::CodeName((tkrzw::Status::Code)num)) {
776
+ code = (tkrzw::Status::Code)num;
777
+ message = sub_message;
778
+ break;
779
+ }
780
+ num++;
781
+ }
782
+ }
783
+ vstatus = MakeStatusValue(tkrzw::Status(code, message));
784
+ }
785
+ rb_ivar_set(vself, id_expt_status, vstatus);
786
+ return Qnil;
787
+ }
788
+
789
+ // Implementation of StatusException#status.
790
+ static VALUE expt_status(VALUE vself) {
791
+ return rb_ivar_get(vself, id_expt_status);
792
+ }
793
+
794
+ // Defines the StatusException class.
795
+ static void DefineStatusException() {
796
+ cls_expt = rb_define_class_under(mod_tkrzw, "StatusException", rb_eRuntimeError);
797
+ rb_define_private_method(cls_expt, "initialize", (METHOD)expt_initialize, 1);
798
+ rb_define_method(cls_expt, "status", (METHOD)expt_status, 0);
799
+ id_expt_status = rb_intern("@status");
800
+ }
801
+
802
+ // Implementation of DBM#del.
803
+ static void dbm_del(void* ptr) {
804
+ StructDBM* sdbm = (StructDBM*)ptr;
805
+ sdbm->dbm.reset(nullptr);
806
+ delete sdbm;
807
+ }
808
+
809
+ // Implementation of DBM.new.
810
+ static VALUE dbm_new(VALUE cls) {
811
+ StructDBM* sdbm = new StructDBM;
812
+ return Data_Wrap_Struct(cls_dbm, 0, dbm_del, sdbm);
813
+ }
814
+
815
+ // Implementation of DBM#initialize.
816
+ static VALUE dbm_initialize(VALUE vself) {
817
+ return Qnil;
818
+ }
819
+
820
+ // Implementation of DBM#destruct.
821
+ static VALUE dbm_destruct(VALUE vself) {
822
+ StructDBM* sdbm = nullptr;
823
+ Data_Get_Struct(vself, StructDBM, sdbm);
824
+ sdbm->dbm.reset(nullptr);
825
+ return Qnil;
826
+ }
827
+
828
+ // Implementation of DBM#open.
829
+ static VALUE dbm_open(int argc, VALUE* argv, VALUE vself) {
830
+ StructDBM* sdbm = nullptr;
831
+ Data_Get_Struct(vself, StructDBM, sdbm);
832
+ if (sdbm->dbm != nullptr) {
833
+ rb_raise(rb_eRuntimeError, "opened database");
834
+ }
835
+ volatile VALUE vpath, vwritable, vparams;
836
+ rb_scan_args(argc, argv, "21", &vpath, &vwritable, &vparams);
837
+ vpath = StringValueEx(vpath);
838
+ const std::string_view path = GetStringView(vpath);
839
+ const bool writable = RTEST(vwritable);
840
+ std::map<std::string, std::string> params = HashToMap(vparams);
841
+ const int32_t num_shards = tkrzw::StrToInt(tkrzw::SearchMap(params, "num_shards", "-1"));
842
+ bool concurrent = false;
843
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "concurrent", "false"))) {
844
+ concurrent = true;
845
+ }
846
+ int32_t open_options = 0;
847
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "truncate", "false"))) {
848
+ open_options |= tkrzw::File::OPEN_TRUNCATE;
849
+ }
850
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "no_create", "false"))) {
851
+ open_options |= tkrzw::File::OPEN_NO_CREATE;
852
+ }
853
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "no_wait", "false"))) {
854
+ open_options |= tkrzw::File::OPEN_NO_WAIT;
855
+ }
856
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "no_lock", "false"))) {
857
+ open_options |= tkrzw::File::OPEN_NO_LOCK;
858
+ }
859
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "sync_hard", "false"))) {
860
+ open_options |= tkrzw::File::OPEN_SYNC_HARD;
861
+ }
862
+ std::string encoding = tkrzw::SearchMap(params, "encoding", "");
863
+ if (encoding.empty()) {
864
+ encoding = "ASCII-8BIT";
865
+ }
866
+ params.erase("concurrent");
867
+ params.erase("truncate");
868
+ params.erase("no_create");
869
+ params.erase("no_wait");
870
+ params.erase("no_lock");
871
+ params.erase("sync_hard");
872
+ params.erase("encoding");
873
+ if (num_shards >= 0) {
874
+ sdbm->dbm.reset(new tkrzw::ShardDBM());
875
+ } else {
876
+ sdbm->dbm.reset(new tkrzw::PolyDBM());
877
+ }
878
+ sdbm->concurrent = concurrent;
879
+ sdbm->venc = GetEncoding(encoding);
880
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
881
+ NativeFunction(sdbm->concurrent, [&]() {
882
+ status = sdbm->dbm->OpenAdvanced(std::string(path), writable, open_options, params);
883
+ });
884
+ return MakeStatusValue(std::move(status));
885
+ }
886
+
887
+ // Implementation of DBM#close.
888
+ static VALUE dbm_close(VALUE vself) {
889
+ StructDBM* sdbm = nullptr;
890
+ Data_Get_Struct(vself, StructDBM, sdbm);
891
+ if (sdbm->dbm == nullptr) {
892
+ rb_raise(rb_eRuntimeError, "not opened database");
893
+ }
894
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
895
+ NativeFunction(sdbm->concurrent, [&]() {
896
+ status = sdbm->dbm->Close();
897
+ });
898
+ sdbm->dbm.reset(nullptr);
899
+ return MakeStatusValue(std::move(status));
900
+ }
901
+
902
+ // Implementation of DBM#include?.
903
+ static VALUE dbm_include(VALUE vself, VALUE vkey) {
904
+ StructDBM* sdbm = nullptr;
905
+ Data_Get_Struct(vself, StructDBM, sdbm);
906
+ if (sdbm->dbm == nullptr) {
907
+ rb_raise(rb_eRuntimeError, "not opened database");
908
+ }
909
+ vkey = StringValueEx(vkey);
910
+ const std::string_view key = GetStringView(vkey);
911
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
912
+ NativeFunction(sdbm->concurrent, [&]() {
913
+ status = sdbm->dbm->Get(key);
914
+ });
915
+ return status == tkrzw::Status::SUCCESS ? Qtrue : Qfalse;
916
+ }
917
+
918
+ // Implementation of DBM#get.
919
+ static VALUE dbm_get(int argc, VALUE* argv, VALUE vself) {
920
+ StructDBM* sdbm = nullptr;
921
+ Data_Get_Struct(vself, StructDBM, sdbm);
922
+ if (sdbm->dbm == nullptr) {
923
+ rb_raise(rb_eRuntimeError, "not opened database");
924
+ }
925
+ volatile VALUE vkey, vstatus;
926
+ rb_scan_args(argc, argv, "11", &vkey, &vstatus);
927
+ vkey = StringValueEx(vkey);
928
+ const std::string_view key = GetStringView(vkey);
929
+ std::string value;
930
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
931
+ NativeFunction(sdbm->concurrent, [&]() {
932
+ status = sdbm->dbm->Get(key, &value);
933
+ });
934
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
935
+ SetStatusValue(vstatus, status);
936
+ }
937
+ if (status == tkrzw::Status::SUCCESS) {
938
+ return MakeString(value, sdbm->venc);
939
+ }
940
+ return Qnil;
941
+ }
942
+
943
+ // Implementation of DBM#get_multi.
944
+ static VALUE dbm_get_multi(VALUE vself, VALUE vkeys) {
945
+ StructDBM* sdbm = nullptr;
946
+ Data_Get_Struct(vself, StructDBM, sdbm);
947
+ if (sdbm->dbm == nullptr) {
948
+ rb_raise(rb_eRuntimeError, "not opened database");
949
+ }
950
+ if (TYPE(vkeys) != T_ARRAY) {
951
+ rb_raise(rb_eRuntimeError, "keys is not an array");
952
+ }
953
+ std::vector<std::string> keys;
954
+ const int32_t num_keys = RARRAY_LEN(vkeys);
955
+ for (int32_t i = 0; i < num_keys; i++) {
956
+ volatile VALUE vkey = rb_ary_entry(vkeys, i);
957
+ vkey = StringValueEx(vkey);
958
+ keys.emplace_back(std::string(RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
959
+ }
960
+ std::vector<std::string_view> key_views(keys.begin(), keys.end());
961
+ std::map<std::string, std::string> records;
962
+ NativeFunction(sdbm->concurrent, [&]() {
963
+ sdbm->dbm->GetMulti(key_views, &records);
964
+ });
965
+ volatile VALUE vhash = rb_hash_new();
966
+ for (const auto& record : records) {
967
+ volatile VALUE vkey = rb_str_new(record.first.data(), record.first.size());
968
+ volatile VALUE vvalue = rb_str_new(record.second.data(), record.second.size());
969
+ rb_hash_aset(vhash, vkey, vvalue);
970
+ }
971
+ return vhash;
972
+ }
973
+
974
+ // Implementation of DBM#set.
975
+ static VALUE dbm_set(int argc, VALUE* argv, VALUE vself) {
976
+ StructDBM* sdbm = nullptr;
977
+ Data_Get_Struct(vself, StructDBM, sdbm);
978
+ if (sdbm->dbm == nullptr) {
979
+ rb_raise(rb_eRuntimeError, "not opened database");
980
+ }
981
+ volatile VALUE vkey, vvalue, voverwrite;
982
+ rb_scan_args(argc, argv, "21", &vkey, &vvalue, &voverwrite);
983
+ vkey = StringValueEx(vkey);
984
+ const std::string_view key = GetStringView(vkey);
985
+ vvalue = StringValueEx(vvalue);
986
+ const std::string_view value = GetStringView(vvalue);
987
+ const bool overwrite = argc > 2 ? RTEST(voverwrite) : true;
988
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
989
+ NativeFunction(sdbm->concurrent, [&]() {
990
+ status = sdbm->dbm->Set(key, value, overwrite);
991
+ });
992
+ return MakeStatusValue(std::move(status));
993
+ }
994
+
995
+ // Implementation of DBM#set_multi.
996
+ static VALUE dbm_set_multi(int argc, VALUE* argv, VALUE vself) {
997
+ StructDBM* sdbm = nullptr;
998
+ Data_Get_Struct(vself, StructDBM, sdbm);
999
+ if (sdbm->dbm == nullptr) {
1000
+ rb_raise(rb_eRuntimeError, "not opened database");
1001
+ }
1002
+ volatile VALUE voverwrite, vrecords;
1003
+ rb_scan_args(argc, argv, "02", &voverwrite, &vrecords);
1004
+ bool overwrite = true;
1005
+ if (argc <= 1 && TYPE(voverwrite) == T_HASH) {
1006
+ vrecords = voverwrite;
1007
+ } else {
1008
+ overwrite = argc > 0 ? RTEST(voverwrite) : true;
1009
+ }
1010
+ const auto& records = HashToMap(vrecords);
1011
+ std::map<std::string_view, std::string_view> record_views;
1012
+ for (const auto& record : records) {
1013
+ record_views.emplace(std::make_pair(
1014
+ std::string_view(record.first), std::string_view(record.second)));
1015
+ }
1016
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1017
+ NativeFunction(sdbm->concurrent, [&]() {
1018
+ status = sdbm->dbm->SetMulti(record_views, overwrite);
1019
+ });
1020
+ return MakeStatusValue(std::move(status));
1021
+ }
1022
+
1023
+ // Implementation of DBM#set_and_get.
1024
+ static VALUE dbm_set_and_get(int argc, VALUE* argv, VALUE vself) {
1025
+ StructDBM* sdbm = nullptr;
1026
+ Data_Get_Struct(vself, StructDBM, sdbm);
1027
+ if (sdbm->dbm == nullptr) {
1028
+ rb_raise(rb_eRuntimeError, "not opened database");
1029
+ }
1030
+ volatile VALUE vkey, vvalue, voverwrite;
1031
+ rb_scan_args(argc, argv, "21", &vkey, &vvalue, &voverwrite);
1032
+ vkey = StringValueEx(vkey);
1033
+ const std::string_view key = GetStringView(vkey);
1034
+ vvalue = StringValueEx(vvalue);
1035
+ const std::string_view value = GetStringView(vvalue);
1036
+ const bool overwrite = argc > 2 ? RTEST(voverwrite) : true;
1037
+ tkrzw::Status impl_status(tkrzw::Status::SUCCESS);
1038
+ std::string old_value;
1039
+ bool hit = false;
1040
+ class Processor final : public tkrzw::DBM::RecordProcessor {
1041
+ public:
1042
+ Processor(tkrzw::Status* status, std::string_view value, bool overwrite,
1043
+ std::string* old_value, bool* hit)
1044
+ : status_(status), value_(value), overwrite_(overwrite),
1045
+ old_value_(old_value), hit_(hit) {}
1046
+ std::string_view ProcessFull(std::string_view key, std::string_view value) override {
1047
+ *old_value_ = value;
1048
+ *hit_ = true;
1049
+ if (overwrite_) {
1050
+ return value_;
1051
+ }
1052
+ status_->Set(tkrzw::Status::DUPLICATION_ERROR);
1053
+ return NOOP;
1054
+ }
1055
+ std::string_view ProcessEmpty(std::string_view key) override {
1056
+ return value_;
1057
+ }
1058
+ private:
1059
+ tkrzw::Status* status_;
1060
+ std::string_view value_;
1061
+ bool overwrite_;
1062
+ std::string* old_value_;
1063
+ bool* hit_;
1064
+ };
1065
+ Processor proc(&impl_status, value, overwrite, &old_value, &hit);
1066
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1067
+ NativeFunction(sdbm->concurrent, [&]() {
1068
+ status = sdbm->dbm->Process(key, &proc, true);
1069
+ });
1070
+ status |= impl_status;
1071
+ volatile VALUE vpair = rb_ary_new2(2);
1072
+ rb_ary_push(vpair, MakeStatusValue(std::move(status)));
1073
+ if (hit) {
1074
+ rb_ary_push(vpair, MakeString(old_value, sdbm->venc));
1075
+ } else {
1076
+ rb_ary_push(vpair, Qnil);
1077
+ }
1078
+ return vpair;
1079
+ }
1080
+
1081
+ // Implementation of DBM#remove.
1082
+ static VALUE dbm_remove(VALUE vself, VALUE vkey) {
1083
+ StructDBM* sdbm = nullptr;
1084
+ Data_Get_Struct(vself, StructDBM, sdbm);
1085
+ if (sdbm->dbm == nullptr) {
1086
+ rb_raise(rb_eRuntimeError, "not opened database");
1087
+ }
1088
+ vkey = StringValueEx(vkey);
1089
+ const std::string_view key = GetStringView(vkey);
1090
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1091
+ NativeFunction(sdbm->concurrent, [&]() {
1092
+ status = sdbm->dbm->Remove(key);
1093
+ });
1094
+ return MakeStatusValue(std::move(status));
1095
+ }
1096
+
1097
+ // Implementation of DBM#remove_multi.
1098
+ static VALUE dbm_remove_multi(VALUE vself, VALUE vkeys) {
1099
+ StructDBM* sdbm = nullptr;
1100
+ Data_Get_Struct(vself, StructDBM, sdbm);
1101
+ if (sdbm->dbm == nullptr) {
1102
+ rb_raise(rb_eRuntimeError, "not opened database");
1103
+ }
1104
+ if (TYPE(vkeys) != T_ARRAY) {
1105
+ rb_raise(rb_eRuntimeError, "keys is not an array");
1106
+ }
1107
+ std::vector<std::string> keys;
1108
+ const int32_t num_keys = RARRAY_LEN(vkeys);
1109
+ for (int32_t i = 0; i < num_keys; i++) {
1110
+ volatile VALUE vkey = rb_ary_entry(vkeys, i);
1111
+ vkey = StringValueEx(vkey);
1112
+ keys.emplace_back(std::string(RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
1113
+ }
1114
+ std::vector<std::string_view> key_views(keys.begin(), keys.end());
1115
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1116
+ NativeFunction(sdbm->concurrent, [&]() {
1117
+ status = sdbm->dbm->RemoveMulti(key_views);
1118
+ });
1119
+ return MakeStatusValue(std::move(status));
1120
+ }
1121
+
1122
+ // Implementation of DBM#remove_and_get.
1123
+ static VALUE dbm_remove_and_get(VALUE vself, VALUE vkey) {
1124
+ StructDBM* sdbm = nullptr;
1125
+ Data_Get_Struct(vself, StructDBM, sdbm);
1126
+ if (sdbm->dbm == nullptr) {
1127
+ rb_raise(rb_eRuntimeError, "not opened database");
1128
+ }
1129
+ vkey = StringValueEx(vkey);
1130
+ const std::string_view key = GetStringView(vkey);
1131
+ tkrzw::Status impl_status(tkrzw::Status::SUCCESS);
1132
+ std::string old_value;
1133
+ class Processor final : public tkrzw::DBM::RecordProcessor {
1134
+ public:
1135
+ Processor(tkrzw::Status* status, std::string* old_value)
1136
+ : status_(status), old_value_(old_value) {}
1137
+ std::string_view ProcessFull(std::string_view key, std::string_view value) override {
1138
+ *old_value_ = value;
1139
+ return REMOVE;
1140
+ }
1141
+ std::string_view ProcessEmpty(std::string_view key) override {
1142
+ status_->Set(tkrzw::Status::NOT_FOUND_ERROR);
1143
+ return NOOP;
1144
+ }
1145
+ private:
1146
+ tkrzw::Status* status_;
1147
+ std::string* old_value_;
1148
+ };
1149
+ Processor proc(&impl_status, &old_value);
1150
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1151
+ NativeFunction(sdbm->concurrent, [&]() {
1152
+ status = sdbm->dbm->Process(key, &proc, true);
1153
+ });
1154
+ status |= impl_status;
1155
+ volatile VALUE vpair = rb_ary_new2(2);
1156
+ rb_ary_push(vpair, MakeStatusValue(std::move(status)));
1157
+ if (status == tkrzw::Status::SUCCESS) {
1158
+ rb_ary_push(vpair, MakeString(old_value, sdbm->venc));
1159
+ } else {
1160
+ rb_ary_push(vpair, Qnil);
1161
+ }
1162
+ return vpair;
1163
+ }
1164
+
1165
+ // Implementation of DBM#append.
1166
+ static VALUE dbm_append(int argc, VALUE* argv, VALUE vself) {
1167
+ StructDBM* sdbm = nullptr;
1168
+ Data_Get_Struct(vself, StructDBM, sdbm);
1169
+ if (sdbm->dbm == nullptr) {
1170
+ rb_raise(rb_eRuntimeError, "not opened database");
1171
+ }
1172
+ volatile VALUE vkey, vvalue, vdelim;
1173
+ rb_scan_args(argc, argv, "21", &vkey, &vvalue, &vdelim);
1174
+ vkey = StringValueEx(vkey);
1175
+ const std::string_view key = GetStringView(vkey);
1176
+ vvalue = StringValueEx(vvalue);
1177
+ const std::string_view value = GetStringView(vvalue);
1178
+ vdelim = StringValueEx(vdelim);
1179
+ const std::string_view delim = GetStringView(vdelim);
1180
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1181
+ NativeFunction(sdbm->concurrent, [&]() {
1182
+ status = sdbm->dbm->Append(key, value, delim);
1183
+ });
1184
+ return MakeStatusValue(std::move(status));
1185
+ }
1186
+
1187
+ // Implementation of DBM#append_multi.
1188
+ static VALUE dbm_append_multi(int argc, VALUE* argv, VALUE vself) {
1189
+ StructDBM* sdbm = nullptr;
1190
+ Data_Get_Struct(vself, StructDBM, sdbm);
1191
+ if (sdbm->dbm == nullptr) {
1192
+ rb_raise(rb_eRuntimeError, "not opened database");
1193
+ }
1194
+ volatile VALUE vdelim, vrecords;
1195
+ rb_scan_args(argc, argv, "02", &vdelim, &vrecords);
1196
+ std::string_view delim = "";
1197
+ if (argc <= 1 && TYPE(vdelim) == T_HASH) {
1198
+ vrecords = vdelim;
1199
+ } else {
1200
+ delim = argc > 0 ? GetStringView(vdelim) : std::string_view("");
1201
+ }
1202
+ const auto& records = HashToMap(vrecords);
1203
+ std::map<std::string_view, std::string_view> record_views;
1204
+ for (const auto& record : records) {
1205
+ record_views.emplace(std::make_pair(
1206
+ std::string_view(record.first), std::string_view(record.second)));
1207
+ }
1208
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1209
+ NativeFunction(sdbm->concurrent, [&]() {
1210
+ status = sdbm->dbm->AppendMulti(record_views, delim);
1211
+ });
1212
+ return MakeStatusValue(std::move(status));
1213
+ }
1214
+
1215
+ // Implementation of DBM#compare_exchange.
1216
+ static VALUE dbm_compare_exchange(VALUE vself, VALUE vkey, VALUE vexpected, VALUE vdesired) {
1217
+ StructDBM* sdbm = nullptr;
1218
+ Data_Get_Struct(vself, StructDBM, sdbm);
1219
+ if (sdbm->dbm == nullptr) {
1220
+ rb_raise(rb_eRuntimeError, "not opened database");
1221
+ }
1222
+ vkey = StringValueEx(vkey);
1223
+ const std::string_view key = GetStringView(vkey);
1224
+ std::string_view expected;
1225
+ if (vexpected != Qnil) {
1226
+ if (vexpected == obj_dbm_any_data) {
1227
+ expected = tkrzw::DBM::ANY_DATA;
1228
+ } else {
1229
+ vexpected = StringValueEx(vexpected);
1230
+ expected = GetStringView(vexpected);
1231
+ }
1232
+ }
1233
+ std::string_view desired;
1234
+ if (vdesired != Qnil) {
1235
+ if (vdesired == obj_dbm_any_data) {
1236
+ desired = tkrzw::DBM::ANY_DATA;
1237
+ } else {
1238
+ vdesired = StringValueEx(vdesired);
1239
+ desired = GetStringView(vdesired);
1240
+ }
1241
+ }
1242
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1243
+ NativeFunction(sdbm->concurrent, [&]() {
1244
+ status = sdbm->dbm->CompareExchange(key, expected, desired);
1245
+ });
1246
+ return MakeStatusValue(std::move(status));
1247
+ }
1248
+
1249
+ // Implementation of DBM#compare_exchange_and_get.
1250
+ static VALUE dbm_compare_exchange_and_get(
1251
+ VALUE vself, VALUE vkey, VALUE vexpected, VALUE vdesired) {
1252
+ StructDBM* sdbm = nullptr;
1253
+ Data_Get_Struct(vself, StructDBM, sdbm);
1254
+ if (sdbm->dbm == nullptr) {
1255
+ rb_raise(rb_eRuntimeError, "not opened database");
1256
+ }
1257
+ vkey = StringValueEx(vkey);
1258
+ const std::string_view key = GetStringView(vkey);
1259
+ std::string_view expected;
1260
+ if (vexpected != Qnil) {
1261
+ if (vexpected == obj_dbm_any_data) {
1262
+ expected = tkrzw::DBM::ANY_DATA;
1263
+ } else {
1264
+ vexpected = StringValueEx(vexpected);
1265
+ expected = GetStringView(vexpected);
1266
+ }
1267
+ }
1268
+ std::string_view desired;
1269
+ if (vdesired != Qnil) {
1270
+ if (vdesired == obj_dbm_any_data) {
1271
+ desired = tkrzw::DBM::ANY_DATA;
1272
+ } else {
1273
+ vdesired = StringValueEx(vdesired);
1274
+ desired = GetStringView(vdesired);
1275
+ }
1276
+ }
1277
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1278
+ std::string actual;
1279
+ bool found = false;
1280
+ NativeFunction(sdbm->concurrent, [&]() {
1281
+ status = sdbm->dbm->CompareExchange(key, expected, desired, &actual, &found);
1282
+ });
1283
+ volatile VALUE vpair = rb_ary_new2(2);
1284
+ rb_ary_push(vpair, MakeStatusValue(std::move(status)));
1285
+ if (found) {
1286
+ rb_ary_push(vpair, MakeString(actual, sdbm->venc));
1287
+ } else {
1288
+ rb_ary_push(vpair, Qnil);
1289
+ }
1290
+ return vpair;
1291
+ }
1292
+
1293
+ // Implementation of DBM#increment.
1294
+ static VALUE dbm_increment(int argc, VALUE* argv, VALUE vself) {
1295
+ StructDBM* sdbm = nullptr;
1296
+ Data_Get_Struct(vself, StructDBM, sdbm);
1297
+ if (sdbm->dbm == nullptr) {
1298
+ rb_raise(rb_eRuntimeError, "not opened database");
1299
+ }
1300
+ volatile VALUE vkey, vinc, vinit, vstatus;
1301
+ rb_scan_args(argc, argv, "13", &vkey, &vinc, &vinit, &vstatus);
1302
+ vkey = StringValueEx(vkey);
1303
+ const std::string_view key = GetStringView(vkey);
1304
+ const int64_t inc = vinc == Qnil ? 1 : GetInteger(vinc);
1305
+ const int64_t init = vinit == Qnil ? 0 : GetInteger(vinit);
1306
+ int64_t current = 0;
1307
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1308
+ NativeFunction(sdbm->concurrent, [&]() {
1309
+ status = sdbm->dbm->Increment(key, inc, &current, init);
1310
+ });
1311
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
1312
+ SetStatusValue(vstatus, status);
1313
+ }
1314
+ if (status == tkrzw::Status::SUCCESS) {
1315
+ return LL2NUM(current);
1316
+ }
1317
+ return Qnil;
1318
+ }
1319
+
1320
+ // Implementation of DBM#compare_exchange_multi.
1321
+ static VALUE dbm_compare_exchange_multi(VALUE vself, VALUE vexpected, VALUE vdesired) {
1322
+ StructDBM* sdbm = nullptr;
1323
+ Data_Get_Struct(vself, StructDBM, sdbm);
1324
+ if (sdbm->dbm == nullptr) {
1325
+ rb_raise(rb_eRuntimeError, "not opened database");
1326
+ }
1327
+ if (TYPE(vexpected) != T_ARRAY || TYPE(vdesired) != T_ARRAY) {
1328
+ rb_raise(rb_eRuntimeError, "expected or desired is not an array");
1329
+ }
1330
+ const auto& expected = ExtractSVPairs(vexpected);
1331
+ const auto& desired = ExtractSVPairs(vdesired);
1332
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1333
+ NativeFunction(sdbm->concurrent, [&]() {
1334
+ status = sdbm->dbm->CompareExchangeMulti(expected, desired);
1335
+ });
1336
+ return MakeStatusValue(std::move(status));
1337
+ }
1338
+
1339
+ // Implementation of DBM#rekey.
1340
+ static VALUE dbm_rekey(int argc, VALUE* argv, VALUE vself) {
1341
+ StructDBM* sdbm = nullptr;
1342
+ Data_Get_Struct(vself, StructDBM, sdbm);
1343
+ if (sdbm->dbm == nullptr) {
1344
+ rb_raise(rb_eRuntimeError, "not opened database");
1345
+ }
1346
+ volatile VALUE vold_key, vnew_key, voverwrite, vcopying;
1347
+ rb_scan_args(argc, argv, "22", &vold_key, &vnew_key, &voverwrite);
1348
+ vold_key = StringValueEx(vold_key);
1349
+ const std::string_view old_key = GetStringView(vold_key);
1350
+ vnew_key = StringValueEx(vnew_key);
1351
+ const std::string_view new_key = GetStringView(vnew_key);
1352
+ const bool overwrite = argc > 2 ? RTEST(voverwrite) : true;
1353
+ const bool copying = argc > 3 ? RTEST(vcopying) : false;
1354
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1355
+ NativeFunction(sdbm->concurrent, [&]() {
1356
+ status = sdbm->dbm->Rekey(old_key, new_key, overwrite, copying);
1357
+ });
1358
+ return MakeStatusValue(std::move(status));
1359
+ }
1360
+
1361
+ // Implementation of DBM#pop_first.
1362
+ static VALUE dbm_pop_first(int argc, VALUE* argv, VALUE vself) {
1363
+ StructDBM* sdbm = nullptr;
1364
+ Data_Get_Struct(vself, StructDBM, sdbm);
1365
+ if (sdbm->dbm == nullptr) {
1366
+ rb_raise(rb_eRuntimeError, "not opened database");
1367
+ }
1368
+ volatile VALUE vstatus;
1369
+ rb_scan_args(argc, argv, "01", &vstatus);
1370
+ std::string key, value;
1371
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1372
+ NativeFunction(sdbm->concurrent, [&]() {
1373
+ status = sdbm->dbm->PopFirst(&key, &value);
1374
+ });
1375
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
1376
+ SetStatusValue(vstatus, status);
1377
+ }
1378
+ if (status == tkrzw::Status::SUCCESS) {
1379
+ volatile VALUE vary = rb_ary_new2(2);
1380
+ rb_ary_push(vary, MakeString(key, sdbm->venc));
1381
+ rb_ary_push(vary, MakeString(value, sdbm->venc));
1382
+ return vary;
1383
+ }
1384
+ return Qnil;
1385
+ }
1386
+
1387
+ // Implementation of DBM#push_last.
1388
+ static VALUE dbm_push_last(int argc, VALUE* argv, VALUE vself) {
1389
+ StructDBM* sdbm = nullptr;
1390
+ Data_Get_Struct(vself, StructDBM, sdbm);
1391
+ if (sdbm->dbm == nullptr) {
1392
+ rb_raise(rb_eRuntimeError, "not opened database");
1393
+ }
1394
+ volatile VALUE vvalue, vwtime;
1395
+ rb_scan_args(argc, argv, "11", &vvalue, &vwtime);
1396
+ vvalue = StringValueEx(vvalue);
1397
+ const std::string_view value = GetStringView(vvalue);
1398
+ const double wtime = vwtime == Qnil ? -1.0 : GetFloat(vwtime);
1399
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1400
+ NativeFunction(sdbm->concurrent, [&]() {
1401
+ status = sdbm->dbm->PushLast(value, wtime);
1402
+ });
1403
+ return MakeStatusValue(std::move(status));
1404
+ }
1405
+
1406
+ // Implementation of DBM#count.
1407
+ static VALUE dbm_count(VALUE vself) {
1408
+ StructDBM* sdbm = nullptr;
1409
+ Data_Get_Struct(vself, StructDBM, sdbm);
1410
+ if (sdbm->dbm == nullptr) {
1411
+ rb_raise(rb_eRuntimeError, "not opened database");
1412
+ }
1413
+ int64_t count = 0;
1414
+ NativeFunction(sdbm->concurrent, [&]() {
1415
+ count = sdbm->dbm->CountSimple();
1416
+ });
1417
+ if (count >= 0) {
1418
+ return LL2NUM(count);
1419
+ }
1420
+ return Qnil;
1421
+ }
1422
+
1423
+ // Implementation of DBM#file_size.
1424
+ static VALUE dbm_file_size(VALUE vself) {
1425
+ StructDBM* sdbm = nullptr;
1426
+ Data_Get_Struct(vself, StructDBM, sdbm);
1427
+ if (sdbm->dbm == nullptr) {
1428
+ rb_raise(rb_eRuntimeError, "not opened database");
1429
+ }
1430
+ int64_t file_size = 0;
1431
+ NativeFunction(sdbm->concurrent, [&]() {
1432
+ file_size = sdbm->dbm->GetFileSizeSimple();
1433
+ });
1434
+ if (file_size >= 0) {
1435
+ return LL2NUM(file_size);
1436
+ }
1437
+ return Qnil;
1438
+ }
1439
+
1440
+ // Implementation of DBM#file_path.
1441
+ static VALUE dbm_file_path(VALUE vself) {
1442
+ StructDBM* sdbm = nullptr;
1443
+ Data_Get_Struct(vself, StructDBM, sdbm);
1444
+ if (sdbm->dbm == nullptr) {
1445
+ rb_raise(rb_eRuntimeError, "not opened database");
1446
+ }
1447
+ std::string path;
1448
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1449
+ NativeFunction(sdbm->concurrent, [&]() {
1450
+ status = sdbm->dbm->GetFilePath(&path);
1451
+ });
1452
+ if (status == tkrzw::Status::SUCCESS) {
1453
+ return rb_str_new(path.data(), path.size());
1454
+ }
1455
+ return Qnil;
1456
+ }
1457
+
1458
+ // Implementation of DBM#timestamp.
1459
+ static VALUE dbm_timestamp(VALUE vself) {
1460
+ StructDBM* sdbm = nullptr;
1461
+ Data_Get_Struct(vself, StructDBM, sdbm);
1462
+ if (sdbm->dbm == nullptr) {
1463
+ rb_raise(rb_eRuntimeError, "not opened database");
1464
+ }
1465
+ double timestamp = 0;
1466
+
1467
+ NativeFunction(sdbm->concurrent, [&]() {
1468
+ timestamp = sdbm->dbm->GetTimestampSimple();
1469
+ });
1470
+ if (timestamp >= 0) {
1471
+ return rb_float_new(timestamp);
1472
+ }
1473
+ return Qnil;
1474
+ }
1475
+
1476
+ // Implementation of DBM#clear.
1477
+ static VALUE dbm_clear(VALUE vself) {
1478
+ StructDBM* sdbm = nullptr;
1479
+ Data_Get_Struct(vself, StructDBM, sdbm);
1480
+ if (sdbm->dbm == nullptr) {
1481
+ rb_raise(rb_eRuntimeError, "not opened database");
1482
+ }
1483
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1484
+ NativeFunction(sdbm->concurrent, [&]() {
1485
+ status = sdbm->dbm->Clear();
1486
+ });
1487
+ return MakeStatusValue(std::move(status));
1488
+ }
1489
+
1490
+ // Implementation of DBM#rebuild.
1491
+ static VALUE dbm_rebuild(int argc, VALUE* argv, VALUE vself) {
1492
+ StructDBM* sdbm = nullptr;
1493
+ Data_Get_Struct(vself, StructDBM, sdbm);
1494
+ if (sdbm->dbm == nullptr) {
1495
+ rb_raise(rb_eRuntimeError, "not opened database");
1496
+ }
1497
+ volatile VALUE vparams;
1498
+ rb_scan_args(argc, argv, "01", &vparams);
1499
+ const std::map<std::string, std::string> params = HashToMap(vparams);
1500
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1501
+ NativeFunction(sdbm->concurrent, [&]() {
1502
+ status = sdbm->dbm->RebuildAdvanced(params);
1503
+ });
1504
+ return MakeStatusValue(std::move(status));
1505
+ }
1506
+
1507
+ // Implementation of DBM#should_be_rebuilt?.
1508
+ static VALUE dbm_should_be_rebuilt(VALUE vself) {
1509
+ StructDBM* sdbm = nullptr;
1510
+ Data_Get_Struct(vself, StructDBM, sdbm);
1511
+ if (sdbm->dbm == nullptr) {
1512
+ rb_raise(rb_eRuntimeError, "not opened database");
1513
+ }
1514
+ bool tobe = false;
1515
+ NativeFunction(sdbm->concurrent, [&]() {
1516
+ tobe = sdbm->dbm->ShouldBeRebuiltSimple();
1517
+ });
1518
+ return tobe ? Qtrue : Qfalse;
1519
+ }
1520
+
1521
+ // Implementation of DBM#synchronize.
1522
+ static VALUE dbm_synchronize(int argc, VALUE* argv, VALUE vself) {
1523
+ StructDBM* sdbm = nullptr;
1524
+ Data_Get_Struct(vself, StructDBM, sdbm);
1525
+ if (sdbm->dbm == nullptr) {
1526
+ rb_raise(rb_eRuntimeError, "not opened database");
1527
+ }
1528
+ volatile VALUE vhard, vparams;
1529
+ rb_scan_args(argc, argv, "11", &vhard, &vparams);
1530
+ const bool hard = RTEST(vhard);
1531
+ const std::map<std::string, std::string> params = HashToMap(vparams);
1532
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1533
+ NativeFunction(sdbm->concurrent, [&]() {
1534
+ status = sdbm->dbm->SynchronizeAdvanced(hard, nullptr, params);
1535
+ });
1536
+ return MakeStatusValue(std::move(status));
1537
+ }
1538
+
1539
+ // Implementation of DBM#copy_file_data.
1540
+ static VALUE dbm_copy_file_data(int argc, VALUE* argv, VALUE vself) {
1541
+ StructDBM* sdbm = nullptr;
1542
+ Data_Get_Struct(vself, StructDBM, sdbm);
1543
+ if (sdbm->dbm == nullptr) {
1544
+ rb_raise(rb_eRuntimeError, "not opened database");
1545
+ }
1546
+ volatile VALUE vdestpath, vsynchard;
1547
+ rb_scan_args(argc, argv, "11", &vdestpath, &vsynchard);
1548
+ vdestpath = StringValueEx(vdestpath);
1549
+ const std::string_view dest_path = GetStringView(vdestpath);
1550
+ const bool sync_hard = RTEST(vsynchard);
1551
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1552
+ NativeFunction(sdbm->concurrent, [&]() {
1553
+ status = sdbm->dbm->CopyFileData(std::string(dest_path), sync_hard);
1554
+ });
1555
+ return MakeStatusValue(std::move(status));
1556
+ }
1557
+
1558
+ // Implementation of DBM#export.
1559
+ static VALUE dbm_export(VALUE vself, VALUE vdestdbm) {
1560
+ StructDBM* sdbm = nullptr;
1561
+ Data_Get_Struct(vself, StructDBM, sdbm);
1562
+ if (sdbm->dbm == nullptr) {
1563
+ rb_raise(rb_eRuntimeError, "not opened database");
1564
+ }
1565
+ if (!rb_obj_is_instance_of(vdestdbm, cls_dbm)) {
1566
+ return MakeStatusValue(tkrzw::Status(tkrzw::Status::INVALID_ARGUMENT_ERROR));
1567
+ }
1568
+ StructDBM* sdest_dbm = nullptr;
1569
+ Data_Get_Struct(vdestdbm, StructDBM, sdest_dbm);
1570
+ if (sdest_dbm->dbm == nullptr) {
1571
+ rb_raise(rb_eRuntimeError, "not opened database");
1572
+ }
1573
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1574
+ NativeFunction(sdbm->concurrent, [&]() {
1575
+ status = sdbm->dbm->Export(sdest_dbm->dbm.get());
1576
+ });
1577
+ return MakeStatusValue(std::move(status));
1578
+ }
1579
+
1580
+ // Implementation of DBM#export_to_flat_records.
1581
+ static VALUE dbm_export_to_flat_records(VALUE vself, VALUE vdest_file) {
1582
+ StructDBM* sdbm = nullptr;
1583
+ Data_Get_Struct(vself, StructDBM, sdbm);
1584
+ if (sdbm->dbm == nullptr) {
1585
+ rb_raise(rb_eRuntimeError, "not opened database");
1586
+ }
1587
+ StructFile* sdest_file = nullptr;
1588
+ Data_Get_Struct(vdest_file, StructFile, sdest_file);
1589
+ if (sdest_file->file == nullptr) {
1590
+ rb_raise(rb_eRuntimeError, "not opened file");
1591
+ }
1592
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1593
+ NativeFunction(sdbm->concurrent, [&]() {
1594
+ status = tkrzw::ExportDBMToFlatRecords(sdbm->dbm.get(), sdest_file->file.get());
1595
+ });
1596
+ return MakeStatusValue(std::move(status));
1597
+ }
1598
+
1599
+ // Implementation of DBM#import_from_flat_records.
1600
+ static VALUE dbm_import_from_flat_records(VALUE vself, VALUE vsrc_file) {
1601
+ StructDBM* sdbm = nullptr;
1602
+ Data_Get_Struct(vself, StructDBM, sdbm);
1603
+ if (sdbm->dbm == nullptr) {
1604
+ rb_raise(rb_eRuntimeError, "not opened database");
1605
+ }
1606
+ StructFile* ssrc_file = nullptr;
1607
+ Data_Get_Struct(vsrc_file, StructFile, ssrc_file);
1608
+ if (ssrc_file->file == nullptr) {
1609
+ rb_raise(rb_eRuntimeError, "not opened file");
1610
+ }
1611
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1612
+ NativeFunction(sdbm->concurrent, [&]() {
1613
+ status = tkrzw::ImportDBMFromFlatRecords(sdbm->dbm.get(), ssrc_file->file.get());
1614
+ });
1615
+ return MakeStatusValue(std::move(status));
1616
+ }
1617
+
1618
+ // Implementation of DBM#export_keys_as_lines.
1619
+ static VALUE dbm_export_keys_as_lines(VALUE vself, VALUE vdest_file) {
1620
+ StructDBM* sdbm = nullptr;
1621
+ Data_Get_Struct(vself, StructDBM, sdbm);
1622
+ if (sdbm->dbm == nullptr) {
1623
+ rb_raise(rb_eRuntimeError, "not opened database");
1624
+ }
1625
+ StructFile* sdest_file = nullptr;
1626
+ Data_Get_Struct(vdest_file, StructFile, sdest_file);
1627
+ if (sdest_file->file == nullptr) {
1628
+ rb_raise(rb_eRuntimeError, "not opened file");
1629
+ }
1630
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1631
+ NativeFunction(sdbm->concurrent, [&]() {
1632
+ status = tkrzw::ExportDBMKeysAsLines(sdbm->dbm.get(), sdest_file->file.get());
1633
+ });
1634
+ return MakeStatusValue(std::move(status));
1635
+ }
1636
+
1637
+ // Implementation of DBM#inspect_details.
1638
+ static VALUE dbm_inspect_details(VALUE vself) {
1639
+ StructDBM* sdbm = nullptr;
1640
+ Data_Get_Struct(vself, StructDBM, sdbm);
1641
+ if (sdbm->dbm == nullptr) {
1642
+ rb_raise(rb_eRuntimeError, "not opened database");
1643
+ }
1644
+ std::vector<std::pair<std::string, std::string>> records;
1645
+ NativeFunction(sdbm->concurrent, [&]() {
1646
+ records = sdbm->dbm->Inspect();
1647
+ });
1648
+ volatile VALUE vhash = rb_hash_new();
1649
+ for (const auto& record : records) {
1650
+ volatile VALUE vkey = rb_str_new(record.first.data(), record.first.size());
1651
+ volatile VALUE vvalue = rb_str_new(record.second.data(), record.second.size());
1652
+ rb_hash_aset(vhash, vkey, vvalue);
1653
+ }
1654
+ return vhash;
1655
+ }
1656
+
1657
+ // Implementation of DBM#open?.
1658
+ static VALUE dbm_is_open(VALUE vself) {
1659
+ StructDBM* sdbm = nullptr;
1660
+ Data_Get_Struct(vself, StructDBM, sdbm);
1661
+ return sdbm->dbm == nullptr ? Qfalse : Qtrue;
1662
+ }
1663
+
1664
+ // Implementation of DBM#writable?.
1665
+ static VALUE dbm_is_writable(VALUE vself) {
1666
+ StructDBM* sdbm = nullptr;
1667
+ Data_Get_Struct(vself, StructDBM, sdbm);
1668
+ if (sdbm->dbm == nullptr) {
1669
+ rb_raise(rb_eRuntimeError, "not opened database");
1670
+ }
1671
+ const bool writable = sdbm->dbm->IsWritable();;
1672
+ return writable ? Qtrue : Qfalse;
1673
+ }
1674
+
1675
+ // Implementation of DBM#healthy?.
1676
+ static VALUE dbm_is_healthy(VALUE vself) {
1677
+ StructDBM* sdbm = nullptr;
1678
+ Data_Get_Struct(vself, StructDBM, sdbm);
1679
+ if (sdbm->dbm == nullptr) {
1680
+ rb_raise(rb_eRuntimeError, "not opened database");
1681
+ }
1682
+ const bool healthy = sdbm->dbm->IsHealthy();
1683
+ return healthy ? Qtrue : Qfalse;
1684
+ }
1685
+
1686
+ // Implementation of DBM#ordered?.
1687
+ static VALUE dbm_is_ordered(VALUE vself) {
1688
+ StructDBM* sdbm = nullptr;
1689
+ Data_Get_Struct(vself, StructDBM, sdbm);
1690
+ if (sdbm->dbm == nullptr) {
1691
+ rb_raise(rb_eRuntimeError, "not opened database");
1692
+ }
1693
+ const bool ordered = sdbm->dbm->IsOrdered();
1694
+ return ordered ? Qtrue : Qfalse;
1695
+ }
1696
+
1697
+ // Implementation of DBM#search.
1698
+ static VALUE dbm_search(int argc, VALUE* argv, VALUE vself) {
1699
+ StructDBM* sdbm = nullptr;
1700
+ Data_Get_Struct(vself, StructDBM, sdbm);
1701
+ if (sdbm->dbm == nullptr) {
1702
+ rb_raise(rb_eRuntimeError, "not opened database");
1703
+ }
1704
+ volatile VALUE vmode, vpattern, vcapacity;
1705
+ rb_scan_args(argc, argv, "21", &vmode, &vpattern, &vcapacity);
1706
+ vmode = StringValueEx(vmode);
1707
+ const std::string_view mode = GetStringView(vmode);
1708
+ vpattern = StringValueEx(vpattern);
1709
+ const std::string_view pattern = GetStringView(vpattern);
1710
+ const int64_t capacity = GetInteger(vcapacity);
1711
+ std::vector<std::string> keys;
1712
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1713
+ NativeFunction(sdbm->concurrent, [&]() {
1714
+ status = tkrzw::SearchDBMModal(sdbm->dbm.get(), mode, pattern, &keys, capacity);
1715
+ });
1716
+ if (status != tkrzw::Status::SUCCESS) {
1717
+ const std::string& message = tkrzw::ToString(status);
1718
+ rb_raise(cls_expt, "%s", message.c_str());
1719
+ }
1720
+ volatile VALUE vkeys = rb_ary_new2(keys.size());
1721
+ for (const auto& key : keys) {
1722
+ rb_ary_push(vkeys, MakeString(key, sdbm->venc));
1723
+ }
1724
+ return vkeys;
1725
+ }
1726
+
1727
+ // Implementation of DBM#make_iterator.
1728
+ static VALUE dbm_make_iterator(VALUE vself) {
1729
+ return rb_class_new_instance(1, &vself, cls_iter);
1730
+ }
1731
+
1732
+ // Implementation of DBM.restore_database.
1733
+ static VALUE dbm_restore_database(int argc, VALUE* argv, VALUE vself) {
1734
+ volatile VALUE vold_file_path, vnew_file_path, vclass_name, vend_offset;
1735
+ rb_scan_args(argc, argv, "22", &vold_file_path, &vnew_file_path, &vclass_name, &vend_offset);
1736
+ const std::string_view old_file_path = GetStringView(StringValueEx(vold_file_path));
1737
+ const std::string_view new_file_path = GetStringView(StringValueEx(vnew_file_path));
1738
+ const std::string_view class_name = GetStringView(StringValueEx(vclass_name));
1739
+ const int64_t end_offset = vend_offset == Qnil ? -1 : GetInteger(vend_offset);
1740
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1741
+ int32_t num_shards = 0;
1742
+ if (tkrzw::ShardDBM::GetNumberOfShards(std::string(old_file_path), &num_shards) ==
1743
+ tkrzw::Status::SUCCESS) {
1744
+ NativeFunction(true, [&]() {
1745
+ status = tkrzw::ShardDBM::RestoreDatabase(
1746
+ std::string(old_file_path), std::string(new_file_path),
1747
+ std::string(class_name), end_offset);
1748
+ });
1749
+ } else {
1750
+ NativeFunction(true, [&]() {
1751
+ status = tkrzw::PolyDBM::RestoreDatabase(
1752
+ std::string(old_file_path), std::string(new_file_path),
1753
+ std::string(class_name), end_offset);
1754
+ });
1755
+ }
1756
+ return MakeStatusValue(std::move(status));
1757
+ }
1758
+
1759
+ // Implementation of DBM#to_s.
1760
+ static VALUE dbm_to_s(VALUE vself) {
1761
+ StructDBM* sdbm = nullptr;
1762
+ Data_Get_Struct(vself, StructDBM, sdbm);
1763
+ if (sdbm->dbm == nullptr) {
1764
+ rb_raise(rb_eRuntimeError, "not opened database");
1765
+ }
1766
+ std::string class_name = "unknown";
1767
+ std::string path = "-";
1768
+ int64_t count = -1;
1769
+ NativeFunction(sdbm->concurrent, [&]() {
1770
+ for (const auto& rec : sdbm->dbm->Inspect()) {
1771
+ if (rec.first == "class") {
1772
+ class_name = rec.second;
1773
+ } else if (rec.first == "path") {
1774
+ path = rec.second;
1775
+ }
1776
+ }
1777
+ count = sdbm->dbm->CountSimple();
1778
+ });
1779
+ const std::string expr =
1780
+ tkrzw::StrCat(class_name, ":", tkrzw::StrEscapeC(path, true), ":", count);
1781
+ return rb_str_new(expr.data(), expr.size());
1782
+ }
1783
+
1784
+ // Implementation of DBM#to_i.
1785
+ static VALUE dbm_to_i(VALUE vself) {
1786
+ StructDBM* sdbm = nullptr;
1787
+ Data_Get_Struct(vself, StructDBM, sdbm);
1788
+ if (sdbm->dbm == nullptr) {
1789
+ rb_raise(rb_eRuntimeError, "not opened database");
1790
+ }
1791
+ int64_t count = -1;
1792
+ NativeFunction(sdbm->concurrent, [&]() {
1793
+ count = sdbm->dbm->CountSimple();
1794
+ });
1795
+ return LL2NUM(count);
1796
+ }
1797
+
1798
+ // Implementation of DBM#inspect.
1799
+ static VALUE dbm_inspect(VALUE vself) {
1800
+ StructDBM* sdbm = nullptr;
1801
+ Data_Get_Struct(vself, StructDBM, sdbm);
1802
+ if (sdbm->dbm == nullptr) {
1803
+ rb_raise(rb_eRuntimeError, "not opened database");
1804
+ }
1805
+ std::string class_name = "unknown";
1806
+ std::string path = "-";
1807
+ int64_t count = -1;
1808
+ NativeFunction(sdbm->concurrent, [&]() {
1809
+ for (const auto& rec : sdbm->dbm->Inspect()) {
1810
+ if (rec.first == "class") {
1811
+ class_name = rec.second;
1812
+ } else if (rec.first == "path") {
1813
+ path = rec.second;
1814
+ }
1815
+ }
1816
+ count = sdbm->dbm->CountSimple();
1817
+ });
1818
+ const std::string expr = tkrzw::StrCat(
1819
+ "#<Tkrzw::DBM:", class_name, ":", tkrzw::StrEscapeC(path, true), ":", count, ">");
1820
+ return rb_str_new(expr.data(), expr.size());
1821
+ }
1822
+
1823
+ // Implementation of DBM#[].
1824
+ static VALUE dbm_ss_get(VALUE vself, VALUE vkey) {
1825
+ StructDBM* sdbm = nullptr;
1826
+ Data_Get_Struct(vself, StructDBM, sdbm);
1827
+ if (sdbm->dbm == nullptr) {
1828
+ rb_raise(rb_eRuntimeError, "not opened database");
1829
+ }
1830
+ vkey = StringValueEx(vkey);
1831
+ const std::string_view key = GetStringView(vkey);
1832
+ std::string value;
1833
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1834
+ NativeFunction(sdbm->concurrent, [&]() {
1835
+ status = sdbm->dbm->Get(key, &value);
1836
+ });
1837
+ if (status == tkrzw::Status::SUCCESS) {
1838
+ return MakeString(value, sdbm->venc);
1839
+ }
1840
+ return Qnil;
1841
+ }
1842
+
1843
+ // Implementation of DBM#[]=.
1844
+ static VALUE dbm_ss_set(VALUE vself, VALUE vkey, VALUE vvalue) {
1845
+ StructDBM* sdbm = nullptr;
1846
+ Data_Get_Struct(vself, StructDBM, sdbm);
1847
+ if (sdbm->dbm == nullptr) {
1848
+ rb_raise(rb_eRuntimeError, "not opened database");
1849
+ }
1850
+ vkey = StringValueEx(vkey);
1851
+ const std::string_view key = GetStringView(vkey);
1852
+ vvalue = StringValueEx(vvalue);
1853
+ const std::string_view value = GetStringView(vvalue);
1854
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1855
+ NativeFunction(sdbm->concurrent, [&]() {
1856
+ status = sdbm->dbm->Set(key, value);
1857
+ });
1858
+ return vvalue;
1859
+ }
1860
+
1861
+ // Implementation of DBM#delete.
1862
+ static VALUE dbm_delete(VALUE vself, VALUE vkey) {
1863
+ StructDBM* sdbm = nullptr;
1864
+ Data_Get_Struct(vself, StructDBM, sdbm);
1865
+ if (sdbm->dbm == nullptr) {
1866
+ rb_raise(rb_eRuntimeError, "not opened database");
1867
+ }
1868
+ vkey = StringValueEx(vkey);
1869
+ const std::string_view key = GetStringView(vkey);
1870
+ tkrzw::Status impl_status(tkrzw::Status::SUCCESS);
1871
+ std::string old_value;
1872
+ class Processor final : public tkrzw::DBM::RecordProcessor {
1873
+ public:
1874
+ Processor(tkrzw::Status* status, std::string* old_value)
1875
+ : status_(status), old_value_(old_value) {}
1876
+ std::string_view ProcessFull(std::string_view key, std::string_view value) override {
1877
+ *old_value_ = value;
1878
+ return REMOVE;
1879
+ }
1880
+ std::string_view ProcessEmpty(std::string_view key) override {
1881
+ status_->Set(tkrzw::Status::NOT_FOUND_ERROR);
1882
+ return NOOP;
1883
+ }
1884
+ private:
1885
+ tkrzw::Status* status_;
1886
+ std::string* old_value_;
1887
+ };
1888
+ Processor proc(&impl_status, &old_value);
1889
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1890
+ NativeFunction(sdbm->concurrent, [&]() {
1891
+ status = sdbm->dbm->Process(key, &proc, true);
1892
+ });
1893
+ status |= impl_status;
1894
+ if (status != tkrzw::Status::SUCCESS) {
1895
+ return Qnil;
1896
+ }
1897
+ return MakeString(old_value, sdbm->venc);
1898
+ }
1899
+
1900
+ // Implementation of DBM#each.
1901
+ static VALUE dbm_each(VALUE vself) {
1902
+ StructDBM* sdbm = nullptr;
1903
+ Data_Get_Struct(vself, StructDBM, sdbm);
1904
+ if (sdbm->dbm == nullptr) {
1905
+ rb_raise(rb_eRuntimeError, "not opened database");
1906
+ }
1907
+ if (!rb_block_given_p()) {
1908
+ rb_raise(rb_eArgError, "block is not given");
1909
+ }
1910
+ std::unique_ptr<tkrzw::DBM::Iterator> iter;
1911
+ NativeFunction(sdbm->concurrent, [&]() {
1912
+ iter = sdbm->dbm->MakeIterator();
1913
+ iter->First();
1914
+ });
1915
+ while (true) {
1916
+ std::string key, value;
1917
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
1918
+ NativeFunction(sdbm->concurrent, [&]() {
1919
+ status = iter->Get(&key, &value);
1920
+ });
1921
+ if (status != tkrzw::Status::SUCCESS) {
1922
+ break;
1923
+ }
1924
+ volatile VALUE args =
1925
+ rb_ary_new3(2, MakeString(key, sdbm->venc), MakeString(value, sdbm->venc));
1926
+ int result = 0;
1927
+ rb_protect(YieldToBlock, args, &result);
1928
+ if (result != 0) {
1929
+ rb_jump_tag(result);
1930
+ break;
1931
+ }
1932
+ NativeFunction(sdbm->concurrent, [&]() {
1933
+ iter->Next();
1934
+ });
1935
+ }
1936
+ return Qnil;
1937
+ }
1938
+
1939
+ // Defines the DBM class.
1940
+ static void DefineDBM() {
1941
+ cls_dbm = rb_define_class_under(mod_tkrzw, "DBM", rb_cObject);
1942
+ rb_define_alloc_func(cls_dbm, dbm_new);
1943
+ obj_dbm_any_data = INT2FIX((std::numeric_limits<int>::max() >> 1) - 1);
1944
+ rb_define_const(cls_dbm, "ANY_DATA", obj_dbm_any_data);
1945
+ rb_define_private_method(cls_dbm, "initialize", (METHOD)dbm_initialize, 0);
1946
+ rb_define_method(cls_dbm, "destruct", (METHOD)dbm_destruct, 0);
1947
+ rb_define_method(cls_dbm, "open", (METHOD)dbm_open, -1);
1948
+ rb_define_method(cls_dbm, "close", (METHOD)dbm_close, 0);
1949
+ rb_define_method(cls_dbm, "include?", (METHOD)dbm_include, 1);
1950
+ rb_define_method(cls_dbm, "get", (METHOD)dbm_get, -1);
1951
+ rb_define_method(cls_dbm, "get_multi", (METHOD)dbm_get_multi, -2);
1952
+ rb_define_method(cls_dbm, "set", (METHOD)dbm_set, -1);
1953
+ rb_define_method(cls_dbm, "set_multi", (METHOD)dbm_set_multi, -1);
1954
+ rb_define_method(cls_dbm, "set_and_get", (METHOD)dbm_set_and_get, -1);
1955
+ rb_define_method(cls_dbm, "remove", (METHOD)dbm_remove, 1);
1956
+ rb_define_method(cls_dbm, "remove_multi", (METHOD)dbm_remove_multi, -2);
1957
+ rb_define_method(cls_dbm, "remove_and_get", (METHOD)dbm_remove_and_get, 1);
1958
+ rb_define_method(cls_dbm, "append", (METHOD)dbm_append, -1);
1959
+ rb_define_method(cls_dbm, "append_multi", (METHOD)dbm_append_multi, -1);
1960
+ rb_define_method(cls_dbm, "compare_exchange", (METHOD)dbm_compare_exchange, 3);
1961
+ rb_define_method(cls_dbm, "compare_exchange_and_get", (METHOD)dbm_compare_exchange_and_get, 3);
1962
+ rb_define_method(cls_dbm, "increment", (METHOD)dbm_increment, -1);
1963
+ rb_define_method(cls_dbm, "compare_exchange_multi", (METHOD)dbm_compare_exchange_multi, 2);
1964
+ rb_define_method(cls_dbm, "rekey", (METHOD)dbm_rekey, -1);
1965
+ rb_define_method(cls_dbm, "pop_first", (METHOD)dbm_pop_first, -1);
1966
+ rb_define_method(cls_dbm, "push_last", (METHOD)dbm_push_last, -1);
1967
+ rb_define_method(cls_dbm, "count", (METHOD)dbm_count, 0);
1968
+ rb_define_method(cls_dbm, "file_size", (METHOD)dbm_file_size, 0);
1969
+ rb_define_method(cls_dbm, "file_path", (METHOD)dbm_file_path, 0);
1970
+ rb_define_method(cls_dbm, "timestamp", (METHOD)dbm_timestamp, 0);
1971
+ rb_define_method(cls_dbm, "clear", (METHOD)dbm_clear, 0);
1972
+ rb_define_method(cls_dbm, "rebuild", (METHOD)dbm_rebuild, -1);
1973
+ rb_define_method(cls_dbm, "should_be_rebuilt?", (METHOD)dbm_should_be_rebuilt, 0);
1974
+ rb_define_method(cls_dbm, "synchronize", (METHOD)dbm_synchronize, -1);
1975
+ rb_define_method(cls_dbm, "copy_file_data", (METHOD)dbm_copy_file_data, -1);
1976
+ rb_define_method(cls_dbm, "export", (METHOD)dbm_export, 1);
1977
+ rb_define_method(cls_dbm, "export_to_flat_records", (METHOD)dbm_export_to_flat_records, 1);
1978
+ rb_define_method(cls_dbm, "import_from_flat_records", (METHOD)dbm_import_from_flat_records, 1);
1979
+ rb_define_method(cls_dbm, "export_keys_as_lines", (METHOD)dbm_export_keys_as_lines, 1);
1980
+ rb_define_method(cls_dbm, "inspect_details", (METHOD)dbm_inspect_details, 0);
1981
+ rb_define_method(cls_dbm, "open?", (METHOD)dbm_is_open, 0);
1982
+ rb_define_method(cls_dbm, "writable?", (METHOD)dbm_is_writable, 0);
1983
+ rb_define_method(cls_dbm, "healthy?", (METHOD)dbm_is_healthy, 0);
1984
+ rb_define_method(cls_dbm, "ordered?", (METHOD)dbm_is_ordered, 0);
1985
+ rb_define_method(cls_dbm, "search", (METHOD)dbm_search, -1);
1986
+ rb_define_method(cls_dbm, "make_iterator", (METHOD)dbm_make_iterator, 0);
1987
+ rb_define_singleton_method(cls_dbm, "restore_database", (METHOD)dbm_restore_database, -1);
1988
+ rb_define_method(cls_dbm, "to_s", (METHOD)dbm_to_s, 0);
1989
+ rb_define_method(cls_dbm, "to_i", (METHOD)dbm_to_i, 0);
1990
+ rb_define_method(cls_dbm, "inspect", (METHOD)dbm_inspect, 0);
1991
+ rb_define_method(cls_dbm, "[]", (METHOD)dbm_ss_get, 1);
1992
+ rb_define_method(cls_dbm, "[]=", (METHOD)dbm_ss_set, 2);
1993
+ rb_define_method(cls_dbm, "delete", (METHOD)dbm_delete, 1);
1994
+ rb_define_method(cls_dbm, "each", (METHOD)dbm_each, 0);
1995
+ }
1996
+
1997
+ // Implementation of Iterator#del.
1998
+ static void iter_del(void* ptr) {
1999
+ StructIter* siter = (StructIter*)ptr;
2000
+ siter->iter.reset(nullptr);
2001
+ delete siter;
2002
+ }
2003
+
2004
+ // Implementation of Iterator.new.
2005
+ static VALUE iter_new(VALUE cls) {
2006
+ StructIter* siter = new StructIter;
2007
+ return Data_Wrap_Struct(cls_iter, 0, iter_del, siter);
2008
+ }
2009
+
2010
+ // Implementation of Iterator#initialize.
2011
+ static VALUE iter_initialize(VALUE vself, VALUE vdbm) {
2012
+ if (!rb_obj_is_instance_of(vdbm, cls_dbm)) {
2013
+ rb_raise(rb_eArgError, "#<Tkrzw::StatusException>");
2014
+ }
2015
+ StructDBM* sdbm = nullptr;
2016
+ Data_Get_Struct(vdbm, StructDBM, sdbm);
2017
+ if (sdbm->dbm == nullptr) {
2018
+ rb_raise(rb_eRuntimeError, "not opened database");
2019
+ }
2020
+ StructIter* siter = nullptr;
2021
+ Data_Get_Struct(vself, StructIter, siter);
2022
+ siter->iter = sdbm->dbm->MakeIterator();
2023
+ siter->concurrent = sdbm->concurrent;
2024
+ siter->venc = sdbm->venc;
2025
+ return Qnil;
2026
+ }
2027
+
2028
+ // Implementation of Iterator#destruct.
2029
+ static VALUE iter_destruct(VALUE vself) {
2030
+ StructIter* siter = nullptr;
2031
+ Data_Get_Struct(vself, StructIter, siter);
2032
+ siter->iter.reset(nullptr);
2033
+ return Qnil;
2034
+ }
2035
+
2036
+ // Implementation of Iterator#first.
2037
+ static VALUE iter_first(VALUE vself) {
2038
+ StructIter* siter = nullptr;
2039
+ Data_Get_Struct(vself, StructIter, siter);
2040
+ if (siter->iter == nullptr) {
2041
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2042
+ }
2043
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2044
+ NativeFunction(siter->concurrent, [&]() {
2045
+ status = siter->iter->First();
2046
+ });
2047
+ return MakeStatusValue(std::move(status));
2048
+ }
2049
+
2050
+ // Implementation of Iterator#last.
2051
+ static VALUE iter_last(VALUE vself) {
2052
+ StructIter* siter = nullptr;
2053
+ Data_Get_Struct(vself, StructIter, siter);
2054
+ if (siter->iter == nullptr) {
2055
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2056
+ }
2057
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2058
+ NativeFunction(siter->concurrent, [&]() {
2059
+ status = siter->iter->Last();
2060
+ });
2061
+ return MakeStatusValue(std::move(status));
2062
+ }
2063
+
2064
+ // Implementation of Iterator#jump.
2065
+ static VALUE iter_jump(VALUE vself, VALUE vkey) {
2066
+ StructIter* siter = nullptr;
2067
+ Data_Get_Struct(vself, StructIter, siter);
2068
+ if (siter->iter == nullptr) {
2069
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2070
+ }
2071
+ vkey = StringValueEx(vkey);
2072
+ const std::string_view key = GetStringView(vkey);
2073
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2074
+ NativeFunction(siter->concurrent, [&]() {
2075
+ status = siter->iter->Jump(key);
2076
+ });
2077
+ return MakeStatusValue(std::move(status));
2078
+ }
2079
+
2080
+ // Implementation of Iterator#jump_lower.
2081
+ static VALUE iter_jump_lower(int argc, VALUE* argv, VALUE vself) {
2082
+ StructIter* siter = nullptr;
2083
+ Data_Get_Struct(vself, StructIter, siter);
2084
+ if (siter->iter == nullptr) {
2085
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2086
+ }
2087
+ volatile VALUE vkey, vinclusive;
2088
+ rb_scan_args(argc, argv, "11", &vkey, &vinclusive);
2089
+ vkey = StringValueEx(vkey);
2090
+ const std::string_view key = GetStringView(vkey);
2091
+ const bool inclusive = argc > 1 ? RTEST(vinclusive) :false;
2092
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2093
+ NativeFunction(siter->concurrent, [&]() {
2094
+ status = siter->iter->JumpLower(key, inclusive);
2095
+ });
2096
+ return MakeStatusValue(std::move(status));
2097
+ }
2098
+
2099
+ // Implementation of Iterator#jump_upper.
2100
+ static VALUE iter_jump_upper(int argc, VALUE* argv, VALUE vself) {
2101
+ StructIter* siter = nullptr;
2102
+ Data_Get_Struct(vself, StructIter, siter);
2103
+ if (siter->iter == nullptr) {
2104
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2105
+ }
2106
+ volatile VALUE vkey, vinclusive;
2107
+ rb_scan_args(argc, argv, "11", &vkey, &vinclusive);
2108
+ vkey = StringValueEx(vkey);
2109
+ const std::string_view key = GetStringView(vkey);
2110
+ const bool inclusive = argc > 1 ? RTEST(vinclusive) :false;
2111
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2112
+ NativeFunction(siter->concurrent, [&]() {
2113
+ status = siter->iter->JumpUpper(key, inclusive);
2114
+ });
2115
+ return MakeStatusValue(std::move(status));
2116
+ }
2117
+
2118
+ // Implementation of Iterator#next.
2119
+ static VALUE iter_next(VALUE vself) {
2120
+ StructIter* siter = nullptr;
2121
+ Data_Get_Struct(vself, StructIter, siter);
2122
+ if (siter->iter == nullptr) {
2123
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2124
+ }
2125
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2126
+ NativeFunction(siter->concurrent, [&]() {
2127
+ status = siter->iter->Next();
2128
+ });
2129
+ return MakeStatusValue(std::move(status));
2130
+ }
2131
+
2132
+ // Implementation of Iterator#previous.
2133
+ static VALUE iter_previous(VALUE vself) {
2134
+ StructIter* siter = nullptr;
2135
+ Data_Get_Struct(vself, StructIter, siter);
2136
+ if (siter->iter == nullptr) {
2137
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2138
+ }
2139
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2140
+ NativeFunction(siter->concurrent, [&]() {
2141
+ status = siter->iter->Previous();
2142
+ });
2143
+ return MakeStatusValue(std::move(status));
2144
+ }
2145
+
2146
+ // Implementation of Iterator#get.
2147
+ static VALUE iter_get(int argc, VALUE* argv, VALUE vself) {
2148
+ StructIter* siter = nullptr;
2149
+ Data_Get_Struct(vself, StructIter, siter);
2150
+ if (siter->iter == nullptr) {
2151
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2152
+ }
2153
+ volatile VALUE vstatus;
2154
+ rb_scan_args(argc, argv, "01", &vstatus);
2155
+ std::string key, value;
2156
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2157
+ NativeFunction(siter->concurrent, [&]() {
2158
+ status = siter->iter->Get(&key, &value);
2159
+ });
2160
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
2161
+ SetStatusValue(vstatus, status);
2162
+ }
2163
+ if (status == tkrzw::Status::SUCCESS) {
2164
+ volatile VALUE vary = rb_ary_new2(2);
2165
+ rb_ary_push(vary, MakeString(key, siter->venc));
2166
+ rb_ary_push(vary, MakeString(value, siter->venc));
2167
+ return vary;
2168
+ }
2169
+ return Qnil;
2170
+ }
2171
+
2172
+ // Implementation of Iterator#get_key.
2173
+ static VALUE iter_get_key(int argc, VALUE* argv, VALUE vself) {
2174
+ StructIter* siter = nullptr;
2175
+ Data_Get_Struct(vself, StructIter, siter);
2176
+ if (siter->iter == nullptr) {
2177
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2178
+ }
2179
+ volatile VALUE vstatus;
2180
+ rb_scan_args(argc, argv, "01", &vstatus);
2181
+ std::string key;
2182
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2183
+ NativeFunction(siter->concurrent, [&]() {
2184
+ status = siter->iter->Get(&key);
2185
+ });
2186
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
2187
+ SetStatusValue(vstatus, status);
2188
+ }
2189
+ if (status == tkrzw::Status::SUCCESS) {
2190
+ return MakeString(key, siter->venc);
2191
+ }
2192
+ return Qnil;
2193
+ }
2194
+
2195
+ // Implementation of Iterator#get_value.
2196
+ static VALUE iter_get_value(int argc, VALUE* argv, VALUE vself) {
2197
+ StructIter* siter = nullptr;
2198
+ Data_Get_Struct(vself, StructIter, siter);
2199
+ if (siter->iter == nullptr) {
2200
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2201
+ }
2202
+ volatile VALUE vstatus;
2203
+ rb_scan_args(argc, argv, "01", &vstatus);
2204
+ std::string value;
2205
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2206
+ NativeFunction(siter->concurrent, [&]() {
2207
+ status = siter->iter->Get(nullptr, &value);
2208
+ });
2209
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
2210
+ SetStatusValue(vstatus, status);
2211
+ }
2212
+ if (status == tkrzw::Status::SUCCESS) {
2213
+ return MakeString(value, siter->venc);
2214
+ }
2215
+ return Qnil;
2216
+ }
2217
+
2218
+ // Implementation of Iterator#set.
2219
+ static VALUE iter_set(VALUE vself, VALUE vvalue) {
2220
+ StructIter* siter = nullptr;
2221
+ Data_Get_Struct(vself, StructIter, siter);
2222
+ if (siter->iter == nullptr) {
2223
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2224
+ }
2225
+ vvalue = StringValueEx(vvalue);
2226
+ const std::string_view value = GetStringView(vvalue);
2227
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2228
+ NativeFunction(siter->concurrent, [&]() {
2229
+ status = siter->iter->Set(value);
2230
+ });
2231
+ return MakeStatusValue(std::move(status));
2232
+ }
2233
+
2234
+ // Implementation of Iterator#remove.
2235
+ static VALUE iter_remove(VALUE vself) {
2236
+ StructIter* siter = nullptr;
2237
+ Data_Get_Struct(vself, StructIter, siter);
2238
+ if (siter->iter == nullptr) {
2239
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2240
+ }
2241
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2242
+ NativeFunction(siter->concurrent, [&]() {
2243
+ status = siter->iter->Remove();
2244
+ });
2245
+ return MakeStatusValue(std::move(status));
2246
+ }
2247
+
2248
+ // Implementation of Iterator#step.
2249
+ static VALUE iter_step(int argc, VALUE* argv, VALUE vself) {
2250
+ StructIter* siter = nullptr;
2251
+ Data_Get_Struct(vself, StructIter, siter);
2252
+ if (siter->iter == nullptr) {
2253
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2254
+ }
2255
+ volatile VALUE vstatus;
2256
+ rb_scan_args(argc, argv, "01", &vstatus);
2257
+ std::string key, value;
2258
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2259
+ NativeFunction(siter->concurrent, [&]() {
2260
+ status = siter->iter->Step(&key, &value);
2261
+ });
2262
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
2263
+ SetStatusValue(vstatus, status);
2264
+ }
2265
+ if (status == tkrzw::Status::SUCCESS) {
2266
+ volatile VALUE vary = rb_ary_new2(2);
2267
+ rb_ary_push(vary, MakeString(key, siter->venc));
2268
+ rb_ary_push(vary, MakeString(value, siter->venc));
2269
+ return vary;
2270
+ }
2271
+ return Qnil;
2272
+ }
2273
+
2274
+ // Implementation of Iterator#to_s.
2275
+ static VALUE iter_to_s(VALUE vself) {
2276
+ StructIter* siter = nullptr;
2277
+ Data_Get_Struct(vself, StructIter, siter);
2278
+ if (siter->iter == nullptr) {
2279
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2280
+ }
2281
+ std::string key;
2282
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2283
+ NativeFunction(siter->concurrent, [&]() {
2284
+ status = siter->iter->Get(&key);
2285
+ });
2286
+ if (status != tkrzw::Status::SUCCESS) {
2287
+ key = "(unlocated)";
2288
+ }
2289
+ const std::string& expr = tkrzw::StrEscapeC(key, true);
2290
+ return rb_str_new(expr.data(), expr.size());
2291
+ }
2292
+
2293
+ // Implementation of Iterator#inspect.
2294
+ static VALUE iter_inspect(VALUE vself) {
2295
+ StructIter* siter = nullptr;
2296
+ Data_Get_Struct(vself, StructIter, siter);
2297
+ if (siter->iter == nullptr) {
2298
+ rb_raise(rb_eRuntimeError, "destructed Iterator");
2299
+ }
2300
+ std::string key;
2301
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2302
+ NativeFunction(siter->concurrent, [&]() {
2303
+ status = siter->iter->Get(&key);
2304
+ });
2305
+ if (status != tkrzw::Status::SUCCESS) {
2306
+ key = "(unlocated)";
2307
+ }
2308
+ const std::string& expr =
2309
+ tkrzw::StrCat("#<Tkrzw::Iterator:", tkrzw::StrEscapeC(key, true), ">");
2310
+ return rb_str_new(expr.data(), expr.size());
2311
+ }
2312
+
2313
+ // Defines the Iterator class.
2314
+ static void DefineIterator() {
2315
+ cls_iter = rb_define_class_under(mod_tkrzw, "Iterator", rb_cObject);
2316
+ rb_define_alloc_func(cls_iter, iter_new);
2317
+ rb_define_private_method(cls_iter, "initialize", (METHOD)iter_initialize, 1);
2318
+ rb_define_method(cls_iter, "destruct", (METHOD)iter_destruct, 0);
2319
+ rb_define_method(cls_iter, "first", (METHOD)iter_first, 0);
2320
+ rb_define_method(cls_iter, "last", (METHOD)iter_last, 0);
2321
+ rb_define_method(cls_iter, "jump", (METHOD)iter_jump, 1);
2322
+ rb_define_method(cls_iter, "jump_lower", (METHOD)iter_jump_lower, -1);
2323
+ rb_define_method(cls_iter, "jump_upper", (METHOD)iter_jump_upper, -1);
2324
+ rb_define_method(cls_iter, "next", (METHOD)iter_next, 0);
2325
+ rb_define_method(cls_iter, "previous", (METHOD)iter_previous, 0);
2326
+ rb_define_method(cls_iter, "get", (METHOD)iter_get, -1);
2327
+ rb_define_method(cls_iter, "get_key", (METHOD)iter_get_key, -1);
2328
+ rb_define_method(cls_iter, "get_value", (METHOD)iter_get_value, -1);
2329
+ rb_define_method(cls_iter, "set", (METHOD)iter_set, 1);
2330
+ rb_define_method(cls_iter, "remove", (METHOD)iter_remove, 0);
2331
+ rb_define_method(cls_iter, "step", (METHOD)iter_step, -1);
2332
+ rb_define_method(cls_iter, "to_s", (METHOD)iter_to_s, 0);
2333
+ rb_define_method(cls_iter, "inspect", (METHOD)iter_inspect, 0);
2334
+ }
2335
+
2336
+ // Implementation of AsyncDBM#del.
2337
+ static void asyncdbm_del(void* ptr) {
2338
+ StructAsyncDBM* sasync = (StructAsyncDBM*)ptr;
2339
+ sasync->async.reset(nullptr);
2340
+ delete sasync;
2341
+ }
2342
+
2343
+ // Implementation of AsyncDBM.new.
2344
+ static VALUE asyncdbm_new(VALUE cls) {
2345
+ StructAsyncDBM* sasync = new StructAsyncDBM;
2346
+ return Data_Wrap_Struct(cls_asyncdbm, 0, asyncdbm_del, sasync);
2347
+ }
2348
+
2349
+ // Implementation of AsyncDBM#initialize.
2350
+ static VALUE asyncdbm_initialize(int argc, VALUE* argv, VALUE vself) {
2351
+ StructAsyncDBM* sasync = nullptr;
2352
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2353
+ volatile VALUE vdbm, vnum_threads;
2354
+ rb_scan_args(argc, argv, "20", &vdbm, &vnum_threads);
2355
+ StructDBM* sdbm = nullptr;
2356
+ Data_Get_Struct(vdbm, StructDBM, sdbm);
2357
+ if (sdbm->dbm == nullptr) {
2358
+ rb_raise(rb_eRuntimeError, "not opened database");
2359
+ }
2360
+ const int32_t num_threads = GetInteger(vnum_threads);
2361
+ sasync->async = std::make_unique<tkrzw::AsyncDBM>(sdbm->dbm.get(), num_threads);
2362
+ return Qnil;
2363
+ }
2364
+
2365
+ // Implementation of AsyncDBM#destruct.
2366
+ static VALUE asyncdbm_destruct(VALUE vself) {
2367
+ StructAsyncDBM* sasync = nullptr;
2368
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2369
+ sasync->async.reset(nullptr);
2370
+ return Qnil;
2371
+ }
2372
+
2373
+ // Implementation of AsyncDBM#to_s.
2374
+ static VALUE asyncdbm_to_s(VALUE vself) {
2375
+ StructAsyncDBM* sasync = nullptr;
2376
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2377
+ if (sasync->async == nullptr) {
2378
+ rb_raise(rb_eRuntimeError, "destructed object");
2379
+ }
2380
+ const std::string str = tkrzw::SPrintF("AsyncDBM:%p", (void*)sasync->async.get());
2381
+ return rb_str_new(str.data(), str.size());
2382
+ }
2383
+
2384
+ // Implementation of AsyncDBM#inspect.
2385
+ static VALUE asyncdbm_inspect(VALUE vself) {
2386
+ StructAsyncDBM* sasync = nullptr;
2387
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2388
+ if (sasync->async == nullptr) {
2389
+ rb_raise(rb_eRuntimeError, "destructed object");
2390
+ }
2391
+ const std::string str = tkrzw::SPrintF(
2392
+ "#<Tkrzw::AsyncDBM: %p>", (void*)sasync->async.get());
2393
+ return rb_str_new(str.data(), str.size());
2394
+ }
2395
+
2396
+ // Implementation of AsyncDBM#get.
2397
+ static VALUE asyncdbm_get(VALUE vself, VALUE vkey) {
2398
+ StructAsyncDBM* sasync = nullptr;
2399
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2400
+ if (sasync->async == nullptr) {
2401
+ rb_raise(rb_eRuntimeError, "destructed object");
2402
+ }
2403
+ vkey = StringValueEx(vkey);
2404
+ const std::string_view key = GetStringView(vkey);
2405
+ tkrzw::StatusFuture future(sasync->async->Get(key));
2406
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2407
+ }
2408
+
2409
+ // Implementation of AsyncDBM#get_multi.
2410
+ static VALUE asyncdbm_get_multi(VALUE vself, VALUE vkeys) {
2411
+ StructAsyncDBM* sasync = nullptr;
2412
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2413
+ if (sasync->async == nullptr) {
2414
+ rb_raise(rb_eRuntimeError, "destructed object");
2415
+ }
2416
+ if (TYPE(vkeys) != T_ARRAY) {
2417
+ rb_raise(rb_eRuntimeError, "keys is not an array");
2418
+ }
2419
+ std::vector<std::string> keys;
2420
+ const int32_t num_keys = RARRAY_LEN(vkeys);
2421
+ for (int32_t i = 0; i < num_keys; i++) {
2422
+ volatile VALUE vkey = rb_ary_entry(vkeys, i);
2423
+ vkey = StringValueEx(vkey);
2424
+ keys.emplace_back(std::string(RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
2425
+ }
2426
+ std::vector<std::string_view> key_views(keys.begin(), keys.end());
2427
+ tkrzw::StatusFuture future(sasync->async->GetMulti(key_views));
2428
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2429
+ }
2430
+
2431
+ // Implementation of AsyncDBM#set.
2432
+ static VALUE asyncdbm_set(int argc, VALUE* argv, VALUE vself) {
2433
+ StructAsyncDBM* sasync = nullptr;
2434
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2435
+ if (sasync->async == nullptr) {
2436
+ rb_raise(rb_eRuntimeError, "destructed object");
2437
+ }
2438
+ volatile VALUE vkey, vvalue, voverwrite;
2439
+ rb_scan_args(argc, argv, "21", &vkey, &vvalue, &voverwrite);
2440
+ vkey = StringValueEx(vkey);
2441
+ const std::string_view key = GetStringView(vkey);
2442
+ vvalue = StringValueEx(vvalue);
2443
+ const std::string_view value = GetStringView(vvalue);
2444
+ const bool overwrite = argc > 2 ? RTEST(voverwrite) : true;
2445
+ tkrzw::StatusFuture future(sasync->async->Set(key, value, overwrite));
2446
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2447
+ }
2448
+
2449
+ // Implementation of AsyncDBM#set_multi.
2450
+ static VALUE asyncdbm_set_multi(int argc, VALUE* argv, VALUE vself) {
2451
+ StructAsyncDBM* sasync = nullptr;
2452
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2453
+ if (sasync->async == nullptr) {
2454
+ rb_raise(rb_eRuntimeError, "destructed object");
2455
+ }
2456
+ volatile VALUE voverwrite, vrecords;
2457
+ rb_scan_args(argc, argv, "02", &voverwrite, &vrecords);
2458
+ bool overwrite = true;
2459
+ if (argc <= 1 && TYPE(voverwrite) == T_HASH) {
2460
+ vrecords = voverwrite;
2461
+ } else {
2462
+ overwrite = argc > 0 ? RTEST(voverwrite) : true;
2463
+ }
2464
+ const auto& records = HashToMap(vrecords);
2465
+ std::map<std::string_view, std::string_view> record_views;
2466
+ for (const auto& record : records) {
2467
+ record_views.emplace(std::make_pair(
2468
+ std::string_view(record.first), std::string_view(record.second)));
2469
+ }
2470
+ tkrzw::StatusFuture future(sasync->async->SetMulti(record_views, overwrite));
2471
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2472
+ }
2473
+
2474
+ // Implementation of AsyncDBM#remove.
2475
+ static VALUE asyncdbm_remove(VALUE vself, VALUE vkey) {
2476
+ StructAsyncDBM* sasync = nullptr;
2477
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2478
+ if (sasync->async == nullptr) {
2479
+ rb_raise(rb_eRuntimeError, "destructed object");
2480
+ }
2481
+ vkey = StringValueEx(vkey);
2482
+ const std::string_view key = GetStringView(vkey);
2483
+ tkrzw::StatusFuture future(sasync->async->Remove(key));
2484
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2485
+ }
2486
+
2487
+ // Implementation of AsyncDBM#remove_multi.
2488
+ static VALUE asyncdbm_remove_multi(VALUE vself, VALUE vkeys) {
2489
+ StructAsyncDBM* sasync = nullptr;
2490
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2491
+ if (sasync->async == nullptr) {
2492
+ rb_raise(rb_eRuntimeError, "destructed object");
2493
+ }
2494
+ if (TYPE(vkeys) != T_ARRAY) {
2495
+ rb_raise(rb_eRuntimeError, "keys is not an array");
2496
+ }
2497
+ std::vector<std::string> keys;
2498
+ const int32_t num_keys = RARRAY_LEN(vkeys);
2499
+ for (int32_t i = 0; i < num_keys; i++) {
2500
+ volatile VALUE vkey = rb_ary_entry(vkeys, i);
2501
+ vkey = StringValueEx(vkey);
2502
+ keys.emplace_back(std::string(RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
2503
+ }
2504
+ std::vector<std::string_view> key_views(keys.begin(), keys.end());
2505
+ tkrzw::StatusFuture future(sasync->async->RemoveMulti(key_views));
2506
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2507
+ }
2508
+
2509
+ // Implementation of AsyncDBM#append.
2510
+ static VALUE asyncdbm_append(int argc, VALUE* argv, VALUE vself) {
2511
+ StructAsyncDBM* sasync = nullptr;
2512
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2513
+ if (sasync->async == nullptr) {
2514
+ rb_raise(rb_eRuntimeError, "destructed object");
2515
+ }
2516
+ volatile VALUE vkey, vvalue, vdelim;
2517
+ rb_scan_args(argc, argv, "21", &vkey, &vvalue, &vdelim);
2518
+ vkey = StringValueEx(vkey);
2519
+ const std::string_view key = GetStringView(vkey);
2520
+ vvalue = StringValueEx(vvalue);
2521
+ const std::string_view value = GetStringView(vvalue);
2522
+ vdelim = StringValueEx(vdelim);
2523
+ const std::string_view delim = GetStringView(vdelim);
2524
+ tkrzw::StatusFuture future(sasync->async->Append(key, value, delim));
2525
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2526
+ }
2527
+
2528
+ // Implementation of AsyncDBM#append_multi.
2529
+ static VALUE asyncdbm_append_multi(int argc, VALUE* argv, VALUE vself) {
2530
+ StructAsyncDBM* sasync = nullptr;
2531
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2532
+ if (sasync->async == nullptr) {
2533
+ rb_raise(rb_eRuntimeError, "destructed object");
2534
+ }
2535
+ volatile VALUE vdelim, vrecords;
2536
+ rb_scan_args(argc, argv, "02", &vdelim, &vrecords);
2537
+ std::string_view delim = "";
2538
+ if (argc <= 1 && TYPE(vdelim) == T_HASH) {
2539
+ vrecords = vdelim;
2540
+ } else {
2541
+ delim = argc > 0 ? GetStringView(vdelim) : std::string_view("");
2542
+ }
2543
+ const auto& records = HashToMap(vrecords);
2544
+ std::map<std::string_view, std::string_view> record_views;
2545
+ for (const auto& record : records) {
2546
+ record_views.emplace(std::make_pair(
2547
+ std::string_view(record.first), std::string_view(record.second)));
2548
+ }
2549
+ tkrzw::StatusFuture future(sasync->async->AppendMulti(record_views, delim));
2550
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2551
+ }
2552
+
2553
+ // Implementation of AsyncDBM#compare_exchange.
2554
+ static VALUE asyncdbm_compare_exchange(VALUE vself, VALUE vkey, VALUE vexpected, VALUE vdesired) {
2555
+ StructAsyncDBM* sasync = nullptr;
2556
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2557
+ if (sasync->async == nullptr) {
2558
+ rb_raise(rb_eRuntimeError, "destructed object");
2559
+ }
2560
+ vkey = StringValueEx(vkey);
2561
+ const std::string_view key = GetStringView(vkey);
2562
+ std::string_view expected;
2563
+ if (vexpected != Qnil) {
2564
+ if (vexpected == obj_dbm_any_data) {
2565
+ expected = tkrzw::DBM::ANY_DATA;
2566
+ } else {
2567
+ vexpected = StringValueEx(vexpected);
2568
+ expected = GetStringView(vexpected);
2569
+ }
2570
+ }
2571
+ std::string_view desired;
2572
+ if (vdesired != Qnil) {
2573
+ if (vdesired == obj_dbm_any_data) {
2574
+ desired = tkrzw::DBM::ANY_DATA;
2575
+ } else {
2576
+ vdesired = StringValueEx(vdesired);
2577
+ desired = GetStringView(vdesired);
2578
+ }
2579
+ }
2580
+ tkrzw::StatusFuture future(sasync->async->CompareExchange(key, expected, desired));
2581
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2582
+ }
2583
+
2584
+ // Implementation of AsyncDBM#increment.
2585
+ static VALUE asyncdbm_increment(int argc, VALUE* argv, VALUE vself) {
2586
+ StructAsyncDBM* sasync = nullptr;
2587
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2588
+ if (sasync->async == nullptr) {
2589
+ rb_raise(rb_eRuntimeError, "destructed object");
2590
+ }
2591
+ volatile VALUE vkey, vinc, vinit, vstatus;
2592
+ rb_scan_args(argc, argv, "13", &vkey, &vinc, &vinit, &vstatus);
2593
+ vkey = StringValueEx(vkey);
2594
+ const std::string_view key = GetStringView(vkey);
2595
+ const int64_t inc = vinc == Qnil ? 1 : GetInteger(vinc);
2596
+ const int64_t init = vinit == Qnil ? 0 : GetInteger(vinit);
2597
+ tkrzw::StatusFuture future(sasync->async->Increment(key, inc, init));
2598
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2599
+ }
2600
+
2601
+ // Implementation of AsyncDBM#compare_exchange_multi.
2602
+ static VALUE asyncdbm_compare_exchange_multi(VALUE vself, VALUE vexpected, VALUE vdesired) {
2603
+ StructAsyncDBM* sasync = nullptr;
2604
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2605
+ if (sasync->async == nullptr) {
2606
+ rb_raise(rb_eRuntimeError, "destructed object");
2607
+ }
2608
+ if (TYPE(vexpected) != T_ARRAY || TYPE(vdesired) != T_ARRAY) {
2609
+ rb_raise(rb_eRuntimeError, "expected or desired is not an array");
2610
+ }
2611
+ const auto& expected = ExtractSVPairs(vexpected);
2612
+ const auto& desired = ExtractSVPairs(vdesired);
2613
+ tkrzw::StatusFuture future(sasync->async->CompareExchangeMulti(expected, desired));
2614
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2615
+ }
2616
+
2617
+ // Implementation of AsyncDBM#rekey.
2618
+ static VALUE asyncdbm_rekey(int argc, VALUE* argv, VALUE vself) {
2619
+ StructAsyncDBM* sasync = nullptr;
2620
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2621
+ if (sasync->async == nullptr) {
2622
+ rb_raise(rb_eRuntimeError, "destructed object");
2623
+ }
2624
+ volatile VALUE vold_key, vnew_key, voverwrite, vcopying;
2625
+ rb_scan_args(argc, argv, "22", &vold_key, &vnew_key, &voverwrite, &vcopying);
2626
+ vold_key = StringValueEx(vold_key);
2627
+ const std::string_view old_key = GetStringView(vold_key);
2628
+ vnew_key = StringValueEx(vnew_key);
2629
+ const std::string_view new_key = GetStringView(vnew_key);
2630
+ const bool overwrite = argc > 2 ? RTEST(voverwrite) : true;
2631
+ const bool copying = argc > 3 ? RTEST(vcopying) : false;
2632
+ tkrzw::StatusFuture future(sasync->async->Rekey(old_key, new_key, overwrite, copying));
2633
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2634
+ }
2635
+
2636
+ // Implementation of AsyncDBM#pop_first.
2637
+ static VALUE asyncdbm_pop_first(VALUE vself) {
2638
+ StructAsyncDBM* sasync = nullptr;
2639
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2640
+ if (sasync->async == nullptr) {
2641
+ rb_raise(rb_eRuntimeError, "destructed object");
2642
+ }
2643
+ tkrzw::StatusFuture future(sasync->async->PopFirst());
2644
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2645
+ }
2646
+
2647
+ // Implementation of AsyncDBM#push_last.
2648
+ static VALUE asyncdbm_push_last(int argc, VALUE* argv, VALUE vself) {
2649
+ StructAsyncDBM* sasync = nullptr;
2650
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2651
+ if (sasync->async == nullptr) {
2652
+ rb_raise(rb_eRuntimeError, "destructed object");
2653
+ }
2654
+ volatile VALUE vvalue, vwtime;
2655
+ rb_scan_args(argc, argv, "11", &vvalue, &vwtime);
2656
+ vvalue = StringValueEx(vvalue);
2657
+ const std::string_view value = GetStringView(vvalue);
2658
+ const double wtime = vwtime == Qnil ? -1.0 : GetFloat(vwtime);
2659
+ tkrzw::StatusFuture future(sasync->async->PushLast(value, wtime));
2660
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2661
+ }
2662
+
2663
+ // Implementation of AsyncDBM#clear.
2664
+ static VALUE asyncdbm_clear(VALUE vself) {
2665
+ StructAsyncDBM* sasync = nullptr;
2666
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2667
+ if (sasync->async == nullptr) {
2668
+ rb_raise(rb_eRuntimeError, "destructed object");
2669
+ }
2670
+ tkrzw::StatusFuture future(sasync->async->Clear());
2671
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2672
+ }
2673
+
2674
+ // Implementation of AsyncDBM#rebuild.
2675
+ static VALUE asyncdbm_rebuild(int argc, VALUE* argv, VALUE vself) {
2676
+ StructAsyncDBM* sasync = nullptr;
2677
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2678
+ if (sasync->async == nullptr) {
2679
+ rb_raise(rb_eRuntimeError, "destructed object");
2680
+ }
2681
+ volatile VALUE vparams;
2682
+ rb_scan_args(argc, argv, "01", &vparams);
2683
+ const std::map<std::string, std::string> params = HashToMap(vparams);
2684
+ tkrzw::StatusFuture future(sasync->async->Rebuild(params));
2685
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2686
+ }
2687
+
2688
+ // Implementation of AsyncDBM#synchronize.
2689
+ static VALUE asyncdbm_synchronize(int argc, VALUE* argv, VALUE vself) {
2690
+ StructAsyncDBM* sasync = nullptr;
2691
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2692
+ if (sasync->async == nullptr) {
2693
+ rb_raise(rb_eRuntimeError, "destructed object");
2694
+ }
2695
+ volatile VALUE vhard, vparams;
2696
+ rb_scan_args(argc, argv, "11", &vhard, &vparams);
2697
+ const bool hard = RTEST(vhard);
2698
+ const std::map<std::string, std::string> params = HashToMap(vparams);
2699
+ tkrzw::StatusFuture future(sasync->async->Synchronize(hard, nullptr, params));
2700
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2701
+ }
2702
+
2703
+ // Implementation of AsyncDBM#copy_file_data.
2704
+ static VALUE asyncdbm_copy_file_data(int argc, VALUE* argv, VALUE vself) {
2705
+ StructAsyncDBM* sasync = nullptr;
2706
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2707
+ if (sasync->async == nullptr) {
2708
+ rb_raise(rb_eRuntimeError, "destructed object");
2709
+ }
2710
+ volatile VALUE vdestpath, vsynchard;
2711
+ rb_scan_args(argc, argv, "11", &vdestpath, &vsynchard);
2712
+ vdestpath = StringValueEx(vdestpath);
2713
+ const std::string_view dest_path = GetStringView(vdestpath);
2714
+ const bool sync_hard = RTEST(vsynchard);
2715
+ tkrzw::StatusFuture future(sasync->async->CopyFileData(std::string(dest_path), sync_hard));
2716
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2717
+ }
2718
+
2719
+ // Implementation of AsyncDBM#export.
2720
+ static VALUE asyncdbm_export(VALUE vself, VALUE vdestdbm) {
2721
+ StructAsyncDBM* sasync = nullptr;
2722
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2723
+ if (sasync->async == nullptr) {
2724
+ rb_raise(rb_eRuntimeError, "destructed object");
2725
+ }
2726
+ if (!rb_obj_is_instance_of(vdestdbm, cls_dbm)) {
2727
+ return MakeStatusValue(tkrzw::Status(tkrzw::Status::INVALID_ARGUMENT_ERROR));
2728
+ }
2729
+ StructDBM* sdest_dbm = nullptr;
2730
+ Data_Get_Struct(vdestdbm, StructDBM, sdest_dbm);
2731
+ if (sdest_dbm->dbm == nullptr) {
2732
+ rb_raise(rb_eRuntimeError, "not opened database");
2733
+ }
2734
+ tkrzw::StatusFuture future(sasync->async->Export(sdest_dbm->dbm.get()));
2735
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2736
+ }
2737
+
2738
+ // Implementation of AsyncDBM#export_to_flat_records.
2739
+ static VALUE asyncdbm_export_to_flat_records(VALUE vself, VALUE vdest_file) {
2740
+ StructAsyncDBM* sasync = nullptr;
2741
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2742
+ if (sasync->async == nullptr) {
2743
+ rb_raise(rb_eRuntimeError, "destructed object");
2744
+ }
2745
+ StructFile* sdest_file = nullptr;
2746
+ Data_Get_Struct(vdest_file, StructFile, sdest_file);
2747
+ if (sdest_file->file == nullptr) {
2748
+ rb_raise(rb_eRuntimeError, "not opened file");
2749
+ }
2750
+ tkrzw::StatusFuture future(sasync->async->ExportToFlatRecords(sdest_file->file.get()));
2751
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2752
+ }
2753
+
2754
+ // Implementation of AsyncDBM#import_from_flat_records.
2755
+ static VALUE asyncdbm_import_from_flat_records(VALUE vself, VALUE vsrc_file) {
2756
+ StructAsyncDBM* sasync = nullptr;
2757
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2758
+ if (sasync->async == nullptr) {
2759
+ rb_raise(rb_eRuntimeError, "destructed object");
2760
+ }
2761
+ StructFile* ssrc_file = nullptr;
2762
+ Data_Get_Struct(vsrc_file, StructFile, ssrc_file);
2763
+ if (ssrc_file->file == nullptr) {
2764
+ rb_raise(rb_eRuntimeError, "not opened file");
2765
+ }
2766
+ tkrzw::StatusFuture future(sasync->async->ImportFromFlatRecords(ssrc_file->file.get()));
2767
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2768
+ }
2769
+
2770
+ // Implementation of AsyncDBM#search.
2771
+ static VALUE asyncdbm_search(int argc, VALUE* argv, VALUE vself) {
2772
+ StructAsyncDBM* sasync = nullptr;
2773
+ Data_Get_Struct(vself, StructAsyncDBM, sasync);
2774
+ if (sasync->async == nullptr) {
2775
+ rb_raise(rb_eRuntimeError, "destructed object");
2776
+ }
2777
+ volatile VALUE vmode, vpattern, vcapacity;
2778
+ rb_scan_args(argc, argv, "21", &vmode, &vpattern, &vcapacity);
2779
+ vmode = StringValueEx(vmode);
2780
+ const std::string_view mode = GetStringView(vmode);
2781
+ vpattern = StringValueEx(vpattern);
2782
+ const std::string_view pattern = GetStringView(vpattern);
2783
+ const int64_t capacity = GetInteger(vcapacity);
2784
+ tkrzw::StatusFuture future(sasync->async->SearchModal(mode, pattern, capacity));
2785
+ return MakeFutureValue(std::move(future), sasync->concurrent, sasync->venc);
2786
+ }
2787
+
2788
+ // Defines the AsyncDBM class.
2789
+ static void DefineAsyncDBM() {
2790
+ cls_asyncdbm = rb_define_class_under(mod_tkrzw, "AsyncDBM", rb_cObject);
2791
+ rb_define_alloc_func(cls_asyncdbm, asyncdbm_new);
2792
+ rb_define_private_method(cls_asyncdbm, "initialize", (METHOD)asyncdbm_initialize, -1);
2793
+ rb_define_method(cls_asyncdbm, "destruct", (METHOD)asyncdbm_destruct, 0);
2794
+ rb_define_method(cls_asyncdbm, "get", (METHOD)asyncdbm_get, 1);
2795
+ rb_define_method(cls_asyncdbm, "get_multi", (METHOD)asyncdbm_get_multi, -2);
2796
+ rb_define_method(cls_asyncdbm, "set", (METHOD)asyncdbm_set, -1);
2797
+ rb_define_method(cls_asyncdbm, "set_multi", (METHOD)asyncdbm_set_multi, -1);
2798
+ rb_define_method(cls_asyncdbm, "remove", (METHOD)asyncdbm_remove, 1);
2799
+ rb_define_method(cls_asyncdbm, "remove_multi", (METHOD)asyncdbm_remove_multi, -2);
2800
+ rb_define_method(cls_asyncdbm, "append", (METHOD)asyncdbm_append, -1);
2801
+ rb_define_method(cls_asyncdbm, "append_multi", (METHOD)asyncdbm_append_multi, -1);
2802
+ rb_define_method(cls_asyncdbm, "compare_exchange", (METHOD)asyncdbm_compare_exchange, 3);
2803
+ rb_define_method(cls_asyncdbm, "increment", (METHOD)asyncdbm_increment, -1);
2804
+ rb_define_method(cls_asyncdbm, "compare_exchange_multi",
2805
+ (METHOD)asyncdbm_compare_exchange_multi, 2);
2806
+ rb_define_method(cls_asyncdbm, "rekey", (METHOD)asyncdbm_rekey, -1);
2807
+ rb_define_method(cls_asyncdbm, "pop_first", (METHOD)asyncdbm_pop_first, 0);
2808
+ rb_define_method(cls_asyncdbm, "push_last", (METHOD)asyncdbm_push_last, -1);
2809
+ rb_define_method(cls_asyncdbm, "clear", (METHOD)asyncdbm_clear, 0);
2810
+ rb_define_method(cls_asyncdbm, "rebuild", (METHOD)asyncdbm_rebuild, -1);
2811
+ rb_define_method(cls_asyncdbm, "synchronize", (METHOD)asyncdbm_synchronize, -1);
2812
+ rb_define_method(cls_asyncdbm, "copy_file_data", (METHOD)asyncdbm_copy_file_data, -1);
2813
+ rb_define_method(cls_asyncdbm, "export", (METHOD)asyncdbm_export, 1);
2814
+ rb_define_method(cls_asyncdbm, "export_to_flat_records",
2815
+ (METHOD)asyncdbm_export_to_flat_records, 1);
2816
+ rb_define_method(cls_asyncdbm, "import_from_flat_records",
2817
+ (METHOD)asyncdbm_import_from_flat_records, 1);
2818
+ rb_define_method(cls_asyncdbm, "search", (METHOD)asyncdbm_search, -1);
2819
+ rb_define_method(cls_asyncdbm, "to_s", (METHOD)asyncdbm_to_s, 0);
2820
+ rb_define_method(cls_asyncdbm, "inspect", (METHOD)asyncdbm_inspect, 0);
2821
+ }
2822
+
2823
+ // Implementation of File#del.
2824
+ static void file_del(void* ptr) {
2825
+ StructFile* sfile = (StructFile*)ptr;
2826
+ sfile->file.reset(nullptr);
2827
+ delete sfile;
2828
+ }
2829
+
2830
+ // Implementation of File.new.
2831
+ static VALUE file_new(VALUE cls) {
2832
+ StructFile* sfile = new StructFile(new tkrzw::PolyFile);
2833
+ return Data_Wrap_Struct(cls_file, 0, file_del, sfile);
2834
+ }
2835
+
2836
+ // Implementation of File#initialize.
2837
+ static VALUE file_initialize(VALUE vself) {
2838
+ return Qnil;
2839
+ }
2840
+
2841
+ // Implementation of File#destruct.
2842
+ static VALUE file_destruct(VALUE vself) {
2843
+ StructFile* sfile = nullptr;
2844
+ Data_Get_Struct(vself, StructFile, sfile);
2845
+ sfile->file.reset(nullptr);
2846
+ return Qnil;
2847
+ }
2848
+
2849
+ // Implementation of File#open.
2850
+ static VALUE file_open(int argc, VALUE* argv, VALUE vself) {
2851
+ StructFile* sfile = nullptr;
2852
+ Data_Get_Struct(vself, StructFile, sfile);
2853
+ if (sfile->file == nullptr) {
2854
+ rb_raise(rb_eRuntimeError, "destructed File");
2855
+ }
2856
+ volatile VALUE vpath, vwritable, vparams;
2857
+ rb_scan_args(argc, argv, "21", &vpath, &vwritable, &vparams);
2858
+ vpath = StringValueEx(vpath);
2859
+ const std::string_view path = GetStringView(vpath);
2860
+ const bool writable = RTEST(vwritable);
2861
+ std::map<std::string, std::string> params = HashToMap(vparams);
2862
+ bool concurrent = false;
2863
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "concurrent", "false"))) {
2864
+ concurrent = true;
2865
+ }
2866
+ int32_t open_options = 0;
2867
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "truncate", "false"))) {
2868
+ open_options |= tkrzw::File::OPEN_TRUNCATE;
2869
+ }
2870
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "no_create", "false"))) {
2871
+ open_options |= tkrzw::File::OPEN_NO_CREATE;
2872
+ }
2873
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "no_wait", "false"))) {
2874
+ open_options |= tkrzw::File::OPEN_NO_WAIT;
2875
+ }
2876
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "no_lock", "false"))) {
2877
+ open_options |= tkrzw::File::OPEN_NO_LOCK;
2878
+ }
2879
+ if (tkrzw::StrToBool(tkrzw::SearchMap(params, "sync_hard", "false"))) {
2880
+ open_options |= tkrzw::File::OPEN_SYNC_HARD;
2881
+ }
2882
+ std::string encoding = tkrzw::SearchMap(params, "encoding", "");
2883
+ if (encoding.empty()) {
2884
+ encoding = "ASCII-8BIT";
2885
+ }
2886
+ sfile->concurrent = concurrent;
2887
+ sfile->venc = GetEncoding(encoding);
2888
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2889
+ NativeFunction(sfile->concurrent, [&]() {
2890
+ status = sfile->file->OpenAdvanced(std::string(path), writable, open_options, params);
2891
+ });
2892
+ return MakeStatusValue(std::move(status));
2893
+ }
2894
+
2895
+ // Implementation of File#close.
2896
+ static VALUE file_close(VALUE vself) {
2897
+ StructFile* sfile = nullptr;
2898
+ Data_Get_Struct(vself, StructFile, sfile);
2899
+ if (sfile->file == nullptr) {
2900
+ rb_raise(rb_eRuntimeError, "destructed File");
2901
+ }
2902
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2903
+ NativeFunction(true, [&]() {
2904
+ status = sfile->file->Close();
2905
+ });
2906
+ return MakeStatusValue(std::move(status));
2907
+ }
2908
+
2909
+ // Implementation of File#read.
2910
+ static VALUE file_read(int argc, VALUE* argv, VALUE vself) {
2911
+ StructFile* sfile = nullptr;
2912
+ Data_Get_Struct(vself, StructFile, sfile);
2913
+ if (sfile->file == nullptr) {
2914
+ rb_raise(rb_eRuntimeError, "destructed File");
2915
+ }
2916
+ volatile VALUE voff, vsize, vstatus;
2917
+ rb_scan_args(argc, argv, "21", &voff, &vsize, &vstatus);
2918
+ const int64_t off = std::max<int64_t>(0, GetInteger(voff));
2919
+ const int64_t size = std::max<int64_t>(0, GetInteger(vsize));
2920
+ char* buf = new char[size];
2921
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2922
+ NativeFunction(sfile->concurrent, [&]() {
2923
+ status = sfile->file->Read(off, buf, size);
2924
+ });
2925
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
2926
+ SetStatusValue(vstatus, status);
2927
+ }
2928
+ if (status == tkrzw::Status::SUCCESS) {
2929
+ volatile VALUE vdata = MakeString(std::string_view(buf, size), sfile->venc);
2930
+ delete[] buf;
2931
+ return vdata;
2932
+ }
2933
+ delete[] buf;
2934
+ return Qnil;
2935
+ }
2936
+
2937
+ // Implementation of File#write.
2938
+ static VALUE file_write(VALUE vself, VALUE voff, VALUE vdata) {
2939
+ StructFile* sfile = nullptr;
2940
+ Data_Get_Struct(vself, StructFile, sfile);
2941
+ if (sfile->file == nullptr) {
2942
+ rb_raise(rb_eRuntimeError, "destructed File");
2943
+ }
2944
+ const int64_t off = std::max<int64_t>(0, GetInteger(voff));
2945
+ vdata = StringValueEx(vdata);
2946
+ const std::string_view data = GetStringView(vdata);
2947
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2948
+ NativeFunction(sfile->concurrent, [&]() {
2949
+ status = sfile->file->Write(off, data.data(), data.size());
2950
+ });
2951
+ return MakeStatusValue(std::move(status));
2952
+ }
2953
+
2954
+ // Implementation of File#append.
2955
+ static VALUE file_append(int argc, VALUE* argv, VALUE vself) {
2956
+ StructFile* sfile = nullptr;
2957
+ Data_Get_Struct(vself, StructFile, sfile);
2958
+ if (sfile->file == nullptr) {
2959
+ rb_raise(rb_eRuntimeError, "destructed File");
2960
+ }
2961
+ volatile VALUE vdata, vstatus;
2962
+ rb_scan_args(argc, argv, "11", &vdata, &vstatus);
2963
+ vdata = StringValueEx(vdata);
2964
+ const std::string_view data = GetStringView(vdata);
2965
+ int64_t new_off = 0;
2966
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2967
+ NativeFunction(sfile->concurrent, [&]() {
2968
+ status = sfile->file->Append(data.data(), data.size(), &new_off);
2969
+ });
2970
+ if (rb_obj_is_instance_of(vstatus, cls_status)) {
2971
+ SetStatusValue(vstatus, status);
2972
+ }
2973
+ if (status == tkrzw::Status::SUCCESS) {
2974
+ return LL2NUM(new_off);
2975
+ }
2976
+ return Qnil;
2977
+ }
2978
+
2979
+ // Implementation of File#truncate.
2980
+ static VALUE file_truncate(VALUE vself, VALUE vsize) {
2981
+ StructFile* sfile = nullptr;
2982
+ Data_Get_Struct(vself, StructFile, sfile);
2983
+ if (sfile->file == nullptr) {
2984
+ rb_raise(rb_eRuntimeError, "destructed File");
2985
+ }
2986
+ const int64_t size = std::max<int64_t>(0, GetInteger(vsize));
2987
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
2988
+ NativeFunction(true, [&]() {
2989
+ status = sfile->file->Truncate(size);
2990
+ });
2991
+ return MakeStatusValue(std::move(status));
2992
+ }
2993
+
2994
+ // Implementation of File#synchronize.
2995
+ static VALUE file_synchronize(int argc, VALUE* argv, VALUE vself) {
2996
+ StructFile* sfile = nullptr;
2997
+ Data_Get_Struct(vself, StructFile, sfile);
2998
+ if (sfile->file == nullptr) {
2999
+ rb_raise(rb_eRuntimeError, "destructed File");
3000
+ }
3001
+ volatile VALUE vhard, voff, vsize;
3002
+ rb_scan_args(argc, argv, "12", &vhard, &voff, &vsize);
3003
+ const bool hard = RTEST(vhard);
3004
+ const int64_t off = voff == Qnil ? 0 : std::max<int64_t>(0, GetInteger(voff));
3005
+ const int64_t size = vsize == Qnil ? 0 : std::max<int64_t>(0, GetInteger(vsize));
3006
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
3007
+ NativeFunction(sfile->concurrent, [&]() {
3008
+ status = sfile->file->Synchronize(hard, off, size);
3009
+ });
3010
+ return MakeStatusValue(std::move(status));
3011
+ }
3012
+
3013
+ // Implementation of File#get_size.
3014
+ static VALUE file_get_size(VALUE vself) {
3015
+ StructFile* sfile = nullptr;
3016
+ Data_Get_Struct(vself, StructFile, sfile);
3017
+ if (sfile->file == nullptr) {
3018
+ rb_raise(rb_eRuntimeError, "destructed File");
3019
+ }
3020
+ int64_t size = 0;
3021
+ NativeFunction(sfile->concurrent, [&]() {
3022
+ size = sfile->file->GetSizeSimple();
3023
+ });
3024
+ if (size >= 0) {
3025
+ return LL2NUM(size);
3026
+ }
3027
+ return Qnil;
3028
+ }
3029
+
3030
+ // Implementation of File#get_path.
3031
+ static VALUE file_get_path(VALUE vself) {
3032
+ StructFile* sfile = nullptr;
3033
+ Data_Get_Struct(vself, StructFile, sfile);
3034
+ if (sfile->file == nullptr) {
3035
+ rb_raise(rb_eRuntimeError, "destructed File");
3036
+ }
3037
+ std::string path;
3038
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
3039
+ NativeFunction(sfile->concurrent, [&]() {
3040
+ status = sfile->file->GetPath(&path);
3041
+ });
3042
+ if (status == tkrzw::Status::SUCCESS) {
3043
+ return rb_str_new(path.data(), path.size());
3044
+ }
3045
+ return Qnil;
3046
+ }
3047
+
3048
+ // Implementation of File#search.
3049
+ static VALUE file_search(int argc, VALUE* argv, VALUE vself) {
3050
+ StructFile* sfile = nullptr;
3051
+ Data_Get_Struct(vself, StructFile, sfile);
3052
+ if (sfile->file == nullptr) {
3053
+ rb_raise(rb_eRuntimeError, "destructed File");
3054
+ }
3055
+ volatile VALUE vmode, vpattern, vcapacity;
3056
+ rb_scan_args(argc, argv, "21", &vmode, &vpattern, &vcapacity);
3057
+ vmode = StringValueEx(vmode);
3058
+ const std::string_view mode = GetStringView(vmode);
3059
+ vpattern = StringValueEx(vpattern);
3060
+ const std::string_view pattern = GetStringView(vpattern);
3061
+ const int64_t capacity = GetInteger(vcapacity);
3062
+ std::vector<std::string> lines;
3063
+ tkrzw::Status status(tkrzw::Status::SUCCESS);
3064
+ NativeFunction(sfile->concurrent, [&]() {
3065
+ status = tkrzw::SearchTextFileModal(sfile->file.get(), mode, pattern, &lines, capacity);
3066
+ });
3067
+ if (status != tkrzw::Status::SUCCESS) {
3068
+ const std::string& message = tkrzw::ToString(status);
3069
+ rb_raise(cls_expt, "%s", message.c_str());
3070
+ }
3071
+ volatile VALUE vlines = rb_ary_new2(lines.size());
3072
+ for (const auto& line : lines) {
3073
+ rb_ary_push(vlines, MakeString(line, sfile->venc));
3074
+ }
3075
+ return vlines;
3076
+ }
3077
+
3078
+ // Implementation of File#to_s.
3079
+ static VALUE file_to_s(VALUE vself) {
3080
+ StructFile* sfile = nullptr;
3081
+ Data_Get_Struct(vself, StructFile, sfile);
3082
+ std::string class_name = "unknown";
3083
+ auto* in_file = sfile->file->GetInternalFile();
3084
+ if (in_file != nullptr) {
3085
+ const auto& file_type = in_file->GetType();
3086
+ if (file_type == typeid(tkrzw::StdFile)) {
3087
+ class_name = "StdFile";
3088
+ } else if (file_type == typeid(tkrzw::MemoryMapParallelFile)) {
3089
+ class_name = "MemoryMapParallelFile";
3090
+ } else if (file_type == typeid(tkrzw::MemoryMapAtomicFile)) {
3091
+ class_name = "MemoryMapAtomicFile";
3092
+ } else if (file_type == typeid(tkrzw::PositionalParallelFile)) {
3093
+ class_name = "PositionalParallelFile";
3094
+ } else if (file_type == typeid(tkrzw::PositionalAtomicFile)) {
3095
+ class_name = "PositionalAtomicFile";
3096
+ }
3097
+ }
3098
+ const std::string path = sfile->file->GetPathSimple();
3099
+ const int64_t size = sfile->file->GetSizeSimple();
3100
+ const std::string expr =
3101
+ tkrzw::StrCat(class_name, ":", tkrzw::StrEscapeC(path, true), ":", size);
3102
+ return rb_str_new(expr.data(), expr.size());
3103
+ }
3104
+
3105
+ // Implementation of File#inspect.
3106
+ static VALUE file_inspect(VALUE vself) {
3107
+ StructFile* sfile = nullptr;
3108
+ Data_Get_Struct(vself, StructFile, sfile);
3109
+ std::string class_name = "unknown";
3110
+ auto* in_file = sfile->file->GetInternalFile();
3111
+ if (in_file != nullptr) {
3112
+ const auto& file_type = in_file->GetType();
3113
+ if (file_type == typeid(tkrzw::StdFile)) {
3114
+ class_name = "StdFile";
3115
+ } else if (file_type == typeid(tkrzw::MemoryMapParallelFile)) {
3116
+ class_name = "MemoryMapParallelFile";
3117
+ } else if (file_type == typeid(tkrzw::MemoryMapAtomicFile)) {
3118
+ class_name = "MemoryMapAtomicFile";
3119
+ } else if (file_type == typeid(tkrzw::PositionalParallelFile)) {
3120
+ class_name = "PositionalParallelFile";
3121
+ } else if (file_type == typeid(tkrzw::PositionalAtomicFile)) {
3122
+ class_name = "PositionalAtomicFile";
3123
+ }
3124
+ }
3125
+ const std::string path = sfile->file->GetPathSimple();
3126
+ const int64_t size = sfile->file->GetSizeSimple();
3127
+ const std::string expr = tkrzw::StrCat(
3128
+ "#<Tkrzw::File:", class_name, ":", tkrzw::StrEscapeC(path, true), ":", size, ">");
3129
+ return rb_str_new(expr.data(), expr.size());
3130
+ }
3131
+
3132
+ // Defines the File class.
3133
+ static void DefineFile() {
3134
+ cls_file = rb_define_class_under(mod_tkrzw, "File", rb_cObject);
3135
+ rb_define_alloc_func(cls_file, file_new);
3136
+ rb_define_private_method(cls_file, "initialize", (METHOD)file_initialize, 0);
3137
+ rb_define_method(cls_file, "destruct", (METHOD)file_destruct, 0);
3138
+ rb_define_method(cls_file, "open", (METHOD)file_open, -1);
3139
+ rb_define_method(cls_file, "close", (METHOD)file_close, 0);
3140
+ rb_define_method(cls_file, "read", (METHOD)file_read, -1);
3141
+ rb_define_method(cls_file, "write", (METHOD)file_write, 2);
3142
+ rb_define_method(cls_file, "append", (METHOD)file_append, -1);
3143
+ rb_define_method(cls_file, "truncate", (METHOD)file_truncate, 1);
3144
+ rb_define_method(cls_file, "synchronize", (METHOD)file_synchronize, -1);
3145
+ rb_define_method(cls_file, "get_size", (METHOD)file_get_size, 0);
3146
+ rb_define_method(cls_file, "get_path", (METHOD)file_get_path, 0);
3147
+ rb_define_method(cls_file, "search", (METHOD)file_search, -1);
3148
+ rb_define_method(cls_file, "to_s", (METHOD)file_to_s, 0);
3149
+ rb_define_method(cls_file, "inspect", (METHOD)file_inspect, 0);
3150
+ }
3151
+
3152
+ // Entry point of the library.
3153
+ void Init_tkrzw() {
3154
+ DefineModule();
3155
+ DefineUtility();
3156
+ DefineStatus();
3157
+ DefineFuture();
3158
+ DefineStatusException();
3159
+ DefineDBM();
3160
+ DefineIterator();
3161
+ DefineAsyncDBM();
3162
+ DefineFile();
3163
+ }
3164
+
3165
+ } // extern "C"
3166
+
3167
+ // END OF FILE