Exemple #1
0
void search_and_delete_dbdirent(DDSTAT *ddstat)
{
    BDBCUR *cur;
    char *key, *value;
    int size;
    unsigned long long dir;
    unsigned long long ent;

    /* traverse records */
    cur = tcbdbcurnew(dbdirent);
    tcbdbcurfirst(cur);
    while ((key = tcbdbcurkey2(cur)) != NULL) {
        memcpy(&dir, key, sizeof(dir));
        value = tcbdbcurval(cur, &size);;
        if (value) {
            memcpy(&ent, value, sizeof(ent));
            if ( ent == ddstat->stbuf.st_ino) {
               printf("Delete %llu:%llu\n",dir,ent);
               btdelete_curkey(dbdirent, &dir,
                             sizeof(unsigned long long), &ent,
                             sizeof(unsigned long long)); 
            }
            free(value);
        }
        free(key);
        tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);

}
Exemple #2
0
int check_directory_has_parent(unsigned long long inode)
{
    BDBCUR *cur;
    char *key, *value;
    int size;
    unsigned long long dir;
    unsigned long long ent;
    int hasparent=0;

    /* traverse records */
    cur = tcbdbcurnew(dbdirent);
    tcbdbcurfirst(cur);
    while ((key = tcbdbcurkey2(cur)) != NULL) {
        memcpy(&dir, key, sizeof(dir));
        value = tcbdbcurval(cur, &size);;
        if (value) {
            memcpy(&ent, value, sizeof(ent));
            if ( inode == ent ) hasparent=1;
            free(value);
        }
        free(key);
        tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);
    return(hasparent);
}
Exemple #3
0
static PyObject *
list2(PyObject *self,PyObject *args){
    const char *dbname;
    if (!PyArg_ParseTuple(args, "s", &dbname))
        return NULL;
    TCADB *adb = openadb(dbname);
    PyObject* pList = PyList_New(0);
    if(!tcadbiterinit(adb)){
        return NULL;
    }
    BDBCUR *cur = tcbdbcurnew(adb->bdb);
    TCXSTR *key = tcxstrnew();
    TCXSTR *val = tcxstrnew();
    if(!tcbdbcurfirst(cur) && tcbdbecode(adb->bdb) != TCENOREC){
        return NULL;
    }
    while(tcbdbcurrec(cur, key, val)){
        PyObject* pList1 = PyList_New(0);
        PyList_Append(pList1,Py_BuildValue("s",tcxstrptr(key)));
        PyList_Append(pList1,Py_BuildValue("s",tcxstrptr(val)));
        PyList_Append(pList,pList1);
        if(!tcbdbcurnext(cur) && tcbdbecode(adb->bdb) != TCENOREC){
            return NULL;
        }
    }
    tcxstrdel(val);
    tcxstrdel(key);
    tcbdbcurdel(cur);
    closeadb(adb);
    return Py_BuildValue("O",pList);
}
Exemple #4
0
IoObject *IoTokyoCabinetCursor_next(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*doc TokyoCabinetCursor next
	Move cursor to next record. Returns true if there is another key, 
	or false if there is no next record.
	*/

	IOASSERT(TokyoCabinetCursor(self), "invalid TokyoCabinetCursor");
	return IOBOOL(self, tcbdbcurnext(TokyoCabinetCursor(self)));
}
IoObject *IoTokyoCabinetPrefixCursor_next(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*doc TokyoCabinetPrefixCursor next
	Move cursor to next record. Returns true if there is another key, 
	or false if there is no next record.
	*/

	IoSeq *prefix = IoObject_getSlot_(self, IOSYMBOL("prefix"));
	IOASSERT(ISSEQ(prefix), "prefix must be a sequence");
	IOASSERT(TokyoCabinetPrefixCursor(self), "invalid TokyoCabinetPrefixCursor");
	tcbdbcurnext(TokyoCabinetPrefixCursor(self));
	return IOBOOL(self, IoTokyoCabinetPrefixCursor_keyBeginsWithPrefix_(self, prefix));
}
Exemple #6
0
ex_t db_foreach(void *vhandle, db_foreach_t hook, void *userdata)
{
    int ret = 0;

    dbh_t *handle = vhandle;
    TCBDB *dbp = handle->dbp;
    BDBCUR *cursor;

    dbv_t dbv_key, dbv_data;
    int ksiz, dsiz;
    char *key, *data;

    cursor = tcbdbcurnew(dbp);
    ret = tcbdbcurfirst(cursor);
    if (ret) {
	while ((key = tcbdbcurkey(cursor, &ksiz))) {
	    data = tcbdbcurval(cursor, &dsiz);
	    if (data) {
		/* switch to "dbv_t *" variables */
		dbv_key.leng = ksiz;
		dbv_key.data = xmalloc(dbv_key.leng+1);
		memcpy(dbv_key.data, key, ksiz);
		((char *)dbv_key.data)[dbv_key.leng] = '\0';

		dbv_data.data = data;
		dbv_data.leng = dsiz;		/* read count */

		/* call user function */
		ret = hook(&dbv_key, &dbv_data, userdata);

		xfree(dbv_key.data);

		if (ret != 0)
		    break;
		free(data); /* not xfree() as allocated by dpget() */
	    }
	    free(key); /* not xfree() as allocated by dpiternext() */

	    tcbdbcurnext(cursor);
	}
    } else {
	print_error(__FILE__, __LINE__, "(tc) tcbdbcurfirst err: %d, %s",
		    tcbdbecode(dbp), tcbdberrmsg(tcbdbecode(dbp)));
	exit(EX_ERROR);
    }

    tcbdbcurdel(cursor);
    return EX_OK;
}
IoObject *IoTokyoCabinetPrefixCursor_first(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*doc TokyoCabinetPrefixCursor first
	Move cursor to first record. Returns self
	*/

	IoSeq *prefix = IoObject_getSlot_(self, IOSYMBOL("prefix"));
	IOASSERT(ISSEQ(prefix), "prefix must be a sequence");
	IOASSERT(TokyoCabinetPrefixCursor(self), "invalid TokyoCabinetPrefixCursor");
	
	tcbdbcurjump(TokyoCabinetPrefixCursor(self), (const void *)IoSeq_rawBytes(prefix), (int)IoSeq_rawSizeInBytes(prefix));
	
	if(!IoTokyoCabinetPrefixCursor_keyBeginsWithPrefix_(self, prefix))
	{
		tcbdbcurnext(TokyoCabinetPrefixCursor(self));
	}
	
	return IOBOOL(self, IoTokyoCabinetPrefixCursor_keyBeginsWithPrefix_(self, prefix));
}
Exemple #8
0
/* iterators */
dict_si_t
dict_sym_iter(dict_t d)
{
/* uses static state */
	static BDBCUR *c;
	dict_si_t res;
	const void *vp;
	int z[1U];

	if (UNLIKELY(c == NULL)) {
		c = tcbdbcurnew(d);
		tcbdbcurjump(c, SYM_SPACE, sizeof(SYM_SPACE));
	}

	if (UNLIKELY((vp = tcbdbcurval3(c, z)) == NULL)) {
		goto null;
	} else if (*z != sizeof(int)) {
		goto null;
	}
	/* otherwise fill res */
	res.sid = *(const int*)vp;

	if (UNLIKELY((vp = tcbdbcurkey3(c, z)) == NULL)) {
		goto null;
	}
	/* or fill */
	res.sym = vp;
	/* also iterate to the next thing */
	tcbdbcurnext(c);
	return res;

null:
	if (LIKELY(c != NULL)) {
		tcbdbcurdel(c);
	}
	c = NULL;
	return (dict_si_t){};
}
Exemple #9
0
int main() 
{
	void *bdb1, *bdb2;
	char *key1, *key2, *val1, *val2;
	char *key, *value;
	BDBCUR *cur1, *cur2;
	char str_key[FRML_ID_CMP_LEN + 1];
	char str_val[FRML_ID_CMP_LEN + 2];
	unsigned int cnt, dup, steps;
	int cmp_res;
	FILE *f1, *f2;
		
	bdb1 = bdb_init2("./test1.bdb", &frml_id_cmp);	
	bdb2 = bdb_init2("./test2.bdb", &frml_id_cmp);	
	if (bdb1 == NULL || bdb2 == NULL)
		return;

#if 0
	srand(92019);

	printf("put db1...\n");
	cnt = 0;
	while (cnt < 10000) {
		snprintf(str_key, FRML_ID_CMP_LEN + 1, 
		         "%0*d\n", FRML_ID_CMP_LEN, rand());
		sprintf(str_val, "v%s", str_key);
		bdb_put2(bdb1, str_key, str_val);
		cnt ++;
	}
	
	printf("put db2...\n");
	cnt = 0;
	while (cnt < 1000090) {
		snprintf(str_key, FRML_ID_CMP_LEN + 1, 
		         "%0*d\n", FRML_ID_CMP_LEN, rand());
		sprintf(str_val, "v%s", str_key);
		bdb_put2(bdb2, str_key, str_val);
		cnt ++;
	}
	
	/*
	f1 = fopen("log1.txt", "w");
	f2 = fopen("log2.txt", "w");

	cur1 = tcbdbcurnew(bdb1);
	tcbdbcurfirst(cur1);
	while ((key = tcbdbcurkey2(cur1)) != NULL) {
		value = tcbdbcurval2(cur1);
		if (value) {
			fprintf(f1, "%s\n", key);
			free(value);
		}
		free(key);
		tcbdbcurnext(cur1);
	}
	tcbdbcurdel(cur1);

	cur2 = tcbdbcurnew(bdb2);
	tcbdbcurfirst(cur2);
	while ((key = tcbdbcurkey2(cur2)) != NULL) {
		value = tcbdbcurval2(cur2);
		if (value) {
			fprintf(f2, "%s\n", key);
			free(value);
		}
		free(key);
		tcbdbcurnext(cur2);
	}
	tcbdbcurdel(cur2);

	fclose(f1);
	fclose(f2);
	*/

#else
#define NUM 10

	printf("go one-by-one 100 times...\n");
	for (cnt = 0, dup = 0, steps = 0; cnt < NUM; cnt++) {
		cur1 = tcbdbcurnew(bdb1);
		tcbdbcurfirst(cur1);
		while ((key = tcbdbcurkey2(cur1)) != NULL) {
			value = tcbdbcurval2(cur1);
			if (value) {
				//printf("one-by-one: %s\n", key);
				if (bdb_get2(bdb2, key)) {
					//printf("%s duplicated.\n", key);
					dup ++;
				}
				free(value);
			}
			free(key);
			tcbdbcurnext(cur1);
			steps ++;
		}
		tcbdbcurdel(cur1);
	}
	printf("%d duplicates in %d steps.\n", dup, steps);
	
	printf("go jumping 100 times...\n");
	for (dup = 0, cnt = 0, steps = 0; cnt < NUM; cnt++) {
		cur1 = tcbdbcurnew(bdb1);
		cur2 = tcbdbcurnew(bdb2);
		tcbdbcurfirst(cur1);
		tcbdbcurfirst(cur2);
		do {
			key1 = tcbdbcurkey2(cur1);
			key2 = tcbdbcurkey2(cur2);
			if (key1 == NULL || key2 == NULL) {
				if (key1)
					free(key1);
				else 
					free(key2);
				break;
			}

			cmp_res = frml_id_cmp_(key1, key2);
			//printf("jumping %s and %s\n", key1, key2);
			steps ++;
			if (cmp_res == 0) {
				val1 = tcbdbcurval2(cur1);
				val2 = tcbdbcurval2(cur2);
				dup ++;

				tcbdbcurnext(cur1);
				free(val1);
				free(val2);
			} else if (cmp_res > 0) /* key1 is larger */ {
				tcbdbcurjump2(cur2, key1);
			} else /* key2 is larger */ {
				tcbdbcurjump2(cur1, key2);
			}

			free(key1);
			free(key2);
		} while (1);
	}
	printf("%d duplicates in %d steps.\n", dup, steps);

#endif

	bdb_release(bdb1);	
	bdb_release(bdb2);	
}
Exemple #10
0
void check_directory_structure()
{
    BDBCUR *cur;
    char *key, *value;
    int size;
    int hasparent;
    unsigned long long dir;
    unsigned long long lastdir=0;
    unsigned long long ent;
    int multi=0;
    DBT *data;
    

    /* traverse records */
recheck:
    cur = tcbdbcurnew(dbdirent);
    tcbdbcurfirst(cur);
    while ((key = tcbdbcurkey2(cur)) != NULL) {
        show_progress(); 
        memcpy(&dir, key, sizeof(dir));
        if ( lastdir == dir ) {
           if ( multi == 2 ) {
              tcbdbcurnext(cur);
              continue;
           } else {
              multi=1;
           }
        } else multi=0;
        value = tcbdbcurval(cur, &size);;
        if (value) {
            memcpy(&ent, value, sizeof(ent));
            data = search_dbdata(dbp, &ent, sizeof(unsigned long long));
            if (NULL == data) {
                printf("%ccheck_directory_structure : delete inode %llu present in dbdirent but not found in dbp.\n", BACKSPACE,ent);
                btdelete_curkey(dbdirent, &dir, sizeof(unsigned long long),
                                &ent, sizeof(unsigned long long));
                free(value);
                free(key);
                tcbdbcurdel(cur);
                goto recheck;
            } 
            DBTfree(data);
            free(value);
            if ( multi == 1 ) {
               multi++;
               if ( dir > 1 ) { // Skip the root directory.
               // This is a directory, check if it is linked.
                  hasparent=check_directory_has_parent(dir);
                  if ( hasparent == 0 ) {
                     printf("Directory with inode number %llu is orphaned, relink to lost+found\n", dir);
                     btbin_write_dup(dbdirent, &lafinode, sizeof(unsigned long long),
                            &dir, sizeof(unsigned long long));
                  }
               }
            }
        }
        lastdir=dir;
        free(key);
        tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);
}
Exemple #11
0
/* perform list command */
static int proclist(const char *path, TCCMP cmp, int omode, int max, bool pv, bool px, bool bk,
        const char *jstr, const char *bstr, const char *estr, const char *fmstr) {
    TCBDB *bdb = tcbdbnew();
    if (!INVALIDHANDLE(g_dbgfd)) tcbdbsetdbgfd(bdb, g_dbgfd);
    if (cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
    if (!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb);
    if (!tcbdbopen(bdb, path, BDBOREADER | omode)) {
        printerr(bdb);
        tcbdbdel(bdb);
        return 1;
    }
    bool err = false;
    if (bstr || fmstr) {
        TCLIST *keys = fmstr ? tcbdbfwmkeys2(bdb, fmstr, max) :
                tcbdbrange(bdb, bstr, strlen(bstr), true, estr, strlen(estr), true, max);
        int cnt = 0;
        for (int i = 0; i < tclistnum(keys); i++) {
            int ksiz;
            const char *kbuf = tclistval(keys, i, &ksiz);
            if (pv) {
                TCLIST *vals = tcbdbget4(bdb, kbuf, ksiz);
                if (vals) {
                    for (int j = 0; j < tclistnum(vals); j++) {
                        int vsiz;
                        const char *vbuf = tclistval(vals, j, &vsiz);
                        printdata(kbuf, ksiz, px);
                        putchar('\t');
                        printdata(vbuf, vsiz, px);
                        putchar('\n');
                        if (max >= 0 && ++cnt >= max) break;
                    }
                    tclistdel(vals);
                }
            } else {
                int num = tcbdbvnum(bdb, kbuf, ksiz);
                for (int j = 0; j < num; j++) {
                    printdata(kbuf, ksiz, px);
                    putchar('\n');
                    if (max >= 0 && ++cnt >= max) break;
                }
            }
            if (max >= 0 && cnt >= max) break;
        }
        tclistdel(keys);
    } else {
        BDBCUR *cur = tcbdbcurnew(bdb);
        if (bk) {
            if (jstr) {
                if (!tcbdbcurjumpback(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC) {
                    printerr(bdb);
                    err = true;
                }
            } else {
                if (!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC) {
                    printerr(bdb);
                    err = true;
                }
            }
        } else {
            if (jstr) {
                if (!tcbdbcurjump(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC) {
                    printerr(bdb);
                    err = true;
                }
            } else {
                if (!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC) {
                    printerr(bdb);
                    err = true;
                }
            }
        }
        TCXSTR *key = tcxstrnew();
        TCXSTR *val = tcxstrnew();
        int cnt = 0;
        while (tcbdbcurrec(cur, key, val)) {
            printdata(tcxstrptr(key), tcxstrsize(key), px);
            if (pv) {
                putchar('\t');
                printdata(tcxstrptr(val), tcxstrsize(val), px);
            }
            putchar('\n');
            if (bk) {
                if (!tcbdbcurprev(cur) && tcbdbecode(bdb) != TCENOREC) {
                    printerr(bdb);
                    err = true;
                }
            } else {
                if (!tcbdbcurnext(cur) && tcbdbecode(bdb) != TCENOREC) {
                    printerr(bdb);
                    err = true;
                }
            }
            if (max >= 0 && ++cnt >= max) break;
        }
        tcxstrdel(val);
        tcxstrdel(key);
        tcbdbcurdel(cur);
    }
    if (!tcbdbclose(bdb)) {
        if (!err) printerr(bdb);
        err = true;
    }
    tcbdbdel(bdb);
    return err ? 1 : 0;
}
Exemple #12
0
static int control(ErlDrvData handle, unsigned int command, char* buf, int count, char** res, int res_size) {
  tcbdb_drv_t *driver = (tcbdb_drv_t*)handle;
  TCBDB *bdb = driver->bdb;

  int index = 1, tp, sz, version;
  char key[MAX_KEY_SIZE], value[MAX_VALUE_SIZE];
  long key_size, value_size;
  long long long_tmp;
  bool rs;
  const char *value_tmp = NULL;

  ei_x_buff x;
  ei_x_new_with_version(&x);
  if (bdb == NULL && command != OPEN)
    return ei_error(&x, "database_not_opened", res, res_size);

  ei_decode_version(buf, &index, &version);

  switch (command) {
    case OPEN:
      // open(Filepath::string())
      if (bdb == NULL) {
        ei_get_type(buf, &index, &tp, &sz);
        if (tp != ERL_STRING_EXT)
          return ei_error(&x, "invalid_argument", res, res_size);

        TCBDB *bdb = tcbdbnew();
        tcbdbsetmutex(bdb);
        tcbdbsetcache(bdb, 104800, 51200);
        tcbdbsetxmsiz(bdb, 1048576);
        tcbdbtune(bdb, 0, 0, 0, 7, -1, BDBTLARGE);

        char *file = driver_alloc(sz + 1);
        ei_decode_string(buf, &index, file);
        rs = tcbdbopen(bdb, file, BDBOWRITER | BDBOCREAT);
        driver_free(file);
        if (!rs)
          return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size);
        driver->bdb = bdb;
        ei_x_encode_atom(&x, "ok");
      } else
        return ei_error(&x, "database already opened", res, res_size);
      break;

    case CLOSE:
      tcbdbclose(bdb);
      tcbdbdel(bdb);
      driver->bdb = NULL;
      ei_x_encode_atom(&x, "ok");
      break;

    case PUT:
    case PUTDUP:
    case PUTCAT:
    case PUTKEEP:
      // put({Key::binary(), Value::binary()})
      ei_get_type(buf, &index, &tp, &sz);
      if (tp != ERL_SMALL_TUPLE_EXT || sz != 2)
        return ei_error(&x, "invalid_argument", res, res_size);
      ei_decode_tuple_header(buf, &index, &sz);

      ei_get_type(buf, &index, &tp, &sz);
      if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE)
        return ei_error(&x, "invalid_argument", res, res_size);
      ei_decode_binary(buf, &index, &key[0], &key_size);
      ei_get_type(buf, &index, &tp, &sz);
      if (tp != ERL_BINARY_EXT)
        return ei_error(&x, "invalid_argument", res, res_size);
      if (sz <= MAX_VALUE_SIZE) {
        ei_decode_binary(buf, &index, &value[0], &value_size);
        switch (command) {
          case PUT: rs = tcbdbput(bdb, &key[0], key_size, &value[0], value_size); break;
          case PUTDUP: rs = tcbdbputdup(bdb, &key[0], key_size, &value[0], value_size); break;
          case PUTCAT: rs = tcbdbputcat(bdb, &key[0], key_size, &value[0], value_size); break;
          default: rs = tcbdbputkeep(bdb, &key[0], key_size, &value[0], value_size);
        }
      } else {
        void *p = driver_alloc(sz);
        if (p) {
          ei_decode_binary(buf, &index, p, &value_size);
          switch (command) {
            case PUT: rs = tcbdbput(bdb, &key[0], key_size, p, value_size); break;
            case PUTDUP: rs = tcbdbputdup(bdb, &key[0], key_size, p, value_size); break;
            case PUTCAT: rs = tcbdbputcat(bdb, &key[0], key_size, p, value_size); break;
            default: rs = tcbdbputkeep(bdb, &key[0], key_size, p, value_size);
          }
          driver_free(p);
        } else
          return ei_error(&x, "too long value", res, res_size);
      };
      if (!rs)
        return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size);
      ei_x_encode_atom(&x, "ok");
      break;

    case GET:
      // get(Key::binary()) -> {ok, Value} | {error, not_found}
      ei_get_type(buf, &index, &tp, &sz);
      if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE)
        return ei_error(&x, "invalid_argument", res, res_size);
      ei_decode_binary(buf, &index, &key[0], &key_size);

      value_tmp = tcbdbget3(bdb, &key[0], key_size, &sz);
      ei_x_encode_tuple_header(&x, 2);
      if (value_tmp != NULL) {
        ei_x_encode_atom(&x, "ok");
        ei_x_encode_binary(&x, value_tmp, sz);
      } else {
        ei_x_encode_atom(&x, "error");
        ei_x_encode_atom(&x, "not_found");
      }
      break;

    case GETDUP:
      // get(Key::binary()) -> {ok, Values}
      ei_get_type(buf, &index, &tp, &sz);
      if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE)
        return ei_error(&x, "invalid_argument", res, res_size);
      ei_decode_binary(buf, &index, &key[0], &key_size);

      TCLIST *vals = tcbdbget4(bdb, key, key_size);
      if (vals) {
        ei_x_encode_tuple_header(&x, 2);
        ei_x_encode_atom(&x, "ok");
        int j;
        for (j=0; j<tclistnum(vals); j++) {
          value_tmp = tclistval(vals, j, &sz);
          ei_x_encode_list_header(&x, 1);
          ei_x_encode_binary(&x, value_tmp, sz);
        }
        tclistdel(vals);
        ei_x_encode_empty_list(&x);
      } else {
        ei_x_encode_tuple_header(&x, 2);
        ei_x_encode_atom(&x, "ok");
        ei_x_encode_empty_list(&x);
      }
      break;

    case REMOVE:
      // remove(Keys::list())
      // remove(Key::binary())
      // remove({Key::binary(), Value::binary()})
      ei_get_type(buf, &index, &tp, &sz);
      if (tp == ERL_LIST_EXT) {
        int count, j;
        ei_decode_list_header(buf, &index, &count);
        for (j=0; j<count; j++) {
          ei_get_type(buf, &index, &tp, &sz);
          if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE)
            return ei_error(&x, "invalid_argument", res, res_size);
          ei_decode_binary(buf, &index, &key[0], &key_size);
          if (!tcbdbout3(bdb, &key[0], key_size))
            return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size);
        }
        ei_x_encode_atom(&x, "ok");
      } else if (tp == ERL_BINARY_EXT && sz <= MAX_KEY_SIZE) {
        ei_decode_binary(buf, &index, &key[0], &key_size);
        tcbdbout3(bdb, &key[0], key_size);
        ei_x_encode_atom(&x, "ok");
      } else if (tp == ERL_SMALL_TUPLE_EXT && sz == 2) {
        ei_decode_tuple_header(buf, &index, &sz);
        // get key
        ei_get_type(buf, &index, &tp, &sz);
        if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE)
          return ei_error(&x, "invalid_argument", res, res_size);
        ei_decode_binary(buf, &index, &key[0], &key_size);
        // get value
        ei_get_type(buf, &index, &tp, &sz);
        if (tp != ERL_BINARY_EXT || sz > MAX_VALUE_SIZE)
          return ei_error(&x, "invalid_argument", res, res_size);
        ei_decode_binary(buf, &index, &value[0], &value_size);
        // remove by key&value
        BDBCUR *cur = tcbdbcurnew(bdb);
        if (!tcbdbcurjump(cur, &key[0], key_size))
          return ei_error(&x, "record_not_found", res, res_size);
        
        bool removed = false, not_found = false;
        while (!removed && !not_found) {
          int cur_key_size, cur_val_size;
          const void *curkey = tcbdbcurkey3(cur, &cur_key_size);
          if (cur_key_size == key_size && memcmp(curkey, key, key_size) == 0) {
            const void *curval = tcbdbcurval3(cur, &cur_val_size);
            if (cur_val_size == value_size && memcmp(curval, value, value_size) == 0) {
              tcbdbcurout(cur);
              removed = true;
            } else
              if (!tcbdbcurnext(cur))
                not_found = true;
          } else not_found = true;
        }
        if (not_found) ei_x_encode_atom(&x, "not_found");
        else ei_x_encode_atom(&x, "ok");
        
      } else
        return ei_error(&x, "invalid_argument", res, res_size);
      break;

    case RANGE:
      /*
       * range({Prefix::binary(), limit:integer()})
       * range({StartKey::binary(), BeginInclusion::boolean(), EndKey::binary(), EndInclusion::binary(), limit:integer()})
       */
      ei_get_type(buf, &index, &tp, &sz);
      if (tp != ERL_SMALL_TUPLE_EXT || sz < 2)
        return ei_error(&x, "invalid_argument", res, res_size);
      ei_decode_tuple_header(buf, &index, &sz);

      ei_get_type(buf, &index, &tp, &sz);
      if (tp == ERL_BINARY_EXT && sz <= MAX_KEY_SIZE) {
        char keys[MAX_KEY_SIZE], keyf[MAX_KEY_SIZE];
        long keys_size, keyf_size;
        int keys_inc, keyf_inc;
        long max = -1;
        TCLIST *range;

        ei_decode_binary(buf, &index, &keys[0], &keys_size);
        ei_get_type(buf, &index, &tp, &sz);
        if (tp == ERL_ATOM_EXT) {
          // range
          ei_decode_boolean(buf, &index, &keys_inc);
          ei_get_type(buf, &index, &tp, &sz);
          if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE)
            return ei_error(&x, "invalid_argument", res, res_size);
          ei_decode_binary(buf, &index, &keyf[0], &keyf_size);
          ei_get_type(buf, &index, &tp, &sz);
          if (tp != ERL_ATOM_EXT)
            return ei_error(&x, "invalid_argument", res, res_size);
          ei_decode_boolean(buf, &index, &keyf_inc);

          ei_get_type(buf, &index, &tp, &sz);
          if (tp != ERL_INTEGER_EXT)
            return ei_error(&x, "invalid_argument", res, res_size);
          ei_decode_long(buf, &index, &max);

          range = tcbdbrange(bdb, &keys[0], keys_size, keys_inc == 1, &keyf[0], keyf_size, keyf_inc == 1, max);
        } else if (tp == ERL_INTEGER_EXT) {
          // prefix
          ei_get_type(buf, &index, &tp, &sz);
          if (tp != ERL_INTEGER_EXT)
            return ei_error(&x, "invalid_argument", res, res_size);
          ei_decode_long(buf, &index, &max);

          range = tcbdbfwmkeys(bdb, &keys[0], keys_size, max);
        } else
          return ei_error(&x, "invalid_argument", res, res_size);

        const char *key;
        int key_size, value_size;
        int idx, cnt = 0, rcount = tclistnum(range);

        ei_x_encode_tuple_header(&x, 2);
        ei_x_encode_atom(&x, "ok");

        BDBCUR *cur = tcbdbcurnew(bdb);
        for (idx=0; idx<rcount; idx++) {
          key = tclistval(range, idx, &key_size);
          TCLIST *vals = tcbdbget4(bdb, key, key_size);
          if (vals) {
            int j;
            for (j=0; j<tclistnum(vals); j++) {
              ei_x_encode_list_header(&x, 1);
              value_tmp = tclistval(vals, j, &value_size);
              ei_x_encode_binary(&x, value_tmp, value_size);
              if (max >= 0 && ++cnt >= max) break;
            }
            tclistdel(vals);
          }
          idx++;
        }
        tcbdbcurdel(cur);
        tclistdel(range);
        ei_x_encode_empty_list(&x);

      } else
        return ei_error(&x, "invalid_argument", res, res_size);
      break;

    case SYNC:
      // sync()
      if (!tcbdbsync(bdb))
        return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size);
      ei_x_encode_atom(&x, "ok");
      break;

    case INFO:
      // info()
      ei_x_encode_tuple_header(&x, 3);
      ei_x_encode_atom(&x, "ok");
      long_tmp = tcbdbrnum(bdb);
      ei_x_encode_longlong(&x, long_tmp);
      long_tmp = tcbdbfsiz(bdb);
      ei_x_encode_longlong(&x, long_tmp);
      break;

    case ITERATE:
      // Read(none) -> {ok, Key} | {error, not_found}
      // Read(Key::binary()) -> {ok, Key} | {error, not_found}
      ei_get_type(buf, &index, &tp, &sz);
      BDBCUR *cur = tcbdbcurnew(bdb);
      if (tp == ERL_BINARY_EXT && sz <= MAX_KEY_SIZE) {
        ei_decode_binary(buf, &index, &key[0], &key_size);
        rs = tcbdbcurjump(cur, &key[0], key_size) && tcbdbcurnext(cur);
      } else
        rs = tcbdbcurfirst(cur);
      if (rs) {
        int key_size;
        const char *key = tcbdbcurkey3(cur, &key_size);

        ei_x_encode_tuple_header(&x, 2);
        ei_x_encode_atom(&x, "ok");
        ei_x_encode_binary(&x, key, key_size);
        tcbdbcurdel(cur);
      } else {
        tcbdbcurdel(cur);
        return ei_error(&x, "not_found", res, res_size);
      }
      break;

    case VANISH:
      // vanish() -> ok
      if (!tcbdbvanish(bdb))
        return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size);
      ei_x_encode_atom(&x, "ok");
      break;

    case BACKUP:
      // backup(path::string()) -> ok | {error, Reason}
      ei_get_type(buf, &index, &tp, &sz);
      if (tp == ERL_STRING_EXT) {
        char *file = driver_alloc(sz + 1);
        ei_decode_string(buf, &index, file);
        if (tcbdbcopy(driver->bdb, file))
          ei_x_encode_atom(&x, "ok");
        else
          return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size);
      } else
        return ei_error(&x, "invalid_argument", res, res_size);
      break;

    default:
      return ei_error(&x, "invalid_command", res, res_size);
  }

  if (res_size < x.index)
    (*res) = (char *)driver_alloc(x.index);
  int n = x.index;
  memcpy(*res, x.buff, x.index);
  ei_x_free(&x);
  return n;
};
/* next */
JNIEXPORT jboolean JNICALL Java_tokyocabinet_BDBCUR_next
(JNIEnv *env, jobject self){
  BDBCUR *cur = (BDBCUR *)(intptr_t)(*env)->GetLongField(env, self, bdbcur_fid_ptr);
  return tcbdbcurnext(cur);
}