tkrzw-unofficial 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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