static inline int si_plannerpeek_gc(siplanner *p, siplan *plan) { /* try to peek a node with a biggest number * of branches which is ready for gc */ int rc_inprogress = 0; sinode *n; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->compact, pn))) { n = sscast(pn, sinode, nodecompact); sdindexheader *h = n->self.index.h; if (sslikely(h->dupkeys == 0) || (h->dupmin >= plan->a)) continue; uint32_t used = (h->dupkeys * 100) / h->keys; if (used >= plan->b) { if (n->flags & SI_LOCK) { rc_inprogress = 2; continue; } goto match; } } if (rc_inprogress) plan->explain = SI_ERETRY; return rc_inprogress; match: si_nodelock(n); plan->explain = SI_ENONE; plan->node = n; return 1; }
static inline int si_plannerpeek_checkpoint(siplanner *p, siplan *plan) { /* try to peek a node which has min * lsn <= required value */ int rc_inprogress = 0; sinode *n; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->branch, pn))) { n = sscast(pn, sinode, nodebranch); if (n->i0.lsnmin <= plan->a) { if (n->flags & SI_LOCK) { rc_inprogress = 2; continue; } goto match; } } if (rc_inprogress) plan->explain = SI_ERETRY; return rc_inprogress; match: si_nodelock(n); plan->explain = SI_ENONE; plan->node = n; return 1; }
sx *sx_find(sxmanager *m, uint64_t id) { ssrbnode *n = NULL; int rc = sx_matchtx(&m->i, NULL, (char*)&id, sizeof(id), &n); if (rc == 0 && n) return sscast(n, sx, node); return NULL; }
static inline void sx_deadlock_unmark(sslist *mark) { sslist *i, *n; ss_listforeach_safe(mark, i, n) { sx *t = sscast(i, sx, deadlock); ss_listinit(&t->deadlock); }
int si_profiler(siprofiler *p) { uint32_t temperature_total = 0; uint64_t memory_used = 0; ssrbnode *pn; sinode *n; pn = ss_rbmin(&p->i->i); while (pn) { n = sscast(pn, sinode, node); if (p->temperature_max < n->temperature) p->temperature_max = n->temperature; if (p->temperature_min > n->temperature) p->temperature_min = n->temperature; temperature_total += n->temperature; p->total_node_count++; p->count += n->i0.count; p->count += n->i1.count; p->total_branch_count += n->branch_count; if (p->total_branch_max < n->branch_count) p->total_branch_max = n->branch_count; if (n->branch_count < 20) p->histogram_branch[n->branch_count]++; else p->histogram_branch_20plus++; memory_used += n->i0.used; memory_used += n->i1.used; sibranch *b = n->branch; while (b) { p->count += b->index.h->keys; p->count_dup += b->index.h->dupkeys; int indexsize = sd_indexsize_ext(b->index.h); p->total_snapshot_size += indexsize; p->total_node_size += indexsize + b->index.h->total; p->total_node_origin_size += indexsize + b->index.h->totalorigin; p->total_page_count += b->index.h->count; if (b->index.h->extensions & SD_INDEXEXT_AMQF) { p->total_amqf_size += sizeof(sdindexamqf) + sd_indexamqf(&b->index)->size; } b = b->next; } pn = ss_rbnext(&p->i->i, pn); } if (p->total_node_count > 0) { p->total_branch_avg = p->total_branch_count / p->total_node_count; p->temperature_avg = temperature_total / p->total_node_count; } p->memory_used = memory_used; p->read_disk = p->i->read_disk; p->read_cache = p->i->read_cache; si_profiler_histogram_branch(p); si_profiler_histogram_temperature(p); return 0; }
static inline svv* getv(svindex *i, sr *r, uint64_t vlsn, svv *key) { ssrbnode *n = NULL; int rc = sv_indexmatch(&i->i, r->scheme, sv_vpointer(key), sv_vsize(key), &n); if (rc == 0 && n) { return sv_visible(sscast(n, svv, node), vlsn); } return NULL; }
static inline void si_recoversize(si *i) { ssrbnode *pn = ss_rbmin(&i->i); while (pn) { sinode *n = sscast(pn, sinode, node); i->size += si_nodesize(n); pn = ss_rbnext(&i->i, pn); } }
uint32_t sx_min(sxmanager *m) { ss_spinlock(&m->lock); uint32_t id = 0; if (sx_count(m) > 0) { ssrbnode *node = ss_rbmin(&m->i); sx *min = sscast(node, sx, node); id = min->id; } ss_spinunlock(&m->lock); return id; }
uint64_t sx_max(sxmanager *m) { ss_spinlock(&m->lock); uint64_t id = 0; if (sx_count(m) > 0) { ssrbnode *node = ss_rbmax(&m->i); sx *max = sscast(node, sx, node); id = max->id; } ss_spinunlock(&m->lock); return id; }
static inline int si_trackvalidate(sitrack *track, ssbuf *buf, sr *r, si *i) { ss_bufreset(buf); ssrbnode *p = ss_rbmax(&track->i); while (p) { sinode *n = sscast(p, sinode, node); switch (n->recover) { case SI_RDB|SI_RDB_DBI|SI_RDB_DBSEAL|SI_RDB_REMOVE: case SI_RDB|SI_RDB_DBSEAL|SI_RDB_REMOVE: case SI_RDB|SI_RDB_REMOVE: case SI_RDB_UNDEF|SI_RDB_DBSEAL|SI_RDB_REMOVE: case SI_RDB|SI_RDB_DBI|SI_RDB_DBSEAL: case SI_RDB|SI_RDB_DBI: case SI_RDB: case SI_RDB|SI_RDB_DBSEAL: case SI_RDB_UNDEF|SI_RDB_DBSEAL: { /* match and remove any leftover ancestor */ sinode *ancestor = si_trackget(track, n->self.id.parent); if (ancestor && (ancestor != n)) ancestor->recover |= SI_RDB_REMOVE; break; } case SI_RDB_DBSEAL: { /* find parent */ sinode *parent = si_trackget(track, n->self.id.parent); if (parent) { /* schedule node for removal, if has incomplete merges */ if (parent->recover & SI_RDB_DBI) n->recover |= SI_RDB_REMOVE; else parent->recover |= SI_RDB_REMOVE; } if (! (n->recover & SI_RDB_REMOVE)) { /* complete node */ int rc = si_nodecomplete(n, r, i->scheme); if (ssunlikely(rc == -1)) return -1; n->recover = SI_RDB; } break; } default: /* corrupted states */ return sr_malfunction(r->e, "corrupted database repository: %s", i->scheme->path); } p = ss_rbprev(&track->i, p); } return 0; }
uint64_t sx_vlsn(sxmanager *m) { ss_spinlock(&m->lock); uint64_t vlsn; if (sx_count(m) > 0) { ssrbnode *node = ss_rbmin(&m->i); sx *min = sscast(node, sx, node); vlsn = min->vlsn; } else { vlsn = sr_seq(m->r->seq, SR_LSN); } ss_spinunlock(&m->lock); return vlsn; }
static inline uint64_t sx_csn(sxmanager *m) { uint64_t csn = UINT64_MAX; if (m->count_rw == 0) return csn; ssrbnode *p = ss_rbmin(&m->i); sx *min = NULL; while (p) { min = sscast(p, sx, node); if (min->type == SXRO) { p = ss_rbnext(&m->i, p); continue; } break; } assert(min != NULL); return min->csn; }
static inline int si_plannerpeek_branch(siplanner *p, siplan *plan) { /* try to peek a node with a biggest in-memory index */ sinode *n; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->branch, pn))) { n = sscast(pn, sinode, nodebranch); if (n->flags & SI_LOCK) continue; if (n->used >= plan->a) goto match; return 0; } return 0; match: si_nodelock(n); plan->explain = SI_EINDEX_SIZE; plan->node = n; return 1; }
int sx_get(sx *x, sxindex *index, sv *key, sv *result) { sxmanager *m = x->manager; ssrbnode *n = NULL; int rc; rc = sx_match(&index->i, index->r->scheme, sv_pointer(key), sv_size(key), &n); if (! (rc == 0 && n)) goto add; sxv *head = sscast(n, sxv, node); sxv *v = sx_vmatch(head, x->id); if (v == NULL) goto add; if (ssunlikely((v->v->flags & SVGET) > 0)) return 0; if (ssunlikely((v->v->flags & SVDELETE) > 0)) return 2; sv vv; sv_init(&vv, &sv_vif, v->v, NULL); svv *ret = sv_vdup(m->r, &vv); if (ssunlikely(ret == NULL)) { rc = sr_oom(m->r->e); } else { sv_init(result, &sv_vif, ret, NULL); rc = 1; } return rc; add: /* track a start of the latest read sequence in the * transactional log */ if (x->log_read == -1) x->log_read = sv_logcount(&x->log); rc = sx_set(x, index, key->v); if (ssunlikely(rc == -1)) return -1; sv_vref((svv*)key->v); return 0; }
static void si_profiler_histogram_temperature(siprofiler *p) { /* build histogram */ struct { int nodes; int branches; } h[100]; memset(h, 0, sizeof(h)); sinode *n; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->i->p.temp, pn))) { n = sscast(pn, sinode, nodetemp); h[pn->v].nodes++; h[pn->v].branches += n->branch_count; } /* prepare histogram string */ int count = 0; int i = 99; int size = 0; while (i >= 0 && count < 10) { if (h[i].nodes == 0) { i--; continue; } size += snprintf(p->histogram_temperature_sz + size, sizeof(p->histogram_temperature_sz) - size, "[%d]:%d-%d ", i, h[i].nodes, h[i].branches); i--; count++; } if (size == 0) p->histogram_temperature_ptr = NULL; else { p->histogram_temperature_ptr = p->histogram_temperature_sz; } }
static inline int si_plannerpeek_compact(siplanner *p, siplan *plan) { /* try to peek a node with a biggest number * of branches */ sinode *n; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->compact, pn))) { n = sscast(pn, sinode, nodecompact); if (n->flags & SI_LOCK) continue; if (n->branch_count >= plan->a) goto match; return 0; } return 0; match: si_nodelock(n); plan->explain = SI_EBRANCH_COUNT; plan->node = n; return 1; }
static void ss_ht_test0(void) { ssht ht; t( ss_htinit(&ht, &st_r.a, 8) == 0 ); int i = 0; while (i < 3431) { if (ss_htisfull(&ht)) { t( ss_htresize(&ht, &st_r.a) == 0 ); } htnode *n = ss_malloc(&st_r.a, sizeof(htnode)); int len = snprintf(n->key, sizeof(n->key), "key_%d", i); n->node.hash = ss_fnv(n->key, len); int pos = ssht_search(&ht, n->node.hash, n->key, len, NULL); ss_htset(&ht, pos, &n->node); i++; } i = 0; while (i < 3431) { htnode key; int len = snprintf(key.key, sizeof(key.key), "key_%d", i); key.node.hash = ss_fnv(key.key, len); int pos = ssht_search(&ht, key.node.hash, key.key, len, NULL); t( ht.i[pos] != NULL ); t( strcmp(sscast(ht.i[pos], htnode, node)->key, key.key) == 0 ); i++; } i = 0; while (i < ht.size) { if (ht.i[i]) ss_free(&st_r.a, ht.i[i]); i++; } ss_htfree(&ht, &st_r.a); }
static inline int si_plannerpeek_compact_temperature(siplanner *p, siplan *plan) { /* try to peek a hottest node with number of * branches >= watermark */ sinode *n; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->temp, pn))) { n = sscast(pn, sinode, nodetemp); if (n->flags & SI_LOCK) continue; if (n->branch_count >= plan->a) goto match; return 0; } return 0; match: si_nodelock(n); plan->explain = SI_ETEMP; plan->node = n; return 1; }
int sx_get(sx *x, sxindex *index, svv *key, svv **result) { ssrbnode *n = NULL; int rc; rc = sx_match(&index->i, index->r->scheme, sv_vpointer(key), 0, &n); if (! (rc == 0 && n)) goto add; sxv *head = sscast(n, sxv, node); sxv *v = sx_vmatch(head, x->id); if (v == NULL) goto add; if (ssunlikely(sv_vflags(v->v, index->r) & SVGET)) return 0; if (ssunlikely(sv_vflags(v->v, index->r) & SVDELETE)) return 2; *result = sv_vbuildraw(index->r, sv_vpointer(v->v)); if (ssunlikely(*result == NULL)) { sr_oom(index->r->e); rc = -1; } else { rc = 1; } return rc; add: /* track a start of the latest read sequence in the * transactional log */ if (x->log_read == -1) x->log_read = sv_logcount(x->log); rc = sx_set(x, index, key); if (ssunlikely(rc == -1)) return -1; sv_vref(key); return 0; }
static inline int si_plannerpeek_age(siplanner *p, siplan *plan) { /* try to peek a node with update >= a and in-memory * index size >= b */ /* full scan */ uint64_t now = ss_utime(); sinode *n = NULL; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->branch, pn))) { n = sscast(pn, sinode, nodebranch); if (n->flags & SI_LOCK) continue; if (n->used >= plan->b && ((now - n->update_time) >= plan->a)) goto match; } return 0; match: si_nodelock(n); plan->explain = SI_EINDEX_AGE; plan->node = n; return 1; }
static inline int si_plannerpeek_backup(siplanner *p, siplan *plan) { /* try to peek a node which has * bsn <= required value */ int rc_inprogress = 0; sinode *n; ssrqnode *pn = NULL; while ((pn = ss_rqprev(&p->branch, pn))) { n = sscast(pn, sinode, nodebranch); if (n->backup < plan->a) { if (n->flags & SI_LOCK) { rc_inprogress = 2; continue; } goto match; } } if (rc_inprogress) { plan->explain = SI_ERETRY; return 2; } si *index = p->i; if (index->backup < plan->a) { plan->plan = SI_BACKUPEND; plan->node = 0; return 1; } return 0; match: si_nodelock(n); plan->explain = SI_ENONE; plan->node = n; return 1; }
ss_listinit(&i->link); i->dsn = 0; i->object = object; i->r = r; ss_listappend(&m->indexes, &i->link); return 0; } int sx_indexset(sxindex *i, uint32_t dsn) { i->dsn = dsn; return 0; } ss_rbtruncate(sx_truncate, sx_vfreeall( ((void**)arg)[1], ((void**)arg)[0], sscast(n, sxv, node))) static inline void sx_indextruncate(sxindex *i, sxmanager *m) { if (i->i.root == NULL) return; void *args[2] = { i->r, &m->pool }; sx_truncate(i->i.root, args); ss_rbinit(&i->i); } int sx_indexfree(sxindex *i, sxmanager *m) { sx_indextruncate(i, m); ss_listunlink(&i->link);
n->temperature_reads = 0; ss_fileinit(&n->file, r->vfs); ss_mmapinit(&n->map); ss_mmapinit(&n->map_swap); sv_indexinit(&n->i0); sv_indexinit(&n->i1); ss_rbinitnode(&n->node); ss_rqinitnode(&n->nodecompact); ss_rqinitnode(&n->nodebranch); ss_rqinitnode(&n->nodetemp); ss_listinit(&n->commit); return n; } ss_rbtruncate(si_nodegc_indexgc, si_gcref((sr*)arg, sscast(n, svref, node))) int si_nodegc_index(sr *r, svindex *i) { if (i->i.root) si_nodegc_indexgc(i->i.root, r); sv_indexinit(i); return 0; } static inline int si_nodeclose(sinode *n, sr *r, int gc) { int rcret = 0; int rc = ss_vfsmunmap(r->vfs, &n->map);
i->dsn = 0; i->ptr = ptr; i->r = r; ss_listappend(&m->indexes, &i->link); return 0; } int sx_indexset(sxindex *i, uint32_t dsn) { i->dsn = dsn; return 0; } ss_rbtruncate(sx_truncate, sx_vfreeall(((sr** )arg)[0], ((ssa**)arg)[1], sscast(n, sxv, node))) static inline void sx_indextruncate(sxindex *i, sxmanager *m) { if (i->i.root == NULL) return; void *argv[2] = { m->r, m->asxv }; sx_truncate(i->i.root, argv); ss_rbinit(&i->i); } int sx_indexfree(sxindex *i, sxmanager *m) { sx_indextruncate(i, m); ss_listunlink(&i->link);
int sx_set(sx *x, sxindex *index, svv *version) { sxmanager *m = x->manager; sr *r = index->r; svlogv lv; lv.index_id = index->dsn; lv.next = UINT32_MAX; lv.v = version; lv.ptr = NULL; /* allocate mvcc container */ sxv *v = sx_valloc(&m->pool, version); if (ssunlikely(v == NULL)) { sv_vunref(r, version); return -1; } v->id = x->id; v->index = index; lv.ptr = v; if (! (sv_vflags(version, index->r) & SVGET)) x->log_read = -1; /* update concurrent index */ ssrbnode *n = NULL; int rc; rc = sx_match(&index->i, index->r->scheme, sv_vpointer(version), 0, &n); if (ssunlikely(rc == 0 && n)) { /* exists */ } else { int pos = rc; /* unique */ v->lo = sv_logcount(x->log); rc = sv_logadd(x->log, r, &lv); if (ssunlikely(rc == -1)) { sr_oom(r->e); goto error; } ss_rbset(&index->i, n, pos, &v->node); return 0; } sxv *head = sscast(n, sxv, node); /* match previous update made by current * transaction */ sxv *own = sx_vmatch(head, x->id); if (ssunlikely(own)) { if (ssunlikely(sv_vflags(version, index->r) & SVUPSERT)) { sr_error(r->e, "%s", "only one upsert statement is " "allowed per a transaction key"); goto error; } /* replace old document with the new one */ lv.next = sv_logat(x->log, own->lo)->next; v->lo = own->lo; if (ssunlikely(sx_vaborted(own))) sx_vabort(v); sx_vreplace(own, v); if (sslikely(head == own)) ss_rbreplace(&index->i, &own->node, &v->node); /* update log */ sv_logreplace(x->log, r, v->lo, &lv); sx_vfree(&m->pool, r, own); return 0; } /* update log */ v->lo = sv_logcount(x->log); rc = sv_logadd(x->log, r, &lv); if (ssunlikely(rc == -1)) { sr_oom(r->e); goto error; } /* add version */ sx_vlink(head, v); return 0; error: sx_vfree(&m->pool, r, v); return -1; }
#include <sophia.h> #include <libss.h> #include <libsf.h> #include <libsr.h> #include <libsv.h> #include <libso.h> #include <libst.h> typedef struct { sshtnode node; char key[32]; } htnode; ss_htsearch(ssht_search, ((sscast(t->i[pos], htnode, node)->node.hash == hash) && (strcmp(sscast(t->i[pos], htnode, node)->key, key) == 0))); static void ss_ht_test0(void) { ssht ht; t( ss_htinit(&ht, &st_r.a, 8) == 0 ); int i = 0; while (i < 3431) { if (ss_htisfull(&ht)) { t( ss_htresize(&ht, &st_r.a) == 0 ); } htnode *n = ss_malloc(&st_r.a, sizeof(htnode)); int len = snprintf(n->key, sizeof(n->key), "key_%d", i);
int sx_set(sx *x, sxindex *index, svv *version) { sxmanager *m = x->manager; sr *r = m->r; if (! (version->flags & SVGET)) { x->log_read = -1; } /* allocate mvcc container */ sxv *v = sx_valloc(m->asxv, version); if (ssunlikely(v == NULL)) { ss_quota(r->quota, SS_QREMOVE, sv_vsize(version)); sv_vfree(r, version); return -1; } v->id = x->id; v->index = index; svlogv lv; lv.id = index->dsn; lv.next = UINT32_MAX; sv_init(&lv.v, &sx_vif, v, NULL); /* update concurrent index */ ssrbnode *n = NULL; int rc = sx_match(&index->i, index->r->scheme, sv_vpointer(version), version->size, &n); if (ssunlikely(rc == 0 && n)) { /* exists */ } else { int pos = rc; /* unique */ v->lo = sv_logcount(&x->log); rc = sv_logadd(&x->log, r->a, &lv, index->ptr); if (ssunlikely(rc == -1)) { sr_oom(r->e); goto error; } ss_rbset(&index->i, n, pos, &v->node); return 0; } sxv *head = sscast(n, sxv, node); /* match previous update made by current * transaction */ sxv *own = sx_vmatch(head, x->id); if (ssunlikely(own)) { if (ssunlikely(version->flags & SVUPDATE)) { sr_error(r->e, "%s", "only one update statement is " "allowed per a transaction key"); goto error; } /* replace old document with the new one */ lv.next = sv_logat(&x->log, own->lo)->next; v->lo = own->lo; if (ssunlikely(sx_vaborted(own))) sx_vabort(v); sx_vreplace(own, v); if (sslikely(head == own)) ss_rbreplace(&index->i, &own->node, &v->node); /* update log */ sv_logreplace(&x->log, v->lo, &lv); ss_quota(r->quota, SS_QREMOVE, sv_vsize(own->v)); sx_vfree(r, m->asxv, own); return 0; } /* update log */ v->lo = sv_logcount(&x->log); rc = sv_logadd(&x->log, r->a, &lv, index->ptr); if (ssunlikely(rc == -1)) { sr_oom(r->e); goto error; } /* add version */ sx_vlink(head, v); return 0; error: ss_quota(r->quota, SS_QREMOVE, sv_vsize(v->v)); sx_vfree(r, m->asxv, v); return -1; }
sr_malfunction(r->e, "db file '%s' unlink error: %s", ss_pathof(&n->file.path), strerror(errno)); rcret = -1; } } si_nodefree_branches(n, r); rc = si_nodeclose(n, r); if (ssunlikely(rc == -1)) rcret = -1; ss_free(r->a, n); return rcret; } ss_rbtruncate(si_nodegc_indexgc, si_gcv((sr*)arg, sscast(n, svv, node))) int si_nodegc_index(sr *r, svindex *i) { if (i->i.root) si_nodegc_indexgc(i->i.root, r); sv_indexinit(i); return 0; } int si_noderead(sinode *n, sr *r, ssbuf *dest) { int rc = ss_bufensure(dest, r->a, n->file.size); if (ssunlikely(rc == -1)) return sr_oom_malfunction(r->e); rc = ss_filepread(&n->file, 0, dest->s, n->file.size);
h->lsnmindup = UINT64_MAX; memset(h->reserve, 0, sizeof(h->reserve)); ss_bufadvance(&b->list, sizeof(sdbuildref)); ss_bufadvance(&b->m, sizeof(sdpageheader)); return 0; } typedef struct { sshtnode node; uint32_t offset; uint32_t offsetstart; uint32_t size; } sdbuildkey; ss_htsearch(sd_buildsearch, (sscast(t->i[pos], sdbuildkey, node)->node.hash == hash) && (sscast(t->i[pos], sdbuildkey, node)->size == size) && (memcmp(((sdbuild*)ptr)->k.s + sscast(t->i[pos], sdbuildkey, node)->offsetstart, key, size) == 0)) static inline int sd_buildadd_keyvalue(sdbuild *b, sr *r, sv *v) { /* calculate key size */ uint32_t keysize = 0; int i = 0; while (i < r->scheme->count) { keysize += sv_keysize(v, r, i); i++; } uint32_t valuesize = sv_valuesize(v, r);
static inline int sd_buildadd_keyvalue(sdbuild *b, sr *r, sv *v) { /* calculate key size */ uint32_t keysize = 0; int i = 0; while (i < r->scheme->count) { keysize += sv_keysize(v, r, i); i++; } uint32_t valuesize = sv_valuesize(v, r); uint32_t size = keysize + valuesize; /* prepare buffer */ uint64_t lsn = sv_lsn(v); uint32_t sizemeta = ss_leb128size(size) + ss_leb128size(lsn); int rc = ss_bufensure(&b->v, r->a, sizemeta); if (ssunlikely(rc == -1)) return sr_oom(r->e); /* write meta */ ss_bufadvance(&b->v, ss_leb128write(b->v.p, size)); ss_bufadvance(&b->v, ss_leb128write(b->v.p, lsn)); /* write key-parts */ i = 0; for (; i < r->scheme->count; i++) { uint32_t partsize = sv_keysize(v, r, i); char *part = sv_key(v, r, i); int offsetstart = ss_bufused(&b->k); int offset = (offsetstart - sd_buildref(b)->k); /* match a key copy */ int is_duplicate = 0; uint32_t hash = 0; int pos = 0; if (b->compress_dup) { hash = ss_fnv(part, partsize); pos = sd_buildsearch(&b->tracker, hash, part, partsize, b); if (b->tracker.i[pos]) { is_duplicate = 1; sdbuildkey *ref = sscast(b->tracker.i[pos], sdbuildkey, node); offset = ref->offset; } } /* offset */ rc = ss_bufensure(&b->v, r->a, ss_leb128size(offset)); if (ssunlikely(rc == -1)) return sr_oom(r->e); ss_bufadvance(&b->v, ss_leb128write(b->v.p, offset)); if (is_duplicate) continue; /* copy key */ int partsize_meta = ss_leb128size(partsize); rc = ss_bufensure(&b->k, r->a, partsize_meta + partsize); if (ssunlikely(rc == -1)) return sr_oom(r->e); ss_bufadvance(&b->k, ss_leb128write(b->k.p, partsize)); memcpy(b->k.p, part, partsize); ss_bufadvance(&b->k, partsize); /* add key reference */ if (b->compress_dup) { if (ssunlikely(ss_htisfull(&b->tracker))) { rc = ss_htresize(&b->tracker, r->a); if (ssunlikely(rc == -1)) return sr_oom(r->e); } sdbuildkey *ref = ss_malloc(r->a, sizeof(sdbuildkey)); if (ssunlikely(rc == -1)) return sr_oom(r->e); ref->node.hash = hash; ref->offset = offset; ref->offsetstart = offsetstart + partsize_meta; ref->size = partsize; ss_htset(&b->tracker, pos, &ref->node); } } /* write value */ rc = ss_bufensure(&b->v, r->a, valuesize); if (ssunlikely(rc == -1)) return sr_oom(r->e); memcpy(b->v.p, sv_value(v, r), valuesize); ss_bufadvance(&b->v, valuesize); return 0; }