tdb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,690 @@
1
+ #include "rbtdb.h"
2
+ #include <sys/types.h>
3
+ #include <sys/stat.h>
4
+ #include <fcntl.h>
5
+ #include <errno.h>
6
+ #ifdef HAVE_RUBY_ST_H
7
+ # include <ruby/st.h>
8
+ #else
9
+ # include <st.h>
10
+ #endif
11
+
12
+ static VALUE cTDB, cERR;
13
+ static st_table *exc_hash;
14
+ static VALUE hashes;
15
+
16
+ /* must be a macro to prevent GC from killing converted 'val's */
17
+ #define TO_TDB_DATA(data,val) do { \
18
+ StringValue(val); \
19
+ (data).dptr = (unsigned char *)RSTRING_PTR(val); \
20
+ (data).dsize = RSTRING_LEN(val); \
21
+ } while (0)
22
+
23
+ static void init_exc(enum TDB_ERROR ecode, const char *name)
24
+ {
25
+ VALUE exc = rb_define_class_under(cERR, name, cERR);
26
+ st_insert(exc_hash, (st_data_t)ecode, (st_data_t)exc);
27
+ }
28
+
29
+ static void init_errors(void)
30
+ {
31
+ cERR = rb_define_class_under(cTDB, "ERR", rb_eStandardError);
32
+ exc_hash = st_init_numtable();
33
+
34
+ init_exc(TDB_ERR_CORRUPT, "CORRUPT");
35
+ init_exc(TDB_ERR_IO, "IO");
36
+ init_exc(TDB_ERR_LOCK, "LOCK");
37
+ init_exc(TDB_ERR_OOM, "OOM");
38
+ init_exc(TDB_ERR_EXISTS, "EXISTS"),
39
+ init_exc(TDB_ERR_NOLOCK, "NOLOCK");
40
+ init_exc(TDB_ERR_LOCK_TIMEOUT, "LOCK_TIMEOUT");
41
+ init_exc(TDB_ERR_EINVAL, "EINVAL");
42
+ init_exc(TDB_ERR_NOEXIST, "NOEXIST");
43
+ init_exc(TDB_ERR_RDONLY, "RDONLY");
44
+ #ifdef HAVE_CONST_TDB_ERR_NESTING
45
+ init_exc(TDB_ERR_NESTING, "NESTING");
46
+ #endif /* HAVE_CONST_TDB_ERR_NESTING */
47
+ }
48
+
49
+ static void my_raise(struct tdb_context *tdb)
50
+ {
51
+ enum TDB_ERROR ecode = tdb_error(tdb);
52
+ const char *str = tdb_errorstr(tdb);
53
+ VALUE exc;
54
+
55
+ switch (ecode) {
56
+ case TDB_SUCCESS:
57
+ rb_bug("attempted to raise with no error");
58
+ case TDB_ERR_CORRUPT:
59
+ case TDB_ERR_IO:
60
+ case TDB_ERR_LOCK:
61
+ case TDB_ERR_OOM:
62
+ case TDB_ERR_EXISTS:
63
+ case TDB_ERR_NOLOCK:
64
+ case TDB_ERR_LOCK_TIMEOUT:
65
+ case TDB_ERR_EINVAL:
66
+ case TDB_ERR_NOEXIST:
67
+ case TDB_ERR_RDONLY:
68
+ #ifdef HAVE_CONST_TDB_ERR_NESTING
69
+ case TDB_ERR_NESTING:
70
+ #endif /* HAVE_CONST_TDB_ERR_NESTING */
71
+ if (!st_lookup(exc_hash, (st_data_t)ecode, (st_data_t *)&exc))
72
+ rb_bug("no-existent exception: %s\n", str);
73
+ }
74
+ rb_raise(exc, str);
75
+ }
76
+
77
+ static void init_hashes(void)
78
+ {
79
+ #define HF(x) \
80
+ rb_hash_aset(hashes,ID2SYM(rb_intern(#x)),ULONG2NUM((unsigned long)rbtdb_##x))
81
+ HF(murmur1);
82
+ HF(murmur1_aligned);
83
+ HF(murmur2);
84
+ HF(murmur2a);
85
+ HF(murmur2_neutral);
86
+ HF(murmur2_aligned);
87
+ HF(fnv1a);
88
+ HF(djb2);
89
+ HF(djb3);
90
+ HF(jenkins_lookup3);
91
+ HF(default);
92
+ }
93
+
94
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
95
+ /* (very) partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
96
+ # include <rubysig.h>
97
+ typedef VALUE rb_blocking_function_t(void *);
98
+ static VALUE my_tbr(rb_blocking_function_t *fn, void *data)
99
+ {
100
+ VALUE rv;
101
+
102
+ TRAP_BEG;
103
+ rv = fn(data);
104
+ TRAP_END;
105
+
106
+ return rv;
107
+ }
108
+ #else
109
+ static VALUE my_tbr(rb_blocking_function_t *fn, void *data)
110
+ {
111
+ return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
112
+ }
113
+ #endif /* HAVE_RUBY_THREAD_BLOCKING_REGION */
114
+
115
+ static void gcfree(void *ptr)
116
+ {
117
+ struct tdb_context *tdb = ptr;
118
+
119
+ if (tdb)
120
+ (void)tdb_close(tdb);
121
+ }
122
+
123
+ static VALUE alloc(VALUE klass)
124
+ {
125
+ return Data_Wrap_Struct(klass, NULL, gcfree, NULL);
126
+ }
127
+
128
+ static struct tdb_context *db(VALUE self, int check_opened)
129
+ {
130
+ struct tdb_context *tdb;
131
+
132
+ Data_Get_Struct(self, struct tdb_context, tdb);
133
+
134
+ if (!tdb && check_opened)
135
+ rb_raise(rb_eIOError, "closed database");
136
+
137
+ return tdb;
138
+ }
139
+
140
+ struct open_args {
141
+ const char *name;
142
+ int hash_size;
143
+ int tdb_flags;
144
+ int open_flags;
145
+ mode_t mode;
146
+ struct tdb_logging_context *log_ctx;
147
+ tdb_hash_func hash_fn;
148
+ };
149
+
150
+ static VALUE nogvl_open(void *ptr)
151
+ {
152
+ struct open_args *o = ptr;
153
+ struct tdb_context *tdb;
154
+
155
+ tdb = tdb_open_ex(o->name, o->hash_size, o->tdb_flags,
156
+ o->open_flags, o->mode, o->log_ctx, o->hash_fn);
157
+
158
+ return (VALUE)tdb;
159
+ }
160
+
161
+ static void set_args(struct open_args *o, VALUE opts)
162
+ {
163
+ VALUE tmp;
164
+
165
+ o->name = NULL;
166
+ o->hash_size = 0; /* default */
167
+ o->tdb_flags = TDB_DEFAULT;
168
+ o->open_flags = O_RDWR | O_CREAT;
169
+ o->mode = 0666;
170
+ o->log_ctx = NULL;
171
+ o->hash_fn = NULL;
172
+
173
+ if (NIL_P(opts))
174
+ return;
175
+ Check_Type(opts, T_HASH);
176
+
177
+ tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash_size")));
178
+ if (!NIL_P(tmp))
179
+ o->hash_size = NUM2INT(tmp);
180
+
181
+ tmp = rb_hash_aref(opts, ID2SYM(rb_intern("mode")));
182
+ if (!NIL_P(tmp))
183
+ o->mode = NUM2UINT(tmp);
184
+
185
+ tmp = rb_hash_aref(opts, ID2SYM(rb_intern("open_flags")));
186
+ if (!NIL_P(tmp))
187
+ o->open_flags = NUM2INT(tmp);
188
+
189
+ tmp = rb_hash_aref(opts, ID2SYM(rb_intern("tdb_flags")));
190
+ if (!NIL_P(tmp))
191
+ o->tdb_flags = NUM2INT(tmp);
192
+
193
+ tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash")));
194
+ if (!NIL_P(tmp)) {
195
+ VALUE num = rb_hash_aref(hashes, tmp);
196
+
197
+ if (NIL_P(num)) {
198
+ tmp = rb_inspect(tmp);
199
+ rb_raise(rb_eArgError,
200
+ "`%s' is not a valid hash function",
201
+ StringValuePtr(tmp));
202
+ }
203
+
204
+ o->hash_fn = (tdb_hash_func)NUM2ULONG(num);
205
+ }
206
+ }
207
+
208
+ static VALUE init(int argc, VALUE *argv, VALUE self)
209
+ {
210
+ struct tdb_context *tdb = db(self, 0);
211
+ VALUE path, opts;
212
+ struct open_args o;
213
+
214
+ if (tdb)
215
+ rb_raise(rb_eRuntimeError, "TDB already initialized");
216
+ rb_scan_args(argc, argv, "11", &path, &opts);
217
+ set_args(&o, opts);
218
+
219
+ if (NIL_P(path))
220
+ o.tdb_flags |= TDB_INTERNAL;
221
+ else
222
+ o.name = StringValuePtr(path);
223
+
224
+ tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
225
+ if (!tdb) {
226
+ switch (errno) {
227
+ case ENOMEM:
228
+ case EMFILE:
229
+ case ENFILE:
230
+ rb_gc();
231
+ tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
232
+ }
233
+ if (!tdb)
234
+ rb_sys_fail("tdb_open_ex");
235
+ }
236
+ DATA_PTR(self) = tdb;
237
+
238
+ return self;
239
+ }
240
+
241
+ /* tdb_close can do a lot, including cancel transactions an munmap */
242
+ static VALUE nogvl_close(void *ptr)
243
+ {
244
+ struct tdb_context *tdb = ptr;
245
+
246
+ return (VALUE)tdb_close(tdb);
247
+ }
248
+
249
+ static VALUE tdbclose(VALUE self)
250
+ {
251
+ struct tdb_context *tdb = db(self, 1);
252
+
253
+ DATA_PTR(self) = NULL;
254
+
255
+ if ((int)my_tbr(nogvl_close, tdb) == -1)
256
+ rb_sys_fail("tdb_close");
257
+
258
+ return Qnil;
259
+ }
260
+
261
+ static VALUE closed(VALUE self)
262
+ {
263
+ struct tdb_context *tdb = db(self, 0);
264
+
265
+ return tdb ? Qfalse : Qtrue;
266
+ }
267
+
268
+ #ifdef HAVE_RB_THREAD_CALL_WITH_GVL
269
+ /* missing prototype in ruby.h: */
270
+ void *rb_thread_call_with_gvl(void *(*func)(void *), void *data);
271
+ #else
272
+ static void * my_rb_thread_call_with_gvl(void *(*func)(void *), void *data)
273
+ {
274
+ return (*func)(data);
275
+ }
276
+ #define rb_thread_call_with_gvl my_rb_thread_call_with_gvl
277
+ #endif /* !HAVE_RB_THREAD_CALL_WITH_GVL */
278
+
279
+ /*
280
+ * We avoid the extra malloc/free pair enforced by tdb_fetch. We
281
+ * use tdb_parse_record to give us pointers to (hopefully) mmap-ed
282
+ * regions and create a String object directly off that region.
283
+ */
284
+ struct fetch_parse_args {
285
+ struct tdb_context *tdb;
286
+ union {
287
+ TDB_DATA key;
288
+ TDB_DATA val;
289
+ } as;
290
+ VALUE value;
291
+ };
292
+
293
+ static VALUE str_new_tdb_data(TDB_DATA *val)
294
+ {
295
+ return rb_str_new((const char *)val->dptr, val->dsize);
296
+ }
297
+
298
+ static void *gvl_str_new(void *data)
299
+ {
300
+ struct fetch_parse_args *f = data;
301
+
302
+ f->value = str_new_tdb_data(&f->as.val);
303
+
304
+ return NULL;
305
+ }
306
+
307
+ static int fetch_parse(TDB_DATA key, TDB_DATA val, void *data)
308
+ {
309
+ struct fetch_parse_args *f = data;
310
+
311
+ f->as.val = val;
312
+ (void)rb_thread_call_with_gvl(gvl_str_new, data);
313
+
314
+ return 0;
315
+ }
316
+
317
+ static VALUE nogvl_parse_record(void *ptr)
318
+ {
319
+ struct fetch_parse_args *f = ptr;
320
+
321
+ if (tdb_parse_record(f->tdb, f->as.key, fetch_parse, ptr) == -1)
322
+ return Qnil;
323
+
324
+ return f->value;
325
+ }
326
+
327
+ static VALUE fetch(VALUE self, VALUE key)
328
+ {
329
+ struct fetch_parse_args f;
330
+
331
+ f.tdb = db(self, 1);
332
+ TO_TDB_DATA(f.as.key, key);
333
+ f.value = Qnil;
334
+
335
+ return my_tbr(nogvl_parse_record, &f);
336
+ }
337
+
338
+ struct store_args {
339
+ struct tdb_context *tdb;
340
+ TDB_DATA key;
341
+ TDB_DATA val;
342
+ int flag;
343
+ };
344
+
345
+ static VALUE nogvl_store(void *ptr)
346
+ {
347
+ struct store_args *s = ptr;
348
+
349
+ return (VALUE)tdb_store(s->tdb, s->key, s->val, s->flag);
350
+ }
351
+
352
+ static VALUE rbtdb_store(VALUE self, VALUE key, VALUE val, int flag, int soft)
353
+ {
354
+ struct store_args s;
355
+
356
+ s.tdb = db(self, 1);
357
+ TO_TDB_DATA(s.key, key);
358
+ TO_TDB_DATA(s.val, val);
359
+ s.flag = flag;
360
+
361
+ if ((int)my_tbr(nogvl_store, &s) == -1) {
362
+ if (soft) {
363
+ int ecode = tdb_error(s.tdb);
364
+
365
+ if ((flag == TDB_INSERT) && (ecode == TDB_ERR_EXISTS))
366
+ return Qnil;
367
+ if ((flag == TDB_MODIFY) && (ecode == TDB_ERR_NOEXIST))
368
+ return Qnil;
369
+ }
370
+ my_raise(s.tdb);
371
+ }
372
+
373
+ return val;
374
+ }
375
+
376
+ static VALUE store(VALUE self, VALUE key, VALUE val)
377
+ {
378
+ return rbtdb_store(self, key, val, 0, 0);
379
+ }
380
+
381
+ static VALUE insert_bang(VALUE self, VALUE key, VALUE val)
382
+ {
383
+ return rbtdb_store(self, key, val, TDB_INSERT, 0);
384
+ }
385
+
386
+ static VALUE insert(VALUE self, VALUE key, VALUE val)
387
+ {
388
+ return rbtdb_store(self, key, val, TDB_INSERT, 1);
389
+ }
390
+
391
+ static VALUE modify_bang(VALUE self, VALUE key, VALUE val)
392
+ {
393
+ return rbtdb_store(self, key, val, TDB_MODIFY, 0);
394
+ }
395
+
396
+ static VALUE modify(VALUE self, VALUE key, VALUE val)
397
+ {
398
+ return rbtdb_store(self, key, val, TDB_MODIFY, 1);
399
+ }
400
+
401
+ struct exists_args {
402
+ struct tdb_context *tdb;
403
+ TDB_DATA key;
404
+ };
405
+
406
+ static VALUE nogvl_exists(void *ptr)
407
+ {
408
+ struct exists_args *e = ptr;
409
+
410
+ return tdb_exists(e->tdb, e->key) == 0 ? Qfalse : Qtrue;
411
+ }
412
+
413
+ static VALUE has_key(VALUE self, VALUE key)
414
+ {
415
+ struct exists_args e;
416
+
417
+ e.tdb = db(self, 1);
418
+ TO_TDB_DATA(e.key, key);
419
+
420
+ return my_tbr(nogvl_exists, &e);
421
+ }
422
+
423
+ struct traverse_args {
424
+ struct tdb_context *tdb;
425
+ TDB_DATA key;
426
+ TDB_DATA val;
427
+ int state;
428
+ };
429
+
430
+ static VALUE protected_yield(VALUE val)
431
+ {
432
+ VALUE *kv = (VALUE *)val;
433
+
434
+ return rb_yield_values(2, kv[0], kv[1]);
435
+ }
436
+
437
+ static void *my_yield(void *data)
438
+ {
439
+ struct traverse_args *t = data;
440
+ VALUE kv[2];
441
+
442
+ kv[0] = str_new_tdb_data(&t->key);
443
+ kv[1] = str_new_tdb_data(&t->val);
444
+
445
+ rb_protect(protected_yield, (VALUE)kv, &t->state);
446
+
447
+ return NULL;
448
+ }
449
+
450
+ static int
451
+ traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA val, void *data)
452
+ {
453
+ struct traverse_args *t = data;
454
+
455
+ t->key = key;
456
+ t->val = val;
457
+ (void)rb_thread_call_with_gvl(my_yield, t);
458
+
459
+ return t->state;
460
+ }
461
+
462
+ static VALUE nogvl_traverse(void *ptr)
463
+ {
464
+ struct traverse_args *t = ptr;
465
+
466
+ (void)tdb_traverse(t->tdb, traverse_fn, t);
467
+
468
+ return Qfalse;
469
+ }
470
+
471
+ static VALUE each(VALUE self)
472
+ {
473
+ struct traverse_args t;
474
+
475
+ t.tdb = db(self, 1);
476
+ t.state = 0;
477
+
478
+ my_tbr(nogvl_traverse, &t);
479
+ if (t.state)
480
+ rb_jump_tag(t.state);
481
+ return self;
482
+ }
483
+
484
+ struct delete_args {
485
+ struct tdb_context *tdb;
486
+ TDB_DATA key;
487
+ };
488
+
489
+ static VALUE nogvl_delete(void *ptr)
490
+ {
491
+ struct delete_args *d = ptr;
492
+
493
+ return tdb_delete(d->tdb, d->key) == 0 ? Qtrue : Qfalse;
494
+ }
495
+
496
+ static VALUE nuke(VALUE self, VALUE key)
497
+ {
498
+ struct delete_args d;
499
+
500
+ d.tdb = db(self, 1);
501
+ TO_TDB_DATA(d.key, key);
502
+
503
+ return my_tbr(nogvl_delete, &d);
504
+ }
505
+
506
+ static VALUE delete(VALUE self, VALUE key)
507
+ {
508
+ VALUE rc = fetch(self, key);
509
+
510
+ if (! NIL_P(rc))
511
+ if (nuke(self, key) == Qfalse)
512
+ return Qnil;
513
+ return rc;
514
+ }
515
+
516
+ static VALUE lockall(VALUE self)
517
+ {
518
+ struct tdb_context *tdb = db(self, 1);
519
+ if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall, tdb))
520
+ my_raise(tdb);
521
+
522
+ return Qtrue;
523
+ }
524
+
525
+ static VALUE trylockall(VALUE self)
526
+ {
527
+ struct tdb_context *tdb = db(self, 1);
528
+ void *fn = tdb_lockall_nonblock;
529
+
530
+ if ((int)my_tbr((rb_blocking_function_t *)fn, tdb)) {
531
+ if (tdb_error(tdb) == TDB_ERR_LOCK)
532
+ return Qfalse;
533
+ my_raise(tdb);
534
+ }
535
+ return Qtrue;
536
+ }
537
+
538
+ static VALUE unlockall(VALUE self)
539
+ {
540
+ struct tdb_context *tdb = db(self, 1);
541
+ if ((int)my_tbr((rb_blocking_function_t *)tdb_unlockall, tdb))
542
+ my_raise(tdb);
543
+ return Qtrue;
544
+ }
545
+
546
+ static VALUE lockall_read(VALUE self)
547
+ {
548
+ struct tdb_context *tdb = db(self, 1);
549
+ if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_read, tdb))
550
+ my_raise(tdb);
551
+ return Qtrue;
552
+ }
553
+
554
+ static VALUE trylockall_read(VALUE self)
555
+ {
556
+ struct tdb_context *tdb = db(self, 1);
557
+ void *fn = tdb_lockall_read_nonblock;
558
+ if ((int)my_tbr((rb_blocking_function_t *)fn, tdb)) {
559
+ if (tdb_error(tdb) == TDB_ERR_LOCK)
560
+ return Qfalse;
561
+ my_raise(tdb);
562
+ }
563
+ return Qtrue;
564
+ }
565
+
566
+ static VALUE unlockall_read(VALUE self)
567
+ {
568
+ struct tdb_context *tdb = db(self, 1);
569
+ if ((int)my_tbr((rb_blocking_function_t *)tdb_unlockall_read, tdb))
570
+ my_raise(tdb);
571
+ return Qtrue;
572
+ }
573
+
574
+ static VALUE lockall_mark(VALUE self)
575
+ {
576
+ struct tdb_context *tdb = db(self, 1);
577
+ if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_mark, tdb))
578
+ my_raise(tdb);
579
+ return Qtrue;
580
+ }
581
+
582
+ static VALUE lockall_unmark(VALUE self)
583
+ {
584
+ struct tdb_context *tdb = db(self, 1);
585
+ if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_unmark, tdb))
586
+ my_raise(tdb);
587
+ return Qtrue;
588
+ }
589
+
590
+ void Init_tdb_ext(void)
591
+ {
592
+ cTDB = rb_define_class("TDB", rb_cObject);
593
+
594
+ hashes = rb_hash_new();
595
+ rb_define_const(cTDB, "HASHES", hashes);
596
+
597
+ rb_define_alloc_func(cTDB, alloc);
598
+ rb_include_module(cTDB, rb_mEnumerable);
599
+
600
+ rb_define_method(cTDB, "initialize", init, -1);
601
+ rb_define_method(cTDB, "close", tdbclose, 0);
602
+ rb_define_method(cTDB, "closed?", closed, 0);
603
+
604
+ rb_define_method(cTDB, "fetch", fetch, 1);
605
+ rb_define_method(cTDB, "[]", fetch, 1);
606
+ rb_define_method(cTDB, "store", store, 2);
607
+ rb_define_method(cTDB, "[]=", store, 2);
608
+ rb_define_method(cTDB, "insert!", insert_bang, 2);
609
+ rb_define_method(cTDB, "modify!", modify_bang, 2);
610
+ rb_define_method(cTDB, "insert", insert, 2);
611
+ rb_define_method(cTDB, "modify", modify, 2);
612
+
613
+ rb_define_method(cTDB, "key?", has_key, 1);
614
+ rb_define_method(cTDB, "has_key?", has_key, 1);
615
+ rb_define_method(cTDB, "include?", has_key, 1);
616
+ rb_define_method(cTDB, "member?", has_key, 1);
617
+ rb_define_method(cTDB, "each", each, 0);
618
+ rb_define_method(cTDB, "nuke!", nuke, 1);
619
+ rb_define_method(cTDB, "delete", delete, 1);
620
+
621
+ rb_define_method(cTDB, "lockall", lockall, 0);
622
+ rb_define_method(cTDB, "trylockall", trylockall, 0);
623
+ rb_define_method(cTDB, "unlockall", unlockall, 0);
624
+ rb_define_method(cTDB, "lockall_read", lockall_read, 0);
625
+ rb_define_method(cTDB, "trylockall_read", trylockall_read, 0);
626
+ rb_define_method(cTDB, "unlockall_read", unlockall_read, 0);
627
+ rb_define_method(cTDB, "lockall_mark", lockall_mark, 0);
628
+ rb_define_method(cTDB, "lockall_unmark", lockall_unmark, 0);
629
+
630
+ init_errors();
631
+ init_hashes();
632
+
633
+ #define tdb_CONST(x) rb_define_const(cTDB, #x, UINT2NUM(TDB_##x))
634
+
635
+ /* just a readability place holder */
636
+ tdb_CONST(DEFAULT);
637
+
638
+ /* clear database if we are the only one with it open */
639
+ tdb_CONST(CLEAR_IF_FIRST);
640
+
641
+ /* don't store on disk, use in-memory database */
642
+ tdb_CONST(INTERNAL);
643
+
644
+ /* don't do any locking */
645
+ tdb_CONST(NOLOCK);
646
+
647
+ /* don't use mmap */
648
+ tdb_CONST(NOMMAP);
649
+
650
+ /* convert endian (internal use) */
651
+ tdb_CONST(CONVERT);
652
+
653
+ /* header is big-endian (internal use) */
654
+ tdb_CONST(BIGENDIAN);
655
+
656
+ /* don't use synchronous transactions */
657
+ tdb_CONST(NOSYNC);
658
+
659
+ /* maintain a sequence number */
660
+ tdb_CONST(SEQNUM);
661
+
662
+ /* Activate the per-hashchain freelist, default 5 */
663
+ tdb_CONST(VOLATILE);
664
+
665
+ #ifdef TDB_ALLOW_NESTING
666
+ /* Allow transactions to nest */
667
+ tdb_CONST(ALLOW_NESTING);
668
+ #endif
669
+
670
+ #ifdef TDB_DISALLOW_NESTING
671
+ /* Disallow transactions to nest */
672
+ tdb_CONST(DISALLOW_NESTING);
673
+ #endif
674
+
675
+ #ifdef TDB_INCOMPATIBLE_HASH
676
+ /* Better hashing: can't be opened by tdb < 1.2.6. */
677
+ tdb_CONST(INCOMPATIBLE_HASH);
678
+ #endif
679
+ }
680
+
681
+ /*
682
+ * Document-class: TDB
683
+ *
684
+ * <code>
685
+ * tdb = TDB.new("/path/to/file", flags => IO::RDWR|IO::CREAT)
686
+ * tdb.store("HELLO", "world")
687
+ * tdb.fetch("HELLO") -> "world"
688
+ * tdb.delete("HELLO") -> "world"
689
+ * </code>
690
+ */