#include #include #include #include #include "kv.h" #include "utils.h" typedef struct kvent_ kvent_t; static uint32_t hash_int_key(void* key) { uint64_t intkey = (uint64_t)(long)key; return hash64(intkey); } static int assign_int_key(void** dst, void* src) { *dst = src; return 0; } static int cmp_int_key(void* key1, void* key2) { uint64_t intkey1 = (uint64_t)(long)key1; uint64_t intkey2 = (uint64_t)(long)key2; if (intkey1 > intkey2) { return 1; } else if (intkey2 > intkey1) { return -1; } return 0; } kv_key_ops_t int_key_ops = { .hash = (kv_key_hash_fn_t)hash_int_key, .assign = (kv_key_assign_fn_t)assign_int_key, .cmp = (kv_key_cmp_fn_t)cmp_int_key }; static uint32_t hash_string_key(void* key) { return hash_string((char*)key); } static int cmp_string_key(void* key1, void* key2) { return strcmp((char*)key1, (char*)key2); } static int assign_string_key(char** dst, char* src) { char* p = strdup(src); if (p == NULL) { return -ENOMEM; } *dst = p; return 1; } static void destroy_string_key(char* s) { if (s == NULL) { return; } free(s); } kv_key_ops_t string_key_ops = { .hash = (kv_key_hash_fn_t)hash_string_key, .cmp = (kv_key_cmp_fn_t)cmp_string_key, .assign = (kv_key_assign_fn_t)assign_string_key, .destroy = (kv_key_destroy_fn_t)destroy_string_key }; struct kvent_ { kvent_t* next; void* key; void* value; }; typedef struct { uint32_t nslots; kv_key_ops_t* keyops; destroy_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); kv_params_t* params = va_arg(ap, kv_params_t*); if ((params == NULL) || (params->nslots <= 1) || (params->keyops == NULL) || (params->keyops->hash == NULL) || (params->keyops->cmp == NULL)) { return -EINVAL; } kvtab->slots = (kvent_t**)calloc(params->nslots, sizeof(*(kvtab->slots))); if (kvtab->slots == NULL) { return -ENOMEM; } kvtab->nslots = params->nslots; kvtab->keyops = params->keyops; kvtab->val_killer = params->datakiller; return 1; } static void kvtab_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); } if (kvtab->keyops->destroy != NULL) { kvtab->keyops->destroy(e->key); } FREEUP(e); } } FREEUP(kvtab->slots); } static kvent_t* kvtab_get(kvtab_t* t, void* key) { uint32_t hc; kvent_t* e = NULL; hc = t->keyops->hash(key) % t->nslots; e = t->slots[hc]; while (e != NULL) { if (t->keyops->cmp(e->key, key) == 0) { break; } e = e->next; } return e; } static int kvtab_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_get(t, key); if (e != NULL) { return -EEXIST; } e = (kvent_t*)calloc(1, sizeof(*e)); if (e == NULL) { return -ENOMEM; } if (t->keyops->assign == NULL) { e->key = key; } else { int rc; rc = t->keyops->assign(&(e->key), key); if (rc < 0) { free(e); return rc; } } e->value = value; hc = t->keyops->hash(key) % t->nslots; e->next = t->slots[hc]; t->slots[hc] = e; return 1; } static int kvtab_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 = t->keyops->hash(key) % t->nslots; e = &(t->slots[hc]); while ((*e) != NULL) { if (t->keyops->cmp((*e)->key, key) == 0) { kvent_t* e1 = *e; *e = e1->next; if (t->val_killer != NULL) { t->val_killer(e1->value); } if (t->keyops->destroy != NULL) { t->keyops->destroy(e1->key); } FREEUP(e1); return 1; } e = &((*e)->next); } return 0; } static int kvtab_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_get(t, key); if (e == NULL) { return -ENOENT; } *value = e->value; return 1; } /* * kvtab_foreach — walk every live entry and invoke cb(key, index, arg). * index is recovered by casting the stored void* value back to int. * Caller must hold any necessary external lock before calling. */ int kvtab_foreach(var kvtab_var, kvtab_foreach_fn_t cb, void* arg) { if (kvtab_var == NULL || cb == NULL) { return -EINVAL; } meta_obj_t* obj = RTTI_DATA_TO_OBJ(kvtab_var); kvtab_t* t = RTTI_OBJ_TO_DATA(kvtab_t, obj); int count = 0; uint32_t i; for (i = 0; i < t->nslots; i++) { kvent_t* e = t->slots[i]; while (e != NULL) { int idx = (int)(long)e->value; cb(e->key, idx, arg); count++; e = e->next; } } return count; } typeinfo_t kvtab_type = { .name = "kvtab", .privateSize = 0, .dataSize = (datasize_fn_t)kvtab_datasize, .init = (obj_init_fn_t)kvtab_init, .destroy = (obj_destroy_fn_t)kvtab_destroy, .interfaces = { .kv = &(intf_kv_t) { .add_key = (key_add_fn_t)kvtab_add, .del_key = (key_del_fn_t)kvtab_del, .find_key = (key_find_fn_t)kvtab_find } } };