From 15fac3cda189cb608bda9672dc161aaea9df544b Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Fri, 13 Feb 2026 17:05:41 +0800 Subject: [PATCH] introduce kv --- src/flake.lock | 8 +- src/include/kv.h | 12 ++ src/lib/kv.c | 312 ++++++++++++++++++++++++++++++++++++ src/lib/meson.build | 2 + src/lib/testsuite/test.c | 2 + src/lib/testsuite/test_kv.c | 172 ++++++++++++++++++++ 6 files changed, 504 insertions(+), 4 deletions(-) create mode 100644 src/include/kv.h create mode 100644 src/lib/kv.c create mode 100644 src/lib/testsuite/test_kv.c diff --git a/src/flake.lock b/src/flake.lock index a6005710..0d915305 100644 --- a/src/flake.lock +++ b/src/flake.lock @@ -117,11 +117,11 @@ "xbuild": "xbuild" }, "locked": { - "lastModified": 1770950940, - "narHash": "sha256-pVnwAEaB6s2vIwkktL1xOP0pyV4VcPyfQYoIXKmv6Yk=", + "lastModified": 1770966016, + "narHash": "sha256-HZEy2nuM2g8anxKidaxv5hbDlV1+4yk18Dzre8fSAHE=", "ref": "master", - "rev": "6f6bb5b4e0b706de2358de462a1fc4889f40c7a1", - "revCount": 8, + "rev": "e5f39839522dcfa7a66dc09a177ba89034659580", + "revCount": 10, "type": "git", "url": "ssh://forgejo@gitea.shenrs.eu/rshen/librtti" }, diff --git a/src/include/kv.h b/src/include/kv.h new file mode 100644 index 00000000..ee4f4104 --- /dev/null +++ b/src/include/kv.h @@ -0,0 +1,12 @@ +#if !defined(_KV_H_) + +#define _KV_H_ 1 + +#include + +typedef void (*kv_value_kill_fn_t)(void*); + +extern typeinfo_t kvtab_int64_type; +extern typeinfo_t kvtab_string_type; + +#endif diff --git a/src/lib/kv.c b/src/lib/kv.c new file mode 100644 index 00000000..0a2e5cb5 --- /dev/null +++ b/src/lib/kv.c @@ -0,0 +1,312 @@ +#include +#include +#include +#include + +#include "kv.h" +#include "utils.h" + +typedef struct kvent_ kvent_t; + +struct kvent_ { + kvent_t* next; + void* key; + void* value; +}; + +typedef struct { + uint32_t nslots; + kv_value_kill_fn_t val_killer; + kvent_t** slots; +} kvtab_t; + +static size_t kvtab_datasize(va_list ap) +{ + size_t sz = sizeof(kvtab_t); + return sz; +} + +static int kvtab_init(meta_obj_t* obj, va_list ap) +{ + kvtab_t* kvtab = RTTI_OBJ_TO_DATA(kvtab_t, obj); + uint32_t nslots = va_arg(ap, uint32_t); + kv_value_kill_fn_t killer = va_arg(ap, kv_value_kill_fn_t); + + if (nslots <= 1) { + return -EINVAL; + } + + kvtab->slots = (kvent_t**)calloc(nslots, sizeof(*(kvtab->slots))); + if (kvtab->slots == NULL) { + return -ENOMEM; + } + + kvtab->nslots = nslots; + kvtab->val_killer = killer; + + return 1; +} + +static void kvtab_int64_destroy(meta_obj_t* obj) +{ + kvtab_t* kvtab = RTTI_OBJ_TO_DATA(kvtab_t, obj); + int i; + + for (i = 0; i < kvtab->nslots; i++) { + kvent_t* e = NULL; + while ((e = kvtab->slots[i]) != NULL) { + kvtab->slots[i] = e->next; + e->next = NULL; + if (kvtab->val_killer != NULL) { + kvtab->val_killer(e->value); + } + + FREEUP(e); + } + } + + FREEUP(kvtab->slots); +} + +static kvent_t* kvtab_int64_get(kvtab_t* t, uint64_t key) +{ + uint32_t hc; + kvent_t* e = NULL; + + hc = hash_block((char*)&key, sizeof(key)) % t->nslots; + e = t->slots[hc]; + + while (e != NULL) { + if (((uint64_t)(long)e->key) == key) { + break; + } + + e = e->next; + } + + return e; +} +static int kvtab_int64_add(meta_obj_t* obj, void* key, void* value) +{ + kvtab_t* t = RTTI_OBJ_TO_DATA(kvtab_t, obj); + kvent_t* e = NULL; + uint32_t hc; + uint64_t keyint; + + e = kvtab_int64_get(t, (uint64_t)(long)key); + if (e != NULL) { + return -EEXIST; + } + + e = (kvent_t*)calloc(1, sizeof(*e)); + if (e == NULL) { + return -ENOMEM; + } + + e->key = key; + e->value = value; + + keyint = (uint64_t)(long)key; + hc = hash_block((char*)&keyint, sizeof(keyint)) % t->nslots; + + e->next = t->slots[hc]; + t->slots[hc] = e; + + return 1; +} + +static int kvtab_int64_del(meta_obj_t* obj, void* key) +{ + uint64_t keyint = (uint64_t)(long)key; + kvtab_t* t = RTTI_OBJ_TO_DATA(kvtab_t, obj); + kvent_t** e = NULL; + uint32_t hc; + + hc = hash_block((char*)&keyint, sizeof(keyint)) % t->nslots; + e = &(t->slots[hc]); + while ((*e) != NULL) { + if ((uint64_t)(long)(*e)->key == keyint) { + kvent_t* e1 = *e; + + *e = e1->next; + + if (t->val_killer != NULL) { + t->val_killer(e1->value); + } + + FREEUP(e1); + + return 1; + } + + e = &((*e)->next); + } + + return 0; +} + +static int kvtab_int64_find(meta_obj_t* obj, void* key, void** value) +{ + uint64_t keyint = (uint64_t)(long)key; + kvtab_t* t = RTTI_OBJ_TO_DATA(kvtab_t, obj); + kvent_t* e = NULL; + + if (value == NULL) { + return -EINVAL; + } + + e = kvtab_int64_get(t, keyint); + if (e == NULL) { + return -ENOENT; + } + + *value = e->value; + + return 1; +} + +static void kvtab_string_destroy(meta_obj_t* obj) +{ + kvtab_t* kvtab = RTTI_OBJ_TO_DATA(kvtab_t, obj); + int i; + + for (i = 0; i < kvtab->nslots; i++) { + kvent_t* e = NULL; + + while ((e = kvtab->slots[i]) != NULL) { + kvtab->slots[i] = e->next; + e->next = NULL; + FREEUP(e->key); + + if (kvtab->val_killer != NULL) { + kvtab->val_killer(e->value); + } + + FREEUP(e); + } + } + + FREEUP(kvtab->slots); +} + +static kvent_t* kvtab_string_get(kvtab_t* t, char* key) +{ + uint32_t hc; + kvent_t* e = NULL; + + hc = hash_string(key) % t->nslots; + e = t->slots[hc]; + + while (e != NULL) { + if (strcmp((char*)e->key, key) == 0) { + break; + } + + e = e->next; + } + + return e; +} + +static int kvtab_string_add(meta_obj_t* obj, void* key, void* value) +{ + kvtab_t* t = RTTI_OBJ_TO_DATA(kvtab_t, obj); + kvent_t* e = NULL; + uint32_t hc; + + e = kvtab_string_get(t, (char*)key); + if (e != NULL) { + return -EEXIST; + } + + e = (kvent_t*)calloc(1, sizeof(*e)); + if (e == NULL) { + return -ENOMEM; + } + + e->key = strdup((char*)key); + if (e->key == NULL) { + FREEUP(e); + return -ENOMEM; + } + + e->value = value; + + hc = hash_string((char*)key) % t->nslots; + + e->next = t->slots[hc]; + t->slots[hc] = e; + + return 1; +} + +static int kvtab_string_del(meta_obj_t* obj, void* key) +{ + kvtab_t* t = RTTI_OBJ_TO_DATA(kvtab_t, obj); + kvent_t** e = NULL; + uint32_t hc; + + hc = hash_string((char*)key) % t->nslots; + e = &(t->slots[hc]); + + while ((*e) != NULL) { + if (strcmp((char*)(*e)->key, (char*)key) == 0) { + kvent_t* e1 = *e; + + *e = e1->next; + FREEUP(e1->key); + + if (t->val_killer != NULL) { + t->val_killer(e1->value); + } + + FREEUP(e1); + + return 1; + } + + e = &((*e)->next); + } + + return 0; +} + +static int kvtab_string_find(meta_obj_t* obj, void* key, void** value) +{ + kvtab_t* t = RTTI_OBJ_TO_DATA(kvtab_t, obj); + kvent_t* e = NULL; + + if (value == NULL) { + return -EINVAL; + } + + e = kvtab_string_get(t, (char*)key); + if (e == NULL) { + return -ENOENT; + } + + *value = e->value; + return 1; +} + +typeinfo_t kvtab_int64_type + = { .name = "kvtab_int", + .privateSize = 0, + .dataSize = (datasize_fn_t)kvtab_datasize, + .init = (obj_init_fn_t)kvtab_init, + .destroy = (obj_destroy_fn_t)kvtab_int64_destroy, + .interfaces = { .kv = &(intf_kv_t) { + .add_key = (key_add_fn_t)kvtab_int64_add, + .del_key = (key_del_fn_t)kvtab_int64_del, + .find_key = (key_find_fn_t)kvtab_int64_find } } }; + +typeinfo_t kvtab_string_type + = { .name = "kvtab_int", + .privateSize = 0, + .dataSize = (datasize_fn_t)kvtab_datasize, + .init = (obj_init_fn_t)kvtab_init, + .destroy = (obj_destroy_fn_t)kvtab_string_destroy, + .interfaces = { .kv = &(intf_kv_t) { + .add_key = (key_add_fn_t)kvtab_string_add, + .del_key = (key_del_fn_t)kvtab_string_del, + .find_key = (key_find_fn_t)kvtab_string_find } } }; diff --git a/src/lib/meson.build b/src/lib/meson.build index 3eb6bb03..e9c79b8e 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -83,6 +83,7 @@ libxce_src = [ gen_srcs[0], 'helper.c', 'resreq.c', 'bitset.c', + 'kv.c', ] incdir = include_directories('../include') @@ -135,6 +136,7 @@ libxce_test_src = ['testsuite/test.c', 'testsuite/test_xvec.c', 'testsuite/test_resreq.c', 'testsuite/test_bitset.c', + 'testsuite/test_kv.c', ] libxce_test_deps = [libxce_deps, librtti_dep, unity_dep, libxce_dep] diff --git a/src/lib/testsuite/test.c b/src/lib/testsuite/test.c index b7a9957f..ef3ee050 100644 --- a/src/lib/testsuite/test.c +++ b/src/lib/testsuite/test.c @@ -23,6 +23,7 @@ extern void test_limit(void); extern void test_xvec(void); extern void test_resreq(void); extern void test_bitset(void); +extern void test_kv(void); void setUp(void) { return; } @@ -115,6 +116,7 @@ int main(int argc, char* argv[]) TEST(test_xvec); TEST(test_resreq); TEST(test_bitset); + TEST(test_kv); return UNITY_END(); } diff --git a/src/lib/testsuite/test_kv.c b/src/lib/testsuite/test_kv.c new file mode 100644 index 00000000..8491e1fe --- /dev/null +++ b/src/lib/testsuite/test_kv.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include + +#include + +#include "kv.h" + +static void test_kv_int_create() +{ + var tab = makeInstance(&kvtab_int64_type, 31, NULL); + + TEST_ASSERT_TRUE(tab != NULL); + + destroy(tab); +} + +static void test_kv_int1() +{ + var tab = makeInstance(&kvtab_int64_type, 31, NULL); + int i; + int rc; + + TEST_ASSERT_TRUE(tab != NULL); + + for (i = 0; i < 9; i++) { + rc = key_add(tab, (void*)(long)i, (void*)(long)(i * i)); + + TEST_ASSERT_TRUE(rc >= 0); + } + + for (i = 0; i < 9; i++) { + void* v; + + rc = key_find(tab, (void*)(long)i, &v); + TEST_ASSERT_TRUE(rc > 0); + + TEST_ASSERT_TRUE((int)(long)v == i * i); + } + + for (i = 100; i < 199; i++) { + void* v = NULL; + + rc = key_find(tab, (void*)(long)i, &v); + + TEST_ASSERT_TRUE(rc == -ENOENT); + } + + for (i = 0; i < 9; i++) { + void* v; + + rc = key_del(tab, (void*)(long)i); + TEST_ASSERT_TRUE(rc >= 0); + + rc = key_find(tab, (void*)(long)i, &v); + TEST_ASSERT_TRUE(rc == -ENOENT); + } + destroy(tab); +} + +static void my_free(void* p) +{ + if (p == NULL) { + return; + } + + free(p); +} + +static void test_kv_int2() +{ + var tab = makeInstance(&kvtab_int64_type, 31, my_free); + int i; + int rc; + char buf[128]; + + TEST_ASSERT_TRUE(tab != NULL); + + for (i = 0; i < 9; i++) { + char* p = NULL; + + sprintf(buf, "value#%d", i); + p = strdup(buf); + + TEST_ASSERT_TRUE(p != NULL); + rc = key_add(tab, (void*)(long)i, (void*)p); + + TEST_ASSERT_TRUE(rc >= 0); + } + + for (i = 0; i < 9; i++) { + void* v; + + rc = key_find(tab, (void*)(long)i, &v); + TEST_ASSERT_TRUE(rc > 0); + + sprintf(buf, "value#%d", i); + TEST_ASSERT_TRUE(strcmp(buf, (char*)v) == 0); + } + + destroy(tab); +} + +static void test_kv_string1() +{ + var tab = makeInstance(&kvtab_string_type, 31, NULL); + int i; + int rc; + char buf[64]; + + TEST_ASSERT_TRUE(tab != NULL); + + for (i = 0; i < 9; i++) { + sprintf(buf, "key#%d", i); + rc = key_add(tab, (void*)buf, (void*)(long)(i * i)); + + TEST_ASSERT_TRUE(rc >= 0); + } + + for (i = 0; i < 9; i++) { + void* v; + + sprintf(buf, "key#%d", i); + rc = key_find(tab, (void*)buf, &v); + TEST_ASSERT_TRUE(rc > 0); + + TEST_ASSERT_TRUE((int)(long)v == i * i); + } + + for (i = 100; i < 199; i++) { + void* v = NULL; + + sprintf(buf, "key#%d", i); + rc = key_find(tab, (void*)buf, &v); + + TEST_ASSERT_TRUE(rc == -ENOENT); + } + + for (i = 0; i < 9; i++) { + void* v; + + sprintf(buf, "key#%d", i); + rc = key_del(tab, (void*)buf); + TEST_ASSERT_TRUE(rc >= 0); + + rc = key_find(tab, (void*)buf, &v); + TEST_ASSERT_TRUE(rc == -ENOENT); + } + destroy(tab); +} + +static void test_kv_string_create() +{ + var tab = makeInstance(&kvtab_string_type, 31, NULL); + + TEST_ASSERT_TRUE(tab != NULL); + + destroy(tab); +} + +void test_kv(void) +{ + printf("\n>> kv test\n\n"); + + RUN_TEST(test_kv_int_create); + RUN_TEST(test_kv_string_create); + RUN_TEST(test_kv_int1); + RUN_TEST(test_kv_int2); + RUN_TEST(test_kv_string1); +}