Ejemplo n.º 1
0
/* put */
JNIEXPORT jboolean JNICALL Java_tokyocabinet_BDB_put
(JNIEnv *env, jobject self, jbyteArray key, jbyteArray val){
  if(!key || !val){
    throwillarg(env);
    return false;
  }
  TCBDB *bdb = (TCBDB *)(intptr_t)(*env)->GetLongField(env, self, bdb_fid_ptr);
  jboolean ick;
  jbyte *kbuf = (*env)->GetByteArrayElements(env, key, &ick);
  if(!kbuf){
    throwoutmem(env);
    return false;
  }
  int ksiz = (*env)->GetArrayLength(env, key);
  jboolean icv;
  jbyte *vbuf = (*env)->GetByteArrayElements(env, val, &icv);
  if(!vbuf){
    throwoutmem(env);
    return false;
  }
  int vsiz = (*env)->GetArrayLength(env, val);
  bool rv = tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz);
  (*env)->ReleaseByteArrayElements(env, val, vbuf, JNI_ABORT);
  (*env)->ReleaseByteArrayElements(env, key, kbuf, JNI_ABORT);
  return rv;
}
Ejemplo n.º 2
0
static int
ypdb_store (TCBDB *dbm, datum key, datum data, int mode)
{
  if (mode != YPDB_REPLACE)
    return 1;

  return !tcbdbput(dbm, key.dptr, key.dsize, data.dptr, data.dsize);
}
Ejemplo n.º 3
0
dict_oid_t
dict_set_next_oid(dict_t d, dict_oid_t oid)
{
	static const char sid[] = SID_SPACE;

	tcbdbput(d, sid, sizeof(sid), &oid, sizeof(oid));
	return oid;
}
Ejemplo n.º 4
0
dict_oid_t
dict_put_sym(dict_t d, const char *sym, dict_oid_t sid)
{
	const size_t ssz = strlen(sym);

	if (tcbdbput(d, sym, ssz, &sid, sizeof(sid)) <= 0) {
		return 0;
	}
	return sid;
}
Ejemplo n.º 5
0
int
mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data,
                       size_t dlen, size_t(*keylen) (const char* fn))
{
#ifndef HAVE_DB4
  char path[_POSIX_PATH_MAX];
  int ksize;
#endif
#if HAVE_GDBM
  datum key;
  datum databuf;
#elif HAVE_DB4
  DBT key;
  DBT databuf;
#endif
  
  if (!h)
    return -1;

#if HAVE_DB4
  if (filename[0] == '/')
    filename++;
  
  mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
  
  mutt_hcache_dbt_empty_init(&databuf);
  databuf.flags = DB_DBT_USERMEM;
  databuf.data = data;
  databuf.size = dlen;
  databuf.ulen = dlen;
  
  return h->db->put(h->db, NULL, &key, &databuf, 0);
#else
  strncpy(path, h->folder, sizeof (path));
  safe_strcat(path, sizeof (path), filename);

  ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
#endif
#if HAVE_QDBM
  return vlput(h->db, path, ksize, data, dlen, VL_DOVER);
#elif HAVE_TC
  return tcbdbput(h->db, path, ksize, data, dlen);
#elif HAVE_GDBM
  key.dptr = path;
  key.dsize = ksize;
  
  databuf.dsize = dlen;
  databuf.dptr = data;
  
  return gdbm_store(h->db, key, databuf, GDBM_REPLACE);
#endif
}
Ejemplo n.º 6
0
/* perform btwrite command */
int dobtwrite(char *name, int rnum, int rnd){
  TCBDB *bdb;
  int i, err, len;
  char buf[RECBUFSIZ];
  if(showprgr) printf("<Writing Test of B+ Tree>\n  name=%s  rnum=%d\n\n", name, rnum);
  /* open a database */
  bdb = tcbdbnew();
  if(rnd){
    tcbdbtune(bdb, 77, 256, -1, 0, 0, 0);
    tcbdbsetcache(bdb, rnum / 77, -1);
  } else {
    tcbdbtune(bdb, 101, 256, -1, 0, 0, 0);
    tcbdbsetcache(bdb, 256, 256);
  }
  tcbdbsetxmsiz(bdb, rnum * 32);
  if(!tcbdbopen(bdb, name, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
    fprintf(stderr, "tcbdbopen failed\n");
    tcbdbdel(bdb);
    return 1;
  }
  err = FALSE;
  /* loop for each record */
  for(i = 1; i <= rnum; i++){
    /* store a record */
    len = sprintf(buf, "%08d", rnd ? myrand() % rnum + 1 : i);
    if(!tcbdbput(bdb, buf, len, buf, len)){
      fprintf(stderr, "tcbdbput failed\n");
      err = TRUE;
      break;
    }
    /* print progression */
    if(showprgr && rnum > 250 && i % (rnum / 250) == 0){
      putchar('.');
      fflush(stdout);
      if(i == rnum || i % (rnum / 10) == 0){
        printf(" (%08d)\n", i);
        fflush(stdout);
      }
    }
  }
  /* close the database */
  if(!tcbdbclose(bdb)){
    fprintf(stderr, "tcbdbclose failed\n");
    tcbdbdel(bdb);
    return 1;
  }
  tcbdbdel(bdb);
  if(showprgr && !err) printf("ok\n\n");
  return err ? 1 : 0;
}
Ejemplo n.º 7
0
static void _mapcache_cache_tc_set(mapcache_context *ctx, mapcache_tile *tile)
{
  struct tc_conn conn;
  mapcache_cache_tc *cache = (mapcache_cache_tc*)tile->tileset->cache;
  char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL);
  apr_time_t now = apr_time_now();
  conn = _tc_get_conn(ctx,tile,0);
  GC_CHECK_ERROR(ctx);

  if(!tile->encoded_data) {
    tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
    GC_CHECK_ERROR(ctx);
  }
  mapcache_buffer_append(tile->encoded_data,sizeof(apr_time_t),&now);
  if(!tcbdbput(conn.bdb, skey, strlen(skey), tile->encoded_data->buf, tile->encoded_data->size)) {
    int ecode = tcbdbecode(conn.bdb);
    ctx->set_error(ctx,500, "tokyocabinet put error: %s\n",tcbdberrmsg(ecode));
  }
  _tc_release_conn(ctx,tile,conn);
}
Ejemplo n.º 8
0
int db_set_dbvalue(void *vhandle, const dbv_t *token, const dbv_t *val)
{
    int ret;
    dbh_t *handle = vhandle;
    TCBDB *dbp = handle->dbp;

    ret = tcbdbput(dbp, token->data, token->leng, val->data, val->leng);

    if (ret == 0) {
	print_error(__FILE__, __LINE__,
		    "(tc) db_set_dbvalue( '%.*s' ) err: %d, %s",
		    CLAMP_INT_MAX(token->leng), (char *)token->data,
		    tcbdbecode(dbp), tcbdberrmsg(tcbdbecode(dbp)));
	exit(EX_ERROR);
    }

    db_optimize(dbp, handle->name);

    return 0;
}
Ejemplo n.º 9
0
/* perform put command */
static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
        TCCMP cmp, int omode, int dmode) {
    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, BDBOWRITER | omode)) {
        printerr(bdb);
        tcbdbdel(bdb);
        return 1;
    }
    bool err = false;
    int inum;
    double dnum;
    switch (dmode) {
        case -1:
            if (!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)) {
                printerr(bdb);
                err = true;
            }
            break;
        case 1:
            if (!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)) {
                printerr(bdb);
                err = true;
            }
            break;
        case 2:
            if (!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)) {
                printerr(bdb);
                err = true;
            }
            break;
        case 3:
            if (!tcbdbputdupback(bdb, kbuf, ksiz, vbuf, vsiz)) {
                printerr(bdb);
                err = true;
            }
            break;
        case 10:
            inum = tcbdbaddint(bdb, kbuf, ksiz, tcatoi(vbuf));
            if (inum == INT_MIN) {
                printerr(bdb);
                err = true;
            } else {
                printf("%d\n", inum);
            }
            break;
        case 11:
            dnum = tcbdbadddouble(bdb, kbuf, ksiz, tcatof(vbuf));
            if (isnan(dnum)) {
                printerr(bdb);
                err = true;
            } else {
                printf("%.6f\n", dnum);
            }
            break;
        default:
            if (!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)) {
                printerr(bdb);
                err = true;
            }
            break;
    }
    if (!tcbdbclose(bdb)) {
        if (!err) printerr(bdb);
        err = true;
    }
    tcbdbdel(bdb);
    return err ? 1 : 0;
}
Ejemplo n.º 10
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;
};
Ejemplo n.º 11
0
/* Synchronize updating contents on memory of a word database object. */
bool tcwdbmemsync(TCWDB *wdb, int level){
  assert(wdb);
  if(!wdb->open || !wdb->cc){
    tcbdbsetecode(wdb->idx, TCEINVALID, __FILE__, __LINE__, __func__);
    return false;
  }
  bool err = false;
  bool (*synccb)(int, int, const char *, void *) = wdb->synccb;
  void *syncopq = wdb->syncopq;
  bool (*addcb)(const char *, void *) = wdb->addcb;
  void *addopq = wdb->addopq;
  TCBDB *idx = wdb->idx;
  TCMAP *cc = wdb->cc;
  if(synccb && !synccb(0, 0, "started", syncopq)){
    tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
    return false;
  }
  if(tcmaprnum(cc) > 0){
    if(synccb && !synccb(0, 0, "getting tokens", syncopq)){
      tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
      return false;
    }
    int kn;
    const char **keys = tcmapkeys2(cc, &kn);
    if(synccb && !synccb(kn, 0, "sorting tokens", syncopq)){
      tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
      tcfree(keys);
      return false;
    }
    qsort(keys, kn, sizeof(*keys), (int(*)(const void *, const void *))tccmpwords);
    for(int i = 0; i < kn; i++){
      if(synccb && !synccb(kn, i + 1, "storing tokens", syncopq)){
        tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
        tcfree(keys);
        return false;
      }
      const char *kbuf = keys[i];
      int ksiz = strlen(kbuf);
      int vsiz;
      const char *vbuf = tcmapget(cc, kbuf, ksiz, &vsiz);
      if(!tcbdbputcat(idx, kbuf, ksiz, vbuf, vsiz)) err = true;
    }
    if(addcb){
      if(synccb && !synccb(0, 0, "storing keyword list", syncopq)){
        tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
        tcfree(keys);
        return false;
      }
      for(int i = 0; i < kn; i++){
        if(!addcb(keys[i], addopq)){
          tcfree(keys);
          return false;
        }
      }
    }
    tcfree(keys);
    tcmapclear(cc);
  }
  TCMAP *dtokens = wdb->dtokens;
  TCIDSET *dids = wdb->dids;
  if(tcmaprnum(dtokens) > 0){
    if(synccb && !synccb(0, 0, "getting deleted tokens", syncopq)){
      tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
      return false;
    }
    int kn;
    const char **keys = tcmapkeys2(dtokens, &kn);
    if(synccb && !synccb(kn, 0, "sorting deleted tokens", syncopq)){
      tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
      tcfree(keys);
      return false;
    }
    qsort(keys, kn, sizeof(*keys), (int(*)(const void *, const void *))tccmpwords);
    for(int i = 0; i < kn; i++){
      if(synccb && !synccb(kn, i + 1, "storing deleted tokens", syncopq)){
        tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
        tcfree(keys);
        return false;
      }
      const char *kbuf = keys[i];
      int ksiz = strlen(kbuf);
      int vsiz;
      const char *vbuf = tcbdbget3(idx, kbuf, ksiz, &vsiz);
      if(!vbuf) continue;
      char *nbuf = tcmalloc(vsiz + 1);
      char *wp = nbuf;
      const char *pv;
      while(vsiz > 0){
        pv = vbuf;
        int step;
        uint64_t id;
        TDREADVNUMBUF64(vbuf, id, step);
        vbuf += step;
        vsiz -= step;
        if(!tcidsetcheck(dids, id)){
          int len = vbuf - pv;
          memcpy(wp, pv, len);
          wp += len;
        }
      }
      int nsiz = wp - nbuf;
      if(nsiz > 0){
        if(!tcbdbput(idx, kbuf, ksiz, nbuf, nsiz)) err = true;
      } else {
        if(!tcbdbout(idx, kbuf, ksiz)) err = true;
      }
      tcfree(nbuf);
    }
    tcfree(keys);
    tcmapclear(dtokens);
    tcidsetclear(dids);
  }
  if(level > 0){
    if(synccb && !synccb(0, 0, "synchronizing database", syncopq)){
      tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
      return false;
    }
    if(!tcbdbmemsync(idx, level > 1)) err = true;
  }
  if(synccb && !synccb(0, 0, "finished", syncopq)){
    tcbdbsetecode(wdb->idx, TCEMISC, __FILE__, __LINE__, __func__);
    return false;
  }
  return !err;
}