Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,9 @@ if(KVSPACE_BUILD_TESTS)
target_include_directories(test_art_scan PRIVATE src)
target_link_libraries(test_art_scan PRIVATE kvspace-c)
add_test(NAME art_scan COMMAND test_art_scan)

add_executable(test_handle_cache tests/test_handle_cache.c)
target_include_directories(test_handle_cache PRIVATE src)
target_link_libraries(test_handle_cache PRIVATE kvspace-c)
add_test(NAME handle_cache COMMAND test_handle_cache)
endif()
30 changes: 30 additions & 0 deletions src/durable_abi.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,36 @@ int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out,
return 0;
}

int kvspaceResolveRef(void *h, const char *key, kvspaceRef_t *ref) {
return kvspaceShmResolveRef((kvspace_t *)h, key, ref);
}

int kvspaceGetByRef(void *h, kvspaceRef_t *ref, const char *key_fallback,
uint8_t **out, uint32_t *out_len) {
int32_t len = 0;
uint8_t *d = kvspaceShmGetByRef((kvspace_t *)h, ref, key_fallback, &len);
if (!d || len <= 0) {
*out = NULL;
*out_len = 0;
return 0;
}
*out = d;
*out_len = (uint32_t)len;
return 0;
}

int kvspaceSetPartByRef(void *h, kvspaceRef_t *ref, const char *key_fallback,
uint32_t offset, const uint8_t *buf, uint32_t buf_len,
char *err, uint32_t err_cap) {
if (kvspaceShmSetPartByRef((kvspace_t *)h, ref, key_fallback, offset, buf,
buf_len) != 0) {
if (err && err_cap)
snprintf(err, err_cap, "kvspace: set-part-by-ref failed");
return 1;
}
return 0;
}

/* 指令边界回收读借用池:SHM 常驻映射,借用恒有效,no-op。 */
void kvspaceReadReset(void *h) { (void)h; }

Expand Down
242 changes: 228 additions & 14 deletions src/kvspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
enum { ART_N4 = 0,
ART_N16 = 1,
ART_N48 = 2,
ART_N256 = 3 };
ART_N256 = 3,
ART_MOVED = 255 };

typedef struct {
uint8_t type, prefix[ART_PREFIX_MAX], prefix_len;
Expand Down Expand Up @@ -385,34 +386,135 @@ static int32_t art_child(kvspace_t *kv, void *n, uint8_t b) {
return -1;
}

/* ---- art_search ---- */
static art_hdr_t *art_search(kvspace_t *kv, int32_t nid, const uint8_t *key,
int klen) {
if (nid < 0 || !key)
return NULL;
int d = 0;
static int32_t art_follow(kvspace_t *kv, int32_t id);

static int32_t art_walk(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen,
int d, int32_t *par, int *pard) {
while (nid >= 0) {
art_hdr_t *h = art_hdr(kv, nid);
if (!h)
return NULL;
if (!h || h->type == ART_MOVED)
return -1;
if (h->prefix_len) {
int s = pfx_shared(h->prefix, h->prefix_len, key + d, klen - d);
if (s != h->prefix_len) {
if (d + s < klen)
return NULL;
return -1;
if (s < h->prefix_len)
return NULL;
return -1;
}
d += h->prefix_len;
if (d > klen)
return NULL;
return -1;
}
if (d == klen)
return h->has_value ? h : NULL;
return h->has_value ? nid : -1;
if (par)
*par = nid;
if (pard)
*pard = d;
nid = art_child(kv, h, key[d]);
d++;
}
return NULL;
return -1;
}

/* ---- art_search ---- */
static int32_t art_find2(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen,
int32_t *par, int *pard) {
if (nid < 0 || !key)
return -1;
int32_t parent = -1;
int parent_d = 0;
int32_t id = art_walk(kv, nid, key, klen, 0, &parent, &parent_d);
if (par)
*par = parent;
if (pard)
*pard = parent_d;
return id;
}
static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key,
int klen) {
return art_find2(kv, nid, key, klen, NULL, NULL);
}

/* Last path '/' or kvlang member '·' (U+00B7, utf-8 C2 B7). */
static int last_key_sep(const uint8_t *key, int klen, int *seplen) {
int slash = -1, mid = -1;
for (int i = 0; i < klen; i++) {
if (key[i] == '/')
slash = i;
if (i + 1 < klen && key[i] == 0xC2 && key[i + 1] == 0xB7)
mid = i;
}
if (mid > slash) {
*seplen = 2;
return mid;
}
if (slash > 0) {
*seplen = 1;
return slash;
}
*seplen = 0;
return -1;
}

/* One walk: leaf plus the ancestor covering last '/' or '·' (after that
* node's prefix). First match: node at the separator, not a deeper unique
* prefix that swallowed it. */
static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key,
int klen, int last_sep, int seplen, int32_t *dirn,
int *dird) {
int d = 0;
int32_t dir = -1;
int dd = 0;
if (nid < 0 || !key)
return -1;
while (nid >= 0) {
art_hdr_t *h = art_hdr(kv, nid);
if (!h || h->type == ART_MOVED)
return -1;
int entry_d = d;
if (h->prefix_len) {
int s = pfx_shared(h->prefix, h->prefix_len, key + d, klen - d);
if (s != h->prefix_len)
return -1;
d += h->prefix_len;
if (d > klen)
return -1;
}
if (dir < 0 && last_sep > 0 && seplen > 0 &&
((entry_d <= last_sep && last_sep < d) ||
d == last_sep + seplen)) {
dir = nid;
dd = d;
}
if (d == klen) {
if (dirn)
*dirn = dir;
if (dird)
*dird = dd;
return h->has_value ? nid : -1;
}
nid = art_child(kv, h, key[d]);
d++;
}
return -1;
}
static art_hdr_t *art_search(kvspace_t *kv, int32_t nid, const uint8_t *key,
int klen) {
int32_t id = art_find(kv, nid, key, klen);
return id < 0 ? NULL : art_hdr(kv, id);
}
static int32_t art_follow(kvspace_t *kv, int32_t id) {
for (int i = 0; i < 8 && id >= 0; i++) {
art_hdr_t *h = art_hdr(kv, id);
if (!h)
return -1;
if (h->type != ART_MOVED)
return id;
id = (int32_t)h->box_offset;
}
return -1;
}

/* ---- node create ---- */
Expand Down Expand Up @@ -526,6 +628,9 @@ static int32_t art_grow(kvspace_t *kv, void *on) {
x->children[j] = tc;
}
}
oh->type = ART_MOVED;
oh->has_value = 0;
oh->box_offset = (uint64_t)(uint32_t)nid;
return nid;
}

Expand Down Expand Up @@ -1295,6 +1400,115 @@ uint8_t *kvspaceShmGet(kvspace_t *kv, const char *key, int resolve,
return raw;
}

/* gen>0: block_id is parent at depth gen; key must share that prefix. */
static int32_t ref_leaf(kvspace_t *kv, kvspaceRef_t *ref, const char *key) {
int32_t id = art_follow(kv, (int32_t)ref->block_id);
if (id < 0)
return -1;
ref->block_id = (uint32_t)id;
if (ref->gen == 0)
return id;
if (!key)
return -1;
int klen = (int)strlen(key);
int d = (int)ref->gen;
if (d < 0 || d >= klen)
return -1;
art_hdr_t *h = art_hdr(kv, id);
if (!h || h->type == ART_MOVED)
return -1;
int32_t cid = art_child(kv, h, (uint8_t)key[d]);
if (cid < 0)
return -1;
return art_walk(kv, cid, (const uint8_t *)key, klen, d + 1, NULL, NULL);
}

int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) {
if (!kv || !key || !ref)
return -1;
memset(ref, 0, sizeof(*ref));
if (kv_sync(kv) != 0)
return -1;
int klen = (int)strlen(key);
int seplen = 0;
int sep = last_key_sep((const uint8_t *)key, klen, &seplen);
int dir_d = 0;
int32_t dn = -1;
int32_t id = art_find_dir(kv, kv->hdr->art_root, (const uint8_t *)key, klen,
sep, seplen, &dn, &dir_d);
if (id < 0)
return -1;
ref->block_id = (uint32_t)id;
ref->gen = 0;
if (dn >= 0 && dir_d > 0) {
ref->parent_id = (uint32_t)dn;
ref->depth = (uint32_t)dir_d;
} else {
ref->parent_id = 0;
ref->depth = 0;
}
return 0;
}

uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref,
const char *key_fallback, int32_t *ol) {
if (!kv || !ref || !ol)
return NULL;
*ol = 0;
if (kv_sync(kv) != 0)
return NULL;
int32_t id = ref_leaf(kv, ref, key_fallback);
art_hdr_t *h = id >= 0 ? art_hdr(kv, id) : NULL;
if (h && h->has_value) {
uint8_t *raw;
int32_t rl;
if (read_tlv(kv, h->box_offset, &raw, &rl) == 0) {
*ol = rl;
return raw;
}
}
/* gen>0: parent walk miss — do not full-Get; caller falls back. */
if (ref->gen != 0)
return NULL;
if (!key_fallback)
return NULL;
uint8_t *raw = kvspaceShmGet(kv, key_fallback, 0, ol);
if (raw)
kvspaceShmResolveRef(kv, key_fallback, ref);
return raw;
}

int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref,
const char *key_fallback, uint32_t offset,
const uint8_t *buf, uint32_t buf_len) {
if (!kv || !ref || !buf)
return -1;
if (kv_sync(kv) != 0)
return -1;
int32_t id = ref_leaf(kv, ref, key_fallback);
art_hdr_t *h = id >= 0 ? art_hdr(kv, id) : NULL;
if (!h || !h->has_value) {
if (ref->gen != 0)
return -1;
if (!key_fallback)
return -1;
int32_t rl = 0;
uint8_t *d = kvspaceShmGet(kv, key_fallback, 0, &rl);
if (!d || offset + buf_len > (uint32_t)rl)
return -1;
memcpy(d + offset, buf, buf_len);
kvspaceShmResolveRef(kv, key_fallback, ref);
return 0;
}
uint8_t *raw;
int32_t rl;
if (read_tlv(kv, h->box_offset, &raw, &rl) < 0 ||
offset + buf_len > (uint32_t)rl)
return -1;
memcpy(raw + offset, buf, buf_len);
return 0;
}

/* ── 值/索引分离(方案2,对齐 kvspace-durable backend.rs) ────────── */
/* 坐标段工具见 xvalue.c:kvspaceCoordIsCoord / kvspaceParseCoord /
* kvspaceCoordCmp。 */
Expand Down
15 changes: 15 additions & 0 deletions src/kvspace_shm.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@

typedef struct kvspace kvspace_t;

/* 对齐 kvspace/include/kvspace/kvspace.h:叶子 + 目录祖先。 */
typedef struct {
uint32_t block_id;
uint32_t gen;
uint32_t parent_id;
uint32_t depth;
} kvspaceRef_t;

/* ================================================================
* 生命周期
* ================================================================ */
Expand All @@ -32,6 +40,13 @@ void kvspaceShmClose(kvspace_t *kv);
uint8_t *kvspaceShmGet(kvspace_t *kv, const char *key, int resolve,
int32_t *out_len);

int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref);
uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref,
const char *key_fallback, int32_t *out_len);
int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref,
const char *key_fallback, uint32_t offset,
const uint8_t *buf, uint32_t buf_len);

// Set: 写入 value(TLV 编码的字节)。总是穿透 link 写入 target。
int kvspaceShmSet(kvspace_t *kv, const char *key, const uint8_t *val,
int32_t val_len);
Expand Down
Loading
Loading