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(
const pc_dbm *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 const pc_dbm_slot *s = &db->slots[j];
66 if (s->state == 1 && s->hash == hash && s->key_len == key_len && memcmp(s->key, key, key_len) == 0)
76int reserve(
const pc_dbm *db, uint64_t hash,
const char *key, uint16_t key_len,
bool *is_new)
79 size_t start = (size_t)(hash % n);
81 for (
size_t i = 0; i < n; i++)
83 size_t j = (start + i) % n;
84 const pc_dbm_slot *s = &db->slots[j];
87 if (s->hash == hash && s->key_len == key_len && memcmp(s->key, key, key_len) == 0)
104 return first_free >= 0 ? first_free : (int)j;
117void replay_cb(uint64_t seq, uint64_t data_off,
const uint8_t *payload, uint32_t len,
void *ctx)
120 ReplayCtx *rc = (ReplayCtx *)ctx;
126 uint8_t op = payload[0];
127 uint16_t klen = get_u16(payload + 1);
128 uint32_t vlen = get_u32(payload + 3);
133 if (DBM_HDR + (
size_t)klen + vlen > len)
137 const char *key = (
const char *)(payload + DBM_HDR);
138 uint64_t h = key_hash(key, klen);
142 int slot = reserve(db, h, key, klen, &is_new);
148 pc_dbm_slot *s = &db->slots[slot];
156 memcpy(s->key, key, klen);
157 s->val_off = data_off + WAL_RECORD_HEADER + DBM_HDR + klen;
162 int slot = find_live(db, h, key, klen);
165 db->slots[slot].state = 2;
172bool pc_dbm_open(pc_dbm *db, WalStore *wal)
174 memset(db, 0,
sizeof(*db));
176 ReplayCtx rc = {db,
false};
178 pc_wal_store_scan(wal, replay_cb, &rc, scratch,
sizeof(scratch));
182bool pc_dbm_put(pc_dbm *db,
const char *key, uint16_t key_len,
const uint8_t *val, uint32_t val_len)
188 uint64_t h = key_hash(key, key_len);
190 int slot = reserve(db, h, key, key_len, &is_new);
198 put_u16(rec + 1, key_len);
199 put_u32(rec + 3, val_len);
200 memcpy(rec + DBM_HDR, key, key_len);
203 memcpy(rec + DBM_HDR + key_len, val, val_len);
205 uint64_t old_head = pc_wal_store_used(db->wal);
206 if (!pc_wal_store_append(db->wal, rec, (uint32_t)(DBM_HDR + key_len + val_len)))
211 pc_dbm_slot *s = &db->slots[slot];
218 s->key_len = key_len;
219 memcpy(s->key, key, key_len);
220 s->val_off = old_head + WAL_RECORD_HEADER + DBM_HDR + key_len;
221 s->val_len = val_len;
225long pc_dbm_get(pc_dbm *db,
const char *key, uint16_t key_len, uint8_t *buf,
size_t cap)
227 uint64_t h = key_hash(key, key_len);
228 int slot = find_live(db, h, key, key_len);
233 const pc_dbm_slot *s = &db->slots[slot];
234 if (s->val_len > cap)
238 if (s->val_len && !pc_wal_store_pread(db->wal, s->val_off, buf, s->val_len))
242 return (
long)s->val_len;
245bool pc_dbm_del(pc_dbm *db,
const char *key, uint16_t key_len)
247 uint64_t h = key_hash(key, key_len);
248 int slot = find_live(db, h, key, key_len);
255 put_u16(rec + 1, key_len);
257 memcpy(rec + DBM_HDR, key, key_len);
258 if (!pc_wal_store_append(db->wal, rec, (uint32_t)(DBM_HDR + key_len)))
262 db->slots[slot].state = 2;
267bool pc_dbm_contains(
const pc_dbm *db,
const char *key, uint16_t key_len)
269 return find_live(db, key_hash(key, key_len), key, key_len) >= 0;
272uint32_t pc_dbm_count(
const pc_dbm *db)
277bool pc_dbm_sync(pc_dbm *db)
279 return pc_wal_store_checkpoint(db->wal);
282uint32_t pc_dbm_iterate(
const pc_dbm *db, pc_dbm_iter_cb cb,
void *ctx)
284 uint32_t visited = 0;
287 const pc_dbm_slot *s = &db->slots[i];
293 if (cb && !cb(s->key, s->key_len, ctx))
301uint64_t pc_dbm_live_bytes(
const pc_dbm *db)
306 const pc_dbm_slot *s = &db->slots[i];
309 bytes += WAL_RECORD_HEADER + DBM_HDR + s->key_len + s->val_len;
315bool pc_dbm_compact(pc_dbm *db, WalStore *dst)
322 const pc_dbm_slot *s = &db->slots[i];
328 put_u16(rec + 1, s->key_len);
329 put_u32(rec + 3, s->val_len);
330 memcpy(rec + DBM_HDR, s->key, s->key_len);
332 if (s->val_len && !pc_wal_store_pread(db->wal, s->val_off, rec + DBM_HDR + s->key_len, s->val_len))
336 if (!pc_wal_store_append(dst, rec, (uint32_t)(DBM_HDR + s->key_len + s->val_len)))
341 if (!pc_wal_store_checkpoint(dst))
345 return pc_dbm_open(db, dst);
Log-structured hash key-value store on the WAL (PC_ENABLE_DBM, requires PC_ENABLE_WAL).