/*************************************************************************************************
 * Ruby binding of Tokyo Cabinet
 *                                                               Copyright (C) 2006-2010 FAL Labs
 * This file is part of Tokyo Cabinet.
 * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
 * the GNU Lesser General Public License as published by the Free Software Foundation; either
 * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with Tokyo
 * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA.
 *************************************************************************************************/


#include "ruby.h"
#include <tcutil.h>
#include <tchdb.h>
#include <tcbdb.h>
#include <tcfdb.h>
#include <tctdb.h>
#include <tcadb.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <math.h>

#define HDBVNDATA      "@hdb"
#define BDBVNDATA      "@bdb"
#define BDBCURVNDATA   "@bdbcur"
#define FDBVNDATA      "@fdb"
#define TDBVNDATA      "@tdb"
#define TDBQRYVNDATA   "@tdbqry"
#define ADBVNDATA      "@adb"
#define NUMBUFSIZ      32

#if !defined(RSTRING_PTR)
#define RSTRING_PTR(TC_s) (RSTRING(TC_s)->ptr)
#endif
#if !defined(RSTRING_LEN)
#define RSTRING_LEN(TC_s) (RSTRING(TC_s)->len)
#endif
#if !defined(RARRAY_LEN)
#define RARRAY_LEN(TC_a) (RARRAY(TC_a)->len)
#endif


/* private function prototypes */
static VALUE StringValueEx(VALUE vobj);
static TCLIST *varytolist(VALUE vary);
static VALUE listtovary(TCLIST *list);
static TCMAP *vhashtomap(VALUE vhash);
static VALUE maptovhash(TCMAP *map);
static void hdb_init(void);
static VALUE hdb_initialize(VALUE vself);
static VALUE hdb_errmsg(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_ecode(VALUE vself);
static VALUE hdb_tune(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_setcache(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_setxmsiz(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_setdfunit(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_open(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_close(VALUE vself);
static VALUE hdb_put(VALUE vself, VALUE vkey, VALUE vval);
static VALUE hdb_putkeep(VALUE vself, VALUE vkey, VALUE vval);
static VALUE hdb_putcat(VALUE vself, VALUE vkey, VALUE vval);
static VALUE hdb_putasync(VALUE vself, VALUE vkey, VALUE vval);
static VALUE hdb_out(VALUE vself, VALUE vkey);
static VALUE hdb_get(VALUE vself, VALUE vkey);
static VALUE hdb_vsiz(VALUE vself, VALUE vkey);
static VALUE hdb_iterinit(VALUE vself);
static VALUE hdb_iternext(VALUE vself);
static VALUE hdb_fwmkeys(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_addint(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE hdb_adddouble(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE hdb_sync(VALUE vself);
static VALUE hdb_optimize(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_vanish(VALUE vself);
static VALUE hdb_copy(VALUE vself, VALUE vpath);
static VALUE hdb_tranbegin(VALUE vself);
static VALUE hdb_trancommit(VALUE vself);
static VALUE hdb_tranabort(VALUE vself);
static VALUE hdb_path(VALUE vself);
static VALUE hdb_rnum(VALUE vself);
static VALUE hdb_fsiz(VALUE vself);
static VALUE hdb_fetch(int argc, VALUE *argv, VALUE vself);
static VALUE hdb_check(VALUE vself, VALUE vkey);
static VALUE hdb_check_value(VALUE vself, VALUE vval);
static VALUE hdb_get_reverse(VALUE vself, VALUE vval);
static VALUE hdb_empty(VALUE vself);
static VALUE hdb_each(VALUE vself);
static VALUE hdb_each_key(VALUE vself);
static VALUE hdb_each_value(VALUE vself);
static VALUE hdb_keys(VALUE vself);
static VALUE hdb_values(VALUE vself);
static void bdb_init(void);
static int bdb_cmpobj(const char *aptr, int asiz, const char *bptr, int bsiz, VALUE vcmp);
static VALUE bdb_initialize(VALUE vself);
static VALUE bdb_errmsg(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_ecode(VALUE vself);
static VALUE bdb_setcmpfunc(VALUE vself, VALUE vcmp);
static VALUE bdb_tune(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_setcache(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_setxmsiz(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_setdfunit(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_open(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_close(VALUE vself);
static VALUE bdb_put(VALUE vself, VALUE vkey, VALUE vval);
static VALUE bdb_putkeep(VALUE vself, VALUE vkey, VALUE vval);
static VALUE bdb_putcat(VALUE vself, VALUE vkey, VALUE vval);
static VALUE bdb_putdup(VALUE vself, VALUE vkey, VALUE vval);
static VALUE bdb_putlist(VALUE vself, VALUE vkey, VALUE vvals);
static VALUE bdb_out(VALUE vself, VALUE vkey);
static VALUE bdb_outlist(VALUE vself, VALUE vkey);
static VALUE bdb_get(VALUE vself, VALUE vkey);
static VALUE bdb_getlist(VALUE vself, VALUE vkey);
static VALUE bdb_vnum(VALUE vself, VALUE vkey);
static VALUE bdb_vsiz(VALUE vself, VALUE vkey);
static VALUE bdb_range(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_fwmkeys(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_addint(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE bdb_adddouble(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE bdb_sync(VALUE vself);
static VALUE bdb_optimize(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_vanish(VALUE vself);
static VALUE bdb_copy(VALUE vself, VALUE vpath);
static VALUE bdb_tranbegin(VALUE vself);
static VALUE bdb_trancommit(VALUE vself);
static VALUE bdb_tranabort(VALUE vself);
static VALUE bdb_path(VALUE vself);
static VALUE bdb_rnum(VALUE vself);
static VALUE bdb_fsiz(VALUE vself);
static VALUE bdb_fetch(int argc, VALUE *argv, VALUE vself);
static VALUE bdb_check(VALUE vself, VALUE vkey);
static VALUE bdb_check_value(VALUE vself, VALUE vval);
static VALUE bdb_get_reverse(VALUE vself, VALUE vval);
static VALUE bdb_empty(VALUE vself);
static VALUE bdb_each(VALUE vself);
static VALUE bdb_each_key(VALUE vself);
static VALUE bdb_each_value(VALUE vself);
static VALUE bdb_keys(VALUE vself);
static VALUE bdb_values(VALUE vself);
static void bdbcur_init(void);
static VALUE bdbcur_initialize(VALUE vself, VALUE vbdb);
static VALUE bdbcur_first(VALUE vself);
static VALUE bdbcur_last(VALUE vself);
static VALUE bdbcur_jump(VALUE vself, VALUE vkey);
static VALUE bdbcur_prev(VALUE vself);
static VALUE bdbcur_next(VALUE vself);
static VALUE bdbcur_put(int argc, VALUE *argv, VALUE vself);
static VALUE bdbcur_out(VALUE vself);
static VALUE bdbcur_key(VALUE vself);
static VALUE bdbcur_val(VALUE vself);
static void fdb_init(void);
static VALUE fdb_initialize(VALUE vself);
static VALUE fdb_errmsg(int argc, VALUE *argv, VALUE vself);
static VALUE fdb_ecode(VALUE vself);
static VALUE fdb_tune(int argc, VALUE *argv, VALUE vself);
static VALUE fdb_open(int argc, VALUE *argv, VALUE vself);
static VALUE fdb_close(VALUE vself);
static VALUE fdb_put(VALUE vself, VALUE vkey, VALUE vval);
static VALUE fdb_putkeep(VALUE vself, VALUE vkey, VALUE vval);
static VALUE fdb_putcat(VALUE vself, VALUE vkey, VALUE vval);
static VALUE fdb_out(VALUE vself, VALUE vkey);
static VALUE fdb_get(VALUE vself, VALUE vkey);
static VALUE fdb_vsiz(VALUE vself, VALUE vkey);
static VALUE fdb_iterinit(VALUE vself);
static VALUE fdb_iternext(VALUE vself);
static VALUE fdb_range(int argc, VALUE *argv, VALUE vself);
static VALUE fdb_addint(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE fdb_adddouble(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE fdb_sync(VALUE vself);
static VALUE fdb_optimize(int argc, VALUE *argv, VALUE vself);
static VALUE fdb_vanish(VALUE vself);
static VALUE fdb_copy(VALUE vself, VALUE vpath);
static VALUE fdb_tranbegin(VALUE vself);
static VALUE fdb_trancommit(VALUE vself);
static VALUE fdb_tranabort(VALUE vself);
static VALUE fdb_path(VALUE vself);
static VALUE fdb_rnum(VALUE vself);
static VALUE fdb_fsiz(VALUE vself);
static VALUE fdb_fetch(int argc, VALUE *argv, VALUE vself);
static VALUE fdb_check(VALUE vself, VALUE vkey);
static VALUE fdb_check_value(VALUE vself, VALUE vval);
static VALUE fdb_get_reverse(VALUE vself, VALUE vval);
static VALUE fdb_empty(VALUE vself);
static VALUE fdb_each(VALUE vself);
static VALUE fdb_each_key(VALUE vself);
static VALUE fdb_each_value(VALUE vself);
static VALUE fdb_keys(VALUE vself);
static VALUE fdb_values(VALUE vself);
static void tdb_init(void);
static VALUE tdb_initialize(VALUE vself);
static VALUE tdb_errmsg(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_ecode(VALUE vself);
static VALUE tdb_tune(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_setcache(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_setxmsiz(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_setdfunit(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_open(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_close(VALUE vself);
static VALUE tdb_put(VALUE vself, VALUE vkey, VALUE vcols);
static VALUE tdb_putkeep(VALUE vself, VALUE vkey, VALUE vcols);
static VALUE tdb_putcat(VALUE vself, VALUE vkey, VALUE vcols);
static VALUE tdb_out(VALUE vself, VALUE vkey);
static VALUE tdb_get(VALUE vself, VALUE vkey);
static VALUE tdb_vsiz(VALUE vself, VALUE vkey);
static VALUE tdb_iterinit(VALUE vself);
static VALUE tdb_iternext(VALUE vself);
static VALUE tdb_fwmkeys(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_addint(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE tdb_adddouble(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE tdb_sync(VALUE vself);
static VALUE tdb_optimize(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_vanish(VALUE vself);
static VALUE tdb_copy(VALUE vself, VALUE vpath);
static VALUE tdb_tranbegin(VALUE vself);
static VALUE tdb_trancommit(VALUE vself);
static VALUE tdb_tranabort(VALUE vself);
static VALUE tdb_path(VALUE vself);
static VALUE tdb_rnum(VALUE vself);
static VALUE tdb_fsiz(VALUE vself);
static VALUE tdb_setindex(VALUE vself, VALUE vname, VALUE vtype);
static VALUE tdb_genuid(VALUE vself);
static VALUE tdb_fetch(int argc, VALUE *argv, VALUE vself);
static VALUE tdb_check(VALUE vself, VALUE vkey);
static VALUE tdb_empty(VALUE vself);
static VALUE tdb_each(VALUE vself);
static VALUE tdb_each_key(VALUE vself);
static VALUE tdb_each_value(VALUE vself);
static VALUE tdb_keys(VALUE vself);
static VALUE tdb_values(VALUE vself);
static void tdbqry_init(void);
static int tdbqry_procrec(const void *pkbuf, int pksiz, TCMAP *cols, void *opq);
static VALUE tdbqry_initialize(VALUE vself, VALUE vtdb);
static VALUE tdbqry_addcond(VALUE vself, VALUE vname, VALUE vop, VALUE vexpr);
static VALUE tdbqry_setorder(int argc, VALUE *argv, VALUE vself);
static VALUE tdbqry_setlimit(int argc, VALUE *argv, VALUE vself);
static VALUE tdbqry_search(VALUE vself);
static VALUE tdbqry_searchout(VALUE vself);
static VALUE tdbqry_proc(VALUE vself, VALUE vproc);
static VALUE tdbqry_hint(VALUE vself);
static VALUE tdbqry_metasearch(int argc, VALUE *argv, VALUE vself);
static VALUE tdbqry_kwic(int argc, VALUE *argv, VALUE vself);
static void adb_init(void);
static VALUE adb_initialize(VALUE vself);
static VALUE adb_open(VALUE vself, VALUE vname);
static VALUE adb_close(VALUE vself);
static VALUE adb_put(VALUE vself, VALUE vkey, VALUE vval);
static VALUE adb_putkeep(VALUE vself, VALUE vkey, VALUE vval);
static VALUE adb_putcat(VALUE vself, VALUE vkey, VALUE vval);
static VALUE adb_out(VALUE vself, VALUE vkey);
static VALUE adb_get(VALUE vself, VALUE vkey);
static VALUE adb_vsiz(VALUE vself, VALUE vkey);
static VALUE adb_iterinit(VALUE vself);
static VALUE adb_iternext(VALUE vself);
static VALUE adb_fwmkeys(int argc, VALUE *argv, VALUE vself);
static VALUE adb_addint(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE adb_adddouble(VALUE vself, VALUE vkey, VALUE vnum);
static VALUE adb_sync(VALUE vself);
static VALUE adb_optimize(int argc, VALUE *argv, VALUE vself);
static VALUE adb_vanish(VALUE vself);
static VALUE adb_copy(VALUE vself, VALUE vpath);
static VALUE adb_tranbegin(VALUE vself);
static VALUE adb_trancommit(VALUE vself);
static VALUE adb_tranabort(VALUE vself);
static VALUE adb_path(VALUE vself);
static VALUE adb_rnum(VALUE vself);
static VALUE adb_size(VALUE vself);
static VALUE adb_misc(int argc, VALUE *argv, VALUE vself);
static VALUE adb_fetch(int argc, VALUE *argv, VALUE vself);
static VALUE adb_check(VALUE vself, VALUE vkey);
static VALUE adb_check_value(VALUE vself, VALUE vval);
static VALUE adb_get_reverse(VALUE vself, VALUE vval);
static VALUE adb_empty(VALUE vself);
static VALUE adb_each(VALUE vself);
static VALUE adb_each_key(VALUE vself);
static VALUE adb_each_value(VALUE vself);
static VALUE adb_keys(VALUE vself);
static VALUE adb_values(VALUE vself);



/*************************************************************************************************
 * public objects
 *************************************************************************************************/


VALUE mod_tokyocabinet;
VALUE cls_hdb;
VALUE cls_hdb_data;
VALUE cls_bdb;
VALUE cls_bdb_data;
VALUE cls_bdbcur;
VALUE cls_bdbcur_data;
ID bdb_cmp_call_mid;
VALUE cls_fdb;
VALUE cls_fdb_data;
VALUE cls_tdb;
VALUE cls_tdb_data;
VALUE cls_tdbqry;
VALUE cls_tdbqry_data;
VALUE cls_adb;
VALUE cls_adb_data;


int Init_tokyocabinet(void){
  mod_tokyocabinet = rb_define_module("TokyoCabinet");
  rb_define_const(mod_tokyocabinet, "VERSION", rb_str_new2(tcversion));
  hdb_init();
  bdb_init();
  bdbcur_init();
  fdb_init();
  tdb_init();
  tdbqry_init();
  adb_init();
  return 0;
}



/*************************************************************************************************
 * private objects
 *************************************************************************************************/


static VALUE StringValueEx(VALUE vobj){
  char kbuf[NUMBUFSIZ];
  int ksiz;
  switch(TYPE(vobj)){
  case T_FIXNUM:
    ksiz = sprintf(kbuf, "%d", (int)FIX2INT(vobj));
    return rb_str_new(kbuf, ksiz);
  case T_BIGNUM:
    ksiz = sprintf(kbuf, "%lld", (long long)NUM2LL(vobj));
    return rb_str_new(kbuf, ksiz);
  case T_TRUE:
    ksiz = sprintf(kbuf, "true");
    return rb_str_new(kbuf, ksiz);
  case T_FALSE:
    ksiz = sprintf(kbuf, "false");
    return rb_str_new(kbuf, ksiz);
  case T_NIL:
    ksiz = sprintf(kbuf, "nil");
    return rb_str_new(kbuf, ksiz);
  }
  return StringValue(vobj);
}


static TCLIST *varytolist(VALUE vary){
  VALUE vval;
  TCLIST *list;
  int i, num;
  num = RARRAY_LEN(vary);
  list = tclistnew2(num);
  for(i = 0; i < num; i++){
    vval = rb_ary_entry(vary, i);
    vval = StringValueEx(vval);
    tclistpush(list, RSTRING_PTR(vval), RSTRING_LEN(vval));
  }
  return list;
}


static VALUE listtovary(TCLIST *list){
  VALUE vary;
  const char *vbuf;
  int i, num, vsiz;
  num = tclistnum(list);
  vary = rb_ary_new2(num);
  for(i = 0; i < num; i++){
    vbuf = tclistval(list, i, &vsiz);
    rb_ary_push(vary, rb_str_new(vbuf, vsiz));
  }
  return vary;
}


static TCMAP *vhashtomap(VALUE vhash){
  VALUE vkeys, vkey, vval;
  TCMAP *map;
  int i, num;
  map = tcmapnew2(31);
  vkeys = rb_funcall(vhash, rb_intern("keys"), 0);
  num = RARRAY_LEN(vkeys);
  for(i = 0; i < num; i++){
    vkey = rb_ary_entry(vkeys, i);
    vval = rb_hash_aref(vhash, vkey);
    vkey = StringValueEx(vkey);
    vval = StringValueEx(vval);
    tcmapput(map, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vval), RSTRING_LEN(vval));
  }
  return map;
}


static VALUE maptovhash(TCMAP *map){
  const char *kbuf, *vbuf;
  int ksiz, vsiz;
  VALUE vhash;
  vhash = rb_hash_new();
  tcmapiterinit(map);
  while((kbuf = tcmapiternext(map, &ksiz)) != NULL){
    vbuf = tcmapiterval(kbuf, &vsiz);
    rb_hash_aset(vhash, rb_str_new(kbuf, ksiz), rb_str_new(vbuf, vsiz));
  }
  return vhash;
}


static void hdb_init(void){
  cls_hdb = rb_define_class_under(mod_tokyocabinet, "HDB", rb_cObject);
  cls_hdb_data = rb_define_class_under(mod_tokyocabinet, "HDB_data", rb_cObject);
  rb_define_const(cls_hdb, "ESUCCESS", INT2NUM(TCESUCCESS));
  rb_define_const(cls_hdb, "ETHREAD", INT2NUM(TCETHREAD));
  rb_define_const(cls_hdb, "EINVALID", INT2NUM(TCEINVALID));
  rb_define_const(cls_hdb, "ENOFILE", INT2NUM(TCENOFILE));
  rb_define_const(cls_hdb, "ENOPERM", INT2NUM(TCENOPERM));
  rb_define_const(cls_hdb, "EMETA", INT2NUM(TCEMETA));
  rb_define_const(cls_hdb, "ERHEAD", INT2NUM(TCERHEAD));
  rb_define_const(cls_hdb, "EOPEN", INT2NUM(TCEOPEN));
  rb_define_const(cls_hdb, "ECLOSE", INT2NUM(TCECLOSE));
  rb_define_const(cls_hdb, "ETRUNC", INT2NUM(TCETRUNC));
  rb_define_const(cls_hdb, "ESYNC", INT2NUM(TCESYNC));
  rb_define_const(cls_hdb, "ESTAT", INT2NUM(TCESTAT));
  rb_define_const(cls_hdb, "ESEEK", INT2NUM(TCESEEK));
  rb_define_const(cls_hdb, "EREAD", INT2NUM(TCEREAD));
  rb_define_const(cls_hdb, "EWRITE", INT2NUM(TCEWRITE));
  rb_define_const(cls_hdb, "EMMAP", INT2NUM(TCEMMAP));
  rb_define_const(cls_hdb, "ELOCK", INT2NUM(TCELOCK));
  rb_define_const(cls_hdb, "EUNLINK", INT2NUM(TCEUNLINK));
  rb_define_const(cls_hdb, "ERENAME", INT2NUM(TCERENAME));
  rb_define_const(cls_hdb, "EMKDIR", INT2NUM(TCEMKDIR));
  rb_define_const(cls_hdb, "ERMDIR", INT2NUM(TCERMDIR));
  rb_define_const(cls_hdb, "EKEEP", INT2NUM(TCEKEEP));
  rb_define_const(cls_hdb, "ENOREC", INT2NUM(TCENOREC));
  rb_define_const(cls_hdb, "EMISC", INT2NUM(TCEMISC));
  rb_define_const(cls_hdb, "TLARGE", INT2NUM(HDBTLARGE));
  rb_define_const(cls_hdb, "TDEFLATE", INT2NUM(HDBTDEFLATE));
  rb_define_const(cls_hdb, "TBZIP", INT2NUM(HDBTBZIP));
  rb_define_const(cls_hdb, "TTCBS", INT2NUM(HDBTTCBS));
  rb_define_const(cls_hdb, "OREADER", INT2NUM(HDBOREADER));
  rb_define_const(cls_hdb, "OWRITER", INT2NUM(HDBOWRITER));
  rb_define_const(cls_hdb, "OCREAT", INT2NUM(HDBOCREAT));
  rb_define_const(cls_hdb, "OTRUNC", INT2NUM(HDBOTRUNC));
  rb_define_const(cls_hdb, "ONOLCK", INT2NUM(HDBONOLCK));
  rb_define_const(cls_hdb, "OLCKNB", INT2NUM(HDBOLCKNB));
  rb_define_const(cls_hdb, "OTSYNC", INT2NUM(HDBOTSYNC));
  rb_define_private_method(cls_hdb, "initialize", hdb_initialize, 0);
  rb_define_method(cls_hdb, "errmsg", hdb_errmsg, -1);
  rb_define_method(cls_hdb, "ecode", hdb_ecode, 0);
  rb_define_method(cls_hdb, "tune", hdb_tune, -1);
  rb_define_method(cls_hdb, "setcache", hdb_setcache, -1);
  rb_define_method(cls_hdb, "setxmsiz", hdb_setxmsiz, -1);
  rb_define_method(cls_hdb, "setdfunit", hdb_setdfunit, -1);
  rb_define_method(cls_hdb, "open", hdb_open, -1);
  rb_define_method(cls_hdb, "close", hdb_close, 0);
  rb_define_method(cls_hdb, "put", hdb_put, 2);
  rb_define_method(cls_hdb, "putkeep", hdb_putkeep, 2);
  rb_define_method(cls_hdb, "putcat", hdb_putcat, 2);
  rb_define_method(cls_hdb, "putasync", hdb_putasync, 2);
  rb_define_method(cls_hdb, "out", hdb_out, 1);
  rb_define_method(cls_hdb, "get", hdb_get, 1);
  rb_define_method(cls_hdb, "vsiz", hdb_vsiz, 1);
  rb_define_method(cls_hdb, "iterinit", hdb_iterinit, 0);
  rb_define_method(cls_hdb, "iternext", hdb_iternext, 0);
  rb_define_method(cls_hdb, "fwmkeys", hdb_fwmkeys, -1);
  rb_define_method(cls_hdb, "addint", hdb_addint, 2);
  rb_define_method(cls_hdb, "adddouble", hdb_adddouble, 2);
  rb_define_method(cls_hdb, "sync", hdb_sync, 0);
  rb_define_method(cls_hdb, "optimize", hdb_optimize, -1);
  rb_define_method(cls_hdb, "vanish", hdb_vanish, 0);
  rb_define_method(cls_hdb, "copy", hdb_copy, 1);
  rb_define_method(cls_hdb, "tranbegin", hdb_tranbegin, 0);
  rb_define_method(cls_hdb, "trancommit", hdb_trancommit, 0);
  rb_define_method(cls_hdb, "tranabort", hdb_tranabort, 0);
  rb_define_method(cls_hdb, "path", hdb_path, 0);
  rb_define_method(cls_hdb, "rnum", hdb_rnum, 0);
  rb_define_method(cls_hdb, "fsiz", hdb_fsiz, 0);
  rb_define_method(cls_hdb, "[]", hdb_get, 1);
  rb_define_method(cls_hdb, "[]=", hdb_put, 2);
  rb_define_method(cls_hdb, "store", hdb_put, 2);
  rb_define_method(cls_hdb, "delete", hdb_out, 1);
  rb_define_method(cls_hdb, "fetch", hdb_fetch, -1);
  rb_define_method(cls_hdb, "has_key?", hdb_check, 1);
  rb_define_method(cls_hdb, "key?", hdb_check, 1);
  rb_define_method(cls_hdb, "include?", hdb_check, 1);
  rb_define_method(cls_hdb, "member?", hdb_check, 1);
  rb_define_method(cls_hdb, "has_value?", hdb_check_value, 1);
  rb_define_method(cls_hdb, "value?", hdb_check_value, 1);
  rb_define_method(cls_hdb, "key", hdb_get_reverse, 1);
  rb_define_method(cls_hdb, "clear", hdb_vanish, 0);
  rb_define_method(cls_hdb, "size", hdb_rnum, 0);
  rb_define_method(cls_hdb, "length", hdb_rnum, 0);
  rb_define_method(cls_hdb, "empty?", hdb_empty, 0);
  rb_define_method(cls_hdb, "each", hdb_each, 0);
  rb_define_method(cls_hdb, "each_pair", hdb_each, 0);
  rb_define_method(cls_hdb, "each_key", hdb_each_key, 0);
  rb_define_method(cls_hdb, "each_value", hdb_each_value, 0);
  rb_define_method(cls_hdb, "keys", hdb_keys, 0);
  rb_define_method(cls_hdb, "values", hdb_values, 0);
}


static VALUE hdb_initialize(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  hdb = tchdbnew();
  tchdbsetmutex(hdb);
  vhdb = Data_Wrap_Struct(cls_hdb_data, 0, tchdbdel, hdb);
  rb_iv_set(vself, HDBVNDATA, vhdb);
  return Qnil;
}


static VALUE hdb_errmsg(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vecode;
  TCHDB *hdb;
  const char *msg;
  int ecode;
  rb_scan_args(argc, argv, "01", &vecode);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  ecode = (vecode == Qnil) ? tchdbecode(hdb) : NUM2INT(vecode);
  msg = tchdberrmsg(ecode);
  return rb_str_new2(msg);
}


static VALUE hdb_ecode(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return INT2NUM(tchdbecode(hdb));
}


static VALUE hdb_tune(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vbnum, vapow, vfpow, vopts;
  TCHDB *hdb;
  int apow, fpow, opts;
  int64_t bnum;
  rb_scan_args(argc, argv, "04", &vbnum, &vapow, &vfpow, &vopts);
  bnum = (vbnum == Qnil) ? -1 : NUM2LL(vbnum);
  apow = (vapow == Qnil) ? -1 : NUM2INT(vapow);
  fpow = (vfpow == Qnil) ? -1 : NUM2INT(vfpow);
  opts = (vopts == Qnil) ? 0 : NUM2INT(vopts);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbtune(hdb, bnum, apow, fpow, opts) ? Qtrue : Qfalse;
}


static VALUE hdb_setcache(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vrcnum;
  TCHDB *hdb;
  int rcnum;
  rb_scan_args(argc, argv, "01", &vrcnum);
  rcnum = (vrcnum == Qnil) ? -1 : NUM2INT(vrcnum);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbsetcache(hdb, rcnum) ? Qtrue : Qfalse;
}


static VALUE hdb_setxmsiz(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vxmsiz;
  TCHDB *hdb;
  int64_t xmsiz;
  rb_scan_args(argc, argv, "01", &vxmsiz);
  xmsiz = (vxmsiz == Qnil) ? -1 : NUM2LL(vxmsiz);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbsetxmsiz(hdb, xmsiz) ? Qtrue : Qfalse;
}


static VALUE hdb_setdfunit(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vdfunit;
  TCHDB *hdb;
  int32_t dfunit;
  rb_scan_args(argc, argv, "01", &vdfunit);
  dfunit = (vdfunit == Qnil) ? -1 : NUM2INT(vdfunit);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbsetdfunit(hdb, dfunit) ? Qtrue : Qfalse;
}


static VALUE hdb_open(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vpath, vomode;
  TCHDB *hdb;
  int omode;
  rb_scan_args(argc, argv, "11", &vpath, &vomode);
  Check_Type(vpath, T_STRING);
  omode = (vomode == Qnil) ? HDBOREADER : NUM2INT(vomode);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbopen(hdb, RSTRING_PTR(vpath), omode) ? Qtrue : Qfalse;
}


static VALUE hdb_close(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbclose(hdb) ? Qtrue : Qfalse;
}


static VALUE hdb_put(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vhdb;
  TCHDB *hdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbput(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                  RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE hdb_putkeep(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vhdb;
  TCHDB *hdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbputkeep(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                      RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE hdb_putcat(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vhdb;
  TCHDB *hdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbputcat(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                     RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE hdb_putasync(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vhdb;
  TCHDB *hdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbputasync(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                       RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE hdb_out(VALUE vself, VALUE vkey){
  VALUE vhdb;
  TCHDB *hdb;
  vkey = StringValueEx(vkey);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbout(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) ? Qtrue : Qfalse;
}


static VALUE hdb_get(VALUE vself, VALUE vkey){
  VALUE vhdb, vval;
  TCHDB *hdb;
  char *vbuf;
  int vsiz;
  vkey = StringValueEx(vkey);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  if(!(vbuf = tchdbget(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static VALUE hdb_vsiz(VALUE vself, VALUE vkey){
  VALUE vhdb;
  TCHDB *hdb;
  vkey = StringValueEx(vkey);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return INT2NUM(tchdbvsiz(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
}


static VALUE hdb_iterinit(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbiterinit(hdb) ? Qtrue : Qfalse;
}


static VALUE hdb_iternext(VALUE vself){
  VALUE vhdb, vval;
  TCHDB *hdb;
  char *vbuf;
  int vsiz;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  if(!(vbuf = tchdbiternext(hdb, &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static VALUE hdb_fwmkeys(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vprefix, vmax, vary;
  TCHDB *hdb;
  TCLIST *keys;
  int max;
  rb_scan_args(argc, argv, "11", &vprefix, &vmax);
  vprefix = StringValueEx(vprefix);
  max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  keys = tchdbfwmkeys(hdb, RSTRING_PTR(vprefix), RSTRING_LEN(vprefix), max);
  vary = listtovary(keys);
  tclistdel(keys);
  return vary;
}


static VALUE hdb_addint(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vhdb;
  TCHDB *hdb;
  int num;
  vkey = StringValueEx(vkey);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  num = tchdbaddint(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), NUM2INT(vnum));
  return num == INT_MIN ? Qnil : INT2NUM(num);
}


static VALUE hdb_adddouble(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vhdb;
  TCHDB *hdb;
  double num;
  vkey = StringValueEx(vkey);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  num = tchdbadddouble(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), NUM2DBL(vnum));
  return isnan(num) ? Qnil : rb_float_new(num);
}


static VALUE hdb_sync(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbsync(hdb) ? Qtrue : Qfalse;
}


static VALUE hdb_optimize(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vbnum, vapow, vfpow, vopts;
  TCHDB *hdb;
  int apow, fpow, opts;
  int64_t bnum;
  rb_scan_args(argc, argv, "04", &vbnum, &vapow, &vfpow, &vopts);
  bnum = (vbnum == Qnil) ? -1 : NUM2LL(vbnum);
  apow = (vapow == Qnil) ? -1 : NUM2INT(vapow);
  fpow = (vfpow == Qnil) ? -1 : NUM2INT(vfpow);
  opts = (vopts == Qnil) ? UINT8_MAX : NUM2INT(vopts);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdboptimize(hdb, bnum, apow, fpow, opts) ? Qtrue : Qfalse;
}


static VALUE hdb_vanish(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbvanish(hdb) ? Qtrue : Qfalse;
}


static VALUE hdb_copy(VALUE vself, VALUE vpath){
  VALUE vhdb;
  TCHDB *hdb;
  Check_Type(vpath, T_STRING);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbcopy(hdb, RSTRING_PTR(vpath)) ? Qtrue : Qfalse;
}


static VALUE hdb_tranbegin(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbtranbegin(hdb) ? Qtrue : Qfalse;
}


static VALUE hdb_trancommit(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbtrancommit(hdb) ? Qtrue : Qfalse;
}


static VALUE hdb_tranabort(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbtranabort(hdb) ? Qtrue : Qfalse;
}


static VALUE hdb_path(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  const char *path;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  if(!(path = tchdbpath(hdb))) return Qnil;
  return rb_str_new2(path);
}


static VALUE hdb_rnum(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return LL2NUM(tchdbrnum(hdb));
}


static VALUE hdb_fsiz(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return LL2NUM(tchdbfsiz(hdb));
}


static VALUE hdb_fetch(int argc, VALUE *argv, VALUE vself){
  VALUE vhdb, vkey, vdef, vval;
  TCHDB *hdb;
  char *vbuf;
  int vsiz;
  rb_scan_args(argc, argv, "11", &vkey, &vdef);
  vkey = StringValueEx(vkey);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  if((vbuf = tchdbget(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz)) != NULL){
    vval = rb_str_new(vbuf, vsiz);
    tcfree(vbuf);
  } else {
    vval = vdef;
  }
  return vval;
}


static VALUE hdb_check(VALUE vself, VALUE vkey){
  VALUE vhdb;
  TCHDB *hdb;
  vkey = StringValueEx(vkey);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbvsiz(hdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) >= 0 ? Qtrue : Qfalse;
}


static VALUE hdb_check_value(VALUE vself, VALUE vval){
  VALUE vhdb;
  TCHDB *hdb;
  TCXSTR *kxstr, *vxstr;
  bool hit;
  vval = StringValueEx(vval);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  hit = false;
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  tchdbiterinit(hdb);
  while(tchdbiternext3(hdb, kxstr, vxstr)){
    if(tcxstrsize(vxstr) == RSTRING_LEN(vval) &&
       memcmp(tcxstrptr(vxstr), RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      hit = true;
      break;
    }
  }
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return hit ? Qtrue : Qfalse;
}


static VALUE hdb_get_reverse(VALUE vself, VALUE vval){
  VALUE vhdb, vrv;
  TCHDB *hdb;
  TCXSTR *kxstr, *vxstr;
  vval = StringValueEx(vval);
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  vrv = Qnil;
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  tchdbiterinit(hdb);
  while(tchdbiternext3(hdb, kxstr, vxstr)){
    if(tcxstrsize(vxstr) == RSTRING_LEN(vval) &&
       memcmp(tcxstrptr(vxstr), RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      vrv = rb_str_new(tcxstrptr(kxstr), tcxstrsize(kxstr));
      break;
    }
  }
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return vrv;
}


static VALUE hdb_empty(VALUE vself){
  VALUE vhdb;
  TCHDB *hdb;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  return tchdbrnum(hdb) < 1 ? Qtrue : Qfalse;
}


static VALUE hdb_each(VALUE vself){
  VALUE vhdb, vrv;
  TCHDB *hdb;
  TCXSTR *kxstr, *vxstr;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  vrv = Qnil;
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  tchdbiterinit(hdb);
  while(tchdbiternext3(hdb, kxstr, vxstr)){
    vrv = rb_yield_values(2, rb_str_new(tcxstrptr(kxstr), tcxstrsize(kxstr)),
                          rb_str_new(tcxstrptr(vxstr), tcxstrsize(vxstr)));
  }
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return vrv;
}


static VALUE hdb_each_key(VALUE vself){
  VALUE vhdb, vrv;
  TCHDB *hdb;
  TCXSTR *kxstr, *vxstr;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  vrv = Qnil;
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  tchdbiterinit(hdb);
  while(tchdbiternext3(hdb, kxstr, vxstr)){
    vrv = rb_yield(rb_str_new(tcxstrptr(kxstr), tcxstrsize(kxstr)));
  }
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return vrv;
}


static VALUE hdb_each_value(VALUE vself){
  VALUE vhdb, vrv;
  TCHDB *hdb;
  TCXSTR *kxstr, *vxstr;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  vrv = Qnil;
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  tchdbiterinit(hdb);
  while(tchdbiternext3(hdb, kxstr, vxstr)){
    vrv = rb_yield(rb_str_new(tcxstrptr(vxstr), tcxstrsize(vxstr)));
  }
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return vrv;
}


static VALUE hdb_keys(VALUE vself){
  VALUE vhdb, vary;
  TCHDB *hdb;
  TCXSTR *kxstr, *vxstr;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  vary = rb_ary_new2(tchdbrnum(hdb));
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  tchdbiterinit(hdb);
  while(tchdbiternext3(hdb, kxstr, vxstr)){
    rb_ary_push(vary, rb_str_new(tcxstrptr(kxstr), tcxstrsize(kxstr)));
  }
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return vary;
}


static VALUE hdb_values(VALUE vself){
  VALUE vhdb, vary;
  TCHDB *hdb;
  TCXSTR *kxstr, *vxstr;
  vhdb = rb_iv_get(vself, HDBVNDATA);
  Data_Get_Struct(vhdb, TCHDB, hdb);
  vary = rb_ary_new2(tchdbrnum(hdb));
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  tchdbiterinit(hdb);
  while(tchdbiternext3(hdb, kxstr, vxstr)){
    rb_ary_push(vary, rb_str_new(tcxstrptr(vxstr), tcxstrsize(vxstr)));
  }
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return vary;
}


static void bdb_init(void){
  cls_bdb = rb_define_class_under(mod_tokyocabinet, "BDB", rb_cObject);
  cls_bdb_data = rb_define_class_under(mod_tokyocabinet, "BDB_data", rb_cObject);
  bdb_cmp_call_mid = rb_intern("call");
  rb_define_const(cls_bdb, "ESUCCESS", INT2NUM(TCESUCCESS));
  rb_define_const(cls_bdb, "ETHREAD", INT2NUM(TCETHREAD));
  rb_define_const(cls_bdb, "EINVALID", INT2NUM(TCEINVALID));
  rb_define_const(cls_bdb, "ENOFILE", INT2NUM(TCENOFILE));
  rb_define_const(cls_bdb, "ENOPERM", INT2NUM(TCENOPERM));
  rb_define_const(cls_bdb, "EMETA", INT2NUM(TCEMETA));
  rb_define_const(cls_bdb, "ERHEAD", INT2NUM(TCERHEAD));
  rb_define_const(cls_bdb, "EOPEN", INT2NUM(TCEOPEN));
  rb_define_const(cls_bdb, "ECLOSE", INT2NUM(TCECLOSE));
  rb_define_const(cls_bdb, "ETRUNC", INT2NUM(TCETRUNC));
  rb_define_const(cls_bdb, "ESYNC", INT2NUM(TCESYNC));
  rb_define_const(cls_bdb, "ESTAT", INT2NUM(TCESTAT));
  rb_define_const(cls_bdb, "ESEEK", INT2NUM(TCESEEK));
  rb_define_const(cls_bdb, "EREAD", INT2NUM(TCEREAD));
  rb_define_const(cls_bdb, "EWRITE", INT2NUM(TCEWRITE));
  rb_define_const(cls_bdb, "EMMAP", INT2NUM(TCEMMAP));
  rb_define_const(cls_bdb, "ELOCK", INT2NUM(TCELOCK));
  rb_define_const(cls_bdb, "EUNLINK", INT2NUM(TCEUNLINK));
  rb_define_const(cls_bdb, "ERENAME", INT2NUM(TCERENAME));
  rb_define_const(cls_bdb, "EMKDIR", INT2NUM(TCEMKDIR));
  rb_define_const(cls_bdb, "ERMDIR", INT2NUM(TCERMDIR));
  rb_define_const(cls_bdb, "EKEEP", INT2NUM(TCEKEEP));
  rb_define_const(cls_bdb, "ENOREC", INT2NUM(TCENOREC));
  rb_define_const(cls_bdb, "EMISC", INT2NUM(TCEMISC));
  rb_define_const(cls_bdb, "CMPLEXICAL", rb_str_new2("CMPLEXICAL"));
  rb_define_const(cls_bdb, "CMPDECIMAL", rb_str_new2("CMPDECIMAL"));
  rb_define_const(cls_bdb, "CMPINT32", rb_str_new2("CMPINT32"));
  rb_define_const(cls_bdb, "CMPINT64", rb_str_new2("CMPINT64"));
  rb_define_const(cls_bdb, "TLARGE", INT2NUM(BDBTLARGE));
  rb_define_const(cls_bdb, "TDEFLATE", INT2NUM(BDBTDEFLATE));
  rb_define_const(cls_bdb, "TBZIP", INT2NUM(BDBTBZIP));
  rb_define_const(cls_bdb, "TTCBS", INT2NUM(BDBTTCBS));
  rb_define_const(cls_bdb, "OREADER", INT2NUM(BDBOREADER));
  rb_define_const(cls_bdb, "OWRITER", INT2NUM(BDBOWRITER));
  rb_define_const(cls_bdb, "OCREAT", INT2NUM(BDBOCREAT));
  rb_define_const(cls_bdb, "OTRUNC", INT2NUM(BDBOTRUNC));
  rb_define_const(cls_bdb, "ONOLCK", INT2NUM(BDBONOLCK));
  rb_define_const(cls_bdb, "OLCKNB", INT2NUM(BDBOLCKNB));
  rb_define_const(cls_bdb, "OTSYNC", INT2NUM(BDBOTSYNC));
  rb_define_private_method(cls_bdb, "initialize", bdb_initialize, 0);
  rb_define_method(cls_bdb, "errmsg", bdb_errmsg, -1);
  rb_define_method(cls_bdb, "ecode", bdb_ecode, 0);
  rb_define_method(cls_bdb, "setcmpfunc", bdb_setcmpfunc, 1);
  rb_define_method(cls_bdb, "tune", bdb_tune, -1);
  rb_define_method(cls_bdb, "setcache", bdb_setcache, -1);
  rb_define_method(cls_bdb, "setxmsiz", bdb_setxmsiz, -1);
  rb_define_method(cls_bdb, "setdfunit", bdb_setdfunit, -1);
  rb_define_method(cls_bdb, "open", bdb_open, -1);
  rb_define_method(cls_bdb, "close", bdb_close, 0);
  rb_define_method(cls_bdb, "put", bdb_put, 2);
  rb_define_method(cls_bdb, "putkeep", bdb_putkeep, 2);
  rb_define_method(cls_bdb, "putcat", bdb_putcat, 2);
  rb_define_method(cls_bdb, "putdup", bdb_putdup, 2);
  rb_define_method(cls_bdb, "putlist", bdb_putlist, 2);
  rb_define_method(cls_bdb, "out", bdb_out, 1);
  rb_define_method(cls_bdb, "outlist", bdb_outlist, 1);
  rb_define_method(cls_bdb, "get", bdb_get, 1);
  rb_define_method(cls_bdb, "getlist", bdb_getlist, 1);
  rb_define_method(cls_bdb, "vnum", bdb_vnum, 1);
  rb_define_method(cls_bdb, "vsiz", bdb_vsiz, 1);
  rb_define_method(cls_bdb, "range", bdb_range, -1);
  rb_define_method(cls_bdb, "fwmkeys", bdb_fwmkeys, -1);
  rb_define_method(cls_bdb, "addint", bdb_addint, 2);
  rb_define_method(cls_bdb, "adddouble", bdb_adddouble, 2);
  rb_define_method(cls_bdb, "sync", bdb_sync, 0);
  rb_define_method(cls_bdb, "optimize", bdb_optimize, -1);
  rb_define_method(cls_bdb, "vanish", bdb_vanish, 0);
  rb_define_method(cls_bdb, "copy", bdb_copy, 1);
  rb_define_method(cls_bdb, "tranbegin", bdb_tranbegin, 0);
  rb_define_method(cls_bdb, "trancommit", bdb_trancommit, 0);
  rb_define_method(cls_bdb, "tranabort", bdb_tranabort, 0);
  rb_define_method(cls_bdb, "path", bdb_path, 0);
  rb_define_method(cls_bdb, "rnum", bdb_rnum, 0);
  rb_define_method(cls_bdb, "fsiz", bdb_fsiz, 0);
  rb_define_method(cls_bdb, "[]", bdb_get, 1);
  rb_define_method(cls_bdb, "[]=", bdb_put, 2);
  rb_define_method(cls_bdb, "store", bdb_put, 2);
  rb_define_method(cls_bdb, "delete", bdb_out, 1);
  rb_define_method(cls_bdb, "fetch", bdb_fetch, -1);
  rb_define_method(cls_bdb, "has_key?", bdb_check, 1);
  rb_define_method(cls_bdb, "key?", bdb_check, 1);
  rb_define_method(cls_bdb, "include?", bdb_check, 1);
  rb_define_method(cls_bdb, "member?", bdb_check, 1);
  rb_define_method(cls_bdb, "has_value?", bdb_check_value, 1);
  rb_define_method(cls_bdb, "value?", bdb_check_value, 1);
  rb_define_method(cls_bdb, "key", bdb_get_reverse, 1);
  rb_define_method(cls_bdb, "clear", bdb_vanish, 0);
  rb_define_method(cls_bdb, "size", bdb_rnum, 0);
  rb_define_method(cls_bdb, "length", bdb_rnum, 0);
  rb_define_method(cls_bdb, "empty?", bdb_empty, 0);
  rb_define_method(cls_bdb, "each", bdb_each, 0);
  rb_define_method(cls_bdb, "each_pair", bdb_each, 0);
  rb_define_method(cls_bdb, "each_key", bdb_each_key, 0);
  rb_define_method(cls_bdb, "each_value", bdb_each_value, 0);
  rb_define_method(cls_bdb, "keys", bdb_keys, 0);
  rb_define_method(cls_bdb, "values", bdb_values, 0);
}


static int bdb_cmpobj(const char *aptr, int asiz, const char *bptr, int bsiz, VALUE vcmp){
  VALUE vrv;
  vrv = rb_funcall(vcmp, bdb_cmp_call_mid, 2, rb_str_new(aptr, asiz), rb_str_new(bptr, bsiz));
  return (vrv == Qnil) ? 0 : NUM2INT(vrv);
}


static VALUE bdb_initialize(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  bdb = tcbdbnew();
  tcbdbsetmutex(bdb);
  vbdb = Data_Wrap_Struct(cls_bdb_data, 0, tcbdbdel, bdb);
  rb_iv_set(vself, BDBVNDATA, vbdb);
  return Qnil;
}


static VALUE bdb_errmsg(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vecode;
  TCBDB *bdb;
  const char *msg;
  int ecode;
  rb_scan_args(argc, argv, "01", &vecode);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  ecode = (vecode == Qnil) ? tcbdbecode(bdb) : NUM2INT(vecode);
  msg = tcbdberrmsg(ecode);
  return rb_str_new2(msg);
}


static VALUE bdb_ecode(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return INT2NUM(tcbdbecode(bdb));
}


static VALUE bdb_setcmpfunc(VALUE vself, VALUE vcmp){
  VALUE vbdb;
  TCBDB *bdb;
  TCCMP cmp;
  cmp = (TCCMP)bdb_cmpobj;
  if(TYPE(vcmp) == T_STRING){
    if(!strcmp(RSTRING_PTR(vcmp), "CMPLEXICAL")){
      cmp = tccmplexical;
    } else if(!strcmp(RSTRING_PTR(vcmp), "CMPDECIMAL")){
      cmp = tccmpdecimal;
    } else if(!strcmp(RSTRING_PTR(vcmp), "CMPINT32")){
      cmp = tccmpint32;
    } else if(!strcmp(RSTRING_PTR(vcmp), "CMPINT64")){
      cmp = tccmpint64;
    } else {
      rb_raise(rb_eArgError, "unknown comparison function: %s", RSTRING_PTR(vcmp));
    }
  } else if(!rb_respond_to(vcmp, bdb_cmp_call_mid)){
    rb_raise(rb_eArgError, "call method is not implemented");
  }
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbsetcmpfunc(bdb, cmp, (void *)(intptr_t)vcmp);
}


static VALUE bdb_tune(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vlmemb, vnmemb, vbnum, vapow, vfpow, vopts;
  TCBDB *bdb;
  int lmemb, nmemb, apow, fpow, opts;
  int64_t bnum;
  rb_scan_args(argc, argv, "06", &vlmemb, &vnmemb, &vbnum, &vapow, &vfpow, &vopts);
  lmemb = (vlmemb == Qnil) ? -1 : NUM2INT(vlmemb);
  nmemb = (vnmemb == Qnil) ? -1 : NUM2INT(vnmemb);
  bnum = (vbnum == Qnil) ? -1 : NUM2LL(vbnum);
  apow = (vapow == Qnil) ? -1 : NUM2INT(vapow);
  fpow = (vfpow == Qnil) ? -1 : NUM2INT(vfpow);
  opts = (vopts == Qnil) ? 0 : NUM2INT(vopts);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts) ? Qtrue : Qfalse;
}


static VALUE bdb_setcache(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vlcnum, vncnum;
  TCBDB *bdb;
  int lcnum, ncnum;
  rb_scan_args(argc, argv, "02", &vlcnum, &vncnum);
  lcnum = (vlcnum == Qnil) ? -1 : NUM2INT(vlcnum);
  ncnum = (vncnum == Qnil) ? -1 : NUM2INT(vncnum);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbsetcache(bdb, lcnum, ncnum);
}


static VALUE bdb_setxmsiz(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vxmsiz;
  TCBDB *bdb;
  int64_t xmsiz;
  rb_scan_args(argc, argv, "01", &vxmsiz);
  xmsiz = (vxmsiz == Qnil) ? -1 : NUM2LL(vxmsiz);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbsetxmsiz(bdb, xmsiz) ? Qtrue : Qfalse;
}


static VALUE bdb_setdfunit(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vdfunit;
  TCBDB *bdb;
  int32_t dfunit;
  rb_scan_args(argc, argv, "01", &vdfunit);
  dfunit = (vdfunit == Qnil) ? -1 : NUM2INT(vdfunit);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbsetdfunit(bdb, dfunit) ? Qtrue : Qfalse;
}


static VALUE bdb_open(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vpath, vomode;
  TCBDB *bdb;
  int omode;
  rb_scan_args(argc, argv, "11", &vpath, &vomode);
  Check_Type(vpath, T_STRING);
  omode = (vomode == Qnil) ? BDBOREADER : NUM2INT(vomode);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbopen(bdb, RSTRING_PTR(vpath), omode) ? Qtrue : Qfalse;
}


static VALUE bdb_close(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbclose(bdb) ? Qtrue : Qfalse;
}


static VALUE bdb_put(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbput(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vval),
                  RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE bdb_putkeep(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbputkeep(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vval),
                      RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE bdb_putcat(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbputcat(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vval),
                     RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE bdb_putdup(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbputdup(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vval),
                     RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE bdb_putlist(VALUE vself, VALUE vkey, VALUE vvals){
  VALUE vbdb;
  TCBDB *bdb;
  TCLIST *tvals;
  bool err;
  vkey = StringValueEx(vkey);
  Check_Type(vvals, T_ARRAY);
  tvals = varytolist(vvals);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  err = false;
  if(!tcbdbputdup3(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), tvals)) err = true;
  tclistdel(tvals);
  return err ? Qfalse : Qtrue;
}


static VALUE bdb_out(VALUE vself, VALUE vkey){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbout(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) ? Qtrue : Qfalse;
}


static VALUE bdb_outlist(VALUE vself, VALUE vkey){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbout3(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) ? Qtrue : Qfalse;
}


static VALUE bdb_get(VALUE vself, VALUE vkey){
  VALUE vbdb;
  TCBDB *bdb;
  const char *vbuf;
  int vsiz;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  if(!(vbuf = tcbdbget3(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz))) return Qnil;
  return rb_str_new(vbuf, vsiz);
}


static VALUE bdb_getlist(VALUE vself, VALUE vkey){
  VALUE vbdb, vary;
  TCBDB *bdb;
  TCLIST *vals;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  if(!(vals = tcbdbget4(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)))) return Qnil;
  vary = listtovary(vals);
  tclistdel(vals);
  return vary;
}


static VALUE bdb_vnum(VALUE vself, VALUE vkey){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return INT2NUM(tcbdbvnum(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
}


static VALUE bdb_vsiz(VALUE vself, VALUE vkey){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return INT2NUM(tcbdbvsiz(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
}


static VALUE bdb_range(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vbkey, vbinc, vekey, veinc, vmax, vary;
  TCBDB *bdb;
  TCLIST *keys;
  const char *bkbuf, *ekbuf;
  int bksiz, eksiz, max;
  bool binc, einc;
  rb_scan_args(argc, argv, "05", &vbkey, &vbinc, &vekey, &veinc, &vmax);
  if(vbkey != Qnil) vbkey = StringValueEx(vbkey);
  if(vekey != Qnil) vekey = StringValueEx(vekey);
  binc = (vbinc != Qnil && vbinc != Qfalse);
  einc = (veinc != Qnil && veinc != Qfalse);
  max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  if(vbkey != Qnil){
    bkbuf = RSTRING_PTR(vbkey);
    bksiz = RSTRING_LEN(vbkey);
  } else {
    bkbuf = NULL;
    bksiz = -1;
  }
  if(vekey != Qnil){
    ekbuf = RSTRING_PTR(vekey);
    eksiz = RSTRING_LEN(vekey);
  } else {
    ekbuf = NULL;
    eksiz = -1;
  }
  keys = tcbdbrange(bdb, bkbuf, bksiz, binc, ekbuf, eksiz, einc, max);
  vary = listtovary(keys);
  tclistdel(keys);
  return vary;
}


static VALUE bdb_fwmkeys(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vprefix, vmax, vary;
  TCBDB *bdb;
  TCLIST *keys;
  int max;
  rb_scan_args(argc, argv, "11", &vprefix, &vmax);
  vprefix = StringValueEx(vprefix);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
  keys = tcbdbfwmkeys(bdb, RSTRING_PTR(vprefix), RSTRING_LEN(vprefix), max);
  vary = listtovary(keys);
  tclistdel(keys);
  return vary;
}


static VALUE bdb_addint(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vbdb;
  TCBDB *bdb;
  int num;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  num = tcbdbaddint(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), NUM2INT(vnum));
  return num == INT_MIN ? Qnil : INT2NUM(num);
}


static VALUE bdb_adddouble(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vbdb;
  TCBDB *bdb;
  double num;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  num = tcbdbadddouble(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), NUM2DBL(vnum));
  return isnan(num) ? Qnil : rb_float_new(num);
}


static VALUE bdb_sync(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbsync(bdb) ? Qtrue : Qfalse;
}


static VALUE bdb_optimize(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vlmemb, vnmemb, vbnum, vapow, vfpow, vopts;
  TCBDB *bdb;
  int lmemb, nmemb, apow, fpow, opts;
  int64_t bnum;
  rb_scan_args(argc, argv, "06", &vlmemb, &vnmemb, &vbnum, &vapow, &vfpow, &vopts);
  lmemb = (vlmemb == Qnil) ? -1 : NUM2INT(vlmemb);
  nmemb = (vnmemb == Qnil) ? -1 : NUM2INT(vnmemb);
  bnum = (vbnum == Qnil) ? -1 : NUM2LL(vbnum);
  apow = (vapow == Qnil) ? -1 : NUM2INT(vapow);
  fpow = (vfpow == Qnil) ? -1 : NUM2INT(vfpow);
  opts = (vopts == Qnil) ? UINT8_MAX : NUM2INT(vopts);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdboptimize(bdb, lmemb, nmemb, bnum, apow, fpow, opts) ? Qtrue : Qfalse;
}


static VALUE bdb_vanish(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbvanish(bdb) ? Qtrue : Qfalse;
}


static VALUE bdb_copy(VALUE vself, VALUE vpath){
  VALUE vbdb;
  TCBDB *bdb;
  Check_Type(vpath, T_STRING);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbcopy(bdb, RSTRING_PTR(vpath)) ? Qtrue : Qfalse;
}


static VALUE bdb_tranbegin(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbtranbegin(bdb) ? Qtrue : Qfalse;
}


static VALUE bdb_trancommit(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbtrancommit(bdb) ? Qtrue : Qfalse;
}


static VALUE bdb_tranabort(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbtranabort(bdb) ? Qtrue : Qfalse;
}


static VALUE bdb_path(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  const char *path;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  if(!(path = tcbdbpath(bdb))) return Qnil;
  return rb_str_new2(path);
}


static VALUE bdb_rnum(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return LL2NUM(tcbdbrnum(bdb));
}


static VALUE bdb_fsiz(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return LL2NUM(tcbdbfsiz(bdb));
}


static VALUE bdb_fetch(int argc, VALUE *argv, VALUE vself){
  VALUE vbdb, vkey, vdef, vval;
  TCBDB *bdb;
  char *vbuf;
  int vsiz;
  rb_scan_args(argc, argv, "11", &vkey, &vdef);
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  if((vbuf = tcbdbget(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz)) != NULL){
    vval = rb_str_new(vbuf, vsiz);
    tcfree(vbuf);
  } else {
    vval = vdef;
  }
  return vval;
}


static VALUE bdb_check(VALUE vself, VALUE vkey){
  VALUE vbdb;
  TCBDB *bdb;
  vkey = StringValueEx(vkey);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbvsiz(bdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) >= 0 ? Qtrue : Qfalse;
}


static VALUE bdb_check_value(VALUE vself, VALUE vval){
  VALUE vbdb;
  TCBDB *bdb;
  BDBCUR *cur;
  const char *tvbuf;
  bool hit;
  int tvsiz;
  vval = StringValueEx(vval);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  hit = false;
  cur = tcbdbcurnew(bdb);
  tcbdbcurfirst(cur);
  while((tvbuf = tcbdbcurval3(cur, &tvsiz)) != NULL){
    if(tvsiz == RSTRING_LEN(vval) && memcmp(tvbuf, RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      hit = true;
      break;
    }
    tcbdbcurnext(cur);
  }
  tcbdbcurdel(cur);
  return hit ? Qtrue : Qfalse;
}


static VALUE bdb_get_reverse(VALUE vself, VALUE vval){
  VALUE vbdb, vrv;
  TCBDB *bdb;
  BDBCUR *cur;
  const char *tvbuf, *tkbuf;
  int tvsiz, tksiz;
  vval = StringValueEx(vval);
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  vrv = Qnil;
  cur = tcbdbcurnew(bdb);
  tcbdbcurfirst(cur);
  while((tvbuf = tcbdbcurval3(cur, &tvsiz)) != NULL){
    if(tvsiz == RSTRING_LEN(vval) && memcmp(tvbuf, RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      if((tkbuf = tcbdbcurkey3(cur, &tksiz)) != NULL)
        vrv = rb_str_new(tkbuf, tksiz);
      break;
    }
    tcbdbcurnext(cur);
  }
  tcbdbcurdel(cur);
  return vrv;
}


static VALUE bdb_empty(VALUE vself){
  VALUE vbdb;
  TCBDB *bdb;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  return tcbdbrnum(bdb) < 1 ? Qtrue : Qfalse;
}


static VALUE bdb_each(VALUE vself){
  VALUE vbdb, vrv;
  TCBDB *bdb;
  BDBCUR *cur;
  TCXSTR *kxstr, *vxstr;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  vrv = Qnil;
  kxstr = tcxstrnew();
  vxstr = tcxstrnew();
  cur = tcbdbcurnew(bdb);
  tcbdbcurfirst(cur);
  while(tcbdbcurrec(cur, kxstr, vxstr)){
    vrv = rb_yield_values(2, rb_str_new(tcxstrptr(kxstr), tcxstrsize(kxstr)),
                          rb_str_new(tcxstrptr(vxstr), tcxstrsize(vxstr)));
    tcbdbcurnext(cur);
  }
  tcbdbcurdel(cur);
  tcxstrdel(vxstr);
  tcxstrdel(kxstr);
  return vrv;
}


static VALUE bdb_each_key(VALUE vself){
  VALUE vbdb, vrv;
  TCBDB *bdb;
  BDBCUR *cur;
  const char *kbuf;
  int ksiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  vrv = Qnil;
  cur = tcbdbcurnew(bdb);
  tcbdbcurfirst(cur);
  while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
    vrv = rb_yield(rb_str_new(kbuf, ksiz));
    tcbdbcurnext(cur);
  }
  tcbdbcurdel(cur);
  return vrv;
}


static VALUE bdb_each_value(VALUE vself){
  VALUE vbdb, vrv;
  TCBDB *bdb;
  BDBCUR *cur;
  const char *vbuf;
  int vsiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  vrv = Qnil;
  cur = tcbdbcurnew(bdb);
  tcbdbcurfirst(cur);
  while((vbuf = tcbdbcurval3(cur, &vsiz)) != NULL){
    vrv = rb_yield(rb_str_new(vbuf, vsiz));
    tcbdbcurnext(cur);
  }
  tcbdbcurdel(cur);
  return vrv;
}


static VALUE bdb_keys(VALUE vself){
  VALUE vbdb, vary;
  TCBDB *bdb;
  BDBCUR *cur;
  const char *kbuf;
  int ksiz;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  vary = rb_ary_new2(tcbdbrnum(bdb));
  cur = tcbdbcurnew(bdb);
  tcbdbcurfirst(cur);
  while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
    rb_ary_push(vary, rb_str_new(kbuf, ksiz));
    tcbdbcurnext(cur);
  }
  tcbdbcurdel(cur);
  return vary;
}


static VALUE bdb_values(VALUE vself){
  VALUE vbdb, vary;
  TCBDB *bdb;
  BDBCUR *cur;
  const char *vbuf;
  int vsiz;
  vbdb = rb_iv_get(vself, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  vary = rb_ary_new2(tcbdbrnum(bdb));
  cur = tcbdbcurnew(bdb);
  tcbdbcurfirst(cur);
  while((vbuf = tcbdbcurval3(cur, &vsiz)) != NULL){
    rb_ary_push(vary, rb_str_new(vbuf, vsiz));
    tcbdbcurnext(cur);
  }
  tcbdbcurdel(cur);
  return vary;
}


static void bdbcur_init(void){
  cls_bdbcur = rb_define_class_under(mod_tokyocabinet, "BDBCUR", rb_cObject);
  cls_bdbcur_data = rb_define_class_under(mod_tokyocabinet, "BDBCUR_data", rb_cObject);
  rb_define_const(cls_bdbcur, "CPCURRENT", INT2NUM(BDBCPCURRENT));
  rb_define_const(cls_bdbcur, "CPBEFORE", INT2NUM(BDBCPBEFORE));
  rb_define_const(cls_bdbcur, "CPAFTER", INT2NUM(BDBCPAFTER));
  rb_define_private_method(cls_bdbcur, "initialize", bdbcur_initialize, 1);
  rb_define_method(cls_bdbcur, "first", bdbcur_first, 0);
  rb_define_method(cls_bdbcur, "last", bdbcur_last, 0);
  rb_define_method(cls_bdbcur, "jump", bdbcur_jump, 1);
  rb_define_method(cls_bdbcur, "prev", bdbcur_prev, 0);
  rb_define_method(cls_bdbcur, "next", bdbcur_next, 0);
  rb_define_method(cls_bdbcur, "put", bdbcur_put, -1);
  rb_define_method(cls_bdbcur, "out", bdbcur_out, 0);
  rb_define_method(cls_bdbcur, "key", bdbcur_key, 0);
  rb_define_method(cls_bdbcur, "val", bdbcur_val, 0);
}


static VALUE bdbcur_initialize(VALUE vself, VALUE vbdb){
  VALUE vcur;
  TCBDB *bdb;
  BDBCUR *cur;
  Check_Type(vbdb, T_OBJECT);
  vbdb = rb_iv_get(vbdb, BDBVNDATA);
  Data_Get_Struct(vbdb, TCBDB, bdb);
  cur = tcbdbcurnew(bdb);
  vcur = Data_Wrap_Struct(cls_bdbcur_data, 0, tcbdbcurdel, cur);
  rb_iv_set(vself, BDBCURVNDATA, vcur);
  rb_iv_set(vself, BDBVNDATA, vbdb);
  return Qnil;
}


static VALUE bdbcur_first(VALUE vself){
  VALUE vcur;
  BDBCUR *cur;
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  return tcbdbcurfirst(cur) ? Qtrue : Qfalse;
}


static VALUE bdbcur_last(VALUE vself){
  VALUE vcur;
  BDBCUR *cur;
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  return tcbdbcurlast(cur) ? Qtrue : Qfalse;
}


static VALUE bdbcur_jump(VALUE vself, VALUE vkey){
  VALUE vcur;
  BDBCUR *cur;
  vkey = StringValueEx(vkey);
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  return tcbdbcurjump(cur, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) ? Qtrue : Qfalse;
}


static VALUE bdbcur_prev(VALUE vself){
  VALUE vcur;
  BDBCUR *cur;
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  return tcbdbcurprev(cur) ? Qtrue : Qfalse;
}


static VALUE bdbcur_next(VALUE vself){
  VALUE vcur;
  BDBCUR *cur;
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  return tcbdbcurnext(cur) ? Qtrue : Qfalse;
}


static VALUE bdbcur_put(int argc, VALUE *argv, VALUE vself){
  VALUE vcur, vval, vcpmode;
  BDBCUR *cur;
  int cpmode;
  rb_scan_args(argc, argv, "11", &vval, &vcpmode);
  vval = StringValueEx(vval);
  cpmode = (vcpmode == Qnil) ? BDBCPCURRENT : NUM2INT(vcpmode);
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  return tcbdbcurput(cur, RSTRING_PTR(vval), RSTRING_LEN(vval), cpmode) ? Qtrue : Qfalse;
}


static VALUE bdbcur_out(VALUE vself){
  VALUE vcur;
  BDBCUR *cur;
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  return tcbdbcurout(cur) ? Qtrue : Qfalse;
}


static VALUE bdbcur_key(VALUE vself){
  VALUE vcur, vkey;
  BDBCUR *cur;
  char *kbuf;
  int ksiz;
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  if(!(kbuf = tcbdbcurkey(cur, &ksiz))) return Qnil;
  vkey = rb_str_new(kbuf, ksiz);
  tcfree(kbuf);
  return vkey;
}


static VALUE bdbcur_val(VALUE vself){
  VALUE vcur, vval;
  BDBCUR *cur;
  char *vbuf;
  int vsiz;
  vcur = rb_iv_get(vself, BDBCURVNDATA);
  Data_Get_Struct(vcur, BDBCUR, cur);
  if(!(vbuf = tcbdbcurval(cur, &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static void fdb_init(void){
  cls_fdb = rb_define_class_under(mod_tokyocabinet, "FDB", rb_cObject);
  cls_fdb_data = rb_define_class_under(mod_tokyocabinet, "FDB_data", rb_cObject);
  rb_define_const(cls_fdb, "ESUCCESS", INT2NUM(TCESUCCESS));
  rb_define_const(cls_fdb, "ETHREAD", INT2NUM(TCETHREAD));
  rb_define_const(cls_fdb, "EINVALID", INT2NUM(TCEINVALID));
  rb_define_const(cls_fdb, "ENOFILE", INT2NUM(TCENOFILE));
  rb_define_const(cls_fdb, "ENOPERM", INT2NUM(TCENOPERM));
  rb_define_const(cls_fdb, "EMETA", INT2NUM(TCEMETA));
  rb_define_const(cls_fdb, "ERHEAD", INT2NUM(TCERHEAD));
  rb_define_const(cls_fdb, "EOPEN", INT2NUM(TCEOPEN));
  rb_define_const(cls_fdb, "ECLOSE", INT2NUM(TCECLOSE));
  rb_define_const(cls_fdb, "ETRUNC", INT2NUM(TCETRUNC));
  rb_define_const(cls_fdb, "ESYNC", INT2NUM(TCESYNC));
  rb_define_const(cls_fdb, "ESTAT", INT2NUM(TCESTAT));
  rb_define_const(cls_fdb, "ESEEK", INT2NUM(TCESEEK));
  rb_define_const(cls_fdb, "EREAD", INT2NUM(TCEREAD));
  rb_define_const(cls_fdb, "EWRITE", INT2NUM(TCEWRITE));
  rb_define_const(cls_fdb, "EMMAP", INT2NUM(TCEMMAP));
  rb_define_const(cls_fdb, "ELOCK", INT2NUM(TCELOCK));
  rb_define_const(cls_fdb, "EUNLINK", INT2NUM(TCEUNLINK));
  rb_define_const(cls_fdb, "ERENAME", INT2NUM(TCERENAME));
  rb_define_const(cls_fdb, "EMKDIR", INT2NUM(TCEMKDIR));
  rb_define_const(cls_fdb, "ERMDIR", INT2NUM(TCERMDIR));
  rb_define_const(cls_fdb, "EKEEP", INT2NUM(TCEKEEP));
  rb_define_const(cls_fdb, "ENOREC", INT2NUM(TCENOREC));
  rb_define_const(cls_fdb, "EMISC", INT2NUM(TCEMISC));
  rb_define_const(cls_fdb, "OREADER", INT2NUM(FDBOREADER));
  rb_define_const(cls_fdb, "OWRITER", INT2NUM(FDBOWRITER));
  rb_define_const(cls_fdb, "OCREAT", INT2NUM(FDBOCREAT));
  rb_define_const(cls_fdb, "OTRUNC", INT2NUM(FDBOTRUNC));
  rb_define_const(cls_fdb, "ONOLCK", INT2NUM(FDBONOLCK));
  rb_define_const(cls_fdb, "OLCKNB", INT2NUM(FDBOLCKNB));
  rb_define_private_method(cls_fdb, "initialize", fdb_initialize, 0);
  rb_define_method(cls_fdb, "errmsg", fdb_errmsg, -1);
  rb_define_method(cls_fdb, "ecode", fdb_ecode, 0);
  rb_define_method(cls_fdb, "tune", fdb_tune, -1);
  rb_define_method(cls_fdb, "open", fdb_open, -1);
  rb_define_method(cls_fdb, "close", fdb_close, 0);
  rb_define_method(cls_fdb, "put", fdb_put, 2);
  rb_define_method(cls_fdb, "putkeep", fdb_putkeep, 2);
  rb_define_method(cls_fdb, "putcat", fdb_putcat, 2);
  rb_define_method(cls_fdb, "out", fdb_out, 1);
  rb_define_method(cls_fdb, "get", fdb_get, 1);
  rb_define_method(cls_fdb, "vsiz", fdb_vsiz, 1);
  rb_define_method(cls_fdb, "iterinit", fdb_iterinit, 0);
  rb_define_method(cls_fdb, "iternext", fdb_iternext, 0);
  rb_define_method(cls_fdb, "range", fdb_range, -1);
  rb_define_method(cls_fdb, "addint", fdb_addint, 2);
  rb_define_method(cls_fdb, "adddouble", fdb_adddouble, 2);
  rb_define_method(cls_fdb, "sync", fdb_sync, 0);
  rb_define_method(cls_fdb, "optimize", fdb_optimize, -1);
  rb_define_method(cls_fdb, "vanish", fdb_vanish, 0);
  rb_define_method(cls_fdb, "copy", fdb_copy, 1);
  rb_define_method(cls_fdb, "tranbegin", fdb_tranbegin, 0);
  rb_define_method(cls_fdb, "trancommit", fdb_trancommit, 0);
  rb_define_method(cls_fdb, "tranabort", fdb_tranabort, 0);
  rb_define_method(cls_fdb, "path", fdb_path, 0);
  rb_define_method(cls_fdb, "rnum", fdb_rnum, 0);
  rb_define_method(cls_fdb, "fsiz", fdb_fsiz, 0);
  rb_define_method(cls_fdb, "[]", fdb_get, 1);
  rb_define_method(cls_fdb, "[]=", fdb_put, 2);
  rb_define_method(cls_fdb, "store", fdb_put, 2);
  rb_define_method(cls_fdb, "delete", fdb_out, 1);
  rb_define_method(cls_fdb, "fetch", fdb_fetch, -1);
  rb_define_method(cls_fdb, "has_key?", fdb_check, 1);
  rb_define_method(cls_fdb, "key?", fdb_check, 1);
  rb_define_method(cls_fdb, "include?", fdb_check, 1);
  rb_define_method(cls_fdb, "member?", fdb_check, 1);
  rb_define_method(cls_fdb, "has_value?", fdb_check_value, 1);
  rb_define_method(cls_fdb, "value?", fdb_check_value, 1);
  rb_define_method(cls_fdb, "key", fdb_get_reverse, 1);
  rb_define_method(cls_fdb, "clear", fdb_vanish, 0);
  rb_define_method(cls_fdb, "size", fdb_rnum, 0);
  rb_define_method(cls_fdb, "length", fdb_rnum, 0);
  rb_define_method(cls_fdb, "empty?", fdb_empty, 0);
  rb_define_method(cls_fdb, "each", fdb_each, 0);
  rb_define_method(cls_fdb, "each_pair", fdb_each, 0);
  rb_define_method(cls_fdb, "each_key", fdb_each_key, 0);
  rb_define_method(cls_fdb, "each_value", fdb_each_value, 0);
  rb_define_method(cls_fdb, "keys", fdb_keys, 0);
  rb_define_method(cls_fdb, "values", fdb_values, 0);
}


static VALUE fdb_initialize(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  fdb = tcfdbnew();
  tcfdbsetmutex(fdb);
  vfdb = Data_Wrap_Struct(cls_fdb_data, 0, tcfdbdel, fdb);
  rb_iv_set(vself, FDBVNDATA, vfdb);
  return Qnil;
}


static VALUE fdb_errmsg(int argc, VALUE *argv, VALUE vself){
  VALUE vfdb, vecode;
  TCFDB *fdb;
  const char *msg;
  int ecode;
  rb_scan_args(argc, argv, "01", &vecode);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  ecode = (vecode == Qnil) ? tcfdbecode(fdb) : NUM2INT(vecode);
  msg = tcfdberrmsg(ecode);
  return rb_str_new2(msg);
}


static VALUE fdb_ecode(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return INT2NUM(tcfdbecode(fdb));
}


static VALUE fdb_tune(int argc, VALUE *argv, VALUE vself){
  VALUE vfdb, vwidth, vlimsiz;
  TCFDB *fdb;
  int width;
  int64_t limsiz;
  rb_scan_args(argc, argv, "02", &vwidth, &vlimsiz);
  width = (vwidth == Qnil) ? -1 : NUM2INT(vwidth);
  limsiz = (vlimsiz == Qnil) ? -1 : NUM2LL(vlimsiz);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbtune(fdb, width, limsiz) ? Qtrue : Qfalse;
}


static VALUE fdb_open(int argc, VALUE *argv, VALUE vself){
  VALUE vfdb, vpath, vomode;
  TCFDB *fdb;
  int omode;
  rb_scan_args(argc, argv, "11", &vpath, &vomode);
  Check_Type(vpath, T_STRING);
  omode = (vomode == Qnil) ? FDBOREADER : NUM2INT(vomode);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbopen(fdb, RSTRING_PTR(vpath), omode) ? Qtrue : Qfalse;
}


static VALUE fdb_close(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbclose(fdb) ? Qtrue : Qfalse;
}


static VALUE fdb_put(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vfdb;
  TCFDB *fdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbput2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                   RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE fdb_putkeep(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vfdb;
  TCFDB *fdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbputkeep2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                       RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE fdb_putcat(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vfdb;
  TCFDB *fdb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbputcat2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                      RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE fdb_out(VALUE vself, VALUE vkey){
  VALUE vfdb;
  TCFDB *fdb;
  vkey = StringValueEx(vkey);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbout2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) ? Qtrue : Qfalse;
}


static VALUE fdb_get(VALUE vself, VALUE vkey){
  VALUE vfdb, vval;
  TCFDB *fdb;
  char *vbuf;
  int vsiz;
  vkey = StringValueEx(vkey);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  if(!(vbuf = tcfdbget2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static VALUE fdb_vsiz(VALUE vself, VALUE vkey){
  VALUE vfdb;
  TCFDB *fdb;
  vkey = StringValueEx(vkey);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return INT2NUM(tcfdbvsiz2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
}


static VALUE fdb_iterinit(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbiterinit(fdb) ? Qtrue : Qfalse;
}


static VALUE fdb_iternext(VALUE vself){
  VALUE vfdb, vval;
  TCFDB *fdb;
  char *vbuf;
  int vsiz;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  if(!(vbuf = tcfdbiternext2(fdb, &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static VALUE fdb_range(int argc, VALUE *argv, VALUE vself){
  VALUE vfdb, vinterval, vmax, vary;
  TCFDB *fdb;
  TCLIST *keys;
  int max;
  rb_scan_args(argc, argv, "11", &vinterval, &vmax);
  vinterval = StringValueEx(vinterval);
  max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  keys = tcfdbrange4(fdb, RSTRING_PTR(vinterval), RSTRING_LEN(vinterval), max);
  vary = listtovary(keys);
  tclistdel(keys);
  return vary;
}


static VALUE fdb_addint(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vfdb;
  TCFDB *fdb;
  int num;
  vkey = StringValueEx(vkey);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  num = tcfdbaddint(fdb, tcfdbkeytoid(RSTRING_PTR(vkey), RSTRING_LEN(vkey)), NUM2INT(vnum));
  return num == INT_MIN ? Qnil : INT2NUM(num);
}


static VALUE fdb_adddouble(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vfdb;
  TCFDB *fdb;
  double num;
  vkey = StringValueEx(vkey);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  num = tcfdbadddouble(fdb, tcfdbkeytoid(RSTRING_PTR(vkey), RSTRING_LEN(vkey)), NUM2DBL(vnum));
  return isnan(num) ? Qnil : rb_float_new(num);
}


static VALUE fdb_sync(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbsync(fdb) ? Qtrue : Qfalse;
}


static VALUE fdb_optimize(int argc, VALUE *argv, VALUE vself){
  VALUE vfdb, vwidth, vlimsiz;
  TCFDB *fdb;
  int width;
  int64_t limsiz;
  rb_scan_args(argc, argv, "02", &vwidth, &vlimsiz);
  width = (vwidth == Qnil) ? -1 : NUM2INT(vwidth);
  limsiz = (vlimsiz == Qnil) ? -1 : NUM2LL(vlimsiz);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdboptimize(fdb, width, limsiz) ? Qtrue : Qfalse;
}


static VALUE fdb_vanish(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbvanish(fdb) ? Qtrue : Qfalse;
}


static VALUE fdb_copy(VALUE vself, VALUE vpath){
  VALUE vfdb;
  TCFDB *fdb;
  Check_Type(vpath, T_STRING);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbcopy(fdb, RSTRING_PTR(vpath)) ? Qtrue : Qfalse;
}


static VALUE fdb_tranbegin(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbtranbegin(fdb) ? Qtrue : Qfalse;
}


static VALUE fdb_trancommit(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbtrancommit(fdb) ? Qtrue : Qfalse;
}


static VALUE fdb_tranabort(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbtranabort(fdb) ? Qtrue : Qfalse;
}


static VALUE fdb_path(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  const char *path;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  if(!(path = tcfdbpath(fdb))) return Qnil;
  return rb_str_new2(path);
}


static VALUE fdb_rnum(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return LL2NUM(tcfdbrnum(fdb));
}


static VALUE fdb_fsiz(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return LL2NUM(tcfdbfsiz(fdb));
}


static VALUE fdb_fetch(int argc, VALUE *argv, VALUE vself){
  VALUE vfdb, vkey, vdef, vval;
  TCFDB *fdb;
  char *vbuf;
  int vsiz;
  rb_scan_args(argc, argv, "11", &vkey, &vdef);
  vkey = StringValueEx(vkey);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  if((vbuf = tcfdbget2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz)) != NULL){
    vval = rb_str_new(vbuf, vsiz);
    tcfree(vbuf);
  } else {
    vval = vdef;
  }
  return vval;
}


static VALUE fdb_check(VALUE vself, VALUE vkey){
  VALUE vfdb;
  TCFDB *fdb;
  vkey = StringValueEx(vkey);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbvsiz2(fdb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) >= 0 ? Qtrue : Qfalse;
}


static VALUE fdb_check_value(VALUE vself, VALUE vval){
  VALUE vfdb;
  TCFDB *fdb;
  char *tvbuf;
  bool hit;
  int tvsiz;
  uint64_t id;
  vval = StringValueEx(vval);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  hit = false;
  tcfdbiterinit(fdb);
  while((id = tcfdbiternext(fdb)) > 0){
    tvbuf = tcfdbget(fdb, id, &tvsiz);
    if(tvbuf && tvsiz == RSTRING_LEN(vval) &&
       memcmp(tvbuf, RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      tcfree(tvbuf);
      hit = true;
      break;
    }
    tcfree(tvbuf);
  }
  return hit ? Qtrue : Qfalse;
}


static VALUE fdb_get_reverse(VALUE vself, VALUE vval){
  VALUE vfdb, vrv;
  TCFDB *fdb;
  char *tvbuf, kbuf[NUMBUFSIZ];
  int tvsiz, ksiz;
  uint64_t id;
  vval = StringValueEx(vval);
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  vrv = Qnil;
  tcfdbiterinit(fdb);
  while((id = tcfdbiternext(fdb)) > 0){
    tvbuf = tcfdbget(fdb, id, &tvsiz);
    if(tvbuf && tvsiz == RSTRING_LEN(vval) &&
       memcmp(tvbuf, RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      tcfree(tvbuf);
      ksiz = sprintf(kbuf, "%llu", (unsigned long long)id);
      vrv = rb_str_new(kbuf, ksiz);
      break;
    }
    tcfree(tvbuf);
  }
  return vrv;
}


static VALUE fdb_empty(VALUE vself){
  VALUE vfdb;
  TCFDB *fdb;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  return tcfdbrnum(fdb) < 1 ? Qtrue : Qfalse;
}


static VALUE fdb_each(VALUE vself){
  VALUE vfdb, vrv;
  TCFDB *fdb;
  char *vbuf, kbuf[NUMBUFSIZ];
  int vsiz, ksiz;
  uint64_t id;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  vrv = Qnil;
  tcfdbiterinit(fdb);
  while((id = tcfdbiternext(fdb)) > 0){
    vbuf = tcfdbget(fdb, id, &vsiz);
    if(vbuf){
      ksiz = sprintf(kbuf, "%llu", (unsigned long long)id);
      vrv = rb_yield_values(2, rb_str_new(kbuf, ksiz), rb_str_new(vbuf, vsiz));
    }
    tcfree(vbuf);
  }
  return vrv;
}


static VALUE fdb_each_key(VALUE vself){
  VALUE vfdb, vrv;
  TCFDB *fdb;
  char kbuf[NUMBUFSIZ];
  int ksiz;
  uint64_t id;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  vrv = Qnil;
  tcfdbiterinit(fdb);
  while((id = tcfdbiternext(fdb)) > 0){
    ksiz = sprintf(kbuf, "%llu", (unsigned long long)id);
    vrv = rb_yield(rb_str_new(kbuf, ksiz));
  }
  return vrv;
}


static VALUE fdb_each_value(VALUE vself){
  VALUE vfdb, vrv;
  TCFDB *fdb;
  char *vbuf;
  int vsiz;
  uint64_t id;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  vrv = Qnil;
  tcfdbiterinit(fdb);
  while((id = tcfdbiternext(fdb)) > 0){
    vbuf = tcfdbget(fdb, id, &vsiz);
    if(vbuf){
      vrv = rb_yield(rb_str_new(vbuf, vsiz));
    }
    free(vbuf);
  }
  return vrv;
}


static VALUE fdb_keys(VALUE vself){
  VALUE vfdb, vary;
  TCFDB *fdb;
  char kbuf[NUMBUFSIZ];
  int ksiz;
  uint64_t id;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  vary = rb_ary_new2(tcfdbrnum(fdb));
  tcfdbiterinit(fdb);
  while((id = tcfdbiternext(fdb)) > 0){
    ksiz = sprintf(kbuf, "%llu", (unsigned long long)id);
    rb_ary_push(vary, rb_str_new(kbuf, ksiz));
  }
  return vary;
}


static VALUE fdb_values(VALUE vself){
  VALUE vfdb, vary;
  TCFDB *fdb;
  char *vbuf;
  int vsiz;
  uint64_t id;
  vfdb = rb_iv_get(vself, FDBVNDATA);
  Data_Get_Struct(vfdb, TCFDB, fdb);
  vary = rb_ary_new2(tcfdbrnum(fdb));
  tcfdbiterinit(fdb);
  while((id = tcfdbiternext(fdb)) > 0){
    vbuf = tcfdbget(fdb, id, &vsiz);
    if(vbuf){
      rb_ary_push(vary, rb_str_new(vbuf, vsiz));
    }
    free(vbuf);
  }
  return vary;
}


static void tdb_init(void){
  cls_tdb = rb_define_class_under(mod_tokyocabinet, "TDB", rb_cObject);
  cls_tdb_data = rb_define_class_under(mod_tokyocabinet, "TDB_data", rb_cObject);
  rb_define_const(cls_tdb, "ESUCCESS", INT2NUM(TCESUCCESS));
  rb_define_const(cls_tdb, "ETHREAD", INT2NUM(TCETHREAD));
  rb_define_const(cls_tdb, "EINVALID", INT2NUM(TCEINVALID));
  rb_define_const(cls_tdb, "ENOFILE", INT2NUM(TCENOFILE));
  rb_define_const(cls_tdb, "ENOPERM", INT2NUM(TCENOPERM));
  rb_define_const(cls_tdb, "EMETA", INT2NUM(TCEMETA));
  rb_define_const(cls_tdb, "ERHEAD", INT2NUM(TCERHEAD));
  rb_define_const(cls_tdb, "EOPEN", INT2NUM(TCEOPEN));
  rb_define_const(cls_tdb, "ECLOSE", INT2NUM(TCECLOSE));
  rb_define_const(cls_tdb, "ETRUNC", INT2NUM(TCETRUNC));
  rb_define_const(cls_tdb, "ESYNC", INT2NUM(TCESYNC));
  rb_define_const(cls_tdb, "ESTAT", INT2NUM(TCESTAT));
  rb_define_const(cls_tdb, "ESEEK", INT2NUM(TCESEEK));
  rb_define_const(cls_tdb, "EREAD", INT2NUM(TCEREAD));
  rb_define_const(cls_tdb, "EWRITE", INT2NUM(TCEWRITE));
  rb_define_const(cls_tdb, "EMMAP", INT2NUM(TCEMMAP));
  rb_define_const(cls_tdb, "ELOCK", INT2NUM(TCELOCK));
  rb_define_const(cls_tdb, "EUNLINK", INT2NUM(TCEUNLINK));
  rb_define_const(cls_tdb, "ERENAME", INT2NUM(TCERENAME));
  rb_define_const(cls_tdb, "EMKDIR", INT2NUM(TCEMKDIR));
  rb_define_const(cls_tdb, "ERMDIR", INT2NUM(TCERMDIR));
  rb_define_const(cls_tdb, "EKEEP", INT2NUM(TCEKEEP));
  rb_define_const(cls_tdb, "ENOREC", INT2NUM(TCENOREC));
  rb_define_const(cls_tdb, "EMISC", INT2NUM(TCEMISC));
  rb_define_const(cls_tdb, "TLARGE", INT2NUM(TDBTLARGE));
  rb_define_const(cls_tdb, "TDEFLATE", INT2NUM(TDBTDEFLATE));
  rb_define_const(cls_tdb, "TBZIP", INT2NUM(TDBTBZIP));
  rb_define_const(cls_tdb, "TTCBS", INT2NUM(TDBTTCBS));
  rb_define_const(cls_tdb, "OREADER", INT2NUM(TDBOREADER));
  rb_define_const(cls_tdb, "OWRITER", INT2NUM(TDBOWRITER));
  rb_define_const(cls_tdb, "OCREAT", INT2NUM(TDBOCREAT));
  rb_define_const(cls_tdb, "OTRUNC", INT2NUM(TDBOTRUNC));
  rb_define_const(cls_tdb, "ONOLCK", INT2NUM(TDBONOLCK));
  rb_define_const(cls_tdb, "OLCKNB", INT2NUM(TDBOLCKNB));
  rb_define_const(cls_tdb, "OTSYNC", INT2NUM(TDBOTSYNC));
  rb_define_const(cls_tdb, "ITLEXICAL", INT2NUM(TDBITLEXICAL));
  rb_define_const(cls_tdb, "ITDECIMAL", INT2NUM(TDBITDECIMAL));
  rb_define_const(cls_tdb, "ITTOKEN", INT2NUM(TDBITTOKEN));
  rb_define_const(cls_tdb, "ITQGRAM", INT2NUM(TDBITQGRAM));
  rb_define_const(cls_tdb, "ITVOID", INT2NUM(TDBITVOID));
  rb_define_const(cls_tdb, "ITKEEP", INT2NUM(TDBITKEEP));
  rb_define_private_method(cls_tdb, "initialize", tdb_initialize, 0);
  rb_define_method(cls_tdb, "errmsg", tdb_errmsg, -1);
  rb_define_method(cls_tdb, "ecode", tdb_ecode, 0);
  rb_define_method(cls_tdb, "tune", tdb_tune, -1);
  rb_define_method(cls_tdb, "setcache", tdb_setcache, -1);
  rb_define_method(cls_tdb, "setxmsiz", tdb_setxmsiz, -1);
  rb_define_method(cls_tdb, "setdfunit", tdb_setdfunit, -1);
  rb_define_method(cls_tdb, "open", tdb_open, -1);
  rb_define_method(cls_tdb, "close", tdb_close, 0);
  rb_define_method(cls_tdb, "put", tdb_put, 2);
  rb_define_method(cls_tdb, "putkeep", tdb_putkeep, 2);
  rb_define_method(cls_tdb, "putcat", tdb_putcat, 2);
  rb_define_method(cls_tdb, "out", tdb_out, 1);
  rb_define_method(cls_tdb, "get", tdb_get, 1);
  rb_define_method(cls_tdb, "vsiz", tdb_vsiz, 1);
  rb_define_method(cls_tdb, "iterinit", tdb_iterinit, 0);
  rb_define_method(cls_tdb, "iternext", tdb_iternext, 0);
  rb_define_method(cls_tdb, "fwmkeys", tdb_fwmkeys, -1);
  rb_define_method(cls_tdb, "addint", tdb_addint, 2);
  rb_define_method(cls_tdb, "adddouble", tdb_adddouble, 2);
  rb_define_method(cls_tdb, "sync", tdb_sync, 0);
  rb_define_method(cls_tdb, "optimize", tdb_optimize, -1);
  rb_define_method(cls_tdb, "vanish", tdb_vanish, 0);
  rb_define_method(cls_tdb, "copy", tdb_copy, 1);
  rb_define_method(cls_tdb, "tranbegin", tdb_tranbegin, 0);
  rb_define_method(cls_tdb, "trancommit", tdb_trancommit, 0);
  rb_define_method(cls_tdb, "tranabort", tdb_tranabort, 0);
  rb_define_method(cls_tdb, "path", tdb_path, 0);
  rb_define_method(cls_tdb, "rnum", tdb_rnum, 0);
  rb_define_method(cls_tdb, "fsiz", tdb_fsiz, 0);
  rb_define_method(cls_tdb, "setindex", tdb_setindex, 2);
  rb_define_method(cls_tdb, "genuid", tdb_genuid, 0);
  rb_define_method(cls_tdb, "[]", tdb_get, 1);
  rb_define_method(cls_tdb, "[]=", tdb_put, 2);
  rb_define_method(cls_tdb, "store", tdb_put, 2);
  rb_define_method(cls_tdb, "delete", tdb_out, 1);
  rb_define_method(cls_tdb, "fetch", tdb_fetch, -1);
  rb_define_method(cls_tdb, "has_key?", tdb_check, 1);
  rb_define_method(cls_tdb, "key?", tdb_check, 1);
  rb_define_method(cls_tdb, "include?", tdb_check, 1);
  rb_define_method(cls_tdb, "member?", tdb_check, 1);
  rb_define_method(cls_tdb, "clear", tdb_vanish, 0);
  rb_define_method(cls_tdb, "size", tdb_rnum, 0);
  rb_define_method(cls_tdb, "length", tdb_rnum, 0);
  rb_define_method(cls_tdb, "empty?", tdb_empty, 0);
  rb_define_method(cls_tdb, "each", tdb_each, 0);
  rb_define_method(cls_tdb, "each_pair", tdb_each, 0);
  rb_define_method(cls_tdb, "each_key", tdb_each_key, 0);
  rb_define_method(cls_tdb, "each_value", tdb_each_value, 0);
  rb_define_method(cls_tdb, "keys", tdb_keys, 0);
  rb_define_method(cls_tdb, "values", tdb_values, 0);
}


static VALUE tdb_initialize(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  tdb = tctdbnew();
  tctdbsetmutex(tdb);
  vtdb = Data_Wrap_Struct(cls_tdb_data, 0, tctdbdel, tdb);
  rb_iv_set(vself, TDBVNDATA, vtdb);
  return Qnil;
}


static VALUE tdb_errmsg(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vecode;
  TCTDB *tdb;
  const char *msg;
  int ecode;
  rb_scan_args(argc, argv, "01", &vecode);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  ecode = (vecode == Qnil) ? tctdbecode(tdb) : NUM2INT(vecode);
  msg = tctdberrmsg(ecode);
  return rb_str_new2(msg);
}


static VALUE tdb_ecode(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return INT2NUM(tctdbecode(tdb));
}


static VALUE tdb_tune(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vbnum, vapow, vfpow, vopts;
  TCTDB *tdb;
  int apow, fpow, opts;
  int64_t bnum;
  rb_scan_args(argc, argv, "04", &vbnum, &vapow, &vfpow, &vopts);
  bnum = (vbnum == Qnil) ? -1 : NUM2LL(vbnum);
  apow = (vapow == Qnil) ? -1 : NUM2INT(vapow);
  fpow = (vfpow == Qnil) ? -1 : NUM2INT(vfpow);
  opts = (vopts == Qnil) ? 0 : NUM2INT(vopts);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbtune(tdb, bnum, apow, fpow, opts) ? Qtrue : Qfalse;
}


static VALUE tdb_setcache(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vrcnum, vlcnum, vncnum;
  TCTDB *tdb;
  int rcnum, lcnum, ncnum;
  rb_scan_args(argc, argv, "03", &vrcnum, &vlcnum, &vncnum);
  rcnum = (vrcnum == Qnil) ? -1 : NUM2INT(vrcnum);
  lcnum = (vlcnum == Qnil) ? -1 : NUM2INT(vlcnum);
  ncnum = (vncnum == Qnil) ? -1 : NUM2INT(vncnum);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbsetcache(tdb, rcnum, lcnum, ncnum) ? Qtrue : Qfalse;
}


static VALUE tdb_setxmsiz(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vxmsiz;
  TCTDB *tdb;
  int64_t xmsiz;
  rb_scan_args(argc, argv, "01", &vxmsiz);
  xmsiz = (vxmsiz == Qnil) ? -1 : NUM2LL(vxmsiz);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbsetxmsiz(tdb, xmsiz) ? Qtrue : Qfalse;
}


static VALUE tdb_setdfunit(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vdfunit;
  TCTDB *tdb;
  int32_t dfunit;
  rb_scan_args(argc, argv, "01", &vdfunit);
  dfunit = (vdfunit == Qnil) ? -1 : NUM2INT(vdfunit);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbsetdfunit(tdb, dfunit) ? Qtrue : Qfalse;
}


static VALUE tdb_open(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vpath, vomode;
  TCTDB *tdb;
  int omode;
  rb_scan_args(argc, argv, "11", &vpath, &vomode);
  Check_Type(vpath, T_STRING);
  omode = (vomode == Qnil) ? TDBOREADER : NUM2INT(vomode);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbopen(tdb, RSTRING_PTR(vpath), omode) ? Qtrue : Qfalse;
}


static VALUE tdb_close(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbclose(tdb) ? Qtrue : Qfalse;
}


static VALUE tdb_put(VALUE vself, VALUE vpkey, VALUE vcols){
  VALUE vtdb, vrv;
  TCTDB *tdb;
  TCMAP *cols;
  vpkey = StringValueEx(vpkey);
  Check_Type(vcols, T_HASH);
  cols = vhashtomap(vcols);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vrv = tctdbput(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey), cols) ? Qtrue : Qfalse;
  tcmapdel(cols);
  return vrv;
}


static VALUE tdb_putkeep(VALUE vself, VALUE vpkey, VALUE vcols){
  VALUE vtdb, vrv;
  TCTDB *tdb;
  TCMAP *cols;
  vpkey = StringValueEx(vpkey);
  Check_Type(vcols, T_HASH);
  cols = vhashtomap(vcols);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vrv = tctdbputkeep(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey), cols) ? Qtrue : Qfalse;
  tcmapdel(cols);
  return vrv;
}


static VALUE tdb_putcat(VALUE vself, VALUE vpkey, VALUE vcols){
  VALUE vtdb, vrv;
  TCTDB *tdb;
  TCMAP *cols;
  vpkey = StringValueEx(vpkey);
  Check_Type(vcols, T_HASH);
  cols = vhashtomap(vcols);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vrv = tctdbputcat(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey), cols) ? Qtrue : Qfalse;
  tcmapdel(cols);
  return vrv;
}


static VALUE tdb_out(VALUE vself, VALUE vpkey){
  VALUE vtdb;
  TCTDB *tdb;
  vpkey = StringValueEx(vpkey);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbout(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey)) ? Qtrue : Qfalse;
}


static VALUE tdb_get(VALUE vself, VALUE vpkey){
  VALUE vtdb, vcols;
  TCTDB *tdb;
  TCMAP *cols;
  vpkey = StringValueEx(vpkey);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  if(!(cols = tctdbget(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey)))) return Qnil;
  vcols = maptovhash(cols);
  tcmapdel(cols);
  return vcols;
}


static VALUE tdb_vsiz(VALUE vself, VALUE vpkey){
  VALUE vtdb;
  TCTDB *tdb;
  vpkey = StringValueEx(vpkey);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return INT2NUM(tctdbvsiz(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey)));
}


static VALUE tdb_iterinit(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbiterinit(tdb) ? Qtrue : Qfalse;
}


static VALUE tdb_iternext(VALUE vself){
  VALUE vtdb, vval;
  TCTDB *tdb;
  char *vbuf;
  int vsiz;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  if(!(vbuf = tctdbiternext(tdb, &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static VALUE tdb_fwmkeys(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vprefix, vmax, vary;
  TCTDB *tdb;
  TCLIST *pkeys;
  int max;
  rb_scan_args(argc, argv, "11", &vprefix, &vmax);
  vprefix = StringValueEx(vprefix);
  max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  pkeys = tctdbfwmkeys(tdb, RSTRING_PTR(vprefix), RSTRING_LEN(vprefix), max);
  vary = listtovary(pkeys);
  tclistdel(pkeys);
  return vary;
}


static VALUE tdb_addint(VALUE vself, VALUE vpkey, VALUE vnum){
  VALUE vtdb;
  TCTDB *tdb;
  int num;
  vpkey = StringValueEx(vpkey);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  num = tctdbaddint(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey), NUM2INT(vnum));
  return num == INT_MIN ? Qnil : INT2NUM(num);
}


static VALUE tdb_adddouble(VALUE vself, VALUE vpkey, VALUE vnum){
  VALUE vtdb;
  TCTDB *tdb;
  double num;
  vpkey = StringValueEx(vpkey);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  num = tctdbadddouble(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey), NUM2DBL(vnum));
  return isnan(num) ? Qnil : rb_float_new(num);
}


static VALUE tdb_sync(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbsync(tdb) ? Qtrue : Qfalse;
}


static VALUE tdb_optimize(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vbnum, vapow, vfpow, vopts;
  TCTDB *tdb;
  int apow, fpow, opts;
  int64_t bnum;
  rb_scan_args(argc, argv, "04", &vbnum, &vapow, &vfpow, &vopts);
  bnum = (vbnum == Qnil) ? -1 : NUM2LL(vbnum);
  apow = (vapow == Qnil) ? -1 : NUM2INT(vapow);
  fpow = (vfpow == Qnil) ? -1 : NUM2INT(vfpow);
  opts = (vopts == Qnil) ? UINT8_MAX : NUM2INT(vopts);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdboptimize(tdb, bnum, apow, fpow, opts) ? Qtrue : Qfalse;
}


static VALUE tdb_vanish(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbvanish(tdb) ? Qtrue : Qfalse;
}


static VALUE tdb_copy(VALUE vself, VALUE vpath){
  VALUE vtdb;
  TCTDB *tdb;
  Check_Type(vpath, T_STRING);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbcopy(tdb, RSTRING_PTR(vpath)) ? Qtrue : Qfalse;
}


static VALUE tdb_tranbegin(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbtranbegin(tdb) ? Qtrue : Qfalse;
}


static VALUE tdb_trancommit(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbtrancommit(tdb) ? Qtrue : Qfalse;
}


static VALUE tdb_tranabort(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbtranabort(tdb) ? Qtrue : Qfalse;
}


static VALUE tdb_path(VALUE vself){
  VALUE vtdb, vpath;
  TCTDB *tdb;
  const char *path;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  if(!(path = tctdbpath(tdb))) return Qnil;
  vpath = rb_str_new2(path);
  return vpath;
}


static VALUE tdb_rnum(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return LL2NUM(tctdbrnum(tdb));
}


static VALUE tdb_fsiz(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return LL2NUM(tctdbfsiz(tdb));
}


static VALUE tdb_setindex(VALUE vself, VALUE vname, VALUE vtype){
  VALUE vtdb;
  TCTDB *tdb;
  Check_Type(vname, T_STRING);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbsetindex(tdb, RSTRING_PTR(vname), NUM2INT(vtype)) ? Qtrue : Qfalse;
}


static VALUE tdb_genuid(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return LL2NUM(tctdbgenuid(tdb));
}


static VALUE tdb_fetch(int argc, VALUE *argv, VALUE vself){
  VALUE vtdb, vpkey, vdef, vcols;
  TCTDB *tdb;
  TCMAP *cols;
  rb_scan_args(argc, argv, "11", &vpkey, &vdef);
  vpkey = StringValueEx(vpkey);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  if((cols = tctdbget(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey))) != NULL){
    vcols = maptovhash(cols);
    tcmapdel(cols);
  } else {
    vcols = vdef;
  }
  return vcols;
}


static VALUE tdb_check(VALUE vself, VALUE vpkey){
  VALUE vtdb;
  TCTDB *tdb;
  vpkey = StringValueEx(vpkey);
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbvsiz(tdb, RSTRING_PTR(vpkey), RSTRING_LEN(vpkey)) >= 0 ? Qtrue : Qfalse;
}


static VALUE tdb_empty(VALUE vself){
  VALUE vtdb;
  TCTDB *tdb;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  return tctdbrnum(tdb) < 1 ? Qtrue : Qfalse;
}


static VALUE tdb_each(VALUE vself){
  VALUE vtdb, vrv;
  TCTDB *tdb;
  TCMAP *cols;
  char *kbuf;
  int ksiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vrv = Qnil;
  tctdbiterinit(tdb);
  while((kbuf = tctdbiternext(tdb, &ksiz)) != NULL){
    if((cols = tctdbget(tdb, kbuf, ksiz)) != NULL){
      vrv = rb_yield_values(2, rb_str_new(kbuf, ksiz), maptovhash(cols));
      tcmapdel(cols);
    }
    tcfree(kbuf);
  }
  return vrv;
}


static VALUE tdb_each_key(VALUE vself){
  VALUE vtdb, vrv;
  TCTDB *tdb;
  char *kbuf;
  int ksiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vrv = Qnil;
  tctdbiterinit(tdb);
  while((kbuf = tctdbiternext(tdb, &ksiz)) != NULL){
    vrv = rb_yield(rb_str_new(kbuf, ksiz));
    tcfree(kbuf);
  }
  return vrv;
}


static VALUE tdb_each_value(VALUE vself){
  VALUE vtdb, vrv;
  TCTDB *tdb;
  TCMAP *cols;
  char *kbuf;
  int ksiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vrv = Qnil;
  tctdbiterinit(tdb);
  while((kbuf = tctdbiternext(tdb, &ksiz)) != NULL){
    if((cols = tctdbget(tdb, kbuf, ksiz)) != NULL){
      vrv = rb_yield(maptovhash(cols));
      tcmapdel(cols);
    }
    tcfree(kbuf);
  }
  return vrv;
}


static VALUE tdb_keys(VALUE vself){
  VALUE vtdb, vary;
  TCTDB *tdb;
  char *kbuf;
  int ksiz;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vary = rb_ary_new2(tctdbrnum(tdb));
  tctdbiterinit(tdb);
  while((kbuf = tctdbiternext(tdb, &ksiz)) != NULL){
    rb_ary_push(vary, rb_str_new(kbuf, ksiz));
    tcfree(kbuf);
  }
  return vary;
}


static VALUE tdb_values(VALUE vself){
  VALUE vtdb, vary;
  TCTDB *tdb;
  TCMAP *cols;
  char *kbuf;
  int ksiz;
  vtdb = rb_iv_get(vself, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  vary = rb_ary_new2(tctdbrnum(tdb));
  tctdbiterinit(tdb);
  while((kbuf = tctdbiternext(tdb, &ksiz)) != NULL){
    if((cols = tctdbget(tdb, kbuf, ksiz)) != NULL){
      rb_ary_push(vary, maptovhash(cols));
      tcmapdel(cols);
    }
    tcfree(kbuf);
  }
  return vary;
}


static void tdbqry_init(void){
  cls_tdbqry = rb_define_class_under(mod_tokyocabinet, "TDBQRY", rb_cObject);
  cls_tdbqry_data = rb_define_class_under(mod_tokyocabinet, "TDBQRY_data", rb_cObject);
  rb_define_const(cls_tdbqry, "QCSTREQ", INT2NUM(TDBQCSTREQ));
  rb_define_const(cls_tdbqry, "QCSTRINC", INT2NUM(TDBQCSTRINC));
  rb_define_const(cls_tdbqry, "QCSTRBW", INT2NUM(TDBQCSTRBW));
  rb_define_const(cls_tdbqry, "QCSTREW", INT2NUM(TDBQCSTREW));
  rb_define_const(cls_tdbqry, "QCSTRAND", INT2NUM(TDBQCSTRAND));
  rb_define_const(cls_tdbqry, "QCSTROR", INT2NUM(TDBQCSTROR));
  rb_define_const(cls_tdbqry, "QCSTROREQ", INT2NUM(TDBQCSTROREQ));
  rb_define_const(cls_tdbqry, "QCSTRRX", INT2NUM(TDBQCSTRRX));
  rb_define_const(cls_tdbqry, "QCNUMEQ", INT2NUM(TDBQCNUMEQ));
  rb_define_const(cls_tdbqry, "QCNUMGT", INT2NUM(TDBQCNUMGT));
  rb_define_const(cls_tdbqry, "QCNUMGE", INT2NUM(TDBQCNUMGE));
  rb_define_const(cls_tdbqry, "QCNUMLT", INT2NUM(TDBQCNUMLT));
  rb_define_const(cls_tdbqry, "QCNUMLE", INT2NUM(TDBQCNUMLE));
  rb_define_const(cls_tdbqry, "QCNUMBT", INT2NUM(TDBQCNUMBT));
  rb_define_const(cls_tdbqry, "QCNUMOREQ", INT2NUM(TDBQCNUMOREQ));
  rb_define_const(cls_tdbqry, "QCFTSPH", INT2NUM(TDBQCFTSPH));
  rb_define_const(cls_tdbqry, "QCFTSAND", INT2NUM(TDBQCFTSAND));
  rb_define_const(cls_tdbqry, "QCFTSOR", INT2NUM(TDBQCFTSOR));
  rb_define_const(cls_tdbqry, "QCFTSEX", INT2NUM(TDBQCFTSEX));
  rb_define_const(cls_tdbqry, "QCNEGATE", INT2NUM(TDBQCNEGATE));
  rb_define_const(cls_tdbqry, "QCNOIDX", INT2NUM(TDBQCNOIDX));
  rb_define_const(cls_tdbqry, "QOSTRASC", INT2NUM(TDBQOSTRASC));
  rb_define_const(cls_tdbqry, "QOSTRDESC", INT2NUM(TDBQOSTRDESC));
  rb_define_const(cls_tdbqry, "QONUMASC", INT2NUM(TDBQONUMASC));
  rb_define_const(cls_tdbqry, "QONUMDESC", INT2NUM(TDBQONUMDESC));
  rb_define_const(cls_tdbqry, "QPPUT", INT2NUM(TDBQPPUT));
  rb_define_const(cls_tdbqry, "QPOUT", INT2NUM(TDBQPOUT));
  rb_define_const(cls_tdbqry, "QPSTOP", INT2NUM(TDBQPSTOP));
  rb_define_const(cls_tdbqry, "MSUNION", INT2NUM(TDBMSUNION));
  rb_define_const(cls_tdbqry, "MSISECT", INT2NUM(TDBMSISECT));
  rb_define_const(cls_tdbqry, "MSDIFF", INT2NUM(TDBMSDIFF));
  rb_define_const(cls_tdbqry, "KWMUTAB", INT2NUM(TCKWMUTAB));
  rb_define_const(cls_tdbqry, "KWMUCTRL", INT2NUM(TCKWMUCTRL));
  rb_define_const(cls_tdbqry, "KWMUBRCT", INT2NUM(TCKWMUBRCT));
  rb_define_const(cls_tdbqry, "KWNOOVER", INT2NUM(TCKWNOOVER));
  rb_define_const(cls_tdbqry, "KWPULEAD", INT2NUM(TCKWPULEAD));
  rb_define_private_method(cls_tdbqry, "initialize", tdbqry_initialize, 1);
  rb_define_method(cls_tdbqry, "addcond", tdbqry_addcond, 3);
  rb_define_method(cls_tdbqry, "setorder", tdbqry_setorder, -1);
  rb_define_method(cls_tdbqry, "setlimit", tdbqry_setlimit, -1);
  rb_define_method(cls_tdbqry, "setmax", tdbqry_setlimit, -1);
  rb_define_method(cls_tdbqry, "search", tdbqry_search, 0);
  rb_define_method(cls_tdbqry, "searchout", tdbqry_searchout, 0);
  rb_define_method(cls_tdbqry, "proc", tdbqry_proc, 0);
  rb_define_method(cls_tdbqry, "hint", tdbqry_hint, 0);
  rb_define_method(cls_tdbqry, "metasearch", tdbqry_metasearch, -1);
  rb_define_method(cls_tdbqry, "kwic", tdbqry_kwic, -1);
}


static int tdbqry_procrec(const void *pkbuf, int pksiz, TCMAP *cols, void *opq){
  VALUE vpkey, vcols, vrv, vkeys, vkey, vval;
  int i, rv, num;
  vpkey = rb_str_new(pkbuf, pksiz);
  vcols = maptovhash(cols);
  vrv = rb_yield_values(2, vpkey, vcols);
  rv = (vrv == Qnil) ? 0 : NUM2INT(vrv);
  if(rv & TDBQPPUT){
    tcmapclear(cols);
    vkeys = rb_funcall(vcols, rb_intern("keys"), 0);
    num = RARRAY_LEN(vkeys);
    for(i = 0; i < num; i++){
      vkey = rb_ary_entry(vkeys, i);
      vval = rb_hash_aref(vcols, vkey);
      vkey = StringValueEx(vkey);
      vval = StringValueEx(vval);
      tcmapput(cols, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vval), RSTRING_LEN(vval));
    }
  }
  return rv;
}


static VALUE tdbqry_initialize(VALUE vself, VALUE vtdb){
  VALUE vqry;
  TCTDB *tdb;
  TDBQRY *qry;
  Check_Type(vtdb, T_OBJECT);
  vtdb = rb_iv_get(vtdb, TDBVNDATA);
  Data_Get_Struct(vtdb, TCTDB, tdb);
  qry = tctdbqrynew(tdb);
  vqry = Data_Wrap_Struct(cls_tdbqry_data, 0, tctdbqrydel, qry);
  rb_iv_set(vself, TDBQRYVNDATA, vqry);
  rb_iv_set(vself, TDBVNDATA, vtdb);
  return Qnil;
}


static VALUE tdbqry_addcond(VALUE vself, VALUE vname, VALUE vop, VALUE vexpr){
  VALUE vqry;
  TDBQRY *qry;
  vname = StringValueEx(vname);
  vexpr = StringValueEx(vexpr);
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  tctdbqryaddcond(qry, RSTRING_PTR(vname), NUM2INT(vop), RSTRING_PTR(vexpr));
  return Qnil;
}


static VALUE tdbqry_setorder(int argc, VALUE *argv, VALUE vself){
  VALUE vqry, vname, vtype;
  TDBQRY *qry;
  int type;
  rb_scan_args(argc, argv, "11", &vname, &vtype);
  vname = StringValueEx(vname);
  type = (vtype == Qnil) ? TDBQOSTRASC : NUM2INT(vtype);
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  tctdbqrysetorder(qry, RSTRING_PTR(vname), type);
  return Qnil;
}


static VALUE tdbqry_setlimit(int argc, VALUE *argv, VALUE vself){
  VALUE vqry, vmax, vskip;
  TDBQRY *qry;
  int max, skip;
  rb_scan_args(argc, argv, "02", &vmax, &vskip);
  max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
  skip = (vskip == Qnil) ? -1 : NUM2INT(vskip);
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  tctdbqrysetlimit(qry, max, skip);
  return Qnil;
}


static VALUE tdbqry_search(VALUE vself){
  VALUE vqry, vary;
  TDBQRY *qry;
  TCLIST *res;
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  res = tctdbqrysearch(qry);
  vary = listtovary(res);
  tclistdel(res);
  return vary;
}


static VALUE tdbqry_searchout(VALUE vself){
  VALUE vqry;
  TDBQRY *qry;
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  return tctdbqrysearchout(qry) ? Qtrue : Qfalse;
}


static VALUE tdbqry_proc(VALUE vself, VALUE vproc){
  VALUE vqry;
  TDBQRY *qry;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  return tctdbqryproc(qry, (TDBQRYPROC)tdbqry_procrec, NULL) ? Qtrue : Qfalse;
}


static VALUE tdbqry_hint(VALUE vself){
  VALUE vqry;
  TDBQRY *qry;
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  return rb_str_new2(tctdbqryhint(qry));
}


static VALUE tdbqry_metasearch(int argc, VALUE *argv, VALUE vself){
  VALUE vqry, vothers, vtype, voqry, vary;
  TDBQRY *qry, **qrys;
  TCLIST *res;
  int i, type, num, qnum;
  rb_scan_args(argc, argv, "11", &vothers, &vtype);
  Check_Type(vothers, T_ARRAY);
  type = (vtype == Qnil) ? TDBMSUNION : NUM2INT(vtype);
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  num = RARRAY_LEN(vothers);
  qrys = tcmalloc(sizeof(*qrys) * (num + 1));
  qnum = 0;
  qrys[qnum++] = qry;
  for(i = 0; i < num; i++){
    voqry = rb_ary_entry(vothers, i);
    if(rb_obj_is_instance_of(voqry, cls_tdbqry) == Qtrue){
      voqry = rb_iv_get(voqry, TDBQRYVNDATA);
      Data_Get_Struct(voqry, TDBQRY, qrys[qnum++]);
    }
  }
  res = tctdbmetasearch(qrys, qnum, type);
  vary = listtovary(res);
  tcfree(qrys);
  tclistdel(res);
  return vary;
}


static VALUE tdbqry_kwic(int argc, VALUE *argv, VALUE vself){
  VALUE vqry, vcols, vname, vwidth, vopts, vval, vary;
  TDBQRY *qry;
  TCMAP *cols;
  const char *name;
  int width, opts;
  rb_scan_args(argc, argv, "13", &vcols, &vname, &vwidth, &vopts);
  Check_Type(vcols, T_HASH);
  width = (vwidth == Qnil) ? -1 : NUM2INT(vwidth);
  opts = (vopts == Qnil) ? 0 : NUM2INT(vopts);
  if(vname == Qnil){
    cols = vhashtomap(vcols);
    name = NULL;
  } else {
    vname = StringValueEx(vname);
    cols = tcmapnew2(1);
    vval = rb_hash_aref(vcols, vname);
    if(vval != Qnil) tcmapput(cols, RSTRING_PTR(vname), RSTRING_LEN(vname),
                              RSTRING_PTR(vval), RSTRING_LEN(vval));
    name = RSTRING_PTR(vname);
  }
  if(width < 0){
    width = 1 << 30;
    opts |= TCKWNOOVER | TCKWPULEAD;
  }
  vqry = rb_iv_get(vself, TDBQRYVNDATA);
  Data_Get_Struct(vqry, TDBQRY, qry);
  TCLIST *texts = tctdbqrykwic(qry, cols, name, width, opts);
  vary = listtovary(texts);
  tclistdel(texts);
  tcmapdel(cols);
  return vary;
}


static void adb_init(void){
  cls_adb = rb_define_class_under(mod_tokyocabinet, "ADB", rb_cObject);
  cls_adb_data = rb_define_class_under(mod_tokyocabinet, "ADB_data", rb_cObject);
  rb_define_private_method(cls_adb, "initialize", adb_initialize, 0);
  rb_define_method(cls_adb, "open", adb_open, 1);
  rb_define_method(cls_adb, "close", adb_close, 0);
  rb_define_method(cls_adb, "put", adb_put, 2);
  rb_define_method(cls_adb, "putkeep", adb_putkeep, 2);
  rb_define_method(cls_adb, "putcat", adb_putcat, 2);
  rb_define_method(cls_adb, "out", adb_out, 1);
  rb_define_method(cls_adb, "get", adb_get, 1);
  rb_define_method(cls_adb, "vsiz", adb_vsiz, 1);
  rb_define_method(cls_adb, "iterinit", adb_iterinit, 0);
  rb_define_method(cls_adb, "iternext", adb_iternext, 0);
  rb_define_method(cls_adb, "fwmkeys", adb_fwmkeys, -1);
  rb_define_method(cls_adb, "addint", adb_addint, 2);
  rb_define_method(cls_adb, "adddouble", adb_adddouble, 2);
  rb_define_method(cls_adb, "sync", adb_sync, 0);
  rb_define_method(cls_adb, "optimize", adb_optimize, -1);
  rb_define_method(cls_adb, "vanish", adb_vanish, 0);
  rb_define_method(cls_adb, "copy", adb_copy, 1);
  rb_define_method(cls_adb, "tranbegin", adb_tranbegin, 0);
  rb_define_method(cls_adb, "trancommit", adb_trancommit, 0);
  rb_define_method(cls_adb, "tranabort", adb_tranabort, 0);
  rb_define_method(cls_adb, "path", adb_path, 0);
  rb_define_method(cls_adb, "rnum", adb_rnum, 0);
  rb_define_method(cls_adb, "size", adb_size, 0);
  rb_define_method(cls_adb, "misc", adb_misc, -1);
  rb_define_method(cls_adb, "[]", adb_get, 1);
  rb_define_method(cls_adb, "[]=", adb_put, 2);
  rb_define_method(cls_adb, "store", adb_put, 2);
  rb_define_method(cls_adb, "delete", adb_out, 1);
  rb_define_method(cls_adb, "fetch", adb_fetch, -1);
  rb_define_method(cls_adb, "has_key?", adb_check, 1);
  rb_define_method(cls_adb, "key?", adb_check, 1);
  rb_define_method(cls_adb, "include?", adb_check, 1);
  rb_define_method(cls_adb, "member?", adb_check, 1);
  rb_define_method(cls_adb, "has_value?", adb_check_value, 1);
  rb_define_method(cls_adb, "value?", adb_check_value, 1);
  rb_define_method(cls_adb, "key", adb_get_reverse, 1);
  rb_define_method(cls_adb, "clear", adb_vanish, 0);
  rb_define_method(cls_adb, "length", adb_rnum, 0);
  rb_define_method(cls_adb, "empty?", adb_empty, 0);
  rb_define_method(cls_adb, "each", adb_each, 0);
  rb_define_method(cls_adb, "each_pair", adb_each, 0);
  rb_define_method(cls_adb, "each_key", adb_each_key, 0);
  rb_define_method(cls_adb, "each_value", adb_each_value, 0);
  rb_define_method(cls_adb, "keys", adb_keys, 0);
  rb_define_method(cls_adb, "values", adb_values, 0);
}


static VALUE adb_initialize(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  adb = tcadbnew();
  vadb = Data_Wrap_Struct(cls_adb_data, 0, tcadbdel, adb);
  rb_iv_set(vself, ADBVNDATA, vadb);
  return Qnil;
}


static VALUE adb_open(VALUE vself, VALUE vname){
  VALUE vadb;
  TCADB *adb;
  Check_Type(vname, T_STRING);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbopen(adb, RSTRING_PTR(vname)) ? Qtrue : Qfalse;
}


static VALUE adb_close(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbclose(adb) ? Qtrue : Qfalse;
}


static VALUE adb_put(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vadb;
  TCADB *adb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbput(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                  RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE adb_putkeep(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vadb;
  TCADB *adb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbputkeep(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                      RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE adb_putcat(VALUE vself, VALUE vkey, VALUE vval){
  VALUE vadb;
  TCADB *adb;
  vkey = StringValueEx(vkey);
  vval = StringValueEx(vval);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbputcat(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey),
                     RSTRING_PTR(vval), RSTRING_LEN(vval)) ? Qtrue : Qfalse;
}


static VALUE adb_out(VALUE vself, VALUE vkey){
  VALUE vadb;
  TCADB *adb;
  vkey = StringValueEx(vkey);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbout(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) ? Qtrue : Qfalse;
}


static VALUE adb_get(VALUE vself, VALUE vkey){
  VALUE vadb, vval;
  TCADB *adb;
  char *vbuf;
  int vsiz;
  vkey = StringValueEx(vkey);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  if(!(vbuf = tcadbget(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static VALUE adb_vsiz(VALUE vself, VALUE vkey){
  VALUE vadb;
  TCADB *adb;
  vkey = StringValueEx(vkey);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return INT2NUM(tcadbvsiz(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
}


static VALUE adb_iterinit(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbiterinit(adb) ? Qtrue : Qfalse;
}


static VALUE adb_iternext(VALUE vself){
  VALUE vadb, vval;
  TCADB *adb;
  char *vbuf;
  int vsiz;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  if(!(vbuf = tcadbiternext(adb, &vsiz))) return Qnil;
  vval = rb_str_new(vbuf, vsiz);
  tcfree(vbuf);
  return vval;
}


static VALUE adb_fwmkeys(int argc, VALUE *argv, VALUE vself){
  VALUE vadb, vprefix, vmax, vary;
  TCADB *adb;
  TCLIST *keys;
  int max;
  rb_scan_args(argc, argv, "11", &vprefix, &vmax);
  vprefix = StringValueEx(vprefix);
  max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  keys = tcadbfwmkeys(adb, RSTRING_PTR(vprefix), RSTRING_LEN(vprefix), max);
  vary = listtovary(keys);
  tclistdel(keys);
  return vary;
}


static VALUE adb_addint(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vadb;
  TCADB *adb;
  int num;
  vkey = StringValueEx(vkey);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  num = tcadbaddint(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), NUM2INT(vnum));
  return num == INT_MIN ? Qnil : INT2NUM(num);
}


static VALUE adb_adddouble(VALUE vself, VALUE vkey, VALUE vnum){
  VALUE vadb;
  TCADB *adb;
  double num;
  vkey = StringValueEx(vkey);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  num = tcadbadddouble(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), NUM2DBL(vnum));
  return isnan(num) ? Qnil : rb_float_new(num);
}


static VALUE adb_sync(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbsync(adb) ? Qtrue : Qfalse;
}


static VALUE adb_optimize(int argc, VALUE *argv, VALUE vself){
  VALUE vadb, vparams;
  TCADB *adb;
  const char *params;
  rb_scan_args(argc, argv, "01", &vparams);
  if(vparams == Qnil){
    params = NULL;
  } else {
    Check_Type(vparams, T_STRING);
    params = RSTRING_PTR(vparams);
  }
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadboptimize(adb, params) ? Qtrue : Qfalse;
}


static VALUE adb_vanish(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbvanish(adb) ? Qtrue : Qfalse;
}


static VALUE adb_copy(VALUE vself, VALUE vpath){
  VALUE vadb;
  TCADB *adb;
  Check_Type(vpath, T_STRING);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbcopy(adb, RSTRING_PTR(vpath)) ? Qtrue : Qfalse;
}


static VALUE adb_tranbegin(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbtranbegin(adb) ? Qtrue : Qfalse;
}


static VALUE adb_trancommit(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbtrancommit(adb) ? Qtrue : Qfalse;
}


static VALUE adb_tranabort(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbtranabort(adb) ? Qtrue : Qfalse;
}


static VALUE adb_path(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  const char *path;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  if(!(path = tcadbpath(adb))) return Qnil;
  return rb_str_new2(path);
}


static VALUE adb_rnum(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return LL2NUM(tcadbrnum(adb));
}


static VALUE adb_size(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return LL2NUM(tcadbsize(adb));
}


static VALUE adb_misc(int argc, VALUE *argv, VALUE vself){
  VALUE vadb, vname, vargs, vrv;
  TCADB *adb;
  TCLIST *targs, *res;
  rb_scan_args(argc, argv, "11", &vname, &vargs);
  vname = StringValueEx(vname);
  if(vargs == Qnil){
    targs = tclistnew2(1);
  } else {
    Check_Type(vargs, T_ARRAY);
    targs = varytolist(vargs);
  }
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  res = tcadbmisc(adb, RSTRING_PTR(vname), targs);
  if(res){
    vrv = listtovary(res);
    tclistdel(res);
  } else {
    vrv = Qnil;
  }
  tclistdel(targs);
  return vrv;
}


static VALUE adb_fetch(int argc, VALUE *argv, VALUE vself){
  VALUE vadb, vkey, vdef, vval;
  TCADB *adb;
  char *vbuf;
  int vsiz;
  rb_scan_args(argc, argv, "11", &vkey, &vdef);
  vkey = StringValueEx(vkey);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  if((vbuf = tcadbget(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &vsiz)) != NULL){
    vval = rb_str_new(vbuf, vsiz);
    tcfree(vbuf);
  } else {
    vval = vdef;
  }
  return vval;
}


static VALUE adb_check(VALUE vself, VALUE vkey){
  VALUE vadb;
  TCADB *adb;
  vkey = StringValueEx(vkey);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbvsiz(adb, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) >= 0 ? Qtrue : Qfalse;
}


static VALUE adb_check_value(VALUE vself, VALUE vval){
  VALUE vadb;
  TCADB *adb;
  char *tkbuf, *tvbuf;
  bool hit;
  int tksiz, tvsiz;
  vval = StringValueEx(vval);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  hit = false;
  tcadbiterinit(adb);
  while((tkbuf = tcadbiternext(adb, &tksiz)) != NULL){
    tvbuf = tcadbget(adb, tkbuf, tksiz, &tvsiz);
    if(tvbuf && tvsiz == RSTRING_LEN(vval) &&
       memcmp(tvbuf, RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      tcfree(tvbuf);
      tcfree(tkbuf);
      hit = true;
      break;
    }
    tcfree(tvbuf);
    tcfree(tkbuf);
  }
  return hit ? Qtrue : Qfalse;
}


static VALUE adb_get_reverse(VALUE vself, VALUE vval){
  VALUE vadb, vrv;
  TCADB *adb;
  char *tkbuf, *tvbuf;
  int tksiz, tvsiz;
  vval = StringValueEx(vval);
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  vrv = Qnil;
  tcadbiterinit(adb);
  while((tkbuf = tcadbiternext(adb, &tksiz)) != NULL){
    tvbuf = tcadbget(adb, tkbuf, tksiz, &tvsiz);
    if(tvbuf && tvsiz == RSTRING_LEN(vval) &&
       memcmp(tvbuf, RSTRING_PTR(vval), RSTRING_LEN(vval)) == 0){
      vrv = rb_str_new(tkbuf, tksiz);
      tcfree(tvbuf);
      tcfree(tkbuf);
      break;
    }
    tcfree(tvbuf);
    tcfree(tkbuf);
  }
  return vrv;
}


static VALUE adb_empty(VALUE vself){
  VALUE vadb;
  TCADB *adb;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  return tcadbrnum(adb) < 1 ? Qtrue : Qfalse;
}


static VALUE adb_each(VALUE vself){
  VALUE vadb, vrv;
  TCADB *adb;
  char *tkbuf, *tvbuf;
  int tksiz, tvsiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  vrv = Qnil;
  tcadbiterinit(adb);
  while((tkbuf = tcadbiternext(adb, &tksiz)) != NULL){
    tvbuf = tcadbget(adb, tkbuf, tksiz, &tvsiz);
    if(tvbuf){
      vrv = rb_yield_values(2, rb_str_new(tkbuf, tksiz), rb_str_new(tvbuf, tvsiz));
      tcfree(tvbuf);
    }
    tcfree(tkbuf);
  }
  return vrv;
}


static VALUE adb_each_key(VALUE vself){
  VALUE vadb, vrv;
  TCADB *adb;
  char *tkbuf;
  int tksiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  vrv = Qnil;
  tcadbiterinit(adb);
  while((tkbuf = tcadbiternext(adb, &tksiz)) != NULL){
    vrv = rb_yield(rb_str_new(tkbuf, tksiz));
    tcfree(tkbuf);
  }
  return vrv;
}


static VALUE adb_each_value(VALUE vself){
  VALUE vadb, vrv;
  TCADB *adb;
  char *tkbuf, *tvbuf;
  int tksiz, tvsiz;
  if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  vrv = Qnil;
  tcadbiterinit(adb);
  while((tkbuf = tcadbiternext(adb, &tksiz)) != NULL){
    tvbuf = tcadbget(adb, tkbuf, tksiz, &tvsiz);
    if(tvbuf){
      vrv = rb_yield(rb_str_new(tvbuf, tvsiz));
      tcfree(tvbuf);
    }
    tcfree(tkbuf);
  }
  return vrv;
}


static VALUE adb_keys(VALUE vself){
  VALUE vadb, vary;
  TCADB *adb;
  char *tkbuf;
  int tksiz;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  vary = rb_ary_new2(tcadbrnum(adb));
  tcadbiterinit(adb);
  while((tkbuf = tcadbiternext(adb, &tksiz)) != NULL){
    rb_ary_push(vary, rb_str_new(tkbuf, tksiz));
    tcfree(tkbuf);
  }
  return vary;
}


static VALUE adb_values(VALUE vself){
  VALUE vadb, vary;
  TCADB *adb;
  char *tkbuf, *tvbuf;
  int tksiz, tvsiz;
  vadb = rb_iv_get(vself, ADBVNDATA);
  Data_Get_Struct(vadb, TCADB, adb);
  vary = rb_ary_new2(tcadbrnum(adb));
  tcadbiterinit(adb);
  while((tkbuf = tcadbiternext(adb, &tksiz)) != NULL){
    tvbuf = tcadbget(adb, tkbuf, tksiz, &tvsiz);
    if(tvbuf){
      rb_ary_push(vary, rb_str_new(tvbuf, tvsiz));
      tcfree(tvbuf);
    }
    tcfree(tkbuf);
  }
  return vary;
}



/* END OF FILE */
