18const size_t DBM_HDR = 7;
20void put_u16(uint8_t *p, uint16_t v)
23 p[1] = (uint8_t)(v >> 8);
25void put_u32(uint8_t *p, uint32_t v)
28 p[1] = (uint8_t)(v >> 8);
29 p[2] = (uint8_t)(v >> 16);
30 p[3] = (uint8_t)(v >> 24);
32uint16_t get_u16(
const uint8_t *p)
34 return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
36uint32_t get_u32(
const uint8_t *p)
38 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
42uint64_t key_hash(
const char *key, uint16_t len)
44 uint64_t h = 0xcbf29ce484222325ULL;
45 for (uint16_t i = 0; i < len; i++)
48 h *= 0x100000001b3ULL;
54int find_live(DetwsDbm *db, uint64_t hash,
const char *key, uint16_t key_len)
57 size_t start = (size_t)(hash % n);
58 for (
size_t i = 0; i < n; i++)
60 size_t j = (start + i) % n;
61 DetwsDbmSlot *s = &db->slots[j];
64 if (s->state == 1 && s->hash == hash && s->key_len == key_len && memcmp(s->key, key, key_len) == 0)
72int reserve(DetwsDbm *db, uint64_t hash,
const char *key, uint16_t key_len,
bool *is_new)
75 size_t start = (size_t)(hash % n);
77 for (
size_t i = 0; i < n; i++)
79 size_t j = (start + i) % n;
80 DetwsDbmSlot *s = &db->slots[j];
83 if (s->hash == hash && s->key_len == key_len && memcmp(s->key, key, key_len) == 0)
98 return first_free >= 0 ? first_free : (int)j;
111void replay_cb(uint64_t seq, uint64_t data_off,
const uint8_t *payload, uint32_t len,
void *ctx)
114 ReplayCtx *rc = (ReplayCtx *)ctx;
115 DetwsDbm *db = rc->db;
118 uint8_t op = payload[0];
119 uint16_t klen = get_u16(payload + 1);
120 uint32_t vlen = get_u32(payload + 3);
123 if (DBM_HDR + (
size_t)klen + vlen > len)
125 const char *key = (
const char *)(payload + DBM_HDR);
126 uint64_t h = key_hash(key, klen);
130 int slot = reserve(db, h, key, klen, &is_new);
136 DetwsDbmSlot *s = &db->slots[slot];
142 memcpy(s->key, key, klen);
143 s->val_off = data_off + WAL_RECORD_HEADER + DBM_HDR + klen;
148 int slot = find_live(db, h, key, klen);
151 db->slots[slot].state = 2;
158bool detws_dbm_open(DetwsDbm *db, WalStore *wal)
160 memset(db, 0,
sizeof(*db));
162 ReplayCtx rc = {db,
false};
164 wal_store_scan(wal, replay_cb, &rc, scratch,
sizeof(scratch));
168bool detws_dbm_put(DetwsDbm *db,
const char *key, uint16_t key_len,
const uint8_t *val, uint32_t val_len)
172 uint64_t h = key_hash(key, key_len);
174 int slot = reserve(db, h, key, key_len, &is_new);
180 put_u16(rec + 1, key_len);
181 put_u32(rec + 3, val_len);
182 memcpy(rec + DBM_HDR, key, key_len);
184 memcpy(rec + DBM_HDR + key_len, val, val_len);
185 uint64_t old_head = wal_store_used(db->wal);
186 if (!wal_store_append(db->wal, rec, (uint32_t)(DBM_HDR + key_len + val_len)))
189 DetwsDbmSlot *s = &db->slots[slot];
194 s->key_len = key_len;
195 memcpy(s->key, key, key_len);
196 s->val_off = old_head + WAL_RECORD_HEADER + DBM_HDR + key_len;
197 s->val_len = val_len;
201long detws_dbm_get(DetwsDbm *db,
const char *key, uint16_t key_len, uint8_t *buf,
size_t cap)
203 uint64_t h = key_hash(key, key_len);
204 int slot = find_live(db, h, key, key_len);
207 DetwsDbmSlot *s = &db->slots[slot];
208 if (s->val_len > cap)
210 if (s->val_len && !wal_store_pread(db->wal, s->val_off, buf, s->val_len))
212 return (
long)s->val_len;
215bool detws_dbm_del(DetwsDbm *db,
const char *key, uint16_t key_len)
217 uint64_t h = key_hash(key, key_len);
218 int slot = find_live(db, h, key, key_len);
223 put_u16(rec + 1, key_len);
225 memcpy(rec + DBM_HDR, key, key_len);
226 if (!wal_store_append(db->wal, rec, (uint32_t)(DBM_HDR + key_len)))
228 db->slots[slot].state = 2;
233bool detws_dbm_contains(DetwsDbm *db,
const char *key, uint16_t key_len)
235 return find_live(db, key_hash(key, key_len), key, key_len) >= 0;
238uint32_t detws_dbm_count(DetwsDbm *db)
243bool detws_dbm_sync(DetwsDbm *db)
245 return wal_store_checkpoint(db->wal);
248uint32_t detws_dbm_iterate(DetwsDbm *db, DetwsDbmIterCb cb,
void *ctx)
250 uint32_t visited = 0;
253 DetwsDbmSlot *s = &db->slots[i];
257 if (cb && !cb(s->key, s->key_len, ctx))
263uint64_t detws_dbm_live_bytes(DetwsDbm *db)
268 const DetwsDbmSlot *s = &db->slots[i];
270 bytes += WAL_RECORD_HEADER + DBM_HDR + s->key_len + s->val_len;
275bool detws_dbm_compact(DetwsDbm *db, WalStore *dst)
282 const DetwsDbmSlot *s = &db->slots[i];
286 put_u16(rec + 1, s->key_len);
287 put_u32(rec + 3, s->val_len);
288 memcpy(rec + DBM_HDR, s->key, s->key_len);
290 if (s->val_len && !wal_store_pread(db->wal, s->val_off, rec + DBM_HDR + s->key_len, s->val_len))
292 if (!wal_store_append(dst, rec, (uint32_t)(DBM_HDR + s->key_len + s->val_len)))
295 if (!wal_store_checkpoint(dst))
297 return detws_dbm_open(db, dst);
#define DETWS_DBM_VAL_MAX
#define DETWS_DBM_KEY_MAX
Log-structured hash key-value store on the WAL (DETWS_ENABLE_DBM, requires DETWS_ENABLE_WAL).