// SPDX-FileCopyrightText: © 2026 Vladimir Zorin // SPDX-License-Identifier: LicenseRef-OWL-1.0-or-later // Licensed under OWL v1.0+. See LICENSE. #ifndef MNEME_H #define MNEME_H #include #include #include #include /* ── Constants ─────────────────────────────────────────────────────── */ #define MNEME_PAGE_SIZE 4096 #define MNEME_MAGIC "MNEM" #define MNEME_MAGIC_LEN 4 #define MNEME_VERSION 4 #define MNEME_MAX_KEY_LEN 4000 #define MNEME_READER_LOCK_BASE ((off_t)1 << 56) #define MNEME_RETIRE_BATCH_PGNOS 480 #define MNEME_RETIRE_HARVEST_BUDGET 131072 /* Cell layout constants */ #define MNEME_LCELL_HDR 15 /* key_len(2) + val_len(4) + ttl(8) + type_tag(1) */ #define MNEME_BCELL_HDR 6 /* child_pgno(4) + key_len(2) */ /* Page types */ #define MNEME_PAGE_META 0x00 #define MNEME_PAGE_BRANCH 0x01 #define MNEME_PAGE_LEAF 0x02 #define MNEME_PAGE_OVERFLOW 0x03 /* Value type tags */ #define MNEME_TYPE_BYTES 0x01 #define MNEME_TYPE_INTEGER 0x02 #define MNEME_TYPE_FLOAT 0x03 #define MNEME_TYPE_ZSET 0x04 #define MNEME_TYPE_LIST 0x05 #define MNEME_TYPE_VECTOR 0x06 #define MNEME_TYPE_FT_META 0x07 /* Open flags */ #define MNEME_OPEN_READONLY 0x01 #define MNEME_OPEN_CREATE 0x02 #define MNEME_OPEN_NOSYNC 0x04 /* Page header size */ #define MNEME_PAGE_HDR_SIZE 16 /* Max dirty pages per transaction (grows dynamically) */ #define MNEME_DIRTY_INIT 64 /* ── Meta page layout ──────────────────────────────────────────────── */ typedef struct { char magic[4]; /* "MNEM" */ uint32_t version; /* format version */ uint32_t page_size; /* 4096 */ uint64_t page_count; /* total pages in file */ uint32_t retired_root_pgno; /* retired-batch tree root (0 = empty) */ uint32_t keyspace_root_pgno; /* root of keyspace directory B-tree */ uint64_t txn_id; /* transaction counter */ uint64_t retired_page_count; /* total retired pages tracked */ uint32_t checksum; /* FNV-1a over all preceding fields */ /* pad to a fixed size within the page */ } __attribute__((packed)) mneme_meta_t; /* ── Encryption key slot format (stored in keyspace directory B-tree) ── */ #define MNEME_ENC_MAX_SLOTS 30 typedef struct { uint8_t fingerprint[32]; uint8_t ephemeral_pk[32]; uint8_t nonce[12]; uint8_t wrapped_dek[32]; uint8_t tag[16]; } __attribute__((packed)) mneme_key_slot_t; /* ── Page header ───────────────────────────────────────────────────── */ typedef struct { uint8_t page_type; uint16_t key_count; uint16_t free_offset; uint16_t cell_area_end; uint32_t right_ptr; uint8_t _pad[3]; } __attribute__((packed)) mneme_page_hdr_t; /* ── Dirty page entry ──────────────────────────────────────────────── */ typedef enum { MNEME_DIRTY_ALLOCATED, MNEME_DIRTY_REUSED, } mneme_dirty_origin_t; typedef struct { uint32_t pgno; uint8_t data[MNEME_PAGE_SIZE]; mneme_dirty_origin_t origin; } mneme_dirty_page_t; /* ── Database handle ───────────────────────────────────────────────── */ typedef struct { char *path; int fd; int probe_fd; uint8_t *map; size_t map_size; uint64_t page_count; uint32_t flags; mneme_meta_t meta; int closed; /* Guards against nested write txns on one handle: flock(2) is per-OFD, so a second LOCK_EX on the same fd succeeds silently and the inner commit's LOCK_UN would drop the outer txn's exclusivity. */ int write_txn_active; } mneme_db_t; /* ── Transaction handle ────────────────────────────────────────────── */ typedef struct { mneme_db_t *db; mneme_meta_t meta; mneme_dirty_page_t *dirty; int dirty_count; int dirty_cap; int readonly; int committed; int reader_lock_fd; uint64_t reader_txn_id; uint64_t commit_txn_id; uint32_t *reuse_pages; int reuse_count; int reuse_cap; uint32_t *consumed_reuse_pages; int consumed_reuse_count; int consumed_reuse_cap; uint32_t *fresh_free_pages; int fresh_free_count; int fresh_free_cap; struct { uint64_t txn_id; uint32_t chunk_idx; uint32_t *pages; int page_count; } *harvested; int harvested_count; int harvested_cap; uint32_t *pending_retire; int pending_retire_count; int pending_retire_cap; } mneme_txn_t; #define MNEME_CURSOR_STACK_DEPTH 32 typedef struct { mneme_txn_t *txn; uint32_t root_pgno; struct { uint32_t pgno; int idx; } stack[MNEME_CURSOR_STACK_DEPTH]; int depth; int valid; const uint8_t *leaf_page; uint32_t leaf_pgno; } mneme_cursor_t; int mneme_db_open(const char *path, uint32_t flags, mneme_db_t **out); void mneme_db_close(mneme_db_t *db); int mneme_db_sync(mneme_db_t *db); int mneme_txn_begin(mneme_db_t *db, int readonly, mneme_txn_t **out); int mneme_txn_commit(mneme_txn_t *txn); void mneme_txn_abort(mneme_txn_t *txn); int mneme_reader_lock_acquire(mneme_txn_t *txn); void mneme_reader_lock_release(mneme_txn_t *txn); int mneme_reader_probe_older(mneme_db_t *db, uint64_t retire_txn_id_minus_one); int mneme_ofd_lock_probe(int fd); uint32_t mneme_page_alloc(mneme_txn_t *txn); int mneme_page_free(mneme_txn_t *txn, uint32_t pgno); int mneme_page_discard_dirty(mneme_txn_t *txn, uint32_t pgno); const uint8_t *mneme_page_read(mneme_db_t *db, uint32_t pgno); int mneme_btree_get(mneme_txn_t *txn, uint32_t root_pgno, const uint8_t *key, uint16_t key_len, const uint8_t **val_out, uint32_t *val_len_out, uint8_t *type_tag_out, int64_t *ttl_out); uint32_t mneme_btree_put(mneme_txn_t *txn, uint32_t root_pgno, const uint8_t *key, uint16_t key_len, const uint8_t *val, uint32_t val_len, uint8_t type_tag, int64_t ttl_expires); uint32_t mneme_btree_del(mneme_txn_t *txn, uint32_t root_pgno, const uint8_t *key, uint16_t key_len); int mneme_btree_free(mneme_txn_t *txn, uint32_t root_pgno); int mneme_cursor_open(mneme_txn_t *txn, uint32_t root_pgno, mneme_cursor_t **out); int mneme_cursor_first(mneme_cursor_t *cur); int mneme_cursor_last(mneme_cursor_t *cur); int mneme_cursor_seek(mneme_cursor_t *cur, const uint8_t *key, uint16_t key_len); int mneme_cursor_next(mneme_cursor_t *cur); int mneme_cursor_prev(mneme_cursor_t *cur); int mneme_cursor_key(mneme_cursor_t *cur, const uint8_t **key_out, uint16_t *key_len_out); int mneme_cursor_val(mneme_cursor_t *cur, const uint8_t **val_out, uint32_t *val_len_out, uint8_t *type_tag_out, int64_t *ttl_out); int mneme_cursor_entry(mneme_cursor_t *cur, const uint8_t **key_out, uint16_t *key_len_out, const uint8_t **val_out, uint32_t *val_len_out, uint8_t *type_tag_out, int64_t *ttl_out); void mneme_cursor_close(mneme_cursor_t *cur); uint32_t mneme_overflow_write(mneme_txn_t *txn, const uint8_t *data, uint32_t len); int mneme_overflow_read(mneme_txn_t *txn, uint32_t pgno, uint8_t *buf, uint32_t buf_len, uint32_t *bytes_read); int mneme_overflow_free(mneme_txn_t *txn, uint32_t pgno); float mneme_vec_dot(const float *a, const float *b, int dim); float mneme_vec_cosine(const float *a, const float *b, int dim); float mneme_vec_l2(const float *a, const float *b, int dim); void mneme_vec_normalize(float *v, int dim); #define MNEME_FT_MAX_TERMS 65536 #define MNEME_FT_MAX_TERM_LEN 255 #define MNEME_FT_MIN_TERM_LEN 2 typedef struct { uint8_t *term; uint16_t len; } mneme_ft_term_t; typedef struct { mneme_ft_term_t *terms; uint8_t *buf; int count; int cap; } mneme_ft_terms_t; int mneme_ft_analyze(const uint8_t *text, uint32_t text_len, mneme_ft_terms_t *out); void mneme_ft_terms_free(mneme_ft_terms_t *t); #define MNEME_OVERFLOW_FLAG 0x80 int mneme_retire_persist(mneme_txn_t *txn); int mneme_retire_harvest_safe(mneme_txn_t *txn, int page_budget); int mneme_retire_apply_harvested(mneme_txn_t *txn); void mneme_retire_key_pack(uint8_t out[12], uint64_t txn_id, uint32_t chunk_idx); void mneme_retire_key_unpack(const uint8_t in[12], uint64_t *txn_id, uint32_t *chunk_idx); static inline void mneme_be32enc(uint8_t out[4], uint32_t v) { out[0] = (uint8_t)(v >> 24); out[1] = (uint8_t)(v >> 16); out[2] = (uint8_t)(v >> 8); out[3] = (uint8_t)v; } static inline uint32_t mneme_be32dec(const uint8_t in[4]) { return ((uint32_t)in[0] << 24) | ((uint32_t)in[1] << 16) | ((uint32_t)in[2] << 8) | (uint32_t)in[3]; } static inline void mneme_be64enc(uint8_t out[8], uint64_t v) { out[0] = (uint8_t)(v >> 56); out[1] = (uint8_t)(v >> 48); out[2] = (uint8_t)(v >> 40); out[3] = (uint8_t)(v >> 32); out[4] = (uint8_t)(v >> 24); out[5] = (uint8_t)(v >> 16); out[6] = (uint8_t)(v >> 8); out[7] = (uint8_t)v; } static inline uint64_t mneme_be64dec(const uint8_t in[8]) { return ((uint64_t)in[0] << 56) | ((uint64_t)in[1] << 48) | ((uint64_t)in[2] << 40) | ((uint64_t)in[3] << 32) | ((uint64_t)in[4] << 24) | ((uint64_t)in[5] << 16) | ((uint64_t)in[6] << 8) | (uint64_t)in[7]; } static inline int mneme_keycmp(const uint8_t *a, uint16_t alen, const uint8_t *b, uint16_t blen) { uint16_t min = (alen < blen) ? alen : blen; int r = memcmp(a, b, min); if (r != 0) return r; return (alen < blen) ? -1 : (alen > blen) ? 1 : 0; } static inline uint16_t mneme_cptr_get(const uint8_t *page, int idx) { uint16_t v; memcpy(&v, page + MNEME_PAGE_HDR_SIZE + idx * 2, 2); return v; } /* ── Shared page search helpers ───────────────────────────────────── */ static inline int mneme_leaf_search(const uint8_t *page, const uint8_t *key, uint16_t key_len, int *found) { const mneme_page_hdr_t *hdr = (const mneme_page_hdr_t *)page; int lo = 0, hi = (int)hdr->key_count - 1; *found = 0; while (lo <= hi) { int mid = (lo + hi) / 2; uint16_t off = mneme_cptr_get(page, mid); uint16_t ckl; memcpy(&ckl, page + off, 2); const uint8_t *ck = page + off + MNEME_LCELL_HDR; int cmp = mneme_keycmp(key, key_len, ck, ckl); if (cmp == 0) { *found = 1; return mid; } else if (cmp < 0) hi = mid - 1; else lo = mid + 1; } return lo; } static inline int mneme_branch_search(const uint8_t *page, const uint8_t *key, uint16_t key_len) { const mneme_page_hdr_t *hdr = (const mneme_page_hdr_t *)page; int lo = 0, hi = (int)hdr->key_count - 1; while (lo <= hi) { int mid = (lo + hi) / 2; uint16_t off = mneme_cptr_get(page, mid); uint16_t ckl; memcpy(&ckl, page + off + 4, 2); const uint8_t *ck = page + off + MNEME_BCELL_HDR; int cmp = mneme_keycmp(key, key_len, ck, ckl); if (cmp < 0) hi = mid - 1; else lo = mid + 1; } return lo; } static inline uint32_t mneme_branch_child(const uint8_t *page, int child_idx) { const mneme_page_hdr_t *hdr = (const mneme_page_hdr_t *)page; if (child_idx >= (int)hdr->key_count) return hdr->right_ptr; uint32_t cpgno; uint16_t off = mneme_cptr_get(page, child_idx); memcpy(&cpgno, page + off, 4); return cpgno; } /* ── Internal helpers ──────────────────────────────────────────────── */ uint32_t mneme_checksum(const void *data, size_t len); int mneme_mmap_refresh(mneme_db_t *db); int mneme_meta_refresh(mneme_db_t *db); int mneme_meta_validate(const mneme_meta_t *m); /* Look up a dirty page by pgno; returns pointer or NULL */ uint8_t *mneme_dirty_find(mneme_txn_t *txn, uint32_t pgno); /* Resolve a page: returns dirty copy if exists, else mmap'd page */ const uint8_t *mneme_page_resolve(mneme_txn_t *txn, uint32_t pgno); #endif /* MNEME_H */