Index: lang/python/pytc/tags/RELEASE-0.1/pytc.c
===================================================================
--- lang/python/pytc/tags/RELEASE-0.1/pytc.c (revision 1993)
+++ lang/python/pytc/tags/RELEASE-0.1/pytc.c (revision 1993)
@@ -0,0 +1,1930 @@
+/* Copyright(C) 2007- Tasuku SUENAGA
+
+  This library 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 (at your option) any later version.
+
+  This library 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 this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+#include <Python.h>
+#include <tcbdb.h>
+#include <tchdb.h>
+#include <tcutil.h>
+
+/* FIXME: handle error */
+/* FIXME: refactoring */
+/* FIXME: setcmpfunc tcbdbcmplexical/decimal/cmpint32/cmpint64' */
+
+/* Error Objects */
+
+static PyObject *PyTCError;
+
+static void
+raise_pytc_error(int ecode, const char *errmsg) {
+  PyObject *obj;
+
+  obj = Py_BuildValue("(is)", ecode, errmsg);
+  PyErr_SetObject(PyTCError, obj);
+  Py_DECREF(obj);
+}
+
+static void
+raise_tchdb_error(TCHDB *hdb) {
+  int ecode = tchdbecode(hdb);
+  const char *errmsg = tchdberrmsg(ecode);
+  raise_pytc_error(ecode, errmsg);
+}
+
+static void
+raise_tcbdb_error(TCBDB *bdb) {
+  int ecode = tcbdbecode(bdb);
+  const char *errmsg = tcbdberrmsg(ecode);
+  raise_pytc_error(ecode, errmsg);
+}
+
+/* Objects */
+
+typedef struct {
+  PyObject_HEAD
+  TCHDB	*hdb;
+} PyTCHDB;
+
+typedef struct {
+  PyObject_HEAD
+  TCBDB	*bdb;
+  PyObject *cmp;
+  PyObject *cmpop;
+} PyTCBDB;
+
+typedef struct {
+  PyObject_HEAD
+  PyTCBDB *bdb;
+  BDBCUR *cur;
+} PyBDBCUR;
+
+/* Macros */
+
+#define BOOL_NOARGS(func,type,call,member,err,errmember) \
+  static PyObject * \
+  func(type *self) { \
+    bool result; \
+  \
+    Py_BEGIN_ALLOW_THREADS \
+    result = call(self->member); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!result) { \
+      err(self->errmember); \
+      return NULL; \
+    } \
+    Py_RETURN_NONE; \
+  }
+
+/* NOTE: this function *doesn't* dealloc pointer returned by tc */
+#define STRING_NOARGS(func,type,call,member,err) \
+  static PyObject * \
+  func(type *self) { \
+    const char *str; \
+    PyObject *ret; \
+  \
+    Py_BEGIN_ALLOW_THREADS \
+    str = call(self->member); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!str) { \
+      err(self->member); \
+      return NULL; \
+    } \
+    ret = PyString_FromString(str); \
+    return ret; \
+  } \
+
+/* NOTE: this function dealloc pointer returned by tc */
+#define STRINGL_NOARGS(func,type,call,member,err,errmember) \
+  static PyObject * \
+  func(type *self) { \
+    char *str; \
+    int str_len; \
+    PyObject *ret; \
+  \
+    Py_BEGIN_ALLOW_THREADS \
+    str = call(self->member, &str_len); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!str) { \
+      err(self->errmember); \
+      return NULL; \
+    } \
+    ret = PyString_FromStringAndSize(str, str_len); \
+    free(str); \
+    return ret; \
+  } \
+
+#define PY_NUM_NOARGS(func,type,rettype,call,member,ecode,err,conv) \
+  static PyObject * \
+  func(type *self) { \
+    rettype val; \
+  \
+    Py_BEGIN_ALLOW_THREADS \
+    val = call(self->member); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (ecode(self->member)) { \
+      err(self->member); \
+      return NULL; \
+    } \
+    return conv(val); \
+  }
+
+#define PY_U_LONG_LONG_NOARGS(func,type,call,member,ecode,err) \
+  PY_NUM_NOARGS(func, type, unsigned PY_LONG_LONG, call, member, ecode, err, \
+                PyLong_FromUnsignedLongLong)
+
+#define BOOL_KEYARGS(func,type,method,call,member,error,errmember) \
+  static PyObject * \
+  func(type *self, PyObject *args, PyObject *keywds) { \
+    char *key; \
+    int key_len; \
+    bool result; \
+    static char *kwlist[] = {"key", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#:" #method, kwlist, \
+                                     &key, &key_len)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    result = call(self->member, key, key_len); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!result) { \
+      error(self->errmember); \
+      return NULL; \
+    } \
+    Py_RETURN_NONE; \
+  }
+
+#define INT_KEYARGS(func,type,method,call,member,error) \
+  static PyObject * \
+  func(type *self, PyObject *args, PyObject *keywds) { \
+    char *key; \
+    int key_len, ret; \
+    static char *kwlist[] = {"key", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#:" #method, kwlist, \
+                                     &key, &key_len)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    ret = call(self->member, key, key_len); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (ret == -1) { \
+      error(self->member); \
+      return NULL; \
+    } \
+    return PyInt_FromLong((long)ret); \
+  }
+
+/* NOTE: this function dealloc pointer returned by tc */
+#define STRINGL_KEYARGS(func,type,method,call,member,error) \
+  static PyObject * \
+  func(type *self, PyObject *args, PyObject *keywds) { \
+    PyObject *ret; \
+    char *key, *value; \
+    int key_len, value_len; \
+    static char *kwlist[] = {"key", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#:" #method, kwlist, \
+                                     &key, &key_len)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    value = call(self->member, key, key_len, &value_len); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!value) { \
+      error(self->member); \
+      return NULL; \
+    } \
+    ret = PyString_FromStringAndSize(value, value_len); \
+    free(value); \
+    return ret; \
+  }
+
+#define PyTCXDB_PUT(func,type,method,call,member,error) \
+  static PyObject * \
+  func(type *self, PyObject *args, PyObject *keywds) { \
+    bool result; \
+    char *key, *value; \
+    int key_len, value_len; \
+    static char *kwlist[] = {"key", "value", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#s#:" #method, kwlist, \
+                                     &key, &key_len, &value, &value_len)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    result = call(self->member, key, key_len, value, value_len); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!result) { \
+      error(self->member); \
+      return NULL; \
+    } \
+    Py_RETURN_NONE; \
+  }
+
+#define PyTCXDB_OPEN(func,type,call_new,call_open,member,call_dealloc,error) \
+  static PyObject * \
+  func(type *self, PyObject *args, PyObject *keywds) { \
+    int omode; \
+    char *path; \
+    bool result; \
+    static char *kwlist[] = {"path", "omode", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "si:open", kwlist, \
+                                     &path, &omode)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    result = call_open(self->member, path, omode); \
+    Py_END_ALLOW_THREADS \
+    if (!result) { \
+      error(self->member); \
+      return NULL; \
+    } \
+    Py_RETURN_NONE; \
+  }
+
+#define BOOL_PATHARGS(func,type,method,call,member,error) \
+  static PyObject * \
+  func(type *self, PyObject *args, PyObject *keywds) { \
+    char *str; \
+    bool result; \
+    static char *kwlist[] = {"path", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s:" #method, kwlist, \
+                                     &str)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    result = call(self->member, str); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!result) { \
+      error(self->member); \
+      return NULL; \
+    } \
+    Py_RETURN_NONE; \
+  }
+
+/* for internal use */
+#define TCXDB_rnum(func,type,call) \
+  static uint64_t \
+  func(type *obj) { \
+    uint64_t ret; \
+    Py_BEGIN_ALLOW_THREADS \
+    ret = call(obj); \
+    Py_END_ALLOW_THREADS \
+    return ret; \
+  }
+
+/*** TCHDB ***/
+
+#define PyTCHDB_TUNE(a,b,c) \
+  static PyObject * \
+  a(PyTCHDB *self, PyObject *args, PyObject *keywds) { \
+    bool result; \
+    char apow, fpow; \
+    PY_LONG_LONG bnum; \
+    unsigned char opts; \
+    static char *kwlist[] = {"bnum", "apow", "fpow", "opts", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "LbbB:" #b, kwlist, \
+                                     &bnum, &apow, &fpow, &opts)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    result = c(self->hdb, bnum, apow, fpow, opts); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!result) { \
+      raise_tchdb_error(self->hdb); \
+      return NULL; \
+    } \
+    Py_RETURN_NONE; \
+  }
+
+static long
+PyTCHDB_Hash(PyObject *self) {
+  PyErr_SetString(PyExc_TypeError, "HDB objects are unhashable");
+  return -1;
+}
+
+static PyObject *
+PyTCHDB_errmsg(PyTypeObject *type, PyObject *args, PyObject *keywds) {
+  int ecode;
+  static char *kwlist[] = {"ecode", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "i:errmsg", kwlist,
+                                   &ecode)) {
+    return NULL;
+  }
+  return PyString_FromString(tchdberrmsg(ecode));
+}
+
+static void
+PyTCHDB_dealloc(PyTCHDB *self)
+{
+  /* NOTE: tchdbsel closes hdb implicitly */
+  /*
+  if (self->hdb->flags & HDBFOPEN) {
+    Py_BEGIN_ALLOW_THREADS
+    tchdbclose(self->hdb);
+    Py_END_ALLOW_THREADS
+  }
+  */
+  if (self->hdb) {
+    Py_BEGIN_ALLOW_THREADS
+    tchdbdel(self->hdb);
+    Py_END_ALLOW_THREADS
+  }
+  self->ob_type->tp_free(self);
+}
+
+static PyObject *
+PyTCHDB_new(PyTypeObject *type, PyObject *args, PyObject *keywds)
+{
+  PyTCHDB *self;
+  if (!(self = (PyTCHDB *)type->tp_alloc(type, 0))) {
+    PyErr_SetString(PyExc_MemoryError, "Cannot alloc PyTCHDB instance");
+    return NULL;
+  }
+  if ((self->hdb = tchdbnew())) {
+    int omode = 0;
+    char *path = NULL;
+    static char *kwlist[] = {"path", "omode", NULL};
+
+    if (PyArg_ParseTupleAndKeywords(args, keywds, "|si:open", kwlist,
+                                    &path, &omode)) {
+      if (path && omode) {
+        bool result;
+        Py_BEGIN_ALLOW_THREADS
+        result = tchdbopen(self->hdb, path, omode);
+        Py_END_ALLOW_THREADS
+        if (result) {
+          return (PyObject *)self;
+        }
+      } else {
+        return (PyObject *)self;
+      }
+      raise_tchdb_error(self->hdb);
+    }
+  } else {
+    PyErr_SetString(PyExc_MemoryError, "Cannot alloc TCHDB instance");
+  }
+  PyTCHDB_dealloc(self);
+  return NULL;
+}
+
+static PyObject *
+PyTCHDB_ecode(PyTCHDB *self) {
+  return PyInt_FromLong((long)tchdbecode(self->hdb));
+}
+
+BOOL_NOARGS(PyTCHDB_setmutex,PyTCHDB,tchdbsetmutex,hdb,raise_tchdb_error,hdb)
+PyTCHDB_TUNE(PyTCHDB_tune, tune, tchdbtune)
+PyTCXDB_OPEN(PyTCHDB_open,PyTCHDB,PyTCHDB_new,tchdbopen,hdb,PyTCHDB_dealloc,raise_tchdb_error)
+BOOL_NOARGS(PyTCHDB_close,PyTCHDB,tchdbclose,hdb,raise_tchdb_error,hdb)
+PyTCXDB_PUT(PyTCHDB_put, PyTCHDB, put, tchdbput, hdb, raise_tchdb_error)
+PyTCXDB_PUT(PyTCHDB_putkeep, PyTCHDB, putkeep, tchdbputkeep, hdb, raise_tchdb_error)
+PyTCXDB_PUT(PyTCHDB_putcat, PyTCHDB, putcat, tchdbputcat, hdb, raise_tchdb_error)
+PyTCXDB_PUT(PyTCHDB_putasync, PyTCHDB, putasync, tchdbputasync, hdb, raise_tchdb_error)
+BOOL_KEYARGS(PyTCHDB_out,PyTCHDB,out,tchdbout,hdb,raise_tchdb_error,hdb)
+STRINGL_KEYARGS(PyTCHDB_get,PyTCHDB,get,tchdbget,hdb,raise_tchdb_error)
+INT_KEYARGS(PyTCHDB_vsiz,PyTCHDB,vsiz,tchdbvsiz,hdb,raise_tchdb_error)
+BOOL_NOARGS(PyTCHDB_iterinit,PyTCHDB,tchdbiterinit,hdb,raise_tchdb_error,hdb)
+
+static PyObject *
+PyTCHDB_GetIter(PyObject *self) {
+  if (PyTCHDB_iterinit((PyTCHDB *)self)) {
+    Py_INCREF(self);
+    return self;
+  }
+  return NULL;
+}
+
+static PyObject *
+PyTCHDB_iternext(PyTCHDB *self) {
+  void *key;
+  int key_len;
+
+  Py_BEGIN_ALLOW_THREADS
+  key = tchdbiternext(self->hdb, &key_len);
+  Py_END_ALLOW_THREADS
+
+  if (key) {
+    PyObject *_key;
+    _key = PyString_FromStringAndSize(key, key_len);
+    free(key);
+    return _key;
+  }
+  return NULL;
+}
+
+BOOL_NOARGS(PyTCHDB_sync,PyTCHDB,tchdbsync,hdb,raise_tchdb_error,hdb)
+PyTCHDB_TUNE(PyTCHDB_optimize, optimize, tchdboptimize)
+STRING_NOARGS(PyTCHDB_path,PyTCHDB,tchdbpath,hdb,raise_tchdb_error)
+PY_U_LONG_LONG_NOARGS(PyTCHDB_rnum, PyTCHDB, tchdbrnum, hdb, tchdbecode, raise_tchdb_error)
+PY_U_LONG_LONG_NOARGS(PyTCHDB_fsiz, PyTCHDB, tchdbrnum, hdb, tchdbecode, raise_tchdb_error)
+BOOL_NOARGS(PyTCHDB_vanish,PyTCHDB,tchdbvanish,hdb,raise_tchdb_error,hdb)
+BOOL_PATHARGS(PyTCHDB_copy, PyTCHDB, copy, tchdbcopy, hdb, raise_tchdb_error)
+
+/* TODO: features for experts */
+
+static int
+PyTCHDB_Contains(PyTCHDB *self, PyObject *_key) {
+  char *key = PyString_AS_STRING(_key);
+  int key_len = PyString_GET_SIZE(_key), value_len;
+
+  if (!key || !key_len) {
+    return -1;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  value_len = tchdbvsiz(self->hdb, key, key_len);
+  Py_END_ALLOW_THREADS
+
+  return (value_len != -1);
+}
+
+static PyObject *
+PyTCHDB___contains__(PyTCHDB *self, PyObject *_key) {
+  return PyBool_FromLong(PyTCHDB_Contains(self, _key) != 0);
+}
+
+static PyObject *
+PyTCHDB___getitem__(PyTCHDB *self, PyObject *_key) {
+  PyObject *ret;
+  char *key = PyString_AS_STRING(_key), *value;
+  int key_len = PyString_GET_SIZE(_key), value_len;
+
+  if (!key || !key_len) {
+    return NULL;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  value = tchdbget(self->hdb, key, key_len, &value_len);
+  Py_END_ALLOW_THREADS
+
+  if (!value) {
+    raise_tchdb_error(self->hdb);
+    return NULL;
+  }
+  ret = PyString_FromStringAndSize(value, value_len);
+  free(value);
+  return ret;
+}
+
+TCXDB_rnum(TCHDB_rnum,TCHDB,tchdbrnum)
+
+static PyObject *
+PyTCHDB_keys(PyTCHDB *self) {
+  int i;
+  PyObject *ret;
+  if (!PyTCHDB_iterinit((PyTCHDB *)self) ||
+      !(ret = PyList_New(TCHDB_rnum(self->hdb)))) {
+    return NULL;
+  }
+  for (i = 0; ; i++) {
+    char *key;
+    int key_len;
+    PyObject *_key;
+
+    Py_BEGIN_ALLOW_THREADS
+    key = tchdbiternext(self->hdb, &key_len);
+    Py_END_ALLOW_THREADS
+    if (!key) { break; }
+    _key = PyString_FromStringAndSize(key, key_len);
+    free(key);
+    if (!_key) {
+      Py_DECREF(ret);
+      return NULL;
+    }
+    PyList_SET_ITEM(ret, i, _key);
+  }
+  return ret;
+}
+
+static PyObject *
+PyTCHDB_items(PyTCHDB *self) {
+  int i, n = TCHDB_rnum(self->hdb);
+  PyObject *ret, *item;
+  if (!PyTCHDB_iterinit((PyTCHDB *)self) ||
+      !(ret = PyList_New(n))) {
+    return NULL;
+  }
+  for (i = 0; i < n; i++) {
+    if (!(item = PyTuple_New(2))) {
+      Py_DECREF(ret);
+      return NULL;
+    }
+    PyList_SET_ITEM(ret, i, item);
+  }
+  for (i = 0; ; i++) {
+    char *key, *value;
+    int key_len, value_len;
+
+    Py_BEGIN_ALLOW_THREADS
+    key = tchdbiternext(self->hdb, &key_len);
+    Py_END_ALLOW_THREADS
+    if (!key) { break; }
+
+    Py_BEGIN_ALLOW_THREADS
+    value = tchdbget(self->hdb, key, key_len, &value_len);
+    Py_END_ALLOW_THREADS
+
+    if (value) {
+      PyObject *_key, *_value;
+      _key = PyString_FromStringAndSize(key, key_len);
+      free(key);
+      if (!_key) {
+        Py_DECREF(ret);
+        return NULL;
+      }
+      _value = PyString_FromStringAndSize(value, value_len);
+      free(value);
+      if (!_value) {
+        Py_DECREF(_key);
+        Py_DECREF(ret);
+        return NULL;
+      }
+      item = PyList_GET_ITEM(ret, i);
+      PyTuple_SET_ITEM(item, 0, _key);
+      PyTuple_SET_ITEM(item, 1, _value);
+    } else {
+      free(key);
+    }
+  }
+  return ret;
+}
+
+static PyObject *
+PyTCHDB_values(PyTCHDB *self) {
+  int i;
+  PyObject *ret;
+  if (!PyTCHDB_iterinit((PyTCHDB *)self) ||
+      !(ret = PyList_New(TCHDB_rnum(self->hdb)))) {
+    return NULL;
+  }
+  for (i = 0; ; i++) {
+    char *key, *value;
+    int key_len, value_len;
+
+    Py_BEGIN_ALLOW_THREADS
+    key = tchdbiternext(self->hdb, &key_len);
+    Py_END_ALLOW_THREADS
+
+    if (!key) { break; }
+
+    Py_BEGIN_ALLOW_THREADS
+    value = tchdbget(self->hdb, key, key_len, &value_len);
+    Py_END_ALLOW_THREADS
+    free(key);
+
+    if (value) {
+      PyObject *_value;
+      _value = PyString_FromStringAndSize(value, value_len);
+      free(value);
+      if (!_value) {
+        Py_DECREF(ret);
+        return NULL;
+      }
+      PyList_SET_ITEM(ret, i, _value);
+    }
+  }
+  return ret;
+}
+
+static long
+PyTCHDB_length(PyTCHDB *self) {
+  return TCHDB_rnum(self->hdb);
+}
+
+static PyObject *
+PyTCHDB_subscript(PyTCHDB *self, PyObject *_key) {
+  PyObject *ret;
+  char *key = PyString_AS_STRING(_key), *value;
+  int key_len = PyString_GET_SIZE(_key), value_len;
+
+  if (!key || !key_len) {
+    return NULL;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  value = tchdbget(self->hdb, key, key_len, &value_len);
+  Py_END_ALLOW_THREADS
+
+  if (!value) {
+    raise_tchdb_error(self->hdb);
+    return NULL;
+  }
+  ret = PyString_FromStringAndSize(value, value_len);
+  free(value);
+  return ret;
+}
+
+int
+PyTCHDB_DelItem(PyTCHDB *self, PyObject *_key) {
+  bool result;
+  char *key = PyString_AS_STRING(_key);
+  int key_len = PyString_GET_SIZE(_key);
+
+  if (!key || !key_len) {
+    return -1;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  result = tchdbout(self->hdb, key, key_len);
+  Py_END_ALLOW_THREADS
+
+  if (!result) {
+    raise_tchdb_error(self->hdb);
+    return -1;
+  }
+  return 0;
+}
+
+int
+PyTCHDB_SetItem(PyTCHDB *self, PyObject *_key, PyObject *_value) {
+  bool result;
+  char *key = PyString_AS_STRING(_key), *value = PyString_AS_STRING(_value);
+  int key_len = PyString_GET_SIZE(_key), value_len = PyString_GET_SIZE(_value);
+
+  if (!key || !key_len || !value) {
+    return -1;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  result = tchdbput(self->hdb, key, key_len, value, value_len);
+  Py_END_ALLOW_THREADS
+
+  if (!result) {
+    raise_tchdb_error(self->hdb);
+    return -1;
+  }
+  return 0;
+}
+
+static int
+PyTCHDB_ass_sub(PyTCHDB *self, PyObject *v, PyObject *w)
+{
+  if (w) {
+    return PyTCHDB_SetItem(self, v, w);
+  } else {
+    return PyTCHDB_DelItem(self, v);
+  }
+}
+
+/* methods of classes */
+static PyMethodDef PyTCHDB_methods[] = {
+  {"errmsg", (PyCFunction)PyTCHDB_errmsg,
+   METH_VARARGS | METH_KEYWORDS | METH_CLASS,
+   "Get the message string corresponding to an error code."},
+  {"ecode", (PyCFunction)PyTCHDB_ecode,
+   METH_NOARGS,
+   "Get the last happened error code of a hash database object."},
+  {"setmutex", (PyCFunction)PyTCHDB_setmutex,
+   METH_NOARGS,
+   "Set mutual exclusion control of a hash database object for threading."},
+  {"tune", (PyCFunction)PyTCHDB_tune,
+   METH_VARARGS | METH_KEYWORDS,
+   "Set the tuning parameters of a hash database object."},
+  {"open", (PyCFunction)PyTCHDB_open,
+   METH_VARARGS | METH_KEYWORDS,
+   "Open a database file and connect a hash database object."},
+  {"close", (PyCFunction)PyTCHDB_close,
+   METH_NOARGS,
+   "Close a hash database object."},
+  {"put", (PyCFunction)PyTCHDB_put,
+   METH_VARARGS | METH_KEYWORDS,
+   "Store a record into a hash database object."},
+  {"putkeep", (PyCFunction)PyTCHDB_putkeep,
+   METH_VARARGS | METH_KEYWORDS,
+   "Store a new record into a hash database object."},
+  {"putcat", (PyCFunction)PyTCHDB_putcat,
+   METH_VARARGS | METH_KEYWORDS,
+   "Concatenate a value at the end of the existing record in a hash database object."},
+  {"putasync", (PyCFunction)PyTCHDB_putasync,
+   METH_VARARGS | METH_KEYWORDS,
+   "Store a record into a hash database object in asynchronous fashion."},
+  {"out", (PyCFunction)PyTCHDB_out,
+   METH_VARARGS | METH_KEYWORDS,
+   "Remove a record of a hash database object."},
+  {"get", (PyCFunction)PyTCHDB_get,
+   METH_VARARGS | METH_KEYWORDS,
+   "Retrieve a record in a hash database object."},
+  {"vsiz", (PyCFunction)PyTCHDB_vsiz,
+   METH_VARARGS | METH_KEYWORDS,
+   "Get the size of the value of a record in a hash database object."},
+  {"iterinit", (PyCFunction)PyTCHDB_iterinit,
+   METH_NOARGS,
+   "Initialize the iterator of a hash database object."},
+  {"iternext", (PyCFunction)PyTCHDB_iternext,
+   METH_NOARGS,
+   "Get the next extensible objects of the iterator of a hash database object."},
+  {"sync", (PyCFunction)PyTCHDB_sync,
+   METH_NOARGS,
+   "Synchronize updated contents of a hash database object with the file and the device."},
+  {"optimize", (PyCFunction)PyTCHDB_optimize,
+   METH_VARARGS | METH_KEYWORDS,
+   "Optimize the file of a hash database object."},
+  {"vanish", (PyCFunction)PyTCHDB_vanish,
+   METH_NOARGS,
+   "Remove all records of a hash database object."},
+  {"path", (PyCFunction)PyTCHDB_path,
+   METH_NOARGS,
+   "Get the file path of a hash database object."},
+  {"copy", (PyCFunction)PyTCHDB_copy,
+   METH_VARARGS | METH_KEYWORDS,
+   "Copy the database file of a hash database object."},
+  {"rnum", (PyCFunction)PyTCHDB_rnum,
+   METH_NOARGS,
+   "Get the number of records of a hash database object."},
+  {"fsiz", (PyCFunction)PyTCHDB_fsiz,
+   METH_NOARGS,
+   "Get the size of the database file of a hash database object."},
+  {"__contains__", (PyCFunction)PyTCHDB___contains__,
+   METH_O | METH_COEXIST,
+   ""},
+  {"__getitem__", (PyCFunction)PyTCHDB___getitem__,
+   METH_O | METH_COEXIST,
+   ""},
+  {"has_key", (PyCFunction)PyTCHDB___contains__,
+   METH_O,
+   ""},
+  {"keys", (PyCFunction)PyTCHDB_keys,
+   METH_NOARGS,
+   ""},
+  {"items", (PyCFunction)PyTCHDB_items,
+   METH_NOARGS,
+   ""},
+  {"values", (PyCFunction)PyTCHDB_values,
+   METH_NOARGS,
+   ""},
+  /*
+  {"", (PyCFunction)PyTCHDB_,
+   METH_VARARGS | METH_KEYWORDS,
+   ""},
+  */
+  {NULL, NULL, 0, NULL}
+};
+
+
+/* Hack to implement "key in dict" */
+static PySequenceMethods PyTCHDB_as_sequence = {
+  0,                             /* sq_length */
+  0,                             /* sq_concat */
+  0,                             /* sq_repeat */
+  0,                             /* sq_item */
+  0,                             /* sq_slice */
+  0,                             /* sq_ass_item */
+  0,                             /* sq_ass_slice */
+  (objobjproc)PyTCHDB_Contains,  /* sq_contains */
+  0,                             /* sq_inplace_concat */
+  0,                             /* sq_inplace_repeat */
+};
+
+static PyMappingMethods PyTCHDB_as_mapping = {
+  (inquiry)PyTCHDB_length, /* mp_length (inquiry/lenfunc )*/
+  (binaryfunc)PyTCHDB_subscript, /* mp_subscript */
+  (objobjargproc)PyTCHDB_ass_sub, /* mp_ass_subscript */
+};
+
+static PyTypeObject PyTCHDB_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,                                           /* ob_size */
+  "pytc.HDB",                                  /* tp_name */
+  sizeof(PyTCHDB),                             /* tp_basicsize */
+  0,                                           /* tp_itemsize */
+  (destructor)PyTCHDB_dealloc,                 /* tp_dealloc */
+  0,                                           /* tp_print */
+  0,                                           /* tp_getattr */
+  0,                                           /* tp_setattr */
+  0,                                           /* tp_compare */
+  0,                                           /* tp_repr */
+  0,                                           /* tp_as_number */
+  &PyTCHDB_as_sequence,                        /* tp_as_sequence */
+  &PyTCHDB_as_mapping,                         /* tp_as_mapping */
+  PyTCHDB_Hash,                                /* tp_hash  */
+  0,                                           /* tp_call */
+  0,                                           /* tp_str */
+  0,                                           /* tp_getattro */
+  0,                                           /* tp_setattro */
+  0,                                           /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,    /* tp_flags */
+  "tchdb",                                     /* tp_doc */
+  0,                                           /* tp_traverse */
+  0,                                           /* tp_clear */
+  0,                                           /* tp_richcompare */
+  0,                                           /* tp_weaklistoffset */
+  PyTCHDB_GetIter,                             /* tp_iter */
+  (iternextfunc)PyTCHDB_iternext,              /* tp_iternext */
+  PyTCHDB_methods,                             /* tp_methods */
+  0,                                           /* tp_members */
+  0,                                           /* tp_getset */
+  0,                                           /* tp_base */
+  0,                                           /* tp_dict */
+  0,                                           /* tp_descr_get */
+  0,                                           /* tp_descr_set */
+  0,                                           /* tp_dictoffset */
+  0,                                           /* tp_init */
+  0,                                           /* tp_alloc */
+  PyTCHDB_new,                                 /* tp_new */
+};
+
+/* BDBCUR */
+
+static PyTypeObject PyTCBDB_Type;
+
+static PyObject *
+PyBDBCUR_new(PyTypeObject *type, PyObject *args, PyObject *keywds)
+{
+  PyTCBDB *bdb;
+  PyBDBCUR *self;
+  static char *kwlist[] = {"bdb", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!:new", kwlist,
+                                   &PyTCBDB_Type, &bdb)) {
+    return NULL;
+  }
+  if (!(self = (PyBDBCUR *)type->tp_alloc(type, 0))) {
+    PyErr_SetString(PyExc_MemoryError, "Cannot alloc PyBDBCUR instance");
+    return NULL;
+  }
+
+  Py_BEGIN_ALLOW_THREADS
+  self->cur = tcbdbcurnew(bdb->bdb);
+  Py_END_ALLOW_THREADS
+
+  if (!self->cur) {
+    self->ob_type->tp_free(self);
+    raise_tcbdb_error(bdb->bdb);
+    return NULL;
+  }
+  Py_INCREF(bdb);
+  self->bdb = bdb;
+  return (PyObject *)self;
+}
+
+static PyObject *
+PyBDBCUR_dealloc(PyBDBCUR *self)
+{
+  Py_BEGIN_ALLOW_THREADS
+  tcbdbcurdel(self->cur);
+  Py_END_ALLOW_THREADS
+  Py_XDECREF(self->bdb);
+  self->ob_type->tp_free(self);
+  Py_RETURN_NONE;
+}
+
+BOOL_NOARGS(PyBDBCUR_first,PyBDBCUR,tcbdbcurfirst,cur,raise_tcbdb_error,bdb->bdb)
+BOOL_NOARGS(PyBDBCUR_last,PyBDBCUR,tcbdbcurlast,cur,raise_tcbdb_error,bdb->bdb)
+BOOL_KEYARGS(PyBDBCUR_jump,PyBDBCUR,jump,tcbdbcurjump,cur,raise_tcbdb_error,bdb->bdb)
+BOOL_NOARGS(PyBDBCUR_prev,PyBDBCUR,tcbdbcurprev,cur,raise_tcbdb_error,bdb->bdb)
+BOOL_NOARGS(PyBDBCUR_next,PyBDBCUR,tcbdbcurnext,cur,raise_tcbdb_error,bdb->bdb)
+
+static PyObject *
+PyBDBCUR_put(PyBDBCUR *self, PyObject *args, PyObject *keywds) {
+  bool result;
+  char *value;
+  int value_len, cpmode;
+  static char *kwlist[] = {"value", "cpmode", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#i:put", kwlist,
+                                   &value, &value_len, &cpmode)) {
+    return NULL;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbcurput(self->cur, value, value_len, cpmode);
+  Py_END_ALLOW_THREADS
+
+  if (!result) {
+    raise_tcbdb_error(self->bdb->bdb);
+    return NULL;
+  }
+  Py_RETURN_NONE;
+}
+
+BOOL_NOARGS(PyBDBCUR_out,PyBDBCUR,tcbdbcurout,cur,raise_tcbdb_error,bdb->bdb)
+STRINGL_NOARGS(PyBDBCUR_key,PyBDBCUR,tcbdbcurkey,cur,raise_tcbdb_error,bdb->bdb)
+STRINGL_NOARGS(PyBDBCUR_val,PyBDBCUR,tcbdbcurval,cur,raise_tcbdb_error,bdb->bdb)
+
+static PyObject *
+PyBDBCUR_rec(PyBDBCUR *self) {
+  PyObject *ret = NULL;
+  TCXSTR *key, *value;
+
+  key = tcxstrnew();
+  value = tcxstrnew();
+  if (key && value) {
+    bool result;
+
+    Py_BEGIN_ALLOW_THREADS
+    result = tcbdbcurrec(self->cur, key, value);
+    Py_END_ALLOW_THREADS
+
+    if (result) {
+      ret = Py_BuildValue("(s#s#)", tcxstrptr(key), tcxstrsize(key),
+                                    tcxstrptr(value), tcxstrsize(value));
+    }
+  }
+  if (!ret) {
+    raise_tcbdb_error(self->bdb->bdb);
+  }
+  if (key) { tcxstrdel(key); }
+  if (value) { tcxstrdel(value); }
+  return ret;
+}
+
+static PyObject *
+PyBDBCUR_iternext(PyBDBCUR *self) {
+  char *key;
+  int key_len;
+  PyObject *ret;
+
+  Py_BEGIN_ALLOW_THREADS
+  key = tcbdbcurkey(self->cur, &key_len);
+  Py_END_ALLOW_THREADS
+
+  if (key) {
+    ret = PyString_FromStringAndSize(key, key_len);
+    free(key);
+    Py_BEGIN_ALLOW_THREADS
+    tcbdbcurnext(self->cur);
+    Py_END_ALLOW_THREADS
+    return ret;
+  }
+  return NULL;
+}
+
+static PyMethodDef PyBDBCUR_methods[] = {
+  {"first", (PyCFunction)PyBDBCUR_first,
+   METH_NOARGS,
+   "Move a cursor object to the first record."},
+  {"last", (PyCFunction)PyBDBCUR_last,
+   METH_NOARGS,
+   "Move a cursor object to the last record."},
+  {"jump", (PyCFunction)PyBDBCUR_jump,
+   METH_VARARGS | METH_KEYWORDS,
+   "Move a cursor object to the front of records corresponding a key."},
+  {"prev", (PyCFunction)PyBDBCUR_prev,
+   METH_NOARGS,
+   "Move a cursor object to the previous record."},
+  {"next", (PyCFunction)PyBDBCUR_next,
+   METH_NOARGS,
+   "Move a cursor object to the next record."},
+  {"put", (PyCFunction)PyBDBCUR_put,
+   METH_VARARGS | METH_KEYWORDS,
+   "Insert a record around a cursor object."},
+  {"out", (PyCFunction)PyBDBCUR_out,
+   METH_NOARGS,
+   "Delete the record where a cursor object is."},
+  {"key", (PyCFunction)PyBDBCUR_key,
+   METH_NOARGS,
+   "Get the key of the record where the cursor object is."},
+  {"val", (PyCFunction)PyBDBCUR_val,
+   METH_NOARGS,
+   "Get the value of the record where the cursor object is."},
+  {"rec", (PyCFunction)PyBDBCUR_rec,
+   METH_NOARGS,
+   "Get the key and the value of the record where the cursor object is."},
+  /*
+  {"", (PyCFunction)PyBDBCUR_,
+   METH_VARARGS | METH_KEYWORDS,
+   ""},
+  */
+  {NULL, NULL, 0, NULL}
+};
+
+static PyTypeObject PyBDBCUR_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,                                           /* ob_size */
+  "pytc.BDBCUR",                               /* tp_name */
+  sizeof(PyBDBCUR),                            /* tp_basicsize */
+  0,                                           /* tp_itemsize */
+  (destructor)PyBDBCUR_dealloc,                /* tp_dealloc */
+  0,                                           /* tp_print */
+  0,                                           /* tp_getattr */
+  0,                                           /* tp_setattr */
+  0,                                           /* tp_compare */
+  0,                                           /* tp_repr */
+  0,                                           /* tp_as_number */
+  0,                                           /* tp_as_sequence */
+  0,                                           /* tp_as_mapping */
+  0,                                           /* tp_hash  */
+  0,                                           /* tp_call */
+  0,                                           /* tp_str */
+  0,                                           /* tp_getattro */
+  0,                                           /* tp_setattro */
+  0,                                           /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,    /* tp_flags */
+  "bdbcur",                                    /* tp_doc */
+  0,                                           /* tp_traverse */
+  0,                                           /* tp_clear */
+  0,                                           /* tp_richcompare */
+  0,                                           /* tp_weaklistoffset */
+  PyObject_SelfIter,                           /* tp_iter */
+  (iternextfunc)PyBDBCUR_iternext,             /* tp_iternext */
+  PyBDBCUR_methods,                            /* tp_methods */
+  0,                                           /* tp_members */
+  0,                                           /* tp_getset */
+  0,                                           /* tp_base */
+  0,                                           /* tp_dict */
+  0,                                           /* tp_descr_get */
+  0,                                           /* tp_descr_set */
+  0,                                           /* tp_dictoffset */
+  0,                                           /* tp_init */
+  0,                                           /* tp_alloc */
+  PyBDBCUR_new,                                /* tp_new */
+};
+
+/*** TCBDB ***/
+
+#define PyTCBDB_TUNE(a,b,c) \
+  static PyObject * \
+  a(PyTCBDB *self, PyObject *args, PyObject *keywds) { \
+    bool result; \
+    char apow, fpow; \
+    int lmemb, nmemb; \
+    PY_LONG_LONG bnum; \
+    unsigned char opts; \
+    static char *kwlist[] = {"lmemb", "nmemb", "bnum", "apow", "fpow", "opts", NULL}; \
+  \
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "iiLbbB:" #b, kwlist, \
+                                     &lmemb, &nmemb, &bnum, \
+                                     &apow, &fpow, &opts)) { \
+      return NULL; \
+    } \
+    Py_BEGIN_ALLOW_THREADS \
+    result = c(self->bdb, lmemb, nmemb, bnum, apow, fpow, opts); \
+    Py_END_ALLOW_THREADS \
+  \
+    if (!result) { \
+      raise_tcbdb_error(self->bdb); \
+      return NULL; \
+    } \
+    Py_RETURN_NONE; \
+  }
+
+static long
+PyTCBDB_Hash(PyObject *self) {
+  PyErr_SetString(PyExc_TypeError, "BDB objects are unhashable");
+  return -1;
+}
+
+static PyObject *
+PyTCBDB_errmsg(PyTypeObject *type, PyObject *args, PyObject *keywds) {
+  int ecode;
+  static char *kwlist[] = {"ecode", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "i:errmsg", kwlist,
+                                   &ecode)) {
+    return NULL;
+  }
+  return PyString_FromString(tcbdberrmsg(ecode));
+}
+
+static void
+PyTCBDB_dealloc(PyTCBDB *self)
+{
+  /* NOTE: tcbdbsel closes bdb implicitly */
+  /*
+  if (self->bdb->flags & BDBFOPEN) {
+    Py_BEGIN_ALLOW_THREADS
+    tcbdbclose(self->bdb);
+    Py_END_ALLOW_THREADS
+  }
+  */
+  Py_XDECREF(self->cmp);
+  Py_XDECREF(self->cmpop);
+  if (self->bdb) {
+    Py_BEGIN_ALLOW_THREADS
+    tcbdbdel(self->bdb);
+    Py_END_ALLOW_THREADS
+  }
+  self->ob_type->tp_free(self);
+}
+
+static PyObject *
+PyTCBDB_new(PyTypeObject *type, PyObject *args, PyObject *keywds)
+{
+  PyTCBDB *self;
+  if (!(self = (PyTCBDB *)type->tp_alloc(type, 0))) {
+    PyErr_SetString(PyExc_MemoryError, "Cannot alloc PyTCBDB instance");
+    return NULL;
+  }
+  /* NOTE: initialize member implicitly */
+  self->cmp = self->cmpop = NULL;
+  if ((self->bdb = tcbdbnew())) {
+    int omode = 0;
+    char *path = NULL;
+    static char *kwlist[] = {"path", "omode", NULL};
+
+    if (PyArg_ParseTupleAndKeywords(args, keywds, "|si:open", kwlist,
+                                    &path, &omode)) {
+      if (path && omode) {
+        bool result;
+        Py_BEGIN_ALLOW_THREADS
+        result = tcbdbopen(self->bdb, path, omode);
+        Py_END_ALLOW_THREADS
+        if (result) {
+          return (PyObject *)self;
+        }
+      } else {
+        return (PyObject *)self;
+      }
+      raise_tcbdb_error(self->bdb);
+    }
+  } else {
+    PyErr_SetString(PyExc_MemoryError, "Cannot alloc TCBDB instance");
+  }
+  PyTCBDB_dealloc(self);
+  return NULL;
+}
+static PyObject *
+PyTCBDB_ecode(PyTCBDB *self) {
+  return PyInt_FromLong((long)tcbdbecode(self->bdb));
+}
+
+BOOL_NOARGS(PyTCBDB_setmutex,PyTCBDB,tcbdbsetmutex,bdb,raise_tcbdb_error,bdb)
+
+static int
+TCBDB_cmpfunc(const char *aptr, int asiz,
+              const char *bptr, int bsiz, PyTCBDB *self) {
+  int ret;
+  PyObject *args, *result;
+  if ((args = Py_BuildValue("(s#s#O)", aptr, asiz, bptr, bsiz, self->cmpop))) {
+    /* TODO: set error */
+    return 0;
+  }
+  result = PyEval_CallObject(self->cmp, args);
+  Py_DECREF(args);
+  if (!result) {
+    /* TODO: set error */
+    return 0;
+  }
+  ret = PyLong_AsLong(result);
+  Py_DECREF(result);
+  return ret;
+}
+
+static PyObject *
+PyTCBDB_setcmpfunc(PyTCBDB *self, PyObject *args, PyObject *keywds) {
+  bool result;
+  PyObject *cmp, *cmpop = NULL;
+  static char *kwlist[] = {"cmp", "cmpop", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|O:setcmpfunc", kwlist,
+                                   &cmp, &cmpop)
+      || !PyCallable_Check(cmp)) {
+    return NULL;
+  }
+  if (!cmpop) {
+    Py_INCREF(Py_None);
+    cmpop = Py_None;
+  }
+
+  Py_INCREF(cmp);
+  Py_XINCREF(cmpop);
+  Py_XDECREF(self->cmp);
+  Py_XDECREF(self->cmpop);
+  self->cmp = cmp;
+  self->cmpop = cmpop;
+
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbsetcmpfunc(self->bdb,
+                           (BDBCMP)TCBDB_cmpfunc, self);
+  Py_END_ALLOW_THREADS
+
+  if (!result) {
+    raise_tcbdb_error(self->bdb);
+    Py_DECREF(self->cmp);
+    Py_XDECREF(self->cmpop);
+    self->cmp = self->cmpop = NULL;
+    return NULL;
+  }
+  Py_RETURN_NONE;
+}
+
+PyTCBDB_TUNE(PyTCBDB_tune, tune, tcbdbtune)
+
+static PyObject *
+PyTCBDB_setcache(PyTCBDB *self, PyObject *args, PyObject *keywds) {
+  int lcnum, ncnum;
+  bool result;
+  static char *kwlist[] = {"lcnum", "ncnum", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "ii:setcache", kwlist,
+                                   &lcnum, &ncnum)) {
+    return NULL;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbsetcache(self->bdb, lcnum, ncnum);
+  Py_END_ALLOW_THREADS
+
+  if (!result) {
+    raise_tcbdb_error(self->bdb);
+    return NULL;
+  }
+  Py_RETURN_NONE;
+}
+
+PyTCXDB_OPEN(PyTCBDB_open,PyTCBDB,PyTCBDB_new,tcbdbopen,bdb,PyTCBDB_dealloc,raise_tcbdb_error)
+BOOL_NOARGS(PyTCBDB_close,PyTCBDB,tcbdbclose,bdb,raise_tcbdb_error,bdb)
+PyTCXDB_PUT(PyTCBDB_put, PyTCBDB, put, tcbdbput, bdb, raise_tcbdb_error)
+PyTCXDB_PUT(PyTCBDB_putkeep, PyTCBDB, putkeep, tcbdbputkeep, bdb, raise_tcbdb_error)
+PyTCXDB_PUT(PyTCBDB_putcat, PyTCBDB, putcat, tcbdbputcat, bdb, raise_tcbdb_error)
+PyTCXDB_PUT(PyTCBDB_putdup, PyTCBDB, putdup, tcbdbputdup, bdb, raise_tcbdb_error)
+
+static PyObject *
+PyTCBDB_putlist(PyTCBDB *self, PyObject *args, PyObject *keywds) {
+  bool result;
+  char *key;
+  TCLIST *tcvalue;
+  PyObject *value;
+  int key_len, value_size, i;
+  static char *kwlist[] = {"key", "value", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#O!:putlist", kwlist,
+                                   &key, &key_len, &PyList_Type, &value) ||
+      !(tcvalue = tclistnew())) {
+    return NULL;
+  }
+
+  value_size = PyList_Size(value);
+  for (i = 0; i < value_size; i++) {
+    PyObject *v = PyList_GetItem(value, i);
+    if (PyString_Check(v)) {
+      tclistpush(tcvalue, PyString_AsString(v), PyString_Size(v));
+    }
+  }
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbputdup3(self->bdb, key, key_len, tcvalue);
+  Py_END_ALLOW_THREADS
+  tclistdel(tcvalue);
+
+  if (!result) {
+    raise_tcbdb_error(self->bdb);
+    return NULL;
+  }
+  Py_RETURN_NONE;
+}
+
+BOOL_KEYARGS(PyTCBDB_out,PyTCBDB,out,tcbdbout,bdb,raise_tcbdb_error,bdb)
+BOOL_KEYARGS(PyTCBDB_outlist,PyTCBDB,outlist,tcbdbout3,bdb,raise_tcbdb_error,bdb)
+STRINGL_KEYARGS(PyTCBDB_get,PyTCBDB,get,tcbdbget,bdb,raise_tcbdb_error)
+
+static PyObject *
+PyTCBDB_getlist(PyTCBDB *self, PyObject *args, PyObject *keywds) {
+  char *key;
+  int key_len;
+  TCLIST *list;
+  static char *kwlist[] = {"key", NULL};
+
+  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#:getlist", kwlist,
+                                   &key, &key_len)) {
+    return NULL;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  list = tcbdbget4(self->bdb, key, key_len);
+  Py_END_ALLOW_THREADS
+
+  if (!list) {
+    raise_tcbdb_error(self->bdb);
+    return NULL;
+  } else {
+    PyObject *ret;
+    int i, n = tclistnum(list);
+    if ((ret = PyList_New(n))) {
+      for (i = 0; i < n; i++) {
+        int value_len;
+        PyObject *_value;
+        const char *value;
+        value = tclistval(list, i, &value_len);
+        _value = PyString_FromStringAndSize(value, value_len);
+        PyList_SET_ITEM(ret, i, _value);
+      }
+    }
+    tclistdel(list);
+    return ret;
+  }
+}
+
+INT_KEYARGS(PyTCBDB_vnum,PyTCBDB,vnum,tcbdbvnum,bdb,raise_tcbdb_error)
+INT_KEYARGS(PyTCBDB_vsiz,PyTCBDB,vsiz,tcbdbvsiz,bdb,raise_tcbdb_error)
+BOOL_NOARGS(PyTCBDB_sync,PyTCBDB,tcbdbsync,bdb,raise_tcbdb_error,bdb)
+PyTCBDB_TUNE(PyTCBDB_optimize, optimize, tcbdboptimize)
+BOOL_PATHARGS(PyTCBDB_copy, PyTCBDB, copy, tcbdbcopy, bdb, raise_tcbdb_error)
+BOOL_NOARGS(PyTCBDB_vanish,PyTCBDB,tcbdbvanish,bdb,raise_tcbdb_error,bdb)
+BOOL_NOARGS(PyTCBDB_tranbegin,PyTCBDB,tcbdbtranbegin,bdb,raise_tcbdb_error,bdb)
+BOOL_NOARGS(PyTCBDB_trancommit,PyTCBDB,tcbdbtrancommit,bdb,raise_tcbdb_error,bdb)
+BOOL_NOARGS(PyTCBDB_tranabort,PyTCBDB,tcbdbtranabort,bdb,raise_tcbdb_error,bdb)
+STRING_NOARGS(PyTCBDB_path,PyTCBDB,tcbdbpath,bdb,raise_tcbdb_error)
+PY_U_LONG_LONG_NOARGS(PyTCBDB_rnum, PyTCBDB, tcbdbrnum, bdb, tcbdbecode, raise_tcbdb_error)
+PY_U_LONG_LONG_NOARGS(PyTCBDB_fsiz, PyTCBDB, tcbdbrnum, bdb, tcbdbecode, raise_tcbdb_error)
+
+/* TODO: features for experts */
+
+static int
+PyTCBDB_Contains(PyTCBDB *self, PyObject *_key) {
+  char *key = PyString_AS_STRING(_key);
+  int key_len = PyString_GET_SIZE(_key), value_len;
+
+  if (!key || !key_len) {
+    return -1;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  value_len = tcbdbvsiz(self->bdb, key, key_len);
+  Py_END_ALLOW_THREADS
+
+  return (value_len != -1);
+}
+
+static PyObject *
+PyTCBDB___contains__(PyTCBDB *self, PyObject *_key) {
+  return PyBool_FromLong(PyTCBDB_Contains(self, _key) != 0);
+}
+
+static PyObject *
+PyTCBDB___getitem__(PyTCBDB *self, PyObject *_key) {
+  PyObject *ret;
+  char *key = PyString_AS_STRING(_key), *value;
+  int key_len = PyString_GET_SIZE(_key), value_len;
+
+  if (!key || !key_len) {
+    return NULL;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  value = tcbdbget(self->bdb, key, key_len, &value_len);
+  Py_END_ALLOW_THREADS
+
+  if (!value) {
+    raise_tcbdb_error(self->bdb);
+    return NULL;
+  }
+  ret = PyString_FromStringAndSize(value, value_len);
+  free(value);
+  return ret;
+}
+
+static PyObject *
+PyTCBDB_curnew(PyTCBDB *self) {
+  PyBDBCUR *cur;
+  PyObject *args;
+
+  args = Py_BuildValue("(O)", self);
+  cur = (PyBDBCUR *)PyBDBCUR_new(&PyBDBCUR_Type, args, NULL);
+  Py_DECREF(args);
+
+  if (cur) {
+    return (PyObject *)cur;
+  }
+  raise_tcbdb_error(self->bdb);
+  return NULL;
+}
+
+static PyObject *
+PyTCBDB_GetIter(PyObject *self) {
+  PyBDBCUR *cur;
+  if ((cur = (PyBDBCUR *)PyTCBDB_curnew((PyTCBDB *)self))) {
+    if (PyBDBCUR_first(cur)) {
+      return (PyObject *)cur;
+    }
+    PyBDBCUR_dealloc(cur);
+  }
+  return NULL;
+}
+
+TCXDB_rnum(TCBDB_rnum,TCBDB,tcbdbrnum)
+
+static PyObject *
+PyTCBDB_keys(PyTCBDB *self) {
+  int i;
+  BDBCUR *cur;
+  PyObject *ret;
+  bool result;
+
+  Py_BEGIN_ALLOW_THREADS
+  cur = tcbdbcurnew(self->bdb);
+  Py_END_ALLOW_THREADS
+
+  if (!cur) {
+    return NULL;
+  }
+
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbcurfirst(cur);
+  Py_END_ALLOW_THREADS
+
+  if (!result || !(ret = PyList_New(TCBDB_rnum(self->bdb)))) {
+    tcbdbcurdel(cur);
+    return NULL;
+  }
+  for (i = 0; result; i++) {
+    char *key;
+    int key_len;
+    PyObject *_key;
+
+    Py_BEGIN_ALLOW_THREADS
+    key = tcbdbcurkey(cur, &key_len);
+    Py_END_ALLOW_THREADS
+
+    if (!key) { break; }
+    _key = PyString_FromStringAndSize(key, key_len);
+    free(key);
+    if (!_key) {
+      Py_DECREF(ret);
+      return NULL;
+    }
+    PyList_SET_ITEM(ret, i, _key);
+
+    Py_BEGIN_ALLOW_THREADS
+    result = tcbdbcurnext(cur);
+    Py_END_ALLOW_THREADS
+  }
+  tcbdbcurdel(cur);
+  return ret;
+}
+
+static PyObject *
+PyTCBDB_items(PyTCBDB *self) {
+  int i;
+  bool result;
+  BDBCUR *cur;
+  PyObject *ret;
+  TCXSTR *key, *value;
+
+  Py_BEGIN_ALLOW_THREADS
+  cur = tcbdbcurnew(self->bdb);
+  Py_END_ALLOW_THREADS
+
+  if (cur) {
+    Py_BEGIN_ALLOW_THREADS
+    result = tcbdbcurfirst(cur);
+    Py_END_ALLOW_THREADS
+    if (result) {
+      if ((key = tcxstrnew())) {
+        if ((value = tcxstrnew())) {
+          if ((ret = PyList_New(TCBDB_rnum(self->bdb)))) {
+            goto main;
+          }
+          tcxstrdel(value);
+        }
+        tcxstrdel(key);
+      }
+    }
+    tcbdbcurdel(cur);
+  }
+  return NULL;
+main:
+  for (i = 0; result; i++) {
+    Py_BEGIN_ALLOW_THREADS
+    result = tcbdbcurrec(cur, key, value);
+    Py_END_ALLOW_THREADS
+    if (result) {
+      PyObject *tuple;
+      tuple = Py_BuildValue("(s#s#)", tcxstrptr(key), tcxstrsize(key),
+                                      tcxstrptr(value), tcxstrsize(value));
+      if (tuple) {
+        PyList_SET_ITEM(ret, i, tuple);
+        Py_BEGIN_ALLOW_THREADS
+        result = tcbdbcurnext(cur);
+        Py_END_ALLOW_THREADS
+        tcxstrclear(key);
+        tcxstrclear(value);
+      } else {
+        break;
+      }
+    }
+  }
+  tcxstrdel(key);
+  tcxstrdel(value);
+  tcbdbcurdel(cur);
+  return ret;
+}
+
+static PyObject *
+PyTCBDB_values(PyTCBDB *self) {
+  int i;
+  BDBCUR *cur;
+  PyObject *ret;
+  bool result;
+
+  Py_BEGIN_ALLOW_THREADS
+  cur = tcbdbcurnew(self->bdb);
+  Py_END_ALLOW_THREADS
+
+  if (!cur) {
+    return NULL;
+  }
+
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbcurfirst(cur);
+  Py_END_ALLOW_THREADS
+
+  if (!result || !(ret = PyList_New(TCBDB_rnum(self->bdb)))) {
+    tcbdbcurdel(cur);
+    return NULL;
+  }
+  for (i = 0; result; i++) {
+    char *value;
+    int value_len;
+    PyObject *_value;
+
+    Py_BEGIN_ALLOW_THREADS
+    value = tcbdbcurval(cur, &value_len);
+    Py_END_ALLOW_THREADS
+
+    if (!value) { break; }
+    _value = PyString_FromStringAndSize(value, value_len);
+    free(value);
+    if (!_value) {
+      Py_DECREF(ret);
+      return NULL;
+    }
+    PyList_SET_ITEM(ret, i, _value);
+
+    Py_BEGIN_ALLOW_THREADS
+    result = tcbdbcurnext(cur);
+    Py_END_ALLOW_THREADS
+  }
+  tcbdbcurdel(cur);
+  return ret;
+}
+
+static long
+PyTCBDB_length(PyTCBDB *self) {
+  return TCBDB_rnum(self->bdb);
+}
+
+static PyObject *
+PyTCBDB_subscript(PyTCBDB *self, PyObject *_key) {
+  PyObject *ret;
+  char *key = PyString_AS_STRING(_key), *value;
+  int key_len = PyString_GET_SIZE(_key), value_len;
+
+  if (!key || !key_len) {
+    return NULL;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  value = tcbdbget(self->bdb, key, key_len, &value_len);
+  Py_END_ALLOW_THREADS
+
+  if (!value) {
+    raise_tcbdb_error(self->bdb);
+    return NULL;
+  }
+  ret = PyString_FromStringAndSize(value, value_len);
+  free(value);
+  return ret;
+}
+
+int
+PyTCBDB_DelItem(PyTCBDB *self, PyObject *_key) {
+  bool result;
+  char *key = PyString_AS_STRING(_key);
+  int key_len = PyString_GET_SIZE(_key);
+
+  if (!key || !key_len) {
+    return -1;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbout(self->bdb, key, key_len);
+  Py_END_ALLOW_THREADS
+
+  if (!result) {
+    raise_tcbdb_error(self->bdb);
+    return -1;
+  }
+  return 0;
+}
+
+int
+PyTCBDB_SetItem(PyTCBDB *self, PyObject *_key, PyObject *_value) {
+  bool result;
+  char *key = PyString_AS_STRING(_key), *value = PyString_AS_STRING(_value);
+  int key_len = PyString_GET_SIZE(_key), value_len = PyString_GET_SIZE(_value);
+
+  if (!key || !key_len || !value) {
+    return -1;
+  }
+  Py_BEGIN_ALLOW_THREADS
+  result = tcbdbput(self->bdb, key, key_len, value, value_len);
+  Py_END_ALLOW_THREADS
+
+  if (!result) {
+    raise_tcbdb_error(self->bdb);
+    return -1;
+  }
+  return 0;
+}
+
+static int
+PyTCBDB_ass_sub(PyTCBDB *self, PyObject *v, PyObject *w)
+{
+  if (w) {
+    return PyTCBDB_SetItem(self, v, w);
+  } else {
+    return PyTCBDB_DelItem(self, v);
+  }
+}
+
+static PyMethodDef PyTCBDB_methods[] = {
+  {"errmsg", (PyCFunction)PyTCBDB_errmsg,
+   METH_VARARGS | METH_KEYWORDS | METH_CLASS,
+   "Get the message string corresponding to an error code."},
+  {"ecode", (PyCFunction)PyTCBDB_ecode,
+   METH_NOARGS,
+   "Get the last happened error code of a B+ tree database object."},
+  {"setcmpfunc", (PyCFunction)PyTCBDB_setcmpfunc,
+   METH_VARARGS | METH_KEYWORDS,
+   "Set the custom comparison function of a B+ tree database object."},
+  {"setmutex", (PyCFunction)PyTCBDB_setmutex,
+   METH_NOARGS,
+   "Set mutual exclusion control of a B+ tree database object for threading."},
+  {"tune", (PyCFunction)PyTCBDB_tune,
+   METH_VARARGS | METH_KEYWORDS,
+   "Set the tuning parameters of a B+ tree database object."},
+  {"setcache", (PyCFunction)PyTCBDB_setcache,
+   METH_VARARGS | METH_KEYWORDS,
+   "Set the caching parameters of a B+ tree database object."},
+  {"open", (PyCFunction)PyTCBDB_open,
+   METH_VARARGS | METH_KEYWORDS,
+   "Open a database file and connect a B+ tree database object."},
+  {"close", (PyCFunction)PyTCBDB_close,
+   METH_NOARGS,
+   "Close a B+ tree database object."},
+  {"put", (PyCFunction)PyTCBDB_put,
+   METH_VARARGS | METH_KEYWORDS,
+   "Store a record into a B+ tree database object."},
+  {"putkeep", (PyCFunction)PyTCBDB_putkeep,
+   METH_VARARGS | METH_KEYWORDS,
+   "Store a new record into a B+ tree database object."},
+  {"putcat", (PyCFunction)PyTCBDB_putcat,
+   METH_VARARGS | METH_KEYWORDS,
+   "Concatenate a value at the end of the existing record in a B+ tree database object."},
+  {"putdup", (PyCFunction)PyTCBDB_putdup,
+   METH_VARARGS | METH_KEYWORDS,
+   "Store a record into a B+ tree database object with allowing duplication of keys."},
+  {"putlist", (PyCFunction)PyTCBDB_putlist,
+   METH_VARARGS | METH_KEYWORDS,
+   "Store records into a B+ tree database object with allowing duplication of keys."},
+  {"out", (PyCFunction)PyTCBDB_out,
+   METH_VARARGS | METH_KEYWORDS,
+   "Remove a record of a B+ tree database object."},
+  {"outlist", (PyCFunction)PyTCBDB_outlist,
+   METH_VARARGS | METH_KEYWORDS,
+   "Remove records of a B+ tree database object."},
+  {"get", (PyCFunction)PyTCBDB_get,
+   METH_VARARGS | METH_KEYWORDS,
+   "Retrieve a record in a B+ tree database object.\n"
+   "If the key of duplicated records is specified, the value of the first record is selected."},
+  {"getlist", (PyCFunction)PyTCBDB_getlist,
+   METH_VARARGS | METH_KEYWORDS,
+   "Retrieve records in a B+ tree database object."},
+  {"vnum", (PyCFunction)PyTCBDB_vnum,
+   METH_VARARGS | METH_KEYWORDS,
+   "Get the number of records corresponding a key in a B+ tree database object."},
+  {"vsiz", (PyCFunction)PyTCBDB_vsiz,
+   METH_VARARGS | METH_KEYWORDS,
+   "Get the size of the value of a record in a B+ tree database object."},
+  {"sync", (PyCFunction)PyTCBDB_sync,
+   METH_NOARGS,
+   "Synchronize updated contents of a B+ tree database object with the file and the device."},
+  {"optimize", (PyCFunction)PyTCBDB_optimize,
+   METH_VARARGS | METH_KEYWORDS,
+   "Optimize the file of a B+ tree database object."},
+  {"vanish", (PyCFunction)PyTCBDB_vanish,
+   METH_NOARGS,
+   "Remove all records of a hash database object."},
+  {"copy", (PyCFunction)PyTCBDB_copy,
+   METH_VARARGS | METH_KEYWORDS,
+   "Copy the database file of a B+ tree database object."},
+  {"tranbegin", (PyCFunction)PyTCBDB_tranbegin,
+   METH_NOARGS,
+   "Begin the transaction of a B+ tree database object."},
+  {"trancommit", (PyCFunction)PyTCBDB_trancommit,
+   METH_NOARGS,
+   "Commit the transaction of a B+ tree database object."},
+  {"tranabort", (PyCFunction)PyTCBDB_tranabort,
+   METH_NOARGS,
+   "Abort the transaction of a B+ tree database object."},
+  {"path", (PyCFunction)PyTCBDB_path,
+   METH_NOARGS,
+   "Get the file path of a hash database object."},
+  {"rnum", (PyCFunction)PyTCBDB_rnum,
+   METH_NOARGS,
+   "Get the number of records of a B+ tree database object."},
+  {"fsiz", (PyCFunction)PyTCBDB_fsiz,
+   METH_NOARGS,
+   "Get the size of the database file of a B+ tree database object."},
+  {"curnew", (PyCFunction)PyTCBDB_curnew,
+   METH_NOARGS,
+   "Create a cursor object."},
+  {"__contains__", (PyCFunction)PyTCBDB___contains__,
+   METH_O | METH_COEXIST,
+   ""},
+  {"__getitem__", (PyCFunction)PyTCBDB___getitem__,
+   METH_O | METH_COEXIST,
+   ""},
+  {"has_key", (PyCFunction)PyTCBDB___contains__,
+   METH_O,
+   ""},
+  {"keys", (PyCFunction)PyTCBDB_keys,
+   METH_NOARGS,
+   ""},
+  {"items", (PyCFunction)PyTCBDB_items,
+   METH_NOARGS,
+   ""},
+  {"values", (PyCFunction)PyTCBDB_values,
+   METH_NOARGS,
+   ""},
+  /*
+  {"", (PyCFunction)PyTCBDB_,
+   METH_VARARGS | METH_KEYWORDS,
+   ""},
+  */
+  {NULL, NULL, 0, NULL}
+};
+
+/* Hack to implement "key in dict" */
+static PySequenceMethods PyTCBDB_as_sequence = {
+  0,                             /* sq_length */
+  0,                             /* sq_concat */
+  0,                             /* sq_repeat */
+  0,                             /* sq_item */
+  0,                             /* sq_slice */
+  0,                             /* sq_ass_item */
+  0,                             /* sq_ass_slice */
+  (objobjproc)PyTCBDB_Contains,  /* sq_contains */
+  0,                             /* sq_inplace_concat */
+  0,                             /* sq_inplace_repeat */
+};
+
+static PyMappingMethods PyTCBDB_as_mapping = {
+  (inquiry)PyTCBDB_length, /* mp_length */
+  (binaryfunc)PyTCBDB_subscript, /* mp_subscript */
+  (objobjargproc)PyTCBDB_ass_sub, /* mp_ass_subscript */
+};
+
+/* type objects */
+
+static PyTypeObject PyTCBDB_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,                                           /* ob_size */
+  "pytc.BDB",                                  /* tp_name */
+  sizeof(PyTCBDB),                             /* tp_basicsize */
+  0,                                           /* tp_itemsize */
+  (destructor)PyTCBDB_dealloc,                 /* tp_dealloc */
+  0,                                           /* tp_print */
+  0,                                           /* tp_getattr */
+  0,                                           /* tp_setattr */
+  0,                                           /* tp_compare */
+  0,                                           /* tp_repr */
+  0,                                           /* tp_as_number */
+  &PyTCBDB_as_sequence,                        /* tp_as_sequence */
+  &PyTCBDB_as_mapping,                         /* tp_as_mapping */
+  PyTCBDB_Hash,                                /* tp_hash  */
+  0,                                           /* tp_call */
+  0,                                           /* tp_str */
+  0,                                           /* tp_getattro */
+  0,                                           /* tp_setattro */
+  0,                                           /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,    /* tp_flags */
+  "tcbdb",                                     /* tp_doc */
+  0,                                           /* tp_traverse */
+  0,                                           /* tp_clear */
+  0,                                           /* tp_richcompare */
+  0,                                           /* tp_weaklistoffset */
+  PyTCBDB_GetIter,                             /* tp_iter */
+  0,                                           /* tp_iternext */
+  PyTCBDB_methods,                             /* tp_methods */
+  0,                                           /* tp_members */
+  0,                                           /* tp_getset */
+  0,                                           /* tp_base */
+  0,                                           /* tp_dict */
+  0,                                           /* tp_descr_get */
+  0,                                           /* tp_descr_set */
+  0,                                           /* tp_dictoffset */
+  0,                                           /* tp_init */
+  0,                                           /* tp_alloc */
+  PyTCBDB_new,                                 /* tp_new */
+};
+
+#define ADD_INT(module,NAME) PyModule_AddIntConstant(module, #NAME, NAME)
+
+#ifndef PyMODINIT_FUNC
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+initpytc(void)
+{
+  PyObject *m, *mod_dic;
+
+  if (!(m = Py_InitModule3("pytc", NULL,
+                           "TokyoCabinet python bindings."))) {
+    goto exit;
+  }
+
+  /* register classes */
+  if (!(mod_dic = PyModule_GetDict(m))) { goto exit; }
+
+  if (PyType_Ready(&PyTCHDB_Type) < 0) { goto exit; }
+  if (PyType_Ready(&PyTCBDB_Type) < 0) { goto exit; }
+  if (PyType_Ready(&PyBDBCUR_Type) < 0) { goto exit; }
+
+  PyTCError = PyErr_NewException("pytc.Error", NULL, NULL);
+  PyDict_SetItemString(mod_dic, "Error", PyTCError);
+
+  Py_INCREF(&PyTCHDB_Type);
+  PyModule_AddObject(m, "HDB", (PyObject *)&PyTCHDB_Type);
+  Py_INCREF(&PyTCBDB_Type);
+  PyModule_AddObject(m, "BDB", (PyObject *)&PyTCBDB_Type);
+  Py_INCREF(&PyBDBCUR_Type);
+  PyModule_AddObject(m, "BDBCUR", (PyObject *)&PyBDBCUR_Type);
+
+  /* register consts */
+
+  /* error */
+  ADD_INT(m, TCESUCCESS);
+  ADD_INT(m, TCETHREAD);
+  ADD_INT(m, TCEINVALID);
+  ADD_INT(m, TCENOFILE);
+  ADD_INT(m, TCENOPERM);
+  ADD_INT(m, TCEMETA);
+  ADD_INT(m, TCERHEAD);
+  ADD_INT(m, TCEOPEN);
+  ADD_INT(m, TCECLOSE);
+  ADD_INT(m, TCETRUNC);
+  ADD_INT(m, TCESYNC);
+  ADD_INT(m, TCESTAT);
+  ADD_INT(m, TCESEEK);
+  ADD_INT(m, TCEREAD);
+  ADD_INT(m, TCEWRITE);
+  ADD_INT(m, TCEMMAP);
+  ADD_INT(m, TCELOCK);
+  ADD_INT(m, TCEUNLINK);
+  ADD_INT(m, TCERENAME);
+  ADD_INT(m, TCEMKDIR);
+  ADD_INT(m, TCERMDIR);
+  ADD_INT(m, TCEKEEP);
+  ADD_INT(m, TCENOREC);
+  ADD_INT(m, TCEMISC);
+
+  /* HDB */
+  ADD_INT(m, HDBTHASH);
+  ADD_INT(m, HDBTBTREE);
+
+  ADD_INT(m, HDBFOPEN);
+  ADD_INT(m, HDBFFATAL);
+
+  ADD_INT(m, HDBTLARGE);
+  ADD_INT(m, HDBTDEFLATE);
+  ADD_INT(m, HDBTTCBS);
+
+  ADD_INT(m, HDBOREADER);
+  ADD_INT(m, HDBOWRITER);
+  ADD_INT(m, HDBOCREAT);
+  ADD_INT(m, HDBOTRUNC);
+  ADD_INT(m, HDBONOLCK);
+  ADD_INT(m, HDBOLCKNB);
+
+  /* BDB */
+  ADD_INT(m, BDBFOPEN);
+  ADD_INT(m, BDBFFATAL);
+
+  ADD_INT(m, BDBTLARGE);
+  ADD_INT(m, BDBTDEFLATE);
+  ADD_INT(m, BDBTTCBS);
+
+  ADD_INT(m, BDBOREADER);
+  ADD_INT(m, BDBOWRITER);
+  ADD_INT(m, BDBOCREAT);
+  ADD_INT(m, BDBOTRUNC);
+  ADD_INT(m, BDBONOLCK);
+  ADD_INT(m, BDBOLCKNB);
+
+  ADD_INT(m, BDBCPCURRENT);
+  ADD_INT(m, BDBCPBEFORE);
+  ADD_INT(m, BDBCPAFTER);
+
+exit:
+  if (PyErr_Occurred()) {
+    /* PyErr_Print(); */
+    PyErr_SetString(PyExc_ImportError, "pytc: init failed");
+  }
+}
Index: lang/python/pytc/tags/RELEASE-0.1/tests/testHDB.py
===================================================================
--- lang/python/pytc/tags/RELEASE-0.1/tests/testHDB.py (revision 1990)
+++ lang/python/pytc/tags/RELEASE-0.1/tests/testHDB.py (revision 1990)
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+
+import os, sys
+import unittest
+import pytc
+
+DBNAME = 'test.hdb'
+DBNAME2 = 'test.hdb.copy'
+
+class TestHDB(unittest.TestCase):
+  def setUp(self):
+    if os.path.exists(DBNAME):
+      os.remove(DBNAME)
+
+  def testAll(self):
+    # new
+    db = pytc.HDB()
+    # tune
+    db.tune(100, 32, 64, pytc.HDBTTCBS)
+    # open
+    db.open(DBNAME2, pytc.HDBOWRITER | pytc.HDBOCREAT)
+    # copy
+    db.copy(DBNAME)
+    # close
+    db.close
+    os.remove(DBNAME2)
+
+    # open
+    db = pytc.HDB(DBNAME, pytc.HDBOWRITER)
+
+    # put
+    db.put('hamu', 'ju')
+    db.put('moru', 'pui')
+    db.put('kiki', 'nya-')
+
+    # get
+    self.assertEqual(db.get('hamu'), 'ju')
+    # vsiz
+    self.assertEqual(db.vsiz('hamu'), len('ju'))
+
+    # putkeep
+    self.assertRaises(
+      pytc.Error,
+      db.putkeep, 'moru', 'puipui')
+    db.putkeep('moruta', 'puipui')
+    self.assertEqual(db.get('moruta'), 'puipui')
+
+    # putcat
+    db.putcat('kiki', 'nya-nya-')
+    self.assertEqual(db.get('kiki'), 'nya-nya-nya-')
+
+    # putasync
+    db.putasync('gunya', 'darari')
+    # sync
+    db.sync
+    self.assertEqual(db.get('gunya'), 'darari')
+
+    # out
+    db.out('gunya')
+    self.assertRaises(
+      pytc.Error,
+      db.get, 'gunya')
+    # optimize
+    db.optimize(100, 32, 64, pytc.HDBTTCBS)
+
+    # path
+    self.assertEqual(db.path(), DBNAME)
+    # rnum
+    self.assertEqual(db.rnum(), 4)
+    # fsiz
+    self.assertNotEqual(db.fsiz(), 0)
+
+    # iterinit
+    db.iterinit()
+    # iternext
+    self.assertEqual(db.iternext(), 'hamu')
+    db.iterinit()
+    self.assertEqual(db.iternext(), 'hamu')
+
+    # dict like interfaces
+    result = []
+    for key in db:
+      result.append(key)
+    self.assertEqual(sorted(result), ['hamu', 'kiki', 'moru', 'moruta'])
+
+    self.assertEqual(sorted(db.keys()), ['hamu', 'kiki', 'moru', 'moruta'])
+    self.assertEqual(sorted(db.values()), ['ju', 'nya-nya-nya-', 'pui', 'puipui'])
+    self.assertEqual(sorted(db.items()),[
+      ('hamu', 'ju'), ('kiki', 'nya-nya-nya-'), ('moru', 'pui'), ('moruta', 'puipui')])
+
+    db['gunya'] = 'tekito'
+    self.assertEqual(db['gunya'], 'tekito')
+    del db['gunya']
+    self.assertRaises(
+      pytc.Error,
+      db.get, 'gunya')
+
+    self.assert_('hamu' in db)
+    self.assert_('python' not in db)
+
+    # vanish
+    db.vanish()
+    self.assertEqual(db.rnum(), 0)
+
+    # remove
+    os.remove(DBNAME)
+
+if __name__=='__main__': unittest.main()
Index: lang/python/pytc/tags/RELEASE-0.1/tests/testBDB.py
===================================================================
--- lang/python/pytc/tags/RELEASE-0.1/tests/testBDB.py (revision 1990)
+++ lang/python/pytc/tags/RELEASE-0.1/tests/testBDB.py (revision 1990)
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+
+# TODO: setcmpfunc
+
+import os, sys
+import unittest
+import pytc
+
+DBNAME = 'test.bdb'
+DBNAME2 = 'test.bdb.copy'
+
+class TestBDB(unittest.TestCase):
+  def setUp(self):
+    if os.path.exists(DBNAME):
+      os.remove(DBNAME)
+
+  def testAll(self):
+    # new
+    db = pytc.BDB()
+    # tune
+    db.tune(2, 4, 19, 4, 5, pytc.BDBTTCBS)
+    # setcache
+    db.setcache(1, 1)
+    # open
+    db.open(DBNAME2, pytc.BDBOWRITER | pytc.BDBOCREAT)
+    # copy
+    db.copy(DBNAME)
+    # close
+    db.close
+    os.remove(DBNAME2)
+
+    # open
+    db = pytc.BDB(DBNAME, pytc.BDBOWRITER)
+
+    # put
+    db.put('hamu', 'ju')
+    db.put('moru', 'pui')
+    db.put('kiki', 'nya-')
+    db.put('gunya', 'gorori')
+
+    # get
+    self.assertEqual(db.get('hamu'), 'ju')
+    # vsiz
+    self.assertEqual(db.vsiz('hamu'), len('ju'))
+
+    # putkeep
+    self.assertRaises(
+      pytc.Error,
+      db.putkeep, 'moru', 'puipui')
+    db.putkeep('moruta', 'puipui')
+    self.assertEqual(db.get('moruta'), 'puipui')
+
+    # putcat
+    db.putcat('kiki', 'nya-nya-')
+    self.assertEqual(db.get('kiki'), 'nya-nya-nya-')
+
+    # putdup
+    db.putdup('kiki', 'unya-n')
+    # getlist
+    self.assertEqual(db.getlist('kiki'), ['nya-nya-nya-', 'unya-n'])
+    # vnum
+    self.assertEqual(db.vnum('kiki'), 2)
+
+    # out
+    db.out('gunya')
+    self.assertRaises(
+      pytc.Error,
+      db.get, 'gunya')
+
+    # putlist
+    db.putlist('gunya', ['gorori', 'harahetta', 'nikutabetai'])
+    self.assertEqual(db.getlist('gunya'), ['gorori', 'harahetta', 'nikutabetai'])
+    # outlist
+    db.outlist('gunya')
+    self.assertEqual(db.vnum('gunya'), 0)
+
+    # optimize
+    db.optimize(2, 4, 19, 4, 5, pytc.BDBTTCBS)
+
+    # path
+    self.assertEqual(db.path(), DBNAME)
+    # rnum
+    self.assertEqual(db.rnum(), 5)
+    # fsiz
+    self.assertNotEqual(db.fsiz(), 0)
+
+    # dict like interfaces
+    result = []
+    for key in db:
+      result.append(key)
+    self.assertEqual(sorted(result), ['hamu', 'kiki', 'kiki', 'moru', 'moruta'])
+
+    self.assertEqual(sorted(db.keys()),
+      ['hamu', 'kiki', 'kiki', 'moru', 'moruta'])
+    self.assertEqual(sorted(db.values()),
+      ['ju', 'nya-nya-nya-', 'pui', 'puipui', 'unya-n'])
+    self.assertEqual(sorted(db.items()),[
+      ('hamu', 'ju'), ('kiki', 'nya-nya-nya-'), ('kiki', 'unya-n'),
+      ('moru', 'pui'), ('moruta', 'puipui')])
+
+    db['gunya'] = 'tekito'
+    self.assertEqual(db['gunya'], 'tekito')
+    del db['gunya']
+    self.assertRaises(
+      pytc.Error,
+      db.get, 'gunya')
+
+    self.assert_('hamu' in db)
+    self.assert_('python' not in db)
+
+    # curnew
+    cur = db.curnew()
+    # BDBCUR.first
+    cur.first()
+    # BDBCUR.key
+    self.assertEqual(cur.key(), 'hamu')
+    # BDBCUR.val
+    self.assertEqual(cur.val(), 'ju')
+    # BDBCUR.rec
+    self.assertEqual(cur.rec(), ('hamu', 'ju'))
+    # BDBCUR.next
+    cur.next()
+    self.assertEqual(cur.rec(), ('kiki', 'nya-nya-nya-'))
+    # BDBCUR.put
+    cur.put('fungofungo', pytc.BDBCPCURRENT)
+    self.assertEqual(cur.rec(), ('kiki', 'fungofungo'))
+    # BDBCUR.out
+    cur.out()
+    self.assertEqual(db.vnum('kiki'), 1)
+    # BDBCUR.prev
+    cur.prev()
+    self.assertEqual(cur.rec(), ('hamu', 'ju'))
+    # BDBCUR.jump
+    cur.jump('moru')
+    self.assertEqual(cur.rec(), ('moru', 'pui'))
+    # BDBCUR.last
+    cur.last()
+    self.assertEqual(cur.rec(), ('moruta', 'puipui'))
+
+    # tranbegin
+    db.tranbegin()
+    db.put('moru', 'pupupu')
+    self.assertEqual(db.get('moru'), 'pupupu')
+    # tranabort
+    db.tranabort()
+    self.assertEqual(db.get('moru'), 'pui')
+
+    db.tranbegin()
+    db.put('moru', 'pupupu')
+    # trancommit
+    db.trancommit()
+    self.assertEqual(db.get('moru'), 'pupupu')
+
+    # vanish
+    db.vanish()
+    self.assertRaises(
+      pytc.Error,
+      db.rnum)
+
+    os.remove(DBNAME)
+
+  def testCmpFunc(self):
+    db = pytc.BDB()
+    # setcmpfunc
+    db.setcmpfunc(lambda x,y:len(x) == len(y), 1)
+    db.open(DBNAME, pytc.BDBOWRITER | pytc.BDBOCREAT)
+
+    db['kiki'] = 'nya-'
+    db['moru'] = 'pui'
+    self.assertEqual(db.get('kiki'), 'pui')
+
+    os.remove(DBNAME)
+
+if __name__=='__main__': unittest.main()
Index: lang/python/pytc/tags/RELEASE-0.1/ChangeLog
===================================================================
--- lang/python/pytc/tags/RELEASE-0.1/ChangeLog (revision 1993)
+++ lang/python/pytc/tags/RELEASE-0.1/ChangeLog (revision 1993)
@@ -0,0 +1,5 @@
+2007-11-15  Tasuku SUENAGA  <gunyarakun@sourceforge.jp>
+
+	* first version
+	* hdb interface (thanks much for Shin ADACHI)
+	* bdb interface
Index: lang/python/pytc/tags/RELEASE-0.1/setup.py
===================================================================
--- lang/python/pytc/tags/RELEASE-0.1/setup.py (revision 1993)
+++ lang/python/pytc/tags/RELEASE-0.1/setup.py (revision 1993)
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import sys
+from distutils.core import setup, Extension
+
+if sys.version_info < (2, 3):
+  raise Error, "Python 2.3 or later is required"
+
+ext = Extension('pytc',
+                libraries = ['tokyocabinet', 'z', 'pthread', 'm', 'c'],
+                sources = ['pytc.c'])
+
+setup(name = 'pytc',
+      version = '0.1',
+      description = 'Tokyo Cabinet Python bindings',
+      long_description = '''
+        Tokyo Cabinet Python bindings
+      ''',
+      license='BSD',
+      author = 'Tasuku SUENAGA',
+      author_email = 'gunyarakun@sourceforge.jp',
+      ext_modules = [ext]
+     )
