sdbm 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ # AUTOGENERATED DEPENDENCIES START
2
+ _sdbm.o: $(RUBY_EXTCONF_H)
3
+ _sdbm.o: $(arch_hdrdir)/ruby/config.h
4
+ _sdbm.o: $(hdrdir)/ruby/backward.h
5
+ _sdbm.o: $(hdrdir)/ruby/defines.h
6
+ _sdbm.o: $(hdrdir)/ruby/intern.h
7
+ _sdbm.o: $(hdrdir)/ruby/missing.h
8
+ _sdbm.o: $(hdrdir)/ruby/ruby.h
9
+ _sdbm.o: $(hdrdir)/ruby/st.h
10
+ _sdbm.o: $(hdrdir)/ruby/subst.h
11
+ _sdbm.o: _sdbm.c
12
+ _sdbm.o: sdbm.h
13
+ init.o: $(RUBY_EXTCONF_H)
14
+ init.o: $(arch_hdrdir)/ruby/config.h
15
+ init.o: $(hdrdir)/ruby/backward.h
16
+ init.o: $(hdrdir)/ruby/defines.h
17
+ init.o: $(hdrdir)/ruby/intern.h
18
+ init.o: $(hdrdir)/ruby/missing.h
19
+ init.o: $(hdrdir)/ruby/ruby.h
20
+ init.o: $(hdrdir)/ruby/st.h
21
+ init.o: $(hdrdir)/ruby/subst.h
22
+ # init.o: $(top_srcdir)/include/ruby.h
23
+ init.o: init.c
24
+ init.o: sdbm.h
25
+ # AUTOGENERATED DEPENDENCIES END
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: false
2
+ require 'mkmf'
3
+
4
+ $defs << "-D""BADMESS=0"
5
+ create_makefile("sdbm")
@@ -0,0 +1,1067 @@
1
+ /************************************************
2
+
3
+ sdbminit.c -
4
+
5
+ $Author$
6
+ created at: Fri May 7 08:34:24 JST 1999
7
+
8
+ Copyright (C) 1995-2001 Yukihiro Matsumoto
9
+
10
+ ************************************************/
11
+
12
+ #include "ruby.h"
13
+
14
+ #include "sdbm.h"
15
+ #include <fcntl.h>
16
+ #include <errno.h>
17
+
18
+ /*
19
+ * Document-class: SDBM
20
+ *
21
+ * SDBM provides a simple file-based key-value store, which can only store
22
+ * String keys and values.
23
+ *
24
+ * Note that Ruby comes with the source code for SDBM, while the DBM and GDBM
25
+ * standard libraries rely on external libraries and headers.
26
+ *
27
+ * === Examples
28
+ *
29
+ * Insert values:
30
+ *
31
+ * require 'sdbm'
32
+ *
33
+ * SDBM.open 'my_database' do |db|
34
+ * db['apple'] = 'fruit'
35
+ * db['pear'] = 'fruit'
36
+ * db['carrot'] = 'vegetable'
37
+ * db['tomato'] = 'vegetable'
38
+ * end
39
+ *
40
+ * Bulk update:
41
+ *
42
+ * require 'sdbm'
43
+ *
44
+ * SDBM.open 'my_database' do |db|
45
+ * db.update('peach' => 'fruit', 'tomato' => 'fruit')
46
+ * end
47
+ *
48
+ * Retrieve values:
49
+ *
50
+ * require 'sdbm'
51
+ *
52
+ * SDBM.open 'my_database' do |db|
53
+ * db.each do |key, value|
54
+ * puts "Key: #{key}, Value: #{value}"
55
+ * end
56
+ * end
57
+ *
58
+ * Outputs:
59
+ *
60
+ * Key: apple, Value: fruit
61
+ * Key: pear, Value: fruit
62
+ * Key: carrot, Value: vegetable
63
+ * Key: peach, Value: fruit
64
+ * Key: tomato, Value: fruit
65
+ */
66
+
67
+ static VALUE rb_cDBM, rb_eDBMError;
68
+
69
+ struct dbmdata {
70
+ int di_size;
71
+ DBM *di_dbm;
72
+ };
73
+
74
+ static void
75
+ closed_sdbm(void)
76
+ {
77
+ rb_raise(rb_eDBMError, "closed SDBM file");
78
+ }
79
+
80
+ #define GetDBM(obj, dbmp) do {\
81
+ TypedData_Get_Struct((obj), struct dbmdata, &sdbm_type, (dbmp));\
82
+ if ((dbmp) == 0) closed_sdbm();\
83
+ if ((dbmp)->di_dbm == 0) closed_sdbm();\
84
+ } while (0)
85
+
86
+ #define GetDBM2(obj, dbmp, dbm) do {\
87
+ GetDBM((obj), (dbmp));\
88
+ (dbm) = (dbmp)->di_dbm;\
89
+ } while (0)
90
+
91
+ static void
92
+ free_sdbm(void *ptr)
93
+ {
94
+ struct dbmdata *dbmp = ptr;
95
+
96
+ if (dbmp->di_dbm) sdbm_close(dbmp->di_dbm);
97
+ ruby_xfree(dbmp);
98
+ }
99
+
100
+ static size_t
101
+ memsize_dbm(const void *ptr)
102
+ {
103
+ size_t size = 0;
104
+ const struct dbmdata *dbmp = ptr;
105
+ if (dbmp) {
106
+ size += sizeof(*dbmp);
107
+ if (dbmp->di_dbm) size += sizeof(DBM);
108
+ }
109
+ return size;
110
+ }
111
+
112
+ static const rb_data_type_t sdbm_type = {
113
+ "sdbm",
114
+ {0, free_sdbm, memsize_dbm,},
115
+ 0, 0,
116
+ RUBY_TYPED_FREE_IMMEDIATELY,
117
+ };
118
+
119
+ /*
120
+ * call-seq:
121
+ * sdbm.close -> nil
122
+ *
123
+ * Closes the database file.
124
+ *
125
+ * Raises SDBMError if the database is already closed.
126
+ */
127
+ static VALUE
128
+ fsdbm_close(VALUE obj)
129
+ {
130
+ struct dbmdata *dbmp;
131
+
132
+ GetDBM(obj, dbmp);
133
+ sdbm_close(dbmp->di_dbm);
134
+ dbmp->di_dbm = 0;
135
+
136
+ return Qnil;
137
+ }
138
+
139
+ /*
140
+ * call-seq:
141
+ * sdbm.closed? -> true or false
142
+ *
143
+ * Returns +true+ if the database is closed.
144
+ */
145
+ static VALUE
146
+ fsdbm_closed(VALUE obj)
147
+ {
148
+ struct dbmdata *dbmp;
149
+
150
+ TypedData_Get_Struct(obj, struct dbmdata, &sdbm_type, dbmp);
151
+ if (dbmp == 0)
152
+ return Qtrue;
153
+ if (dbmp->di_dbm == 0)
154
+ return Qtrue;
155
+
156
+ return Qfalse;
157
+ }
158
+
159
+ static VALUE
160
+ fsdbm_alloc(VALUE klass)
161
+ {
162
+ return TypedData_Wrap_Struct(klass, &sdbm_type, 0);
163
+ }
164
+ /*
165
+ * call-seq:
166
+ * SDBM.new(filename, mode = 0666)
167
+ *
168
+ * Creates a new database handle by opening the given +filename+. SDBM actually
169
+ * uses two physical files, with extensions '.dir' and '.pag'. These extensions
170
+ * will automatically be appended to the +filename+.
171
+ *
172
+ * If the file does not exist, a new file will be created using the given
173
+ * +mode+, unless +mode+ is explicitly set to nil. In the latter case, no
174
+ * database will be created.
175
+ *
176
+ * If the file exists, it will be opened in read/write mode. If this fails, it
177
+ * will be opened in read-only mode.
178
+ */
179
+ static VALUE
180
+ fsdbm_initialize(int argc, VALUE *argv, VALUE obj)
181
+ {
182
+ VALUE file, vmode;
183
+ DBM *dbm;
184
+ struct dbmdata *dbmp;
185
+ int mode;
186
+
187
+ if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) {
188
+ mode = 0666; /* default value */
189
+ }
190
+ else if (NIL_P(vmode)) {
191
+ mode = -1; /* return nil if DB not exist */
192
+ }
193
+ else {
194
+ mode = NUM2INT(vmode);
195
+ }
196
+ FilePathValue(file);
197
+
198
+ dbm = 0;
199
+ if (mode >= 0)
200
+ dbm = sdbm_open(RSTRING_PTR(file), O_RDWR|O_CREAT, mode);
201
+ if (!dbm)
202
+ dbm = sdbm_open(RSTRING_PTR(file), O_RDWR, 0);
203
+ if (!dbm)
204
+ dbm = sdbm_open(RSTRING_PTR(file), O_RDONLY, 0);
205
+
206
+ if (!dbm) {
207
+ if (mode == -1) return Qnil;
208
+ rb_sys_fail_str(file);
209
+ }
210
+
211
+ dbmp = ALLOC(struct dbmdata);
212
+ DATA_PTR(obj) = dbmp;
213
+ dbmp->di_dbm = dbm;
214
+ dbmp->di_size = -1;
215
+
216
+ return obj;
217
+ }
218
+
219
+ /*
220
+ * call-seq:
221
+ * SDBM.open(filename, mode = 0666)
222
+ * SDBM.open(filename, mode = 0666) { |sdbm| ... }
223
+ *
224
+ * If called without a block, this is the same as SDBM.new.
225
+ *
226
+ * If a block is given, the new database will be passed to the block and
227
+ * will be safely closed after the block has executed.
228
+ *
229
+ * Example:
230
+ *
231
+ * require 'sdbm'
232
+ *
233
+ * SDBM.open('my_database') do |db|
234
+ * db['hello'] = 'world'
235
+ * end
236
+ */
237
+ static VALUE
238
+ fsdbm_s_open(int argc, VALUE *argv, VALUE klass)
239
+ {
240
+ VALUE obj = fsdbm_alloc(klass);
241
+
242
+ if (NIL_P(fsdbm_initialize(argc, argv, obj))) {
243
+ return Qnil;
244
+ }
245
+
246
+ if (rb_block_given_p()) {
247
+ return rb_ensure(rb_yield, obj, fsdbm_close, obj);
248
+ }
249
+
250
+ return obj;
251
+ }
252
+
253
+ static VALUE
254
+ fsdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone)
255
+ {
256
+ datum key, value;
257
+ struct dbmdata *dbmp;
258
+ DBM *dbm;
259
+
260
+ ExportStringValue(keystr);
261
+ key.dptr = RSTRING_PTR(keystr);
262
+ key.dsize = RSTRING_LENINT(keystr);
263
+
264
+ GetDBM2(obj, dbmp, dbm);
265
+ value = sdbm_fetch(dbm, key);
266
+ if (value.dptr == 0) {
267
+ if (ifnone == Qnil && rb_block_given_p())
268
+ return rb_yield(rb_external_str_new(key.dptr, key.dsize));
269
+ return ifnone;
270
+ }
271
+ return rb_external_str_new(value.dptr, value.dsize);
272
+ }
273
+
274
+ /*
275
+ * call-seq:
276
+ * sdbm[key] -> value or nil
277
+ *
278
+ * Returns the +value+ in the database associated with the given +key+ string.
279
+ *
280
+ * If no value is found, returns +nil+.
281
+ */
282
+ static VALUE
283
+ fsdbm_aref(VALUE obj, VALUE keystr)
284
+ {
285
+ return fsdbm_fetch(obj, keystr, Qnil);
286
+ }
287
+
288
+ /*
289
+ * call-seq:
290
+ * sdbm.fetch(key) -> value or nil
291
+ * sdbm.fetch(key) { |key| ... }
292
+ *
293
+ * Returns the +value+ in the database associated with the given +key+ string.
294
+ *
295
+ * If a block is provided, the block will be called when there is no
296
+ * +value+ associated with the given +key+. The +key+ will be passed in as an
297
+ * argument to the block.
298
+ *
299
+ * If no block is provided and no value is associated with the given +key+,
300
+ * then an IndexError will be raised.
301
+ */
302
+ static VALUE
303
+ fsdbm_fetch_m(int argc, VALUE *argv, VALUE obj)
304
+ {
305
+ VALUE keystr, valstr, ifnone;
306
+
307
+ rb_scan_args(argc, argv, "11", &keystr, &ifnone);
308
+ valstr = fsdbm_fetch(obj, keystr, ifnone);
309
+ if (argc == 1 && !rb_block_given_p() && NIL_P(valstr))
310
+ rb_raise(rb_eIndexError, "key not found");
311
+
312
+ return valstr;
313
+ }
314
+
315
+ /*
316
+ * call-seq:
317
+ * sdbm.key(value) -> key
318
+ *
319
+ * Returns the +key+ associated with the given +value+. If more than one
320
+ * +key+ corresponds to the given +value+, then the first key to be found
321
+ * will be returned. If no keys are found, +nil+ will be returned.
322
+ */
323
+ static VALUE
324
+ fsdbm_key(VALUE obj, VALUE valstr)
325
+ {
326
+ datum key, val;
327
+ struct dbmdata *dbmp;
328
+ DBM *dbm;
329
+
330
+ ExportStringValue(valstr);
331
+ val.dptr = RSTRING_PTR(valstr);
332
+ val.dsize = RSTRING_LENINT(valstr);
333
+
334
+ GetDBM2(obj, dbmp, dbm);
335
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
336
+ val = sdbm_fetch(dbm, key);
337
+ if (val.dsize == RSTRING_LEN(valstr) &&
338
+ memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0)
339
+ return rb_external_str_new(key.dptr, key.dsize);
340
+ }
341
+ return Qnil;
342
+ }
343
+
344
+ /*
345
+ * :nodoc:
346
+ */
347
+ static VALUE
348
+ fsdbm_index(VALUE hash, VALUE value)
349
+ {
350
+ rb_warn("SDBM#index is deprecated; use SDBM#key");
351
+ return fsdbm_key(hash, value);
352
+ }
353
+
354
+ /* call-seq:
355
+ * sdbm.select { |key, value| ... } -> Array
356
+ *
357
+ * Returns a new Array of key-value pairs for which the block returns +true+.
358
+ *
359
+ * Example:
360
+ *
361
+ * require 'sdbm'
362
+ *
363
+ * SDBM.open 'my_database' do |db|
364
+ * db['apple'] = 'fruit'
365
+ * db['pear'] = 'fruit'
366
+ * db['spinach'] = 'vegetable'
367
+ *
368
+ * veggies = db.select do |key, value|
369
+ * value == 'vegetable'
370
+ * end #=> [["apple", "fruit"], ["pear", "fruit"]]
371
+ * end
372
+ */
373
+ static VALUE
374
+ fsdbm_select(VALUE obj)
375
+ {
376
+ VALUE new = rb_ary_new();
377
+ datum key, val;
378
+ DBM *dbm;
379
+ struct dbmdata *dbmp;
380
+
381
+ GetDBM2(obj, dbmp, dbm);
382
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
383
+ VALUE assoc, v;
384
+ val = sdbm_fetch(dbm, key);
385
+ assoc = rb_assoc_new(rb_external_str_new(key.dptr, key.dsize),
386
+ rb_external_str_new(val.dptr, val.dsize));
387
+ v = rb_yield(assoc);
388
+ if (RTEST(v)) {
389
+ rb_ary_push(new, assoc);
390
+ }
391
+ GetDBM2(obj, dbmp, dbm);
392
+ }
393
+
394
+ return new;
395
+ }
396
+
397
+ /* call-seq:
398
+ * sdbm.values_at(key, ...) -> Array
399
+ *
400
+ * Returns an Array of values corresponding to the given keys.
401
+ */
402
+ static VALUE
403
+ fsdbm_values_at(int argc, VALUE *argv, VALUE obj)
404
+ {
405
+ VALUE new = rb_ary_new2(argc);
406
+ int i;
407
+
408
+ for (i=0; i<argc; i++) {
409
+ rb_ary_push(new, fsdbm_fetch(obj, argv[i], Qnil));
410
+ }
411
+
412
+ return new;
413
+ }
414
+
415
+ static void
416
+ fdbm_modify(VALUE obj)
417
+ {
418
+ if (OBJ_FROZEN(obj)) rb_error_frozen("SDBM");
419
+ }
420
+
421
+ /*
422
+ * call-seq:
423
+ * sdbm.delete(key) -> value or nil
424
+ * sdbm.delete(key) { |key, value| ... }
425
+ *
426
+ * Deletes the key-value pair corresponding to the given +key+. If the
427
+ * +key+ exists, the deleted value will be returned, otherwise +nil+.
428
+ *
429
+ * If a block is provided, the deleted +key+ and +value+ will be passed to
430
+ * the block as arguments. If the +key+ does not exist in the database, the
431
+ * value will be +nil+.
432
+ */
433
+ static VALUE
434
+ fsdbm_delete(VALUE obj, VALUE keystr)
435
+ {
436
+ datum key, value;
437
+ struct dbmdata *dbmp;
438
+ DBM *dbm;
439
+ VALUE valstr;
440
+
441
+ fdbm_modify(obj);
442
+ ExportStringValue(keystr);
443
+ key.dptr = RSTRING_PTR(keystr);
444
+ key.dsize = RSTRING_LENINT(keystr);
445
+
446
+ GetDBM2(obj, dbmp, dbm);
447
+ dbmp->di_size = -1;
448
+
449
+ value = sdbm_fetch(dbm, key);
450
+ if (value.dptr == 0) {
451
+ if (rb_block_given_p()) return rb_yield(keystr);
452
+ return Qnil;
453
+ }
454
+
455
+ /* need to save value before sdbm_delete() */
456
+ valstr = rb_external_str_new(value.dptr, value.dsize);
457
+
458
+ if (sdbm_delete(dbm, key)) {
459
+ dbmp->di_size = -1;
460
+ rb_raise(rb_eDBMError, "dbm_delete failed");
461
+ }
462
+ else if (dbmp->di_size >= 0) {
463
+ dbmp->di_size--;
464
+ }
465
+ return valstr;
466
+ }
467
+
468
+ /*
469
+ * call-seq:
470
+ * sdbm.shift -> Array or nil
471
+ *
472
+ * Removes a key-value pair from the database and returns them as an
473
+ * Array. If the database is empty, returns +nil+.
474
+ */
475
+ static VALUE
476
+ fsdbm_shift(VALUE obj)
477
+ {
478
+ datum key, val;
479
+ struct dbmdata *dbmp;
480
+ DBM *dbm;
481
+ VALUE keystr, valstr;
482
+
483
+ fdbm_modify(obj);
484
+ GetDBM2(obj, dbmp, dbm);
485
+ key = sdbm_firstkey(dbm);
486
+ if (!key.dptr) return Qnil;
487
+ val = sdbm_fetch(dbm, key);
488
+ keystr = rb_external_str_new(key.dptr, key.dsize);
489
+ valstr = rb_external_str_new(val.dptr, val.dsize);
490
+ sdbm_delete(dbm, key);
491
+ if (dbmp->di_size >= 0) {
492
+ dbmp->di_size--;
493
+ }
494
+
495
+ return rb_assoc_new(keystr, valstr);
496
+ }
497
+
498
+ /*
499
+ * call-seq:
500
+ * sdbm.delete_if { |key, value| ... } -> self
501
+ * sdbm.reject! { |key, value| ... } -> self
502
+ *
503
+ * Iterates over the key-value pairs in the database, deleting those for
504
+ * which the block returns +true+.
505
+ */
506
+ static VALUE
507
+ fsdbm_delete_if(VALUE obj)
508
+ {
509
+ datum key, val;
510
+ struct dbmdata *dbmp;
511
+ DBM *dbm;
512
+ VALUE keystr, valstr;
513
+ VALUE ret, ary = rb_ary_new();
514
+ long i;
515
+ int status = 0, n;
516
+
517
+ fdbm_modify(obj);
518
+ GetDBM2(obj, dbmp, dbm);
519
+ n = dbmp->di_size;
520
+ dbmp->di_size = -1;
521
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
522
+ val = sdbm_fetch(dbm, key);
523
+ keystr = rb_external_str_new(key.dptr, key.dsize);
524
+ valstr = rb_external_str_new(val.dptr, val.dsize);
525
+ ret = rb_protect(rb_yield, rb_assoc_new(rb_str_dup(keystr), valstr), &status);
526
+ if (status != 0) break;
527
+ if (RTEST(ret)) rb_ary_push(ary, keystr);
528
+ GetDBM2(obj, dbmp, dbm);
529
+ }
530
+
531
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
532
+ keystr = RARRAY_AREF(ary, i);
533
+ ExportStringValue(keystr);
534
+ key.dptr = RSTRING_PTR(keystr);
535
+ key.dsize = RSTRING_LENINT(keystr);
536
+ if (sdbm_delete(dbm, key)) {
537
+ rb_raise(rb_eDBMError, "sdbm_delete failed");
538
+ }
539
+ }
540
+ if (status) rb_jump_tag(status);
541
+ if (n > 0) dbmp->di_size = n - RARRAY_LENINT(ary);
542
+
543
+ return obj;
544
+ }
545
+
546
+ /*
547
+ * call-seq:
548
+ * sdbm.clear -> self
549
+ *
550
+ * Deletes all data from the database.
551
+ */
552
+ static VALUE
553
+ fsdbm_clear(VALUE obj)
554
+ {
555
+ datum key;
556
+ struct dbmdata *dbmp;
557
+ DBM *dbm;
558
+
559
+ fdbm_modify(obj);
560
+ GetDBM2(obj, dbmp, dbm);
561
+ dbmp->di_size = -1;
562
+ while (key = sdbm_firstkey(dbm), key.dptr) {
563
+ if (sdbm_delete(dbm, key)) {
564
+ rb_raise(rb_eDBMError, "sdbm_delete failed");
565
+ }
566
+ }
567
+ dbmp->di_size = 0;
568
+
569
+ return obj;
570
+ }
571
+
572
+ /*
573
+ * call-seq:
574
+ * sdbm.invert -> Hash
575
+ *
576
+ * Returns a Hash in which the key-value pairs have been inverted.
577
+ *
578
+ * Example:
579
+ *
580
+ * require 'sdbm'
581
+ *
582
+ * SDBM.open 'my_database' do |db|
583
+ * db.update('apple' => 'fruit', 'spinach' => 'vegetable')
584
+ *
585
+ * db.invert #=> {"fruit" => "apple", "vegetable" => "spinach"}
586
+ * end
587
+ */
588
+ static VALUE
589
+ fsdbm_invert(VALUE obj)
590
+ {
591
+ datum key, val;
592
+ struct dbmdata *dbmp;
593
+ DBM *dbm;
594
+ VALUE keystr, valstr;
595
+ VALUE hash = rb_hash_new();
596
+
597
+ GetDBM2(obj, dbmp, dbm);
598
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
599
+ val = sdbm_fetch(dbm, key);
600
+ keystr = rb_external_str_new(key.dptr, key.dsize);
601
+ valstr = rb_external_str_new(val.dptr, val.dsize);
602
+ rb_hash_aset(hash, valstr, keystr);
603
+ }
604
+ return hash;
605
+ }
606
+
607
+ /*
608
+ * call-seq:
609
+ * sdbm[key] = value -> value
610
+ * sdbm.store(key, value) -> value
611
+ *
612
+ * Stores a new +value+ in the database with the given +key+ as an index.
613
+ *
614
+ * If the +key+ already exists, this will update the +value+ associated with
615
+ * the +key+.
616
+ *
617
+ * Returns the given +value+.
618
+ */
619
+ static VALUE
620
+ fsdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
621
+ {
622
+ datum key, val;
623
+ struct dbmdata *dbmp;
624
+ DBM *dbm;
625
+
626
+ if (valstr == Qnil) {
627
+ fsdbm_delete(obj, keystr);
628
+ return Qnil;
629
+ }
630
+
631
+ fdbm_modify(obj);
632
+ ExportStringValue(keystr);
633
+ ExportStringValue(valstr);
634
+
635
+ key.dptr = RSTRING_PTR(keystr);
636
+ key.dsize = RSTRING_LENINT(keystr);
637
+
638
+ val.dptr = RSTRING_PTR(valstr);
639
+ val.dsize = RSTRING_LENINT(valstr);
640
+
641
+ GetDBM2(obj, dbmp, dbm);
642
+ dbmp->di_size = -1;
643
+ if (sdbm_store(dbm, key, val, DBM_REPLACE)) {
644
+ #ifdef HAVE_DBM_CLAERERR
645
+ sdbm_clearerr(dbm);
646
+ #endif
647
+ if (errno == EPERM) rb_sys_fail(0);
648
+ rb_raise(rb_eDBMError, "sdbm_store failed");
649
+ }
650
+
651
+ return valstr;
652
+ }
653
+
654
+ static VALUE
655
+ update_i(RB_BLOCK_CALL_FUNC_ARGLIST(pair, dbm))
656
+ {
657
+ const VALUE *ptr;
658
+ Check_Type(pair, T_ARRAY);
659
+ if (RARRAY_LEN(pair) < 2) {
660
+ rb_raise(rb_eArgError, "pair must be [key, value]");
661
+ }
662
+ ptr = RARRAY_CONST_PTR(pair);
663
+ fsdbm_store(dbm, ptr[0], ptr[1]);
664
+ return Qnil;
665
+ }
666
+
667
+ /*
668
+ * call-seq:
669
+ * sdbm.update(pairs) -> self
670
+ *
671
+ * Insert or update key-value pairs.
672
+ *
673
+ * This method will work with any object which implements an each_pair
674
+ * method, such as a Hash.
675
+ */
676
+ static VALUE
677
+ fsdbm_update(VALUE obj, VALUE other)
678
+ {
679
+ rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
680
+ return obj;
681
+ }
682
+
683
+ /*
684
+ * call-seq:
685
+ * sdbm.replace(pairs) -> self
686
+ *
687
+ * Empties the database, then inserts the given key-value pairs.
688
+ *
689
+ * This method will work with any object which implements an each_pair
690
+ * method, such as a Hash.
691
+ */
692
+ static VALUE
693
+ fsdbm_replace(VALUE obj, VALUE other)
694
+ {
695
+ fsdbm_clear(obj);
696
+ rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
697
+ return obj;
698
+ }
699
+
700
+ /*
701
+ * call-seq:
702
+ * sdbm.length -> integer
703
+ * sdbm.size -> integer
704
+ *
705
+ * Returns the number of keys in the database.
706
+ */
707
+ static VALUE
708
+ fsdbm_length(VALUE obj)
709
+ {
710
+ datum key;
711
+ struct dbmdata *dbmp;
712
+ DBM *dbm;
713
+ int i = 0;
714
+
715
+ GetDBM2(obj, dbmp, dbm);
716
+ if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
717
+
718
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
719
+ i++;
720
+ }
721
+ dbmp->di_size = i;
722
+
723
+ return INT2FIX(i);
724
+ }
725
+
726
+ /*
727
+ * call-seq:
728
+ * sdbm.empty? -> true or false
729
+ *
730
+ * Returns +true+ if the database is empty.
731
+ */
732
+ static VALUE
733
+ fsdbm_empty_p(VALUE obj)
734
+ {
735
+ datum key;
736
+ struct dbmdata *dbmp;
737
+ DBM *dbm;
738
+
739
+ GetDBM(obj, dbmp);
740
+ if (dbmp->di_size < 0) {
741
+ dbm = dbmp->di_dbm;
742
+
743
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
744
+ return Qfalse;
745
+ }
746
+ }
747
+ else {
748
+ if (dbmp->di_size)
749
+ return Qfalse;
750
+ }
751
+ return Qtrue;
752
+ }
753
+
754
+ /*
755
+ * call-seq:
756
+ * sdbm.each_value
757
+ * sdbm.each_value { |value| ... }
758
+ *
759
+ * Iterates over each +value+ in the database.
760
+ *
761
+ * If no block is given, returns an Enumerator.
762
+ */
763
+ static VALUE
764
+ fsdbm_each_value(VALUE obj)
765
+ {
766
+ datum key, val;
767
+ struct dbmdata *dbmp;
768
+ DBM *dbm;
769
+
770
+ RETURN_ENUMERATOR(obj, 0, 0);
771
+
772
+ GetDBM2(obj, dbmp, dbm);
773
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
774
+ val = sdbm_fetch(dbm, key);
775
+ rb_yield(rb_external_str_new(val.dptr, val.dsize));
776
+ GetDBM2(obj, dbmp, dbm);
777
+ }
778
+ return obj;
779
+ }
780
+
781
+ /*
782
+ * call-seq:
783
+ * sdbm.each_key
784
+ * sdbm.each_key { |key| ... }
785
+ *
786
+ * Iterates over each +key+ in the database.
787
+ *
788
+ * If no block is given, returns an Enumerator.
789
+ */
790
+ static VALUE
791
+ fsdbm_each_key(VALUE obj)
792
+ {
793
+ datum key;
794
+ struct dbmdata *dbmp;
795
+ DBM *dbm;
796
+
797
+ RETURN_ENUMERATOR(obj, 0, 0);
798
+
799
+ GetDBM2(obj, dbmp, dbm);
800
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
801
+ rb_yield(rb_external_str_new(key.dptr, key.dsize));
802
+ GetDBM2(obj, dbmp, dbm);
803
+ }
804
+ return obj;
805
+ }
806
+
807
+ /*
808
+ * call-seq:
809
+ * sdbm.each
810
+ * sdbm.each { |key, value| ... }
811
+ * sdbm.each_pair
812
+ * sdbm.each_pair { |key, value| ... }
813
+ *
814
+ * Iterates over each key-value pair in the database.
815
+ *
816
+ * If no block is given, returns an Enumerator.
817
+ */
818
+ static VALUE
819
+ fsdbm_each_pair(VALUE obj)
820
+ {
821
+ datum key, val;
822
+ DBM *dbm;
823
+ struct dbmdata *dbmp;
824
+ VALUE keystr, valstr;
825
+
826
+ RETURN_ENUMERATOR(obj, 0, 0);
827
+
828
+ GetDBM2(obj, dbmp, dbm);
829
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
830
+ val = sdbm_fetch(dbm, key);
831
+ keystr = rb_external_str_new(key.dptr, key.dsize);
832
+ valstr = rb_external_str_new(val.dptr, val.dsize);
833
+ rb_yield(rb_assoc_new(keystr, valstr));
834
+ GetDBM2(obj, dbmp, dbm);
835
+ }
836
+
837
+ return obj;
838
+ }
839
+
840
+ /*
841
+ * call-seq:
842
+ * sdbm.keys -> Array
843
+ *
844
+ * Returns a new Array containing the keys in the database.
845
+ */
846
+ static VALUE
847
+ fsdbm_keys(VALUE obj)
848
+ {
849
+ datum key;
850
+ struct dbmdata *dbmp;
851
+ DBM *dbm;
852
+ VALUE ary;
853
+
854
+ GetDBM2(obj, dbmp, dbm);
855
+ ary = rb_ary_new();
856
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
857
+ rb_ary_push(ary, rb_external_str_new(key.dptr, key.dsize));
858
+ }
859
+
860
+ return ary;
861
+ }
862
+
863
+ /*
864
+ * call-seq:
865
+ * sdbm.values -> Array
866
+ *
867
+ * Returns a new Array containing the values in the database.
868
+ */
869
+ static VALUE
870
+ fsdbm_values(VALUE obj)
871
+ {
872
+ datum key, val;
873
+ struct dbmdata *dbmp;
874
+ DBM *dbm;
875
+ VALUE ary;
876
+
877
+ GetDBM2(obj, dbmp, dbm);
878
+ ary = rb_ary_new();
879
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
880
+ val = sdbm_fetch(dbm, key);
881
+ rb_ary_push(ary, rb_external_str_new(val.dptr, val.dsize));
882
+ }
883
+
884
+ return ary;
885
+ }
886
+
887
+ /*
888
+ * call-seq:
889
+ * sdbm.include?(key) -> true or false
890
+ * sdbm.key?(key) -> true or false
891
+ * sdbm.member?(key) -> true or false
892
+ * sdbm.has_key?(key) -> true or false
893
+ *
894
+ * Returns +true+ if the database contains the given +key+.
895
+ */
896
+ static VALUE
897
+ fsdbm_has_key(VALUE obj, VALUE keystr)
898
+ {
899
+ datum key, val;
900
+ struct dbmdata *dbmp;
901
+ DBM *dbm;
902
+
903
+ ExportStringValue(keystr);
904
+ key.dptr = RSTRING_PTR(keystr);
905
+ key.dsize = RSTRING_LENINT(keystr);
906
+
907
+ GetDBM2(obj, dbmp, dbm);
908
+ val = sdbm_fetch(dbm, key);
909
+ if (val.dptr) return Qtrue;
910
+ return Qfalse;
911
+ }
912
+
913
+ /*
914
+ * call-seq:
915
+ * sdbm.value?(key) -> true or false
916
+ * sdbm.has_value?(key) -> true or false
917
+ *
918
+ * Returns +true+ if the database contains the given +value+.
919
+ */
920
+ static VALUE
921
+ fsdbm_has_value(VALUE obj, VALUE valstr)
922
+ {
923
+ datum key, val;
924
+ struct dbmdata *dbmp;
925
+ DBM *dbm;
926
+
927
+ ExportStringValue(valstr);
928
+ val.dptr = RSTRING_PTR(valstr);
929
+ val.dsize = RSTRING_LENINT(valstr);
930
+
931
+ GetDBM2(obj, dbmp, dbm);
932
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
933
+ val = sdbm_fetch(dbm, key);
934
+ if (val.dsize == RSTRING_LENINT(valstr) &&
935
+ memcmp(val.dptr, RSTRING_PTR(valstr), val.dsize) == 0)
936
+ return Qtrue;
937
+ }
938
+ return Qfalse;
939
+ }
940
+
941
+ /*
942
+ * call-seq:
943
+ * sdbm.to_a -> Array
944
+ *
945
+ * Returns a new Array containing each key-value pair in the database.
946
+ *
947
+ * Example:
948
+ *
949
+ * require 'sdbm'
950
+ *
951
+ * SDBM.open 'my_database' do |db|
952
+ * db.update('apple' => 'fruit', 'spinach' => 'vegetable')
953
+ *
954
+ * db.to_a #=> [["apple", "fruit"], ["spinach", "vegetable"]]
955
+ * end
956
+ */
957
+ static VALUE
958
+ fsdbm_to_a(VALUE obj)
959
+ {
960
+ datum key, val;
961
+ struct dbmdata *dbmp;
962
+ DBM *dbm;
963
+ VALUE ary;
964
+
965
+ GetDBM2(obj, dbmp, dbm);
966
+ ary = rb_ary_new();
967
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
968
+ val = sdbm_fetch(dbm, key);
969
+ rb_ary_push(ary, rb_assoc_new(rb_external_str_new(key.dptr, key.dsize),
970
+ rb_external_str_new(val.dptr, val.dsize)));
971
+ }
972
+
973
+ return ary;
974
+ }
975
+
976
+ /*
977
+ * call-seq:
978
+ * sdbm.to_hash -> Hash
979
+ *
980
+ * Returns a new Hash containing each key-value pair in the database.
981
+ */
982
+ static VALUE
983
+ fsdbm_to_hash(VALUE obj)
984
+ {
985
+ datum key, val;
986
+ struct dbmdata *dbmp;
987
+ DBM *dbm;
988
+ VALUE hash;
989
+
990
+ GetDBM2(obj, dbmp, dbm);
991
+ hash = rb_hash_new();
992
+ for (key = sdbm_firstkey(dbm); key.dptr; key = sdbm_nextkey(dbm)) {
993
+ val = sdbm_fetch(dbm, key);
994
+ rb_hash_aset(hash, rb_external_str_new(key.dptr, key.dsize),
995
+ rb_external_str_new(val.dptr, val.dsize));
996
+ }
997
+
998
+ return hash;
999
+ }
1000
+
1001
+ /*
1002
+ * call-seq:
1003
+ * sdbm.reject { |key, value| ... } -> Hash
1004
+ *
1005
+ * Creates a new Hash using the key-value pairs from the database, then
1006
+ * calls Hash#reject with the given block, which returns a Hash with
1007
+ * only the key-value pairs for which the block returns +false+.
1008
+ */
1009
+ static VALUE
1010
+ fsdbm_reject(VALUE obj)
1011
+ {
1012
+ return rb_hash_delete_if(fsdbm_to_hash(obj));
1013
+ }
1014
+
1015
+ void
1016
+ Init_sdbm(void)
1017
+ {
1018
+ rb_cDBM = rb_define_class("SDBM", rb_cObject);
1019
+ rb_eDBMError = rb_define_class("SDBMError", rb_eStandardError);
1020
+ /* Document-class: SDBMError
1021
+ * Exception class used to return errors from the sdbm library.
1022
+ */
1023
+ rb_include_module(rb_cDBM, rb_mEnumerable);
1024
+
1025
+ rb_define_alloc_func(rb_cDBM, fsdbm_alloc);
1026
+ rb_define_singleton_method(rb_cDBM, "open", fsdbm_s_open, -1);
1027
+
1028
+ rb_define_method(rb_cDBM, "initialize", fsdbm_initialize, -1);
1029
+ rb_define_method(rb_cDBM, "close", fsdbm_close, 0);
1030
+ rb_define_method(rb_cDBM, "closed?", fsdbm_closed, 0);
1031
+ rb_define_method(rb_cDBM, "[]", fsdbm_aref, 1);
1032
+ rb_define_method(rb_cDBM, "fetch", fsdbm_fetch_m, -1);
1033
+ rb_define_method(rb_cDBM, "[]=", fsdbm_store, 2);
1034
+ rb_define_method(rb_cDBM, "store", fsdbm_store, 2);
1035
+ rb_define_method(rb_cDBM, "index", fsdbm_index, 1);
1036
+ rb_define_method(rb_cDBM, "key", fsdbm_key, 1);
1037
+ rb_define_method(rb_cDBM, "select", fsdbm_select, 0);
1038
+ rb_define_method(rb_cDBM, "values_at", fsdbm_values_at, -1);
1039
+ rb_define_method(rb_cDBM, "length", fsdbm_length, 0);
1040
+ rb_define_method(rb_cDBM, "size", fsdbm_length, 0);
1041
+ rb_define_method(rb_cDBM, "empty?", fsdbm_empty_p, 0);
1042
+ rb_define_method(rb_cDBM, "each", fsdbm_each_pair, 0);
1043
+ rb_define_method(rb_cDBM, "each_value", fsdbm_each_value, 0);
1044
+ rb_define_method(rb_cDBM, "each_key", fsdbm_each_key, 0);
1045
+ rb_define_method(rb_cDBM, "each_pair", fsdbm_each_pair, 0);
1046
+ rb_define_method(rb_cDBM, "keys", fsdbm_keys, 0);
1047
+ rb_define_method(rb_cDBM, "values", fsdbm_values, 0);
1048
+ rb_define_method(rb_cDBM, "shift", fsdbm_shift, 0);
1049
+ rb_define_method(rb_cDBM, "delete", fsdbm_delete, 1);
1050
+ rb_define_method(rb_cDBM, "delete_if", fsdbm_delete_if, 0);
1051
+ rb_define_method(rb_cDBM, "reject!", fsdbm_delete_if, 0);
1052
+ rb_define_method(rb_cDBM, "reject", fsdbm_reject, 0);
1053
+ rb_define_method(rb_cDBM, "clear", fsdbm_clear, 0);
1054
+ rb_define_method(rb_cDBM,"invert", fsdbm_invert, 0);
1055
+ rb_define_method(rb_cDBM,"update", fsdbm_update, 1);
1056
+ rb_define_method(rb_cDBM,"replace", fsdbm_replace, 1);
1057
+
1058
+ rb_define_method(rb_cDBM, "has_key?", fsdbm_has_key, 1);
1059
+ rb_define_method(rb_cDBM, "include?", fsdbm_has_key, 1);
1060
+ rb_define_method(rb_cDBM, "key?", fsdbm_has_key, 1);
1061
+ rb_define_method(rb_cDBM, "member?", fsdbm_has_key, 1);
1062
+ rb_define_method(rb_cDBM, "has_value?", fsdbm_has_value, 1);
1063
+ rb_define_method(rb_cDBM, "value?", fsdbm_has_value, 1);
1064
+
1065
+ rb_define_method(rb_cDBM, "to_a", fsdbm_to_a, 0);
1066
+ rb_define_method(rb_cDBM, "to_hash", fsdbm_to_hash, 0);
1067
+ }