add murmur3a hash
[ruby-tdb.git] / ext / tdb / tdb.c
blob61227f6ac168e6406126c2748f77cf6f5adf30d4
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 #include <pthread.h>
13 /* this protects the global list of tdb objects maintained by libtdb */
14 static pthread_mutex_t big_lock = PTHREAD_MUTEX_INITIALIZER;
15 static VALUE cTDB, cERR;
16 static st_table *exc_hash;
17 static VALUE hashes;
19 /* must be a macro to prevent GC from killing converted 'val's */
20 #define TO_TDB_DATA(data,val) do { \
21 StringValue(val); \
22 (data).dptr = (unsigned char *)RSTRING_PTR(val); \
23 (data).dsize = RSTRING_LEN(val); \
24 } while (0)
26 static void init_exc(enum TDB_ERROR ecode, const char *name)
28 VALUE exc = rb_define_class_under(cERR, name, cERR);
29 st_insert(exc_hash, (st_data_t)ecode, (st_data_t)exc);
32 static void init_errors(void)
34 cERR = rb_define_class_under(cTDB, "ERR", rb_eStandardError);
35 exc_hash = st_init_numtable();
37 init_exc(TDB_ERR_CORRUPT, "CORRUPT");
38 init_exc(TDB_ERR_IO, "IO");
39 init_exc(TDB_ERR_LOCK, "LOCK");
40 init_exc(TDB_ERR_OOM, "OOM");
41 init_exc(TDB_ERR_EXISTS, "EXISTS"),
42 init_exc(TDB_ERR_NOLOCK, "NOLOCK");
43 init_exc(TDB_ERR_LOCK_TIMEOUT, "LOCK_TIMEOUT");
44 init_exc(TDB_ERR_EINVAL, "EINVAL");
45 init_exc(TDB_ERR_NOEXIST, "NOEXIST");
46 init_exc(TDB_ERR_RDONLY, "RDONLY");
47 #ifdef HAVE_CONST_TDB_ERR_NESTING
48 init_exc(TDB_ERR_NESTING, "NESTING");
49 #endif /* HAVE_CONST_TDB_ERR_NESTING */
52 static void my_raise(struct tdb_context *tdb)
54 enum TDB_ERROR ecode = tdb_error(tdb);
55 const char *str = tdb_errorstr(tdb);
56 VALUE exc;
58 switch (ecode) {
59 case TDB_SUCCESS:
60 rb_bug("attempted to raise with no error");
61 case TDB_ERR_CORRUPT:
62 case TDB_ERR_IO:
63 case TDB_ERR_LOCK:
64 case TDB_ERR_OOM:
65 case TDB_ERR_EXISTS:
66 case TDB_ERR_NOLOCK:
67 case TDB_ERR_LOCK_TIMEOUT:
68 case TDB_ERR_EINVAL:
69 case TDB_ERR_NOEXIST:
70 case TDB_ERR_RDONLY:
71 #ifdef HAVE_CONST_TDB_ERR_NESTING
72 case TDB_ERR_NESTING:
73 #endif /* HAVE_CONST_TDB_ERR_NESTING */
74 if (!st_lookup(exc_hash, (st_data_t)ecode, (st_data_t *)&exc))
75 rb_bug("no-existent exception: %s\n", str);
77 rb_raise(exc, str);
80 static void init_hashes(void)
82 #define HF(x) \
83 rb_hash_aset(hashes,ID2SYM(rb_intern(#x)),ULONG2NUM((unsigned long)rbtdb_##x))
84 HF(murmur1);
85 HF(murmur1_aligned);
86 HF(murmur2);
87 HF(murmur2a);
88 HF(murmur2_neutral);
89 HF(murmur2_aligned);
90 HF(murmur3a);
91 HF(fnv1a);
92 HF(djb2);
93 HF(djb3);
94 HF(jenkins_lookup3);
95 HF(default);
98 #ifndef HAVE_RB_THREAD_BLOCKING_REGION
99 /* (very) partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
100 # include <rubysig.h>
101 typedef VALUE rb_blocking_function_t(void *);
102 static VALUE my_tbr(rb_blocking_function_t *fn, void *data)
104 VALUE rv;
106 TRAP_BEG;
107 rv = fn(data);
108 TRAP_END;
110 return rv;
112 #else
113 static VALUE my_tbr(rb_blocking_function_t *fn, void *data)
115 return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
117 #endif /* HAVE_RUBY_THREAD_BLOCKING_REGION */
119 static void gcfree(void *ptr)
121 struct tdb_context *tdb = ptr;
123 /* no error checking in GC :< */
124 if (tdb) {
125 (void)pthread_mutex_lock(&big_lock);
126 (void)tdb_close(tdb);
127 (void)pthread_mutex_unlock(&big_lock);
131 static VALUE alloc(VALUE klass)
133 return Data_Wrap_Struct(klass, NULL, gcfree, NULL);
136 static struct tdb_context *db(VALUE self, int check_opened)
138 struct tdb_context *tdb;
140 Data_Get_Struct(self, struct tdb_context, tdb);
142 if (!tdb && check_opened)
143 rb_raise(rb_eIOError, "closed database");
145 return tdb;
148 struct open_args {
149 const char *name;
150 int hash_size;
151 int tdb_flags;
152 int open_flags;
153 mode_t mode;
154 struct tdb_logging_context *log_ctx;
155 tdb_hash_func hash_fn;
158 static VALUE nogvl_open(void *ptr)
160 struct open_args *o = ptr;
161 struct tdb_context *tdb;
163 pthread_mutex_lock(&big_lock);
164 tdb = tdb_open_ex(o->name, o->hash_size, o->tdb_flags,
165 o->open_flags, o->mode, o->log_ctx, o->hash_fn);
166 pthread_mutex_unlock(&big_lock);
168 return (VALUE)tdb;
171 static void set_args(VALUE self, struct open_args *o, VALUE opts)
173 VALUE tmp;
175 o->name = NULL;
176 o->hash_size = 0; /* default */
177 o->tdb_flags = TDB_DEFAULT;
178 o->open_flags = O_RDWR | O_CREAT;
179 o->mode = 0666;
180 o->log_ctx = NULL;
181 o->hash_fn = NULL;
183 if (NIL_P(opts))
184 return;
185 Check_Type(opts, T_HASH);
187 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash_size")));
188 if (!NIL_P(tmp))
189 o->hash_size = NUM2INT(tmp);
191 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("mode")));
192 if (!NIL_P(tmp))
193 o->mode = NUM2UINT(tmp);
195 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("open_flags")));
196 if (!NIL_P(tmp))
197 o->open_flags = NUM2INT(tmp);
199 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("tdb_flags")));
200 if (!NIL_P(tmp))
201 o->tdb_flags = NUM2INT(tmp);
203 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("hash")));
204 if (!NIL_P(tmp)) {
205 VALUE num = rb_hash_aref(hashes, tmp);
207 if (NIL_P(num)) {
208 tmp = rb_inspect(tmp);
209 rb_raise(rb_eArgError,
210 "`%s' is not a valid hash function",
211 StringValuePtr(tmp));
214 o->hash_fn = (tdb_hash_func)NUM2ULONG(num);
217 tmp = rb_hash_aref(opts, ID2SYM(rb_intern("threadsafe")));
218 if (RTEST(tmp))
219 rb_funcall(self, rb_intern("threadsafe!"), 0);
223 * :call-seq:
225 * TDB.new("/path/to/file") -> TDB
226 * TDB.new("/path/to/file", :hash_size => 666) -> TDB
227 * TDB.new("/path/to/file", :hash => :murmur2) -> TDB
228 * TDB.new("/path/to/file", :open_flags => IO::RDONLY) -> TDB
229 * TDB.new("/path/to/file", :tdb_flags => TDB::NOSYNC) -> TDB
231 * Initializes a TDB context. It takes several options.
233 * :hash_size - the number of buckets, this is the most important tuning
234 * parameter when creating large databases. This parameter only affects
235 * the creation of new databases.
237 * :open_flags - a bit mask of IO flags passed directly to open(2),
238 * File.open-compatible flags are accepted.
240 * :hash - any of the hashes described in Hash_Functions.
241 * This must remain the same for all clients.
243 * :tdb_flags - a bitmask of any combination of TDB::CLEAR_IF_FIRST,
244 * TDB::INTERNAL, TDB::NOLOCK, TDB::NOMMAP, TDB::CONVERT,
245 * TDB::BIGENDIAN, TDB::NOSYNC, TDB::SEQNUM, TDB::VOLATILE,
246 * TDB::ALLOW_NESTING, TDB::DISALLOW_NESTING, TDB::INCOMPATIBLE_HASH
248 * :mode - octal mode mask passed to open(2)
250 static VALUE init(int argc, VALUE *argv, VALUE self)
252 struct tdb_context *tdb = db(self, 0);
253 VALUE path, opts;
254 struct open_args o;
256 if (tdb)
257 rb_raise(rb_eRuntimeError, "TDB already initialized");
258 rb_scan_args(argc, argv, "11", &path, &opts);
259 set_args(self, &o, opts);
261 if (NIL_P(path))
262 o.tdb_flags |= TDB_INTERNAL;
263 else
264 o.name = StringValuePtr(path);
266 tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
267 if (!tdb) {
268 switch (errno) {
269 case ENOMEM:
270 case EMFILE:
271 case ENFILE:
272 rb_gc();
273 tdb = (struct tdb_context *)my_tbr(nogvl_open, &o);
275 if (!tdb)
276 rb_sys_fail("tdb_open_ex");
278 DATA_PTR(self) = tdb;
280 return self;
283 /* tdb_close can do a lot, including cancel transactions an munmap */
284 static VALUE nogvl_close(void *ptr)
286 struct tdb_context *tdb = ptr;
287 VALUE rv;
289 pthread_mutex_lock(&big_lock);
290 rv = (VALUE)tdb_close(tdb);
291 pthread_mutex_unlock(&big_lock);
293 return rv;
296 static VALUE tdbclose(VALUE self)
298 struct tdb_context *tdb = db(self, 1);
300 DATA_PTR(self) = NULL;
302 if ((int)my_tbr(nogvl_close, tdb) == -1)
303 rb_sys_fail("tdb_close");
305 return Qnil;
308 static VALUE closed(VALUE self)
310 struct tdb_context *tdb = db(self, 0);
312 return tdb ? Qfalse : Qtrue;
315 #ifdef HAVE_RB_THREAD_CALL_WITH_GVL
316 /* missing prototype in ruby.h: */
317 void *rb_thread_call_with_gvl(void *(*func)(void *), void *data);
318 #else
319 static void * my_rb_thread_call_with_gvl(void *(*func)(void *), void *data)
321 return (*func)(data);
323 #define rb_thread_call_with_gvl my_rb_thread_call_with_gvl
324 #endif /* !HAVE_RB_THREAD_CALL_WITH_GVL */
327 * We avoid the extra malloc/free pair enforced by tdb_fetch. We
328 * use tdb_parse_record to give us pointers to (hopefully) mmap-ed
329 * regions and create a String object directly off that region.
331 struct fetch_parse_args {
332 struct tdb_context *tdb;
333 union {
334 TDB_DATA key;
335 long value_len;
336 char *value_ptr;
337 VALUE value;
338 } as;
339 VALUE value;
342 static VALUE str_new_tdb_data(TDB_DATA *val)
344 return rb_str_new((const char *)val->dptr, val->dsize);
347 static void *gvl_str_resize(void *data)
349 struct fetch_parse_args *f = data;
351 rb_str_resize(f->value, f->as.value_len);
352 f->as.value_ptr = RSTRING_PTR(f->value);
354 return NULL;
357 static int fetch_parse(TDB_DATA key, TDB_DATA val, void *data)
359 struct fetch_parse_args *f = data;
361 f->as.value_len = val.dsize;
362 (void)rb_thread_call_with_gvl(gvl_str_resize, data);
363 memcpy(f->as.value_ptr, val.dptr, val.dsize);
364 f->as.value = f->value;
366 return 0;
369 static VALUE nogvl_parse_record(void *ptr)
371 struct fetch_parse_args *f = ptr;
373 if (tdb_parse_record(f->tdb, f->as.key, fetch_parse, ptr) == -1)
374 return Qnil;
376 return f->value == f->as.value ? f->value : Qnil;
379 static VALUE fetch(int argc, VALUE *argv, VALUE self)
381 struct fetch_parse_args f;
382 VALUE key;
384 rb_scan_args(argc, argv, "11", &key, &f.value);
385 if (NIL_P(f.value)) {
386 f.value = rb_str_new(0, 0);
387 } else {
388 StringValue(f.value);
389 rb_str_set_len(f.value, 0);
392 f.tdb = db(self, 1);
393 TO_TDB_DATA(f.as.key, key);
395 return my_tbr(nogvl_parse_record, &f);
398 struct store_args {
399 struct tdb_context *tdb;
400 TDB_DATA key;
401 TDB_DATA val;
402 int flag;
405 static VALUE nogvl_store(void *ptr)
407 struct store_args *s = ptr;
409 return (VALUE)tdb_store(s->tdb, s->key, s->val, s->flag);
412 static VALUE rbtdb_store(VALUE self, VALUE key, VALUE val, int flag, int soft)
414 struct store_args s;
416 s.tdb = db(self, 1);
417 TO_TDB_DATA(s.key, key);
418 TO_TDB_DATA(s.val, val);
419 s.flag = flag;
421 if ((int)my_tbr(nogvl_store, &s) == -1) {
422 if (soft) {
423 int ecode = tdb_error(s.tdb);
425 if ((flag == TDB_INSERT) && (ecode == TDB_ERR_EXISTS))
426 return Qnil;
427 if ((flag == TDB_MODIFY) && (ecode == TDB_ERR_NOEXIST))
428 return Qnil;
430 my_raise(s.tdb);
433 return val;
436 static VALUE store(VALUE self, VALUE key, VALUE val)
438 return rbtdb_store(self, key, val, 0, 0);
441 static VALUE insert_bang(VALUE self, VALUE key, VALUE val)
443 return rbtdb_store(self, key, val, TDB_INSERT, 0);
446 static VALUE insert(VALUE self, VALUE key, VALUE val)
448 return rbtdb_store(self, key, val, TDB_INSERT, 1);
451 static VALUE modify_bang(VALUE self, VALUE key, VALUE val)
453 return rbtdb_store(self, key, val, TDB_MODIFY, 0);
456 static VALUE modify(VALUE self, VALUE key, VALUE val)
458 return rbtdb_store(self, key, val, TDB_MODIFY, 1);
461 struct exists_args {
462 struct tdb_context *tdb;
463 TDB_DATA key;
466 static VALUE nogvl_exists(void *ptr)
468 struct exists_args *e = ptr;
470 return tdb_exists(e->tdb, e->key) == 0 ? Qfalse : Qtrue;
473 static VALUE has_key(VALUE self, VALUE key)
475 struct exists_args e;
477 e.tdb = db(self, 1);
478 TO_TDB_DATA(e.key, key);
480 return my_tbr(nogvl_exists, &e);
483 struct traverse_args {
484 struct tdb_context *tdb;
485 TDB_DATA key;
486 TDB_DATA val;
487 int state;
490 static VALUE protected_yield(VALUE val)
492 VALUE *kv = (VALUE *)val;
494 return rb_yield_values(2, kv[0], kv[1]);
497 static void *my_yield(void *data)
499 struct traverse_args *t = data;
500 VALUE kv[2];
502 kv[0] = str_new_tdb_data(&t->key);
503 kv[1] = str_new_tdb_data(&t->val);
505 rb_protect(protected_yield, (VALUE)kv, &t->state);
507 return NULL;
510 static int
511 traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA val, void *data)
513 struct traverse_args *t = data;
515 t->key = key;
516 t->val = val;
517 (void)rb_thread_call_with_gvl(my_yield, t);
519 return t->state;
522 static VALUE nogvl_traverse(void *ptr)
524 struct traverse_args *t = ptr;
526 (void)tdb_traverse(t->tdb, traverse_fn, t);
528 return Qfalse;
531 static VALUE each(VALUE self)
533 struct traverse_args t;
535 t.tdb = db(self, 1);
536 t.state = 0;
538 my_tbr(nogvl_traverse, &t);
539 if (t.state)
540 rb_jump_tag(t.state);
541 return self;
544 struct delete_args {
545 struct tdb_context *tdb;
546 TDB_DATA key;
549 static VALUE nogvl_delete(void *ptr)
551 struct delete_args *d = ptr;
553 return tdb_delete(d->tdb, d->key) == 0 ? Qtrue : Qfalse;
556 static VALUE nuke(VALUE self, VALUE key)
558 struct delete_args d;
560 d.tdb = db(self, 1);
561 TO_TDB_DATA(d.key, key);
563 return my_tbr(nogvl_delete, &d);
566 static VALUE aref(VALUE self, VALUE key)
568 return fetch(1, &key, self);
571 static VALUE delete(int argc, VALUE *argv, VALUE self)
573 VALUE rc = fetch(argc, argv, self);
575 if (! NIL_P(rc))
576 if (nuke(self, argv[0]) == Qfalse)
577 return Qnil;
578 return rc;
581 static VALUE lockall(VALUE self)
583 struct tdb_context *tdb = db(self, 1);
584 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall, tdb))
585 my_raise(tdb);
587 return Qtrue;
590 static VALUE trylockall(VALUE self)
592 struct tdb_context *tdb = db(self, 1);
593 void *fn = tdb_lockall_nonblock;
595 if ((int)my_tbr((rb_blocking_function_t *)fn, tdb)) {
596 if (tdb_error(tdb) == TDB_ERR_LOCK)
597 return Qfalse;
598 my_raise(tdb);
600 return Qtrue;
603 static VALUE unlockall(VALUE self)
605 struct tdb_context *tdb = db(self, 1);
606 if ((int)my_tbr((rb_blocking_function_t *)tdb_unlockall, tdb))
607 my_raise(tdb);
608 return Qtrue;
611 static VALUE lockall_read(VALUE self)
613 struct tdb_context *tdb = db(self, 1);
614 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_read, tdb))
615 my_raise(tdb);
616 return Qtrue;
619 static VALUE trylockall_read(VALUE self)
621 struct tdb_context *tdb = db(self, 1);
622 void *fn = tdb_lockall_read_nonblock;
623 if ((int)my_tbr((rb_blocking_function_t *)fn, tdb)) {
624 if (tdb_error(tdb) == TDB_ERR_LOCK)
625 return Qfalse;
626 my_raise(tdb);
628 return Qtrue;
631 static VALUE unlockall_read(VALUE self)
633 struct tdb_context *tdb = db(self, 1);
634 if ((int)my_tbr((rb_blocking_function_t *)tdb_unlockall_read, tdb))
635 my_raise(tdb);
636 return Qtrue;
639 static VALUE lockall_mark(VALUE self)
641 struct tdb_context *tdb = db(self, 1);
642 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_mark, tdb))
643 my_raise(tdb);
644 return Qtrue;
647 static VALUE lockall_unmark(VALUE self)
649 struct tdb_context *tdb = db(self, 1);
650 if ((int)my_tbr((rb_blocking_function_t *)tdb_lockall_unmark, tdb))
651 my_raise(tdb);
652 return Qtrue;
656 * clears out the database
658 static VALUE clear(VALUE self)
660 struct tdb_context *tdb = db(self, 1);
661 if ((int)my_tbr((rb_blocking_function_t *)tdb_wipe_all, tdb))
662 my_raise(tdb);
663 return self;
666 #ifdef HAVE_TDB_REPACK
667 /* repacks a database to reduce fragmentation, available with tdb 1.2.x+ */
668 static VALUE repack(VALUE self)
670 struct tdb_context *tdb = db(self, 1);
671 if ((int)my_tbr((rb_blocking_function_t *)tdb_repack, tdb))
672 my_raise(tdb);
673 return self;
675 #endif /* HAVE_TDB_REPACK */
677 void Init_tdb_ext(void)
679 cTDB = rb_define_class("TDB", rb_cObject);
681 hashes = rb_hash_new();
684 * Available hash functions, the key is the name of the hash
685 * and the value is a pointer for internal for usage.
687 rb_define_const(cTDB, "HASHES", hashes);
689 rb_define_alloc_func(cTDB, alloc);
690 rb_include_module(cTDB, rb_mEnumerable);
692 rb_define_method(cTDB, "initialize", init, -1);
693 rb_define_method(cTDB, "close", tdbclose, 0);
694 rb_define_method(cTDB, "closed?", closed, 0);
696 rb_define_method(cTDB, "fetch", fetch, -1);
697 rb_define_method(cTDB, "[]", aref, 1);
698 rb_define_method(cTDB, "store", store, 2);
699 rb_define_method(cTDB, "[]=", store, 2);
700 rb_define_method(cTDB, "insert!", insert_bang, 2);
701 rb_define_method(cTDB, "modify!", modify_bang, 2);
702 rb_define_method(cTDB, "insert", insert, 2);
703 rb_define_method(cTDB, "modify", modify, 2);
705 rb_define_method(cTDB, "key?", has_key, 1);
706 rb_define_method(cTDB, "has_key?", has_key, 1);
707 rb_define_method(cTDB, "include?", has_key, 1);
708 rb_define_method(cTDB, "member?", has_key, 1);
709 rb_define_method(cTDB, "each", each, 0);
710 rb_define_method(cTDB, "nuke!", nuke, 1);
711 rb_define_method(cTDB, "delete", delete, -1);
713 rb_define_method(cTDB, "lockall", lockall, 0);
714 rb_define_method(cTDB, "trylockall", trylockall, 0);
715 rb_define_method(cTDB, "unlockall", unlockall, 0);
716 rb_define_method(cTDB, "lockall_read", lockall_read, 0);
717 rb_define_method(cTDB, "trylockall_read", trylockall_read, 0);
718 rb_define_method(cTDB, "unlockall_read", unlockall_read, 0);
719 rb_define_method(cTDB, "lockall_mark", lockall_mark, 0);
720 rb_define_method(cTDB, "lockall_unmark", lockall_unmark, 0);
721 rb_define_method(cTDB, "clear", clear, 0);
722 #ifdef HAVE_TDB_REPACK
723 rb_define_method(cTDB, "repack", repack, 0);
724 #endif /* HAVE_TDB_REPACK */
726 init_errors();
727 init_hashes();
729 /* just a readability place holder */
730 rb_define_const(cTDB, "DEFAULT", UINT2NUM(TDB_DEFAULT));
732 /* clear database if we are the only one with it open */
733 rb_define_const(cTDB, "CLEAR_IF_FIRST", UINT2NUM(TDB_CLEAR_IF_FIRST));
735 /* don't store on disk, use in-memory database */
736 rb_define_const(cTDB, "INTERNAL", UINT2NUM(TDB_INTERNAL));
738 /* don't do any locking */
739 rb_define_const(cTDB, "NOLOCK", UINT2NUM(TDB_NOLOCK));
741 /* don't use mmap */
742 rb_define_const(cTDB, "NOMMAP", UINT2NUM(TDB_NOMMAP));
744 /* convert endian (internal use) */
745 rb_define_const(cTDB, "CONVERT", UINT2NUM(TDB_CONVERT));
747 /* header is big-endian (internal use) */
748 rb_define_const(cTDB, "BIGENDIAN", UINT2NUM(TDB_BIGENDIAN));
750 /* don't use synchronous transactions */
751 rb_define_const(cTDB, "NOSYNC", UINT2NUM(TDB_NOSYNC));
753 /* maintain a sequence number */
754 rb_define_const(cTDB, "SEQNUM", UINT2NUM(TDB_SEQNUM));
756 /* Activate the per-hashchain freelist, default 5 */
757 rb_define_const(cTDB, "VOLATILE", UINT2NUM(TDB_VOLATILE));
759 #ifdef TDB_ALLOW_NESTING
760 /* Allow transactions to nest */
761 rb_define_const(cTDB, "ALLOW_NESTING", UINT2NUM(TDB_ALLOW_NESTING));
762 #endif
764 #ifdef TDB_DISALLOW_NESTING
765 /* Disallow transactions to nest */
766 rb_define_const(cTDB, "DISALLOW_NESTING", UINT2NUM(TDB_DISALLOW_NESTING));
767 #endif
769 #ifdef TDB_INCOMPATIBLE_HASH
770 /* Better hashing, but can't be opened by tdb < 1.2.6. */
771 rb_define_const(cTDB, "INCOMPATIBLE_HASH", UINT2NUM(TDB_INCOMPATIBLE_HASH));
772 #endif
773 rbtdb_init_tdb_hash_functions();
777 * Document-class: TDB
779 * <code>
780 * tdb = TDB.new("/path/to/file", flags => IO::RDWR|IO::CREAT)
781 * tdb.store("HELLO", "world")
782 * tdb.fetch("HELLO") -> "world"
783 * tdb.delete("HELLO") -> "world"
784 * </code>