ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
dbm.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file dbm.h
6 * @brief Log-structured hash key-value store on the WAL (PROTOCORE_ENABLE_DBM, requires PROTOCORE_ENABLE_WAL).
7 *
8 * A Bitcask-style key-value store: the value data lives append-only in the write-ahead log (protocore_wal_store.h)
9 * and an in-RAM open-addressed hash index maps each live key to where its latest value sits in the log.
10 * This is the design the measured SD envelope wants (docs/FEATURE_PERFORMANCE.md): every write is one of
11 * the WAL's fast sequential appends, never a slow durable random write.
12 *
13 * - **put / delete** append one record to the WAL and update the index. Writes are batched (unsynced);
14 * call ::protocore_dbm_sync to checkpoint the WAL and make them durable.
15 * - **get** looks up the index and re-reads the value straight from the log (no per-key RAM copy).
16 * - **open** rebuilds the index by scanning the WAL, replaying puts and deletes in order, so the live
17 * key set is exactly what survived the last mount of the underlying store.
18 *
19 * The index is a fixed BSS array of ::PROTOCORE_DBM_SLOTS slots (no heap); keys are bounded by
20 * ::PROTOCORE_DBM_KEY_MAX and values by ::PROTOCORE_DBM_VAL_MAX. Everything fails closed at those bounds. Like the
21 * other services, drive it from one context (a worker / loop), not concurrently.
22 *
23 * On-media record payload (inside a WAL record): `[op u8][key_len u16][val_len u32][key][value]` (LE),
24 * op 0 = put, op 1 = delete (a tombstone, val_len 0).
25 */
26
27#ifndef PROTOCORE_DBM_H
28#define PROTOCORE_DBM_H
29
30#include "protocore_config.h" // the entry point: protocore_types.h for the widths
31
32#if PROTOCORE_ENABLE_DBM
33
35
37
38/** @brief One in-RAM index slot. `state`: 0 empty, 1 live, 2 deleted (tombstone, still probed through). */
39typedef struct
40{
41 uint8_t state;
42 uint16_t key_len;
43 uint64_t hash;
44 uint64_t val_off; ///< data-region offset of the value bytes in the WAL
45 uint32_t val_len;
46 char key[PROTOCORE_DBM_KEY_MAX];
47} protocore_dbm_slot;
48
49/** @brief A dbm handle bound to a mounted ::WalStore. Declare one (static for BSS); no heap. */
50typedef struct protocore_dbm
51{
52 WalStore *wal;
53 uint32_t count; ///< live keys
54 protocore_dbm_slot slots[PROTOCORE_DBM_SLOTS];
55} protocore_dbm;
56
57/**
58 * @brief Bind @p db to a mounted @p wal and rebuild the index by replaying the log.
59 * @return false if the log holds more distinct live keys than ::PROTOCORE_DBM_SLOTS (index would overflow).
60 */
61proto_bool protocore_dbm_open(struct protocore_dbm *db, WalStore *wal);
62
63/**
64 * @brief Insert or overwrite @p key -> @p val. Appends a WAL record and updates the index (not synced).
65 * @return false if @p key_len > ::PROTOCORE_DBM_KEY_MAX, @p val_len > ::PROTOCORE_DBM_VAL_MAX, the index is full
66 * (a new key with no free slot), or the WAL is full.
67 */
68proto_bool protocore_dbm_put(struct protocore_dbm *db, const char *key, uint16_t key_len, const uint8_t *val,
69 uint32_t val_len);
70
71/**
72 * @brief Fetch @p key's value into @p buf (up to @p cap).
73 * @return the value length on success, or -1 if the key is absent or the value is larger than @p cap.
74 */
75long protocore_dbm_get(struct protocore_dbm *db, const char *key, uint16_t key_len, uint8_t *buf, size_t cap);
76
77/**
78 * @brief Delete @p key (appends a tombstone record and drops it from the index).
79 * @return true if the key existed (and the tombstone was appended); false if absent or the WAL is full.
80 */
81proto_bool protocore_dbm_del(struct protocore_dbm *db, const char *key, uint16_t key_len);
82
83/** @brief @return true if @p key is live. */
84proto_bool protocore_dbm_contains(const struct protocore_dbm *db, const char *key, uint16_t key_len);
85
86/** @brief @return the number of live keys. */
87uint32_t protocore_dbm_count(const struct protocore_dbm *db);
88
89/** @brief Make all writes since the last sync durable (checkpoints the WAL). @return false on I/O failure. */
90proto_bool protocore_dbm_sync(struct protocore_dbm *db);
91
92/** @brief Per-key callback for ::protocore_dbm_iterate; return false to stop early. The key bytes are not
93 * NUL-terminated. Do not put/delete during iteration (it mutates the index). */
94typedef proto_bool (*protocore_dbm_iter_cb)(const char *key, uint16_t key_len, void *ctx);
95
96/** @brief Visit every live key (unordered). @return the number of keys visited. */
97uint32_t protocore_dbm_iterate(const struct protocore_dbm *db, protocore_dbm_iter_cb cb, void *ctx);
98
99/**
100 * @brief Bytes the live keys would occupy after a compaction (the summed framed size of one WAL record per
101 * live key). The current log (::protocore_wal_store_used on the bound store) is always at least this large; the
102 * difference is reclaimable dead space from overwritten and deleted keys. Pair the two to decide when the
103 * dead fraction is worth a ::protocore_dbm_compact.
104 */
105uint64_t protocore_dbm_live_bytes(const struct protocore_dbm *db);
106
107/**
108 * @brief Compact the store: copy only the live keys (the latest value each, no tombstones) into a freshly
109 * formatted destination @p dst, checkpoint it, then rebind @p db to @p dst and rebuild the index - reclaiming
110 * all space held by overwritten / deleted keys (Bitcask-style merge to new, never in place).
111 *
112 * @p dst must be a mounted, freshly formatted ::WalStore backed by a DIFFERENT device than @p db's current
113 * log. On success @p db reads and writes through @p dst (the old device can then be reused); on failure
114 * (@p dst too small, or an I/O error) @p db is left UNCHANGED on its original log, so no data is lost and the
115 * caller can retry or keep using the old store.
116 * @return true when every live key was copied, the destination checkpointed, and the index rebuilt.
117 */
118proto_bool protocore_dbm_compact(struct protocore_dbm *db, WalStore *dst);
119
121
122#endif // PROTOCORE_ENABLE_DBM
123
124#endif // PROTOCORE_DBM_H
#define PROTOCORE_DBM_KEY_MAX
#define PROTOCORE_DBM_SLOTS
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97