From 41bf1ba66e15c00f38375d05e49b31aa70f92349 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 21 Mar 2019 20:21:41 +0100 Subject: [PATCH] duplicate and rename keyval in dict + htable next commit will refactorize code --- examples/readline/main.c | 26 +- include/ecoli_complete.h | 4 +- include/ecoli_config.h | 4 +- include/{ecoli_keyval.h => ecoli_dict.h} | 66 +-- include/ecoli_htable.h | 197 +++++++++ include/ecoli_node.h | 6 +- include/ecoli_parse.h | 2 +- include/ecoli_strvec.h | 4 +- include/meson.build | 3 +- src/ecoli_complete.c | 24 +- src/ecoli_config.c | 80 ++-- src/ecoli_dict.c | 517 +++++++++++++++++++++++ src/ecoli_editline.c | 4 +- src/ecoli_htable.c | 448 ++++++++++++++++++++ src/ecoli_keyval.c | 517 ----------------------- src/ecoli_node.c | 26 +- src/ecoli_node_any.c | 6 +- src/ecoli_node_dynamic.c | 6 +- src/ecoli_node_re_lex.c | 8 +- src/ecoli_parse.c | 20 +- src/ecoli_strvec.c | 26 +- src/ecoli_yaml.c | 6 +- src/meson.build | 3 +- 23 files changed, 1325 insertions(+), 678 deletions(-) rename include/{ecoli_keyval.h => ecoli_dict.h} (68%) create mode 100644 include/ecoli_htable.h create mode 100644 src/ecoli_dict.c create mode 100644 src/ecoli_htable.c delete mode 100644 src/ecoli_keyval.c diff --git a/examples/readline/main.c b/examples/readline/main.c index 2c81c53..a30953d 100644 --- a/examples/readline/main.c +++ b/examples/readline/main.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -118,7 +118,7 @@ static char *get_node_help(const struct ec_comp_item *item) state = ec_parse_get_parent(state)) { node = ec_parse_get_node(state); if (node_help == NULL) - node_help = ec_keyval_get(ec_node_attrs(node), "help"); + node_help = ec_dict_get(ec_node_attrs(node), "help"); if (node_desc == NULL) node_desc = ec_node_desc(node); } @@ -244,13 +244,13 @@ static int create_commands(void) ); if (cmd == NULL) goto fail; - ec_keyval_set(ec_node_attrs(cmd), "help", + ec_dict_set(ec_node_attrs(cmd), "help", "say hello to someone several times", NULL); - ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "john")), + ec_dict_set(ec_node_attrs(ec_node_find(cmd, "john")), "help", "specific help for john", NULL); - ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")), + ec_dict_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the name of the person", NULL); - ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "int")), + ec_dict_set(ec_node_attrs(ec_node_find(cmd, "int")), "help", "an integer (0-10)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; @@ -261,11 +261,11 @@ static int create_commands(void) ec_node_int("count", 0, 10, 10)); if (cmd == NULL) goto fail; - ec_keyval_set(ec_node_attrs(cmd), "help", + ec_dict_set(ec_node_attrs(cmd), "help", "say good morning to someone several times", NULL); - ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", + ec_dict_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the person to greet", NULL); - ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "count")), "help", + ec_dict_set(ec_node_attrs(ec_node_find(cmd, "count")), "help", "how many times to greet (0-10)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; @@ -275,7 +275,7 @@ static int create_commands(void) "buy potatoes,carrots,pumpkins"); if (cmd == NULL) goto fail; - ec_keyval_set(ec_node_attrs(cmd), "help", + ec_dict_set(ec_node_attrs(cmd), "help", "buy some vegetables", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; @@ -292,7 +292,7 @@ static int create_commands(void) 1, 0)); if (cmd == NULL) goto fail; - ec_keyval_set(ec_node_attrs(cmd), "help", + ec_dict_set(ec_node_attrs(cmd), "help", "eat vegetables (take some more potatoes)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; @@ -301,7 +301,7 @@ static int create_commands(void) cmd = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "bye") ); - ec_keyval_set(ec_node_attrs(cmd), "help", "say bye", NULL); + ec_dict_set(ec_node_attrs(cmd), "help", "say bye", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; @@ -310,7 +310,7 @@ static int create_commands(void) ec_node_str(EC_NO_ID, "load"), ec_node("file", EC_NO_ID) ); - ec_keyval_set(ec_node_attrs(cmd), "help", "load a file", NULL); + ec_dict_set(ec_node_attrs(cmd), "help", "load a file", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; diff --git a/include/ecoli_complete.h b/include/ecoli_complete.h index dee4123..4c71b5d 100644 --- a/include/ecoli_complete.h +++ b/include/ecoli_complete.h @@ -36,7 +36,7 @@ struct ec_comp_group { const struct ec_node *node; struct ec_comp_item_list items; struct ec_parse *state; - struct ec_keyval *attrs; + struct ec_dict *attrs; }; TAILQ_HEAD(ec_comp_group_list, ec_comp_group); @@ -49,7 +49,7 @@ struct ec_comp { struct ec_parse *cur_state; struct ec_comp_group *cur_group; struct ec_comp_group_list groups; - struct ec_keyval *attrs; + struct ec_dict *attrs; }; /* diff --git a/include/ecoli_config.h b/include/ecoli_config.h index 7aa427f..b6d638e 100644 --- a/include/ecoli_config.h +++ b/include/ecoli_config.h @@ -16,7 +16,7 @@ #endif struct ec_config; -struct ec_keyval; +struct ec_dict; /** * The type identifier for a config value. @@ -66,7 +66,7 @@ struct ec_config { uint64_t u64; /** Unsigned integer value */ char *string; /** String value */ struct ec_node *node; /** Node value */ - struct ec_keyval *dict; /** Hash table value */ + struct ec_dict *dict; /** Hash table value */ struct ec_config_list list; /** List value */ }; diff --git a/include/ecoli_keyval.h b/include/ecoli_dict.h similarity index 68% rename from include/ecoli_keyval.h rename to include/ecoli_dict.h index 38b0d40..5519c4c 100644 --- a/include/ecoli_keyval.h +++ b/include/ecoli_dict.h @@ -15,10 +15,10 @@ #include #include -typedef void (*ec_keyval_elt_free_t)(void *); +typedef void (*ec_dict_elt_free_t)(void *); -struct ec_keyval; -struct ec_keyval_elt_ref; +struct ec_dict; +struct ec_dict_elt_ref; /** * Create a hash table. @@ -26,12 +26,12 @@ struct ec_keyval_elt_ref; * @return * The hash table, or NULL on error (errno is set). */ -struct ec_keyval *ec_keyval(void); +struct ec_dict *ec_dict(void); /** * Get a value from the hash table. * - * @param keyval + * @param dict * The hash table. * @param key * The key string. @@ -39,36 +39,36 @@ struct ec_keyval *ec_keyval(void); * The element if it is found, or NULL on error (errno is set). * In case of success but the element is NULL, errno is set to 0. */ -void *ec_keyval_get(const struct ec_keyval *keyval, const char *key); +void *ec_dict_get(const struct ec_dict *dict, const char *key); /** * Check if the hash table contains this key. * - * @param keyval + * @param dict * The hash table. * @param key * The key string. * @return * true if it contains the key, else false. */ -bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key); +bool ec_dict_has_key(const struct ec_dict *dict, const char *key); /** * Delete an object from the hash table. * - * @param keyval + * @param dict * The hash table. * @param key * The key string. * @return * 0 on success, or -1 on error (errno is set). */ -int ec_keyval_del(struct ec_keyval *keyval, const char *key); +int ec_dict_del(struct ec_dict *dict, const char *key); /** * Add/replace an object in the hash table. * - * @param keyval + * @param dict * The hash table. * @param key * The key string. @@ -76,31 +76,31 @@ int ec_keyval_del(struct ec_keyval *keyval, const char *key); * The pointer to be saved in the hash table. * @param free_cb * An optional pointer to a destructor function called when an - * object is destroyed (ec_keyval_del() or ec_keyval_free()). + * object is destroyed (ec_dict_del() or ec_dict_free()). * @return * 0 on success, or -1 on error (errno is set). * On error, the passed value is freed (free_cb(val) is called). */ -int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val, - ec_keyval_elt_free_t free_cb); +int ec_dict_set(struct ec_dict *dict, const char *key, void *val, + ec_dict_elt_free_t free_cb); /** * Free a hash table an all its objects. * - * @param keyval + * @param dict * The hash table. */ -void ec_keyval_free(struct ec_keyval *keyval); +void ec_dict_free(struct ec_dict *dict); /** * Get the length of a hash table. * - * @param keyval + * @param dict * The hash table. * @return * The length of the hash table. */ -size_t ec_keyval_len(const struct ec_keyval *keyval); +size_t ec_dict_len(const struct ec_dict *dict); /** * Duplicate a hash table @@ -109,22 +109,22 @@ size_t ec_keyval_len(const struct ec_keyval *keyval); * hash tables so that the objects are freed only when * the last reference is destroyed. * - * @param keyval + * @param dict * The hash table. * @return * The duplicated hash table, or NULL on error (errno is set). */ -struct ec_keyval *ec_keyval_dup(const struct ec_keyval *keyval); +struct ec_dict *ec_dict_dup(const struct ec_dict *dict); /** * Dump a hash table. * * @param out * The stream where the dump is sent. - * @param keyval + * @param dict * The hash table. */ -void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval); +void ec_dict_dump(FILE *out, const struct ec_dict *dict); /** * Iterate the elements in the hash table. @@ -132,21 +132,21 @@ void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval); * The typical usage is as below: * * // dump elements - * for (iter = ec_keyval_iter(keyval); + * for (iter = ec_dict_iter(dict); * iter != NULL; - * iter = ec_keyval_iter_next(iter)) { + * iter = ec_dict_iter_next(iter)) { * printf(" %s: %p\n", - * ec_keyval_iter_get_key(iter), - * ec_keyval_iter_get_val(iter)); + * ec_dict_iter_get_key(iter), + * ec_dict_iter_get_val(iter)); * } * - * @param keyval + * @param dict * The hash table. * @return * An iterator element, or NULL if the dict is empty. */ -struct ec_keyval_elt_ref * -ec_keyval_iter(const struct ec_keyval *keyval); +struct ec_dict_elt_ref * +ec_dict_iter(const struct ec_dict *dict); /** * Make the iterator point to the next element in the hash table. @@ -156,8 +156,8 @@ ec_keyval_iter(const struct ec_keyval *keyval); * @return * An iterator element, or NULL there is no more element. */ -struct ec_keyval_elt_ref * -ec_keyval_iter_next(struct ec_keyval_elt_ref *iter); +struct ec_dict_elt_ref * +ec_dict_iter_next(struct ec_dict_elt_ref *iter); /** * Get the key of the current element. @@ -169,7 +169,7 @@ ec_keyval_iter_next(struct ec_keyval_elt_ref *iter); * invalid element. */ const char * -ec_keyval_iter_get_key(const struct ec_keyval_elt_ref *iter); +ec_dict_iter_get_key(const struct ec_dict_elt_ref *iter); /** * Get the value of the current element. @@ -181,7 +181,7 @@ ec_keyval_iter_get_key(const struct ec_keyval_elt_ref *iter); * invalid element. */ void * -ec_keyval_iter_get_val(const struct ec_keyval_elt_ref *iter); +ec_dict_iter_get_val(const struct ec_dict_elt_ref *iter); #endif diff --git a/include/ecoli_htable.h b/include/ecoli_htable.h new file mode 100644 index 0000000..6729d91 --- /dev/null +++ b/include/ecoli_htable.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2016, Olivier MATZ + */ + +/** + * Simple hash table API + * + * This file provides functions to store objects in hash tables, + * using arbitrary data as keys. + */ + +#ifndef ECOLI_HTABLE_ +#define ECOLI_HTABLE_ + +#include +#include + +typedef void (*ec_htable_elt_free_t)(void *); + +struct ec_htable; +struct ec_htable_elt_ref; + +/** + * Create a hash table. + * + * @return + * The hash table, or NULL on error (errno is set). + */ +struct ec_htable *ec_htable(void); + +/** + * Get a value from the hash table. + * + * @param htable + * The hash table. + * @param key + * The key. + * @param key_len + * The key length. + * @return + * The element if it is found, or NULL on error (errno is set). + * In case of success but the element is NULL, errno is set to 0. + */ +void *ec_htable_get(const struct ec_htable *htable, + const void *key, size_t key_len); + +/** + * Check if the hash table contains this key. + * + * @param htable + * The hash table. + * @param key + * The key. + * @param key_len + * The key length. + * @return + * true if it contains the key, else false. + */ +bool ec_htable_has_key(const struct ec_htable *htable, + const void *key, size_t key_len); + +/** + * Delete an object from the hash table. + * + * @param htable + * The hash table. + * @param key + * The key. + * @param key_len + * The key length. + * @return + * 0 on success, or -1 on error (errno is set). + */ +int ec_htable_del(struct ec_htable *htable, const void *key, size_t key_len); + +/** + * Add/replace an object in the hash table. + * + * @param htable + * The hash table. + * @param key + * The key. + * @param key_len + * The key length. + * @param val + * The pointer to be saved in the hash table. + * @param free_cb + * An optional pointer to a destructor function called when an + * object is destroyed (ec_htable_del() or ec_htable_free()). + * @return + * 0 on success, or -1 on error (errno is set). + * On error, the passed value is freed (free_cb(val) is called). + */ +int ec_htable_set(struct ec_htable *htable, const void *key, size_t key_len, + void *val, ec_htable_elt_free_t free_cb); + +/** + * Free a hash table an all its objects. + * + * @param htable + * The hash table. + */ +void ec_htable_free(struct ec_htable *htable); + +/** + * Get the length of a hash table. + * + * @param htable + * The hash table. + * @return + * The length of the hash table. + */ +size_t ec_htable_len(const struct ec_htable *htable); + +/** + * Duplicate a hash table + * + * A reference counter is shared between the clones of + * hash tables so that the objects are freed only when + * the last reference is destroyed. + * + * @param htable + * The hash table. + * @return + * The duplicated hash table, or NULL on error (errno is set). + */ +struct ec_htable *ec_htable_dup(const struct ec_htable *htable); + +/** + * Dump a hash table. + * + * @param out + * The stream where the dump is sent. + * @param htable + * The hash table. + */ +void ec_htable_dump(FILE *out, const struct ec_htable *htable); + +/** + * Iterate the elements in the hash table. + * + * The typical usage is as below: + * + * // dump elements + * for (iter = ec_htable_iter(htable); + * iter != NULL; + * iter = ec_htable_iter_next(iter)) { + * printf(" %s: %p\n", + * ec_htable_iter_get_key(iter), + * ec_htable_iter_get_val(iter)); + * } + * + * @param htable + * The hash table. + * @return + * An iterator element, or NULL if the dict is empty. + */ +struct ec_htable_elt_ref * +ec_htable_iter(const struct ec_htable *htable); + +/** + * Make the iterator point to the next element in the hash table. + * + * @param iter + * The hash table iterator. + * @return + * An iterator element, or NULL there is no more element. + */ +struct ec_htable_elt_ref * +ec_htable_iter_next(struct ec_htable_elt_ref *iter); + +/** + * Get the key of the current element. + * + * @param iter + * The hash table iterator. + * @return + * The current element key, or NULL if the iterator points to an + * invalid element. + */ +const char * +ec_htable_iter_get_key(const struct ec_htable_elt_ref *iter); + +/** + * Get the value of the current element. + * + * @param iter + * The hash table iterator. + * @return + * The current element value, or NULL if the iterator points to an + * invalid element. + */ +void * +ec_htable_iter_get_val(const struct ec_htable_elt_ref *iter); + + +#endif diff --git a/include/ecoli_node.h b/include/ecoli_node.h index 45f3e74..acd1daa 100644 --- a/include/ecoli_node.h +++ b/include/ecoli_node.h @@ -54,7 +54,7 @@ struct ec_node; struct ec_parse; struct ec_comp; struct ec_strvec; -struct ec_keyval; +struct ec_dict; struct ec_config; struct ec_config_schema; @@ -157,7 +157,7 @@ struct ec_node { struct ec_config *config; /**< Generic configuration. */ char *id; char *desc; - struct ec_keyval *attrs; + struct ec_dict *attrs; unsigned int refcnt; struct { enum ec_node_free_state state; /**< State of loop detection */ @@ -192,7 +192,7 @@ ec_node_get_child(const struct ec_node *node, size_t i, /* XXX add more accessors */ const struct ec_node_type *ec_node_type(const struct ec_node *node); -struct ec_keyval *ec_node_attrs(const struct ec_node *node); +struct ec_dict *ec_node_attrs(const struct ec_node *node); const char *ec_node_id(const struct ec_node *node); const char *ec_node_desc(const struct ec_node *node); diff --git a/include/ecoli_parse.h b/include/ecoli_parse.h index 9048808..c88fc18 100644 --- a/include/ecoli_parse.h +++ b/include/ecoli_parse.h @@ -197,7 +197,7 @@ void ec_parse_del_last_child(struct ec_parse *parse); * * */ -struct ec_keyval *ec_parse_get_attrs(struct ec_parse *parse); +struct ec_dict *ec_parse_get_attrs(struct ec_parse *parse); /** * diff --git a/include/ecoli_strvec.h b/include/ecoli_strvec.h index cabe6b2..727d4c8 100644 --- a/include/ecoli_strvec.h +++ b/include/ecoli_strvec.h @@ -166,7 +166,7 @@ const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx); * The read-only attributes (dictionnary) of the string at specified * index, or NULL if there is no attribute. */ -const struct ec_keyval *ec_strvec_get_attrs(const struct ec_strvec *strvec, +const struct ec_dict *ec_strvec_get_attrs(const struct ec_strvec *strvec, size_t idx); /** @@ -183,7 +183,7 @@ const struct ec_keyval *ec_strvec_get_attrs(const struct ec_strvec *strvec, * are freed and must not be used by the caller. */ int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx, - struct ec_keyval *attrs); + struct ec_dict *attrs); /** * Compare two string vectors diff --git a/include/meson.build b/include/meson.build index ea33eb4..1b97922 100644 --- a/include/meson.build +++ b/include/meson.build @@ -5,8 +5,9 @@ libecoli_headers = [ 'ecoli_assert.h', 'ecoli_complete.h', 'ecoli_config.h', + 'ecoli_dict.h', 'ecoli_init.h', - 'ecoli_keyval.h', + 'ecoli_htable.h', 'ecoli_log.h', 'ecoli_malloc.h', 'ecoli_murmurhash.h', diff --git a/src/ecoli_complete.c b/src/ecoli_complete.c index a9becdf..e2f150a 100644 --- a/src/ecoli_complete.c +++ b/src/ecoli_complete.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,7 +31,7 @@ struct ec_comp_item { char *full; /* the full token after completion */ char *completion; /* chars that are added, NULL if not applicable */ char *display; /* what should be displayed by help/completers */ - struct ec_keyval *attrs; + struct ec_dict *attrs; }; struct ec_comp *ec_comp(struct ec_parse *state) @@ -42,7 +42,7 @@ struct ec_comp *ec_comp(struct ec_parse *state) if (comp == NULL) goto fail; - comp->attrs = ec_keyval(); + comp->attrs = ec_dict(); if (comp->attrs == NULL) goto fail; @@ -54,7 +54,7 @@ struct ec_comp *ec_comp(struct ec_parse *state) fail: if (comp != NULL) - ec_keyval_free(comp->attrs); + ec_dict_free(comp->attrs); ec_free(comp); return NULL; @@ -165,7 +165,7 @@ ec_comp_group(const struct ec_node *node, struct ec_parse *parse) if (grp == NULL) return NULL; - grp->attrs = ec_keyval(); + grp->attrs = ec_dict(); if (grp->attrs == NULL) goto fail; @@ -181,7 +181,7 @@ ec_comp_group(const struct ec_node *node, struct ec_parse *parse) fail: if (grp != NULL) { ec_parse_free(grp->state); - ec_keyval_free(grp->attrs); + ec_dict_free(grp->attrs); } ec_free(grp); return NULL; @@ -192,7 +192,7 @@ ec_comp_item(enum ec_comp_type type, const char *start, const char *full) { struct ec_comp_item *item = NULL; - struct ec_keyval *attrs = NULL; + struct ec_dict *attrs = NULL; char *comp_cp = NULL, *start_cp = NULL; char *full_cp = NULL, *display_cp = NULL; @@ -209,7 +209,7 @@ ec_comp_item(enum ec_comp_type type, if (item == NULL) goto fail; - attrs = ec_keyval(); + attrs = ec_dict(); if (attrs == NULL) goto fail; @@ -243,7 +243,7 @@ ec_comp_item(enum ec_comp_type type, return item; fail: - ec_keyval_free(attrs); + ec_dict_free(attrs); ec_free(comp_cp); ec_free(start_cp); ec_free(full_cp); @@ -417,7 +417,7 @@ ec_comp_item_free(struct ec_comp_item *item) ec_free(item->start); ec_free(item->completion); ec_free(item->display); - ec_keyval_free(item->attrs); + ec_dict_free(item->attrs); ec_free(item); } @@ -481,7 +481,7 @@ static void ec_comp_group_free(struct ec_comp_group *grp) ec_comp_item_free(item); } ec_parse_free(ec_parse_get_root(grp->state)); - ec_keyval_free(grp->attrs); + ec_dict_free(grp->attrs); ec_free(grp); } @@ -497,7 +497,7 @@ void ec_comp_free(struct ec_comp *comp) TAILQ_REMOVE(&comp->groups, grp, next); ec_comp_group_free(grp); } - ec_keyval_free(comp->attrs); + ec_dict_free(comp->attrs); ec_free(comp); } diff --git a/src/ecoli_config.c b/src/ecoli_config.c index 3a98843..c8cd316 100644 --- a/src/ecoli_config.c +++ b/src/ecoli_config.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -29,7 +29,7 @@ static int __ec_config_dump(FILE *out, const char *key, const struct ec_config *config, size_t indent); static int -ec_config_dict_validate(const struct ec_keyval *dict, +ec_config_dict_validate(const struct ec_dict *dict, const struct ec_config_schema *schema); bool @@ -324,9 +324,9 @@ struct ec_config * ec_config_dict(void) { struct ec_config *value = NULL; - struct ec_keyval *dict = NULL; + struct ec_dict *dict = NULL; - dict = ec_keyval(); + dict = ec_dict(); if (dict == NULL) goto fail; @@ -340,7 +340,7 @@ ec_config_dict(void) return value; fail: - ec_keyval_free(dict); + ec_dict_free(dict); ec_free(value); return NULL; } @@ -428,7 +428,7 @@ ec_config_free(struct ec_config *value) } break; case EC_CONFIG_TYPE_DICT: - ec_keyval_free(value->dict); + ec_dict_free(value->dict); break; default: break; @@ -455,24 +455,24 @@ ec_config_list_cmp(const struct ec_config_list *list1, return 0; } -/* XXX -> ec_keyval_cmp() */ +/* XXX -> ec_dict_cmp() */ static int -ec_config_dict_cmp(const struct ec_keyval *d1, - const struct ec_keyval *d2) +ec_config_dict_cmp(const struct ec_dict *d1, + const struct ec_dict *d2) { const struct ec_config *v1, *v2; - struct ec_keyval_elt_ref *iter = NULL; + struct ec_dict_elt_ref *iter = NULL; const char *key; - if (ec_keyval_len(d1) != ec_keyval_len(d2)) + if (ec_dict_len(d1) != ec_dict_len(d2)) return -1; - for (iter = ec_keyval_iter(d1); + for (iter = ec_dict_iter(d1); iter != NULL; - iter = ec_keyval_iter_next(iter)) { - key = ec_keyval_iter_get_key(iter); - v1 = ec_keyval_iter_get_val(iter); - v2 = ec_keyval_get(d2, key); + iter = ec_dict_iter_next(iter)) { + key = ec_dict_iter_get_key(iter); + v1 = ec_dict_iter_get_val(iter); + v2 = ec_dict_get(d2, key); if (ec_config_cmp(v1, v2)) goto fail; @@ -550,20 +550,20 @@ ec_config_list_validate(const struct ec_config_list *list, } static int -ec_config_dict_validate(const struct ec_keyval *dict, +ec_config_dict_validate(const struct ec_dict *dict, const struct ec_config_schema *schema) { const struct ec_config *value; - struct ec_keyval_elt_ref *iter = NULL; + struct ec_dict_elt_ref *iter = NULL; const struct ec_config_schema *sch; const char *key; - for (iter = ec_keyval_iter(dict); + for (iter = ec_dict_iter(dict); iter != NULL; - iter = ec_keyval_iter_next(iter)) { + iter = ec_dict_iter_next(iter)) { - key = ec_keyval_iter_get_key(iter); - value = ec_keyval_iter_get_val(iter); + key = ec_dict_iter_get_key(iter); + value = ec_dict_iter_get_val(iter); sch = ec_config_schema_lookup(schema, key); if (sch == NULL || sch->type != value->type) { errno = EBADMSG; @@ -617,7 +617,7 @@ ec_config_dict_get(const struct ec_config *config, const char *key) return NULL; } - return ec_keyval_get(config->dict, key); + return ec_dict_get(config->dict, key); } struct ec_config * @@ -653,7 +653,7 @@ int ec_config_dict_set(struct ec_config *config, const char *key, goto fail; } - return ec_keyval_set(config->dict, key, value, + return ec_dict_set(config->dict, key, value, (void (*)(void *))free_cb); fail: @@ -672,7 +672,7 @@ int ec_config_dict_del(struct ec_config *config, const char *key) return -1; } - return ec_keyval_del(config->dict, key); + return ec_dict_del(config->dict, key); } /* value is consumed */ @@ -731,21 +731,21 @@ fail: } static struct ec_config * -ec_config_dict_dup(const struct ec_keyval *dict) +ec_config_dict_dup(const struct ec_dict *dict) { struct ec_config *dup = NULL, *value; - struct ec_keyval_elt_ref *iter = NULL; + struct ec_dict_elt_ref *iter = NULL; const char *key; dup = ec_config_dict(); if (dup == NULL) goto fail; - for (iter = ec_keyval_iter(dict); + for (iter = ec_dict_iter(dict); iter != NULL; - iter = ec_keyval_iter_next(iter)) { - key = ec_keyval_iter_get_key(iter); - value = ec_config_dup(ec_keyval_iter_get_val(iter)); + iter = ec_dict_iter_next(iter)) { + key = ec_dict_iter_get_key(iter); + value = ec_config_dup(ec_dict_iter_get_val(iter)); if (value == NULL) goto fail; if (ec_config_dict_set(dup, key, value) < 0) @@ -810,11 +810,11 @@ ec_config_list_dump(FILE *out, const char *key, } static int -ec_config_dict_dump(FILE *out, const char *key, const struct ec_keyval *dict, +ec_config_dict_dump(FILE *out, const char *key, const struct ec_dict *dict, size_t indent) { const struct ec_config *value; - struct ec_keyval_elt_ref *iter; + struct ec_dict_elt_ref *iter; const char *k; fprintf(out, "%*s" "%s%s%stype=dict\n", (int)indent * 4, "", @@ -822,11 +822,11 @@ ec_config_dict_dump(FILE *out, const char *key, const struct ec_keyval *dict, key ? key: "", key ? " ": ""); - for (iter = ec_keyval_iter(dict); + for (iter = ec_dict_iter(dict); iter != NULL; - iter = ec_keyval_iter_next(iter)) { - k = ec_keyval_iter_get_key(iter); - value = ec_keyval_iter_get_val(iter); + iter = ec_dict_iter_next(iter)) { + k = ec_dict_iter_get_key(iter); + value = ec_dict_iter_get_val(iter); if (__ec_config_dump(out, k, value, indent + 1) < 0) goto fail; } @@ -981,7 +981,7 @@ static const struct ec_config_schema sch_baseconfig[] = { static int ec_config_testcase(void) { struct ec_node *node = NULL; - struct ec_keyval *dict = NULL; + struct ec_dict *dict = NULL; const struct ec_config *value = NULL; struct ec_config *config = NULL, *config2 = NULL; struct ec_config *list = NULL, *subconfig = NULL; @@ -1139,7 +1139,7 @@ static int ec_config_testcase(void) ec_config_free(list); ec_config_free(subconfig); ec_config_free(config); - ec_keyval_free(dict); + ec_dict_free(dict); ec_node_free(node); return testres; @@ -1149,7 +1149,7 @@ fail: ec_config_free(subconfig); ec_config_free(config); ec_config_free(config2); - ec_keyval_free(dict); + ec_dict_free(dict); ec_node_free(node); return -1; diff --git a/src/ecoli_dict.c b/src/ecoli_dict.c new file mode 100644 index 0000000..09d7dda --- /dev/null +++ b/src/ecoli_dict.c @@ -0,0 +1,517 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2016, Olivier MATZ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define FACTOR 3 + +EC_LOG_TYPE_REGISTER(dict); + +static uint32_t ec_dict_seed; + +struct ec_dict_elt { + char *key; + void *val; + uint32_t hash; + ec_dict_elt_free_t free; + unsigned int refcount; +}; + +struct ec_dict_elt_ref { + TAILQ_ENTRY(ec_dict_elt_ref) next; + TAILQ_ENTRY(ec_dict_elt_ref) hnext; + struct ec_dict_elt *elt; +}; + +TAILQ_HEAD(ec_dict_elt_ref_list, ec_dict_elt_ref); + +struct ec_dict { + size_t len; + size_t table_size; + struct ec_dict_elt_ref_list list; + struct ec_dict_elt_ref_list *table; +}; + +struct ec_dict *ec_dict(void) +{ + struct ec_dict *dict; + + dict = ec_calloc(1, sizeof(*dict)); + if (dict == NULL) + return NULL; + TAILQ_INIT(&dict->list); + + return dict; +} + +static struct ec_dict_elt_ref * +ec_dict_lookup(const struct ec_dict *dict, const char *key) +{ + struct ec_dict_elt_ref *ref; + uint32_t h, mask = dict->table_size - 1; + + if (dict == NULL || key == NULL) { + errno = EINVAL; + return NULL; + } + if (dict->table_size == 0) { + errno = ENOENT; + return NULL; + } + + h = ec_murmurhash3(key, strlen(key), ec_dict_seed); + TAILQ_FOREACH(ref, &dict->table[h & mask], hnext) { + if (strcmp(ref->elt->key, key) == 0) + return ref; + } + + errno = ENOENT; + return NULL; +} + +static void ec_dict_elt_ref_free(struct ec_dict_elt_ref *ref) +{ + struct ec_dict_elt *elt; + + if (ref == NULL) + return; + + elt = ref->elt; + if (elt != NULL && --elt->refcount == 0) { + ec_free(elt->key); + if (elt->free != NULL) + elt->free(elt->val); + ec_free(elt); + } + ec_free(ref); +} + +bool ec_dict_has_key(const struct ec_dict *dict, const char *key) +{ + return !!ec_dict_lookup(dict, key); +} + +void *ec_dict_get(const struct ec_dict *dict, const char *key) +{ + struct ec_dict_elt_ref *ref; + + ref = ec_dict_lookup(dict, key); + if (ref == NULL) + return NULL; + + return ref->elt->val; +} + +int ec_dict_del(struct ec_dict *dict, const char *key) +{ + struct ec_dict_elt_ref *ref; + size_t idx; + + ref = ec_dict_lookup(dict, key); + if (ref == NULL) + return -1; + + /* we could resize table here */ + + TAILQ_REMOVE(&dict->list, ref, next); + idx = ref->elt->hash & (dict->table_size - 1); + TAILQ_REMOVE(&dict->table[idx], ref, hnext); + ec_dict_elt_ref_free(ref); + dict->len--; + + return 0; +} + +static int ec_dict_table_resize(struct ec_dict *dict, size_t new_size) +{ + struct ec_dict_elt_ref_list *new_table; + struct ec_dict_elt_ref *ref; + size_t i; + + if (new_size == 0 || (new_size & (new_size - 1))) { + errno = EINVAL; + return -1; + } + + new_table = ec_calloc(new_size, sizeof(*dict->table)); + if (new_table == NULL) + return -1; + for (i = 0; i < new_size; i++) + TAILQ_INIT(&new_table[i]); + + TAILQ_FOREACH(ref, &dict->list, next) { + i = ref->elt->hash & (dict->table_size - 1); + TAILQ_REMOVE(&dict->table[i], ref, hnext); + i = ref->elt->hash & (new_size - 1); + TAILQ_INSERT_TAIL(&new_table[i], ref, hnext); + } + + ec_free(dict->table); + dict->table = new_table; + dict->table_size = new_size; + + return 0; +} + +static int +__ec_dict_set(struct ec_dict *dict, struct ec_dict_elt_ref *ref) +{ + size_t new_size; + uint32_t mask; + int ret; + + /* remove previous entry if any */ + ec_dict_del(dict, ref->elt->key); + + if (dict->len >= dict->table_size) { + if (dict->table_size != 0) + new_size = dict->table_size << FACTOR; + else + new_size = 1 << FACTOR; + ret = ec_dict_table_resize(dict, new_size); + if (ret < 0) + return ret; + } + + mask = dict->table_size - 1; + TAILQ_INSERT_TAIL(&dict->table[ref->elt->hash & mask], ref, hnext); + TAILQ_INSERT_TAIL(&dict->list, ref, next); + dict->len++; + + return 0; +} + +int ec_dict_set(struct ec_dict *dict, const char *key, void *val, + ec_dict_elt_free_t free_cb) +{ + struct ec_dict_elt *elt = NULL; + struct ec_dict_elt_ref *ref = NULL; + uint32_t h; + + if (dict == NULL || key == NULL) { + errno = EINVAL; + return -1; + } + + ref = ec_calloc(1, sizeof(*ref)); + if (ref == NULL) + goto fail; + + elt = ec_calloc(1, sizeof(*elt)); + if (elt == NULL) + goto fail; + + ref->elt = elt; + elt->refcount = 1; + elt->val = val; + val = NULL; + elt->free = free_cb; + elt->key = ec_strdup(key); + if (elt->key == NULL) + goto fail; + h = ec_murmurhash3(key, strlen(key), ec_dict_seed); + elt->hash = h; + + if (__ec_dict_set(dict, ref) < 0) + goto fail; + + return 0; + +fail: + if (free_cb != NULL && val != NULL) + free_cb(val); + ec_dict_elt_ref_free(ref); + return -1; +} + +void ec_dict_free(struct ec_dict *dict) +{ + struct ec_dict_elt_ref *ref; + size_t idx; + + if (dict == NULL) + return; + + while (!TAILQ_EMPTY(&dict->list)) { + ref = TAILQ_FIRST(&dict->list); + TAILQ_REMOVE(&dict->list, ref, next); + idx = ref->elt->hash & (dict->table_size - 1); + TAILQ_REMOVE(&dict->table[idx], ref, hnext); + ec_dict_elt_ref_free(ref); + } + ec_free(dict->table); + ec_free(dict); +} + +size_t ec_dict_len(const struct ec_dict *dict) +{ + return dict->len; +} + +struct ec_dict_elt_ref * +ec_dict_iter(const struct ec_dict *dict) +{ + if (dict == NULL) + return NULL; + + return TAILQ_FIRST(&dict->list); +} + +struct ec_dict_elt_ref * +ec_dict_iter_next(struct ec_dict_elt_ref *iter) +{ + if (iter == NULL) + return NULL; + + return TAILQ_NEXT(iter, next); +} + +const char * +ec_dict_iter_get_key(const struct ec_dict_elt_ref *iter) +{ + if (iter == NULL) + return NULL; + + return iter->elt->key; +} + +void * +ec_dict_iter_get_val(const struct ec_dict_elt_ref *iter) +{ + if (iter == NULL) + return NULL; + + return iter->elt->val; +} + +void ec_dict_dump(FILE *out, const struct ec_dict *dict) +{ + struct ec_dict_elt_ref *iter; + + if (dict == NULL) { + fprintf(out, "empty dict\n"); + return; + } + + fprintf(out, "dict:\n"); + for (iter = ec_dict_iter(dict); + iter != NULL; + iter = ec_dict_iter_next(iter)) { + fprintf(out, " %s: %p\n", + ec_dict_iter_get_key(iter), + ec_dict_iter_get_val(iter)); + } +} + +struct ec_dict *ec_dict_dup(const struct ec_dict *dict) +{ + struct ec_dict *dup = NULL; + struct ec_dict_elt_ref *ref, *dup_ref = NULL; + + dup = ec_dict(); + if (dup == NULL) + return NULL; + + TAILQ_FOREACH(ref, &dict->list, next) { + dup_ref = ec_calloc(1, sizeof(*ref)); + if (dup_ref == NULL) + goto fail; + dup_ref->elt = ref->elt; + ref->elt->refcount++; + + if (__ec_dict_set(dup, dup_ref) < 0) + goto fail; + } + + return dup; + +fail: + ec_dict_elt_ref_free(dup_ref); + ec_dict_free(dup); + return NULL; +} + +static int ec_dict_init_func(void) +{ + int fd; + ssize_t ret; + + return 0;//XXX for test reproduceability + + fd = open("/dev/urandom", 0); + if (fd == -1) { + fprintf(stderr, "failed to open /dev/urandom\n"); + return -1; + } + ret = read(fd, &ec_dict_seed, sizeof(ec_dict_seed)); + if (ret != sizeof(ec_dict_seed)) { + fprintf(stderr, "failed to read /dev/urandom\n"); + return -1; + } + close(fd); + return 0; +} + +static struct ec_init ec_dict_init = { + .init = ec_dict_init_func, + .priority = 50, +}; + +EC_INIT_REGISTER(ec_dict_init); + +/* LCOV_EXCL_START */ +static int ec_dict_testcase(void) +{ + struct ec_dict *dict, *dup; + struct ec_dict_elt_ref *iter; + char *val; + size_t i, count; + int ret, testres = 0; + FILE *f = NULL; + char *buf = NULL; + size_t buflen = 0; + + dict = ec_dict(); + if (dict == NULL) { + EC_LOG(EC_LOG_ERR, "cannot create dict\n"); + return -1; + } + + count = 0; + for (iter = ec_dict_iter(dict); + iter != NULL; + iter = ec_dict_iter_next(iter)) { + count++; + } + testres |= EC_TEST_CHECK(count == 0, "invalid count in iterator"); + + testres |= EC_TEST_CHECK(ec_dict_len(dict) == 0, "bad dict len"); + ret = ec_dict_set(dict, "key1", "val1", NULL); + testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); + ret = ec_dict_set(dict, "key2", ec_strdup("val2"), ec_free_func); + testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); + testres |= EC_TEST_CHECK(ec_dict_len(dict) == 2, "bad dict len"); + + val = ec_dict_get(dict, "key1"); + testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val1"), + "invalid dict value"); + val = ec_dict_get(dict, "key2"); + testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val2"), + "invalid dict value"); + val = ec_dict_get(dict, "key3"); + testres |= EC_TEST_CHECK(val == NULL, "key3 should be NULL"); + + ret = ec_dict_set(dict, "key1", "another_val1", NULL); + testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); + ret = ec_dict_set(dict, "key2", ec_strdup("another_val2"), + ec_free_func); + testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); + testres |= EC_TEST_CHECK(ec_dict_len(dict) == 2, + "bad dict len"); + + val = ec_dict_get(dict, "key1"); + testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val1"), + "invalid dict value"); + val = ec_dict_get(dict, "key2"); + testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val2"), + "invalid dict value"); + testres |= EC_TEST_CHECK(ec_dict_has_key(dict, "key1"), + "key1 should be in dict"); + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_dict_dump(f, NULL); + fclose(f); + f = NULL; + free(buf); + buf = NULL; + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_dict_dump(f, dict); + fclose(f); + f = NULL; + free(buf); + buf = NULL; + + ret = ec_dict_del(dict, "key1"); + testres |= EC_TEST_CHECK(ret == 0, "cannot del key1"); + testres |= EC_TEST_CHECK(ec_dict_len(dict) == 1, + "invalid dict len"); + ret = ec_dict_del(dict, "key2"); + testres |= EC_TEST_CHECK(ret == 0, "cannot del key2"); + testres |= EC_TEST_CHECK(ec_dict_len(dict) == 0, + "invalid dict len"); + + for (i = 0; i < 100; i++) { + char key[8]; + snprintf(key, sizeof(key), "k%zd", i); + ret = ec_dict_set(dict, key, "val", NULL); + testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); + } + dup = ec_dict_dup(dict); + testres |= EC_TEST_CHECK(dup != NULL, "cannot duplicate dict"); + if (dup != NULL) { + for (i = 0; i < 100; i++) { + char key[8]; + snprintf(key, sizeof(key), "k%zd", i); + val = ec_dict_get(dup, key); + testres |= EC_TEST_CHECK( + val != NULL && !strcmp(val, "val"), + "invalid dict value"); + } + ec_dict_free(dup); + dup = NULL; + } + + count = 0; + for (iter = ec_dict_iter(dict); + iter != NULL; + iter = ec_dict_iter_next(iter)) { + count++; + } + testres |= EC_TEST_CHECK(count == 100, "invalid count in iterator"); + + /* einval */ + ret = ec_dict_set(dict, NULL, "val1", NULL); + testres |= EC_TEST_CHECK(ret == -1, "should not be able to set key"); + val = ec_dict_get(dict, NULL); + testres |= EC_TEST_CHECK(val == NULL, "get(NULL) should no success"); + + ec_dict_free(dict); + + return testres; + +fail: + ec_dict_free(dict); + if (f) + fclose(f); + free(buf); + return -1; +} +/* LCOV_EXCL_STOP */ + +static struct ec_test ec_dict_test = { + .name = "dict", + .test = ec_dict_testcase, +}; + +EC_TEST_REGISTER(ec_dict_test); diff --git a/src/ecoli_editline.c b/src/ecoli_editline.c index 4cc7ec4..ed8ea21 100644 --- a/src/ecoli_editline.c +++ b/src/ecoli_editline.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -397,7 +397,7 @@ static int get_node_help(const struct ec_comp_item *item, state = ec_parse_get_parent(state)) { node = ec_parse_get_node(state); if (node_help == NULL) - node_help = ec_keyval_get(ec_node_attrs(node), "help"); + node_help = ec_dict_get(ec_node_attrs(node), "help"); if (node_desc == NULL) node_desc = ec_node_desc(node); } diff --git a/src/ecoli_htable.c b/src/ecoli_htable.c new file mode 100644 index 0000000..4b45a76 --- /dev/null +++ b/src/ecoli_htable.c @@ -0,0 +1,448 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2016, Olivier MATZ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define FACTOR 3 + +EC_LOG_TYPE_REGISTER(htable); + +static uint32_t ec_htable_seed; + +struct ec_htable_elt { + void *key; + size_t key_len; + void *val; + uint32_t hash; + ec_htable_elt_free_t free; + unsigned int refcount; +}; + +struct ec_htable_elt_ref { + TAILQ_ENTRY(ec_htable_elt_ref) next; + TAILQ_ENTRY(ec_htable_elt_ref) hnext; + struct ec_htable_elt *elt; +}; + +TAILQ_HEAD(ec_htable_elt_ref_list, ec_htable_elt_ref); + +struct ec_htable { + size_t len; + size_t table_size; + struct ec_htable_elt_ref_list list; + struct ec_htable_elt_ref_list *table; +}; + +struct ec_htable *ec_htable(void) +{ + struct ec_htable *htable; + + htable = ec_calloc(1, sizeof(*htable)); + if (htable == NULL) + return NULL; + TAILQ_INIT(&htable->list); + + return htable; +} + +static struct ec_htable_elt_ref * +ec_htable_lookup(const struct ec_htable *htable, const void *key, + size_t key_len) +{ + struct ec_htable_elt_ref *ref; + uint32_t h, mask = htable->table_size - 1; + + if (htable == NULL || key == NULL) { + errno = EINVAL; + return NULL; + } + if (htable->table_size == 0) { + errno = ENOENT; + return NULL; + } + + h = ec_murmurhash3(key, key_len, ec_htable_seed); + TAILQ_FOREACH(ref, &htable->table[h & mask], hnext) { + if (ref->elt->key_len != key_len) + continue; + if (memcmp(ref->elt->key, key, key_len) == 0) + return ref; + } + + errno = ENOENT; + return NULL; +} + +static void ec_htable_elt_ref_free(struct ec_htable_elt_ref *ref) +{ + struct ec_htable_elt *elt; + + if (ref == NULL) + return; + + elt = ref->elt; + if (elt != NULL && --elt->refcount == 0) { + ec_free(elt->key); + if (elt->free != NULL) + elt->free(elt->val); + ec_free(elt); + } + ec_free(ref); +} + +bool ec_htable_has_key(const struct ec_htable *htable, const void *key, + size_t key_len) +{ + return !!ec_htable_lookup(htable, key, key_len); +} + +void *ec_htable_get(const struct ec_htable *htable, const void *key, + size_t key_len) +{ + struct ec_htable_elt_ref *ref; + + ref = ec_htable_lookup(htable, key, key_len); + if (ref == NULL) + return NULL; + + return ref->elt->val; +} + +int ec_htable_del(struct ec_htable *htable, const void *key, size_t key_len) +{ + struct ec_htable_elt_ref *ref; + size_t idx; + + ref = ec_htable_lookup(htable, key, key_len); + if (ref == NULL) + return -1; + + /* we could resize table here */ + + TAILQ_REMOVE(&htable->list, ref, next); + idx = ref->elt->hash & (htable->table_size - 1); + TAILQ_REMOVE(&htable->table[idx], ref, hnext); + ec_htable_elt_ref_free(ref); + htable->len--; + + return 0; +} + +static int ec_htable_table_resize(struct ec_htable *htable, size_t new_size) +{ + struct ec_htable_elt_ref_list *new_table; + struct ec_htable_elt_ref *ref; + size_t i; + + if (new_size == 0 || (new_size & (new_size - 1))) { + errno = EINVAL; + return -1; + } + + new_table = ec_calloc(new_size, sizeof(*htable->table)); + if (new_table == NULL) + return -1; + for (i = 0; i < new_size; i++) + TAILQ_INIT(&new_table[i]); + + TAILQ_FOREACH(ref, &htable->list, next) { + i = ref->elt->hash & (htable->table_size - 1); + TAILQ_REMOVE(&htable->table[i], ref, hnext); + i = ref->elt->hash & (new_size - 1); + TAILQ_INSERT_TAIL(&new_table[i], ref, hnext); + } + + ec_free(htable->table); + htable->table = new_table; + htable->table_size = new_size; + + return 0; +} + +static int +__ec_htable_set(struct ec_htable *htable, struct ec_htable_elt_ref *ref) +{ + size_t new_size; + uint32_t mask; + int ret; + + /* remove previous entry if any */ + ec_htable_del(htable, ref->elt->key, ref->elt->key_len); + + if (htable->len >= htable->table_size) { + if (htable->table_size != 0) + new_size = htable->table_size << FACTOR; + else + new_size = 1 << FACTOR; + ret = ec_htable_table_resize(htable, new_size); + if (ret < 0) + return ret; + } + + mask = htable->table_size - 1; + TAILQ_INSERT_TAIL(&htable->table[ref->elt->hash & mask], ref, hnext); + TAILQ_INSERT_TAIL(&htable->list, ref, next); + htable->len++; + + return 0; +} + +int ec_htable_set(struct ec_htable *htable, const void *key, size_t key_len, + void *val, ec_htable_elt_free_t free_cb) +{ + struct ec_htable_elt *elt = NULL; + struct ec_htable_elt_ref *ref = NULL; + uint32_t h; + + if (htable == NULL || key == NULL || key_len == 0) { + errno = EINVAL; + return -1; + } + + ref = ec_calloc(1, sizeof(*ref)); + if (ref == NULL) + goto fail; + + elt = ec_calloc(1, sizeof(*elt)); + if (elt == NULL) + goto fail; + + ref->elt = elt; + elt->refcount = 1; + elt->val = val; + val = NULL; + elt->free = free_cb; + elt->key_len = key_len; + elt->key = ec_malloc(key_len); + if (elt->key == NULL) + goto fail; + memcpy(elt->key, key, key_len); + h = ec_murmurhash3(key, key_len, ec_htable_seed); + elt->hash = h; + + if (__ec_htable_set(htable, ref) < 0) + goto fail; + + return 0; + +fail: + if (free_cb != NULL && val != NULL) + free_cb(val); + ec_htable_elt_ref_free(ref); + return -1; +} + +void ec_htable_free(struct ec_htable *htable) +{ + struct ec_htable_elt_ref *ref; + size_t idx; + + if (htable == NULL) + return; + + while (!TAILQ_EMPTY(&htable->list)) { + ref = TAILQ_FIRST(&htable->list); + TAILQ_REMOVE(&htable->list, ref, next); + idx = ref->elt->hash & (htable->table_size - 1); + TAILQ_REMOVE(&htable->table[idx], ref, hnext); + ec_htable_elt_ref_free(ref); + } + ec_free(htable->table); + ec_free(htable); +} + +size_t ec_htable_len(const struct ec_htable *htable) +{ + return htable->len; +} + +struct ec_htable_elt_ref * +ec_htable_iter(const struct ec_htable *htable) +{ + if (htable == NULL) + return NULL; + + return TAILQ_FIRST(&htable->list); +} + +struct ec_htable_elt_ref * +ec_htable_iter_next(struct ec_htable_elt_ref *iter) +{ + if (iter == NULL) + return NULL; + + return TAILQ_NEXT(iter, next); +} + +const char * +ec_htable_iter_get_key(const struct ec_htable_elt_ref *iter) +{ + if (iter == NULL) + return NULL; + + return iter->elt->key; +} + +void * +ec_htable_iter_get_val(const struct ec_htable_elt_ref *iter) +{ + if (iter == NULL) + return NULL; + + return iter->elt->val; +} + +void ec_htable_dump(FILE *out, const struct ec_htable *htable) +{ + if (htable == NULL) { + fprintf(out, "empty htable\n"); + return; + } + + fprintf(out, "htable: %zd elements\n", htable->len); +} + +struct ec_htable *ec_htable_dup(const struct ec_htable *htable) +{ + struct ec_htable *dup = NULL; + struct ec_htable_elt_ref *ref, *dup_ref = NULL; + + dup = ec_htable(); + if (dup == NULL) + return NULL; + + TAILQ_FOREACH(ref, &htable->list, next) { + dup_ref = ec_calloc(1, sizeof(*ref)); + if (dup_ref == NULL) + goto fail; + dup_ref->elt = ref->elt; + ref->elt->refcount++; + + if (__ec_htable_set(dup, dup_ref) < 0) + goto fail; + } + + return dup; + +fail: + ec_htable_elt_ref_free(dup_ref); + ec_htable_free(dup); + return NULL; +} + +static int ec_htable_init_func(void) +{ + int fd; + ssize_t ret; + + return 0;//XXX for test reproduceability + + fd = open("/dev/urandom", 0); + if (fd == -1) { + fprintf(stderr, "failed to open /dev/urandom\n"); + return -1; + } + ret = read(fd, &ec_htable_seed, sizeof(ec_htable_seed)); + if (ret != sizeof(ec_htable_seed)) { + fprintf(stderr, "failed to read /dev/urandom\n"); + return -1; + } + close(fd); + return 0; +} + +static struct ec_init ec_htable_init = { + .init = ec_htable_init_func, + .priority = 50, +}; + +EC_INIT_REGISTER(ec_htable_init); + +/* LCOV_EXCL_START */ +static int ec_htable_testcase(void) +{ + struct ec_htable *htable; + struct ec_htable_elt_ref *iter; + size_t count; + int ret, testres = 0; + FILE *f = NULL; + char *buf = NULL; + size_t buflen = 0; + + /* Minimal test, since ec_dict also uses this code and is better + * tested. */ + + htable = ec_htable(); + if (htable == NULL) { + EC_LOG(EC_LOG_ERR, "cannot create htable\n"); + return -1; + } + + count = 0; + for (iter = ec_htable_iter(htable); + iter != NULL; + iter = ec_htable_iter_next(iter)) { + count++; + } + testres |= EC_TEST_CHECK(count == 0, "invalid count in iterator"); + + testres |= EC_TEST_CHECK(ec_htable_len(htable) == 0, "bad htable len"); + ret = ec_htable_set(htable, "key1", 4, "val1", NULL); + testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); + ret = ec_htable_set(htable, "key2", 4, ec_strdup("val2"), ec_free_func); + testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); + testres |= EC_TEST_CHECK(ec_htable_len(htable) == 2, "bad htable len"); + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_htable_dump(f, NULL); + fclose(f); + f = NULL; + free(buf); + buf = NULL; + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_htable_dump(f, htable); + fclose(f); + f = NULL; + free(buf); + buf = NULL; + + ec_htable_free(htable); + + return testres; + +fail: + ec_htable_free(htable); + if (f) + fclose(f); + free(buf); + return -1; +} +/* LCOV_EXCL_STOP */ + +static struct ec_test ec_htable_test = { + .name = "htable", + .test = ec_htable_testcase, +}; + +EC_TEST_REGISTER(ec_htable_test); diff --git a/src/ecoli_keyval.c b/src/ecoli_keyval.c deleted file mode 100644 index a99aaf0..0000000 --- a/src/ecoli_keyval.c +++ /dev/null @@ -1,517 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * Copyright 2016, Olivier MATZ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define FACTOR 3 - -EC_LOG_TYPE_REGISTER(keyval); - -static uint32_t ec_keyval_seed; - -struct ec_keyval_elt { - char *key; - void *val; - uint32_t hash; - ec_keyval_elt_free_t free; - unsigned int refcount; -}; - -struct ec_keyval_elt_ref { - TAILQ_ENTRY(ec_keyval_elt_ref) next; - TAILQ_ENTRY(ec_keyval_elt_ref) hnext; - struct ec_keyval_elt *elt; -}; - -TAILQ_HEAD(ec_keyval_elt_ref_list, ec_keyval_elt_ref); - -struct ec_keyval { - size_t len; - size_t table_size; - struct ec_keyval_elt_ref_list list; - struct ec_keyval_elt_ref_list *table; -}; - -struct ec_keyval *ec_keyval(void) -{ - struct ec_keyval *keyval; - - keyval = ec_calloc(1, sizeof(*keyval)); - if (keyval == NULL) - return NULL; - TAILQ_INIT(&keyval->list); - - return keyval; -} - -static struct ec_keyval_elt_ref * -ec_keyval_lookup(const struct ec_keyval *keyval, const char *key) -{ - struct ec_keyval_elt_ref *ref; - uint32_t h, mask = keyval->table_size - 1; - - if (keyval == NULL || key == NULL) { - errno = EINVAL; - return NULL; - } - if (keyval->table_size == 0) { - errno = ENOENT; - return NULL; - } - - h = ec_murmurhash3(key, strlen(key), ec_keyval_seed); - TAILQ_FOREACH(ref, &keyval->table[h & mask], hnext) { - if (strcmp(ref->elt->key, key) == 0) - return ref; - } - - errno = ENOENT; - return NULL; -} - -static void ec_keyval_elt_ref_free(struct ec_keyval_elt_ref *ref) -{ - struct ec_keyval_elt *elt; - - if (ref == NULL) - return; - - elt = ref->elt; - if (elt != NULL && --elt->refcount == 0) { - ec_free(elt->key); - if (elt->free != NULL) - elt->free(elt->val); - ec_free(elt); - } - ec_free(ref); -} - -bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key) -{ - return !!ec_keyval_lookup(keyval, key); -} - -void *ec_keyval_get(const struct ec_keyval *keyval, const char *key) -{ - struct ec_keyval_elt_ref *ref; - - ref = ec_keyval_lookup(keyval, key); - if (ref == NULL) - return NULL; - - return ref->elt->val; -} - -int ec_keyval_del(struct ec_keyval *keyval, const char *key) -{ - struct ec_keyval_elt_ref *ref; - size_t idx; - - ref = ec_keyval_lookup(keyval, key); - if (ref == NULL) - return -1; - - /* we could resize table here */ - - TAILQ_REMOVE(&keyval->list, ref, next); - idx = ref->elt->hash & (keyval->table_size - 1); - TAILQ_REMOVE(&keyval->table[idx], ref, hnext); - ec_keyval_elt_ref_free(ref); - keyval->len--; - - return 0; -} - -static int ec_keyval_table_resize(struct ec_keyval *keyval, size_t new_size) -{ - struct ec_keyval_elt_ref_list *new_table; - struct ec_keyval_elt_ref *ref; - size_t i; - - if (new_size == 0 || (new_size & (new_size - 1))) { - errno = EINVAL; - return -1; - } - - new_table = ec_calloc(new_size, sizeof(*keyval->table)); - if (new_table == NULL) - return -1; - for (i = 0; i < new_size; i++) - TAILQ_INIT(&new_table[i]); - - TAILQ_FOREACH(ref, &keyval->list, next) { - i = ref->elt->hash & (keyval->table_size - 1); - TAILQ_REMOVE(&keyval->table[i], ref, hnext); - i = ref->elt->hash & (new_size - 1); - TAILQ_INSERT_TAIL(&new_table[i], ref, hnext); - } - - ec_free(keyval->table); - keyval->table = new_table; - keyval->table_size = new_size; - - return 0; -} - -static int -__ec_keyval_set(struct ec_keyval *keyval, struct ec_keyval_elt_ref *ref) -{ - size_t new_size; - uint32_t mask; - int ret; - - /* remove previous entry if any */ - ec_keyval_del(keyval, ref->elt->key); - - if (keyval->len >= keyval->table_size) { - if (keyval->table_size != 0) - new_size = keyval->table_size << FACTOR; - else - new_size = 1 << FACTOR; - ret = ec_keyval_table_resize(keyval, new_size); - if (ret < 0) - return ret; - } - - mask = keyval->table_size - 1; - TAILQ_INSERT_TAIL(&keyval->table[ref->elt->hash & mask], ref, hnext); - TAILQ_INSERT_TAIL(&keyval->list, ref, next); - keyval->len++; - - return 0; -} - -int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val, - ec_keyval_elt_free_t free_cb) -{ - struct ec_keyval_elt *elt = NULL; - struct ec_keyval_elt_ref *ref = NULL; - uint32_t h; - - if (keyval == NULL || key == NULL) { - errno = EINVAL; - return -1; - } - - ref = ec_calloc(1, sizeof(*ref)); - if (ref == NULL) - goto fail; - - elt = ec_calloc(1, sizeof(*elt)); - if (elt == NULL) - goto fail; - - ref->elt = elt; - elt->refcount = 1; - elt->val = val; - val = NULL; - elt->free = free_cb; - elt->key = ec_strdup(key); - if (elt->key == NULL) - goto fail; - h = ec_murmurhash3(key, strlen(key), ec_keyval_seed); - elt->hash = h; - - if (__ec_keyval_set(keyval, ref) < 0) - goto fail; - - return 0; - -fail: - if (free_cb != NULL && val != NULL) - free_cb(val); - ec_keyval_elt_ref_free(ref); - return -1; -} - -void ec_keyval_free(struct ec_keyval *keyval) -{ - struct ec_keyval_elt_ref *ref; - size_t idx; - - if (keyval == NULL) - return; - - while (!TAILQ_EMPTY(&keyval->list)) { - ref = TAILQ_FIRST(&keyval->list); - TAILQ_REMOVE(&keyval->list, ref, next); - idx = ref->elt->hash & (keyval->table_size - 1); - TAILQ_REMOVE(&keyval->table[idx], ref, hnext); - ec_keyval_elt_ref_free(ref); - } - ec_free(keyval->table); - ec_free(keyval); -} - -size_t ec_keyval_len(const struct ec_keyval *keyval) -{ - return keyval->len; -} - -struct ec_keyval_elt_ref * -ec_keyval_iter(const struct ec_keyval *keyval) -{ - if (keyval == NULL) - return NULL; - - return TAILQ_FIRST(&keyval->list); -} - -struct ec_keyval_elt_ref * -ec_keyval_iter_next(struct ec_keyval_elt_ref *iter) -{ - if (iter == NULL) - return NULL; - - return TAILQ_NEXT(iter, next); -} - -const char * -ec_keyval_iter_get_key(const struct ec_keyval_elt_ref *iter) -{ - if (iter == NULL) - return NULL; - - return iter->elt->key; -} - -void * -ec_keyval_iter_get_val(const struct ec_keyval_elt_ref *iter) -{ - if (iter == NULL) - return NULL; - - return iter->elt->val; -} - -void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval) -{ - struct ec_keyval_elt_ref *iter; - - if (keyval == NULL) { - fprintf(out, "empty keyval\n"); - return; - } - - fprintf(out, "keyval:\n"); - for (iter = ec_keyval_iter(keyval); - iter != NULL; - iter = ec_keyval_iter_next(iter)) { - fprintf(out, " %s: %p\n", - ec_keyval_iter_get_key(iter), - ec_keyval_iter_get_val(iter)); - } -} - -struct ec_keyval *ec_keyval_dup(const struct ec_keyval *keyval) -{ - struct ec_keyval *dup = NULL; - struct ec_keyval_elt_ref *ref, *dup_ref = NULL; - - dup = ec_keyval(); - if (dup == NULL) - return NULL; - - TAILQ_FOREACH(ref, &keyval->list, next) { - dup_ref = ec_calloc(1, sizeof(*ref)); - if (dup_ref == NULL) - goto fail; - dup_ref->elt = ref->elt; - ref->elt->refcount++; - - if (__ec_keyval_set(dup, dup_ref) < 0) - goto fail; - } - - return dup; - -fail: - ec_keyval_elt_ref_free(dup_ref); - ec_keyval_free(dup); - return NULL; -} - -static int ec_keyval_init_func(void) -{ - int fd; - ssize_t ret; - - return 0;//XXX for test reproduceability - - fd = open("/dev/urandom", 0); - if (fd == -1) { - fprintf(stderr, "failed to open /dev/urandom\n"); - return -1; - } - ret = read(fd, &ec_keyval_seed, sizeof(ec_keyval_seed)); - if (ret != sizeof(ec_keyval_seed)) { - fprintf(stderr, "failed to read /dev/urandom\n"); - return -1; - } - close(fd); - return 0; -} - -static struct ec_init ec_keyval_init = { - .init = ec_keyval_init_func, - .priority = 50, -}; - -EC_INIT_REGISTER(ec_keyval_init); - -/* LCOV_EXCL_START */ -static int ec_keyval_testcase(void) -{ - struct ec_keyval *keyval, *dup; - struct ec_keyval_elt_ref *iter; - char *val; - size_t i, count; - int ret, testres = 0; - FILE *f = NULL; - char *buf = NULL; - size_t buflen = 0; - - keyval = ec_keyval(); - if (keyval == NULL) { - EC_LOG(EC_LOG_ERR, "cannot create keyval\n"); - return -1; - } - - count = 0; - for (iter = ec_keyval_iter(keyval); - iter != NULL; - iter = ec_keyval_iter_next(iter)) { - count++; - } - testres |= EC_TEST_CHECK(count == 0, "invalid count in iterator"); - - testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 0, "bad keyval len"); - ret = ec_keyval_set(keyval, "key1", "val1", NULL); - testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); - ret = ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free_func); - testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); - testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 2, "bad keyval len"); - - val = ec_keyval_get(keyval, "key1"); - testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val1"), - "invalid keyval value"); - val = ec_keyval_get(keyval, "key2"); - testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val2"), - "invalid keyval value"); - val = ec_keyval_get(keyval, "key3"); - testres |= EC_TEST_CHECK(val == NULL, "key3 should be NULL"); - - ret = ec_keyval_set(keyval, "key1", "another_val1", NULL); - testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); - ret = ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), - ec_free_func); - testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); - testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 2, - "bad keyval len"); - - val = ec_keyval_get(keyval, "key1"); - testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val1"), - "invalid keyval value"); - val = ec_keyval_get(keyval, "key2"); - testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val2"), - "invalid keyval value"); - testres |= EC_TEST_CHECK(ec_keyval_has_key(keyval, "key1"), - "key1 should be in keyval"); - - f = open_memstream(&buf, &buflen); - if (f == NULL) - goto fail; - ec_keyval_dump(f, NULL); - fclose(f); - f = NULL; - free(buf); - buf = NULL; - - f = open_memstream(&buf, &buflen); - if (f == NULL) - goto fail; - ec_keyval_dump(f, keyval); - fclose(f); - f = NULL; - free(buf); - buf = NULL; - - ret = ec_keyval_del(keyval, "key1"); - testres |= EC_TEST_CHECK(ret == 0, "cannot del key1"); - testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 1, - "invalid keyval len"); - ret = ec_keyval_del(keyval, "key2"); - testres |= EC_TEST_CHECK(ret == 0, "cannot del key2"); - testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 0, - "invalid keyval len"); - - for (i = 0; i < 100; i++) { - char key[8]; - snprintf(key, sizeof(key), "k%zd", i); - ret = ec_keyval_set(keyval, key, "val", NULL); - testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); - } - dup = ec_keyval_dup(keyval); - testres |= EC_TEST_CHECK(dup != NULL, "cannot duplicate keyval"); - if (dup != NULL) { - for (i = 0; i < 100; i++) { - char key[8]; - snprintf(key, sizeof(key), "k%zd", i); - val = ec_keyval_get(dup, key); - testres |= EC_TEST_CHECK( - val != NULL && !strcmp(val, "val"), - "invalid keyval value"); - } - ec_keyval_free(dup); - dup = NULL; - } - - count = 0; - for (iter = ec_keyval_iter(keyval); - iter != NULL; - iter = ec_keyval_iter_next(iter)) { - count++; - } - testres |= EC_TEST_CHECK(count == 100, "invalid count in iterator"); - - /* einval */ - ret = ec_keyval_set(keyval, NULL, "val1", NULL); - testres |= EC_TEST_CHECK(ret == -1, "should not be able to set key"); - val = ec_keyval_get(keyval, NULL); - testres |= EC_TEST_CHECK(val == NULL, "get(NULL) should no success"); - - ec_keyval_free(keyval); - - return testres; - -fail: - ec_keyval_free(keyval); - if (f) - fclose(f); - free(buf); - return -1; -} -/* LCOV_EXCL_STOP */ - -static struct ec_test ec_keyval_test = { - .name = "keyval", - .test = ec_keyval_testcase, -}; - -EC_TEST_REGISTER(ec_keyval_test); diff --git a/src/ecoli_node.c b/src/ecoli_node.c index 9789102..07ef9e5 100644 --- a/src/ecoli_node.c +++ b/src/ecoli_node.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -90,7 +90,7 @@ struct ec_node *ec_node_from_type(const struct ec_node_type *type, const char *i if (ec_asprintf(&node->desc, "<%s>", type->name) < 0) goto fail; - node->attrs = ec_keyval(); + node->attrs = ec_dict(); if (node->attrs == NULL) goto fail; @@ -103,7 +103,7 @@ struct ec_node *ec_node_from_type(const struct ec_node_type *type, const char *i fail: if (node != NULL) { - ec_keyval_free(node->attrs); + ec_dict_free(node->attrs); ec_free(node->desc); ec_free(node->id); } @@ -245,7 +245,7 @@ void ec_node_free(struct ec_node *node) node->type->free_priv(node); ec_free(node->id); ec_free(node->desc); - ec_keyval_free(node->attrs); + ec_dict_free(node->attrs); } node->refcnt--; @@ -340,7 +340,7 @@ const struct ec_node_type *ec_node_type(const struct ec_node *node) return node->type; } -struct ec_keyval *ec_node_attrs(const struct ec_node *node) +struct ec_dict *ec_node_attrs(const struct ec_node *node) { return node->attrs; } @@ -351,7 +351,7 @@ const char *ec_node_id(const struct ec_node *node) } static void __ec_node_dump(FILE *out, - const struct ec_node *node, size_t indent, struct ec_keyval *dict) + const struct ec_node *node, size_t indent, struct ec_dict *dict) { const char *id, *typename; struct ec_node *child; @@ -364,13 +364,13 @@ static void __ec_node_dump(FILE *out, typename = node->type->name; snprintf(buf, sizeof(buf), "%p", node); - if (ec_keyval_has_key(dict, buf)) { + if (ec_dict_has_key(dict, buf)) { fprintf(out, "%*s" "type=%s id=%s %p... (loop)\n", (int)indent * 4, "", typename, id, node); return; } - ec_keyval_set(dict, buf, NULL, NULL); + ec_dict_set(dict, buf, NULL, NULL); fprintf(out, "%*s" "type=%s id=%s %p refs=%u free_state=%d free_refs=%d\n", (int)indent * 4, "", typename, id, node, node->refcnt, node->free.state, node->free.refcnt); @@ -386,7 +386,7 @@ static void __ec_node_dump(FILE *out, /* XXX this is too much debug-oriented, we should have a parameter or 2 funcs */ void ec_node_dump(FILE *out, const struct ec_node *node) { - struct ec_keyval *dict = NULL; + struct ec_dict *dict = NULL; fprintf(out, "------------------- node dump:\n"); @@ -395,17 +395,17 @@ void ec_node_dump(FILE *out, const struct ec_node *node) return; } - dict = ec_keyval(); + dict = ec_dict(); if (dict == NULL) goto fail; __ec_node_dump(out, node, 0, dict); - ec_keyval_free(dict); + ec_dict_free(dict); return; fail: - ec_keyval_free(dict); + ec_dict_free(dict); EC_LOG(EC_LOG_ERR, "failed to dump node\n"); } @@ -507,7 +507,7 @@ static int ec_node_testcase(void) testres |= EC_TEST_CHECK(child == NULL, "child with wrong id should be NULL"); - ret = ec_keyval_set(ec_node_attrs(node), "key", "val", NULL); + ret = ec_dict_set(ec_node_attrs(node), "key", "val", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set node attribute\n"); diff --git a/src/ecoli_node_any.c b/src/ecoli_node_any.c index fbcc460..88ac542 100644 --- a/src/ecoli_node_any.c +++ b/src/ecoli_node_any.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -30,7 +30,7 @@ static int ec_node_any_parse(const struct ec_node *gen_node, const struct ec_strvec *strvec) { struct ec_node_any *node = (struct ec_node_any *)gen_node; - const struct ec_keyval *attrs; + const struct ec_dict *attrs; (void)state; @@ -38,7 +38,7 @@ static int ec_node_any_parse(const struct ec_node *gen_node, return EC_PARSE_NOMATCH; if (node->attr_name != NULL) { attrs = ec_strvec_get_attrs(strvec, 0); - if (attrs == NULL || !ec_keyval_has_key(attrs, node->attr_name)) + if (attrs == NULL || !ec_dict_has_key(attrs, node->attr_name)) return EC_PARSE_NOMATCH; } diff --git a/src/ecoli_node_dynamic.c b/src/ecoli_node_dynamic.c index 7c73b65..a7130aa 100644 --- a/src/ecoli_node_dynamic.c +++ b/src/ecoli_node_dynamic.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -47,7 +47,7 @@ ec_node_dynamic_parse(const struct ec_node *gen_node, /* add the node pointer in the attributes, so it will be freed * when parse is freed */ snprintf(key, sizeof(key), "_dyn_%p", child); - ret = ec_keyval_set(ec_parse_get_attrs(parse), key, child, + ret = ec_dict_set(ec_parse_get_attrs(parse), key, child, (void *)node_free); if (ret < 0) { child = NULL; /* already freed */ @@ -81,7 +81,7 @@ ec_node_dynamic_complete(const struct ec_node *gen_node, /* add the node pointer in the attributes, so it will be freed * when parse is freed */ snprintf(key, sizeof(key), "_dyn_%p", child); - ret = ec_keyval_set(comp->attrs, key, child, + ret = ec_dict_set(comp->attrs, key, child, (void *)node_free); if (ret < 0) { child = NULL; /* already freed */ diff --git a/src/ecoli_node_re_lex.c b/src/ecoli_node_re_lex.c index e7751ac..a007aba 100644 --- a/src/ecoli_node_re_lex.c +++ b/src/ecoli_node_re_lex.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,7 +44,7 @@ static struct ec_strvec * tokenize(struct regexp_pattern *table, size_t table_len, const char *str) { struct ec_strvec *strvec = NULL; - struct ec_keyval *attrs = NULL; + struct ec_dict *attrs = NULL; char *dup = NULL; char c; size_t len, off = 0; @@ -81,10 +81,10 @@ tokenize(struct regexp_pattern *table, size_t table_len, const char *str) goto fail; if (table[i].attr_name != NULL) { - attrs = ec_keyval(); + attrs = ec_dict(); if (attrs == NULL) goto fail; - if (ec_keyval_set(attrs, table[i].attr_name, + if (ec_dict_set(attrs, table[i].attr_name, NULL, NULL) < 0) goto fail; if (ec_strvec_set_attrs(strvec, diff --git a/src/ecoli_parse.c b/src/ecoli_parse.c index f224fda..e01ff0a 100644 --- a/src/ecoli_parse.c +++ b/src/ecoli_parse.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,7 +31,7 @@ struct ec_parse { struct ec_parse *parent; const struct ec_node *node; struct ec_strvec *strvec; - struct ec_keyval *attrs; + struct ec_dict *attrs; }; static int __ec_node_parse_child(const struct ec_node *node, @@ -149,7 +149,7 @@ struct ec_parse *ec_parse(const struct ec_node *node) TAILQ_INIT(&parse->children); parse->node = node; - parse->attrs = ec_keyval(); + parse->attrs = ec_dict(); if (parse->attrs == NULL) goto fail; @@ -157,7 +157,7 @@ struct ec_parse *ec_parse(const struct ec_node *node) fail: if (parse != NULL) - ec_keyval_free(parse->attrs); + ec_dict_free(parse->attrs); ec_free(parse); return NULL; @@ -169,7 +169,7 @@ __ec_parse_dup(const struct ec_parse *root, const struct ec_parse *ref, { struct ec_parse *dup = NULL; struct ec_parse *child, *dup_child; - struct ec_keyval *attrs = NULL; + struct ec_dict *attrs = NULL; if (root == NULL) return NULL; @@ -181,10 +181,10 @@ __ec_parse_dup(const struct ec_parse *root, const struct ec_parse *ref, if (root == ref) *new_ref = dup; - attrs = ec_keyval_dup(root->attrs); + attrs = ec_dict_dup(root->attrs); if (attrs == NULL) goto fail; - ec_keyval_free(dup->attrs); + ec_dict_free(dup->attrs); dup->attrs = attrs; if (root->strvec != NULL) { @@ -246,7 +246,7 @@ void ec_parse_free(struct ec_parse *parse) ec_parse_free_children(parse); ec_strvec_free(parse->strvec); - ec_keyval_free(parse->attrs); + ec_dict_free(parse->attrs); ec_free(parse); } @@ -416,7 +416,7 @@ struct ec_parse *ec_parse_find(struct ec_parse *parse, return ec_parse_find_next(parse, NULL, id, 1); } -struct ec_keyval * +struct ec_dict * ec_parse_get_attrs(struct ec_parse *parse) { if (parse == NULL) @@ -497,7 +497,7 @@ static int ec_parse_testcase(void) testres |= EC_TEST_CHECK( ec_parse_len(p) == 1, "bad parse len\n"); - ret = ec_keyval_set(ec_parse_get_attrs(p), "key", "val", NULL); + ret = ec_dict_set(ec_parse_get_attrs(p), "key", "val", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set parse attribute\n"); diff --git a/src/ecoli_strvec.c b/src/ecoli_strvec.c index 98a952f..bf2eca7 100644 --- a/src/ecoli_strvec.c +++ b/src/ecoli_strvec.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include EC_LOG_TYPE_REGISTER(strvec); @@ -19,7 +19,7 @@ EC_LOG_TYPE_REGISTER(strvec); struct ec_strvec_elt { unsigned int refcnt; char *str; - struct ec_keyval *attrs; + struct ec_dict *attrs; }; struct ec_strvec { @@ -63,7 +63,7 @@ __ec_strvec_elt_free(struct ec_strvec_elt *elt) elt->refcnt--; if (elt->refcnt == 0) { ec_free(elt->str); - ec_keyval_free(elt->attrs); + ec_dict_free(elt->attrs); ec_free(elt); } } @@ -217,7 +217,7 @@ const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx) return strvec->vec[idx]->str; } -const struct ec_keyval *ec_strvec_get_attrs(const struct ec_strvec *strvec, +const struct ec_dict *ec_strvec_get_attrs(const struct ec_strvec *strvec, size_t idx) { if (strvec == NULL || idx >= strvec->len) { @@ -229,7 +229,7 @@ const struct ec_keyval *ec_strvec_get_attrs(const struct ec_strvec *strvec, } int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx, - struct ec_keyval *attrs) + struct ec_dict *attrs) { struct ec_strvec_elt *elt; @@ -246,14 +246,14 @@ int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx, } if (elt->attrs != NULL) - ec_keyval_free(elt->attrs); + ec_dict_free(elt->attrs); elt->attrs = attrs; return 0; fail: - ec_keyval_free(attrs); + ec_dict_free(attrs); return -1; } @@ -317,8 +317,8 @@ static int ec_strvec_testcase(void) { struct ec_strvec *strvec = NULL; struct ec_strvec *strvec2 = NULL; - const struct ec_keyval *const_attrs = NULL; - struct ec_keyval *attrs = NULL; + const struct ec_dict *const_attrs = NULL; + struct ec_dict *attrs = NULL; FILE *f = NULL; char *buf = NULL; size_t buflen = 0; @@ -475,12 +475,12 @@ static int ec_strvec_testcase(void) EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } - attrs = ec_keyval(); + attrs = ec_dict(); if (attrs == NULL) { EC_TEST_ERR("cannot create attrs\n"); goto fail; } - if (ec_keyval_set(attrs, "key", "value", NULL) < 0) { + if (ec_dict_set(attrs, "key", "value", NULL) < 0) { EC_TEST_ERR("cannot set attr\n"); goto fail; } @@ -500,7 +500,7 @@ static int ec_strvec_testcase(void) goto fail; } testres |= EC_TEST_CHECK( - ec_keyval_has_key(const_attrs, "key"), "cannot get attrs key\n"); + ec_dict_has_key(const_attrs, "key"), "cannot get attrs key\n"); strvec2 = EC_STRVEC("a", "b", "c", "d", "e", "f"); if (strvec2 == NULL) { @@ -519,7 +519,7 @@ static int ec_strvec_testcase(void) fail: if (f != NULL) fclose(f); - ec_keyval_free(attrs); + ec_dict_free(attrs); ec_strvec_free(strvec); ec_strvec_free(strvec2); free(buf); diff --git a/src/ecoli_yaml.c b/src/ecoli_yaml.c index 326af56..4843ef0 100644 --- a/src/ecoli_yaml.c +++ b/src/ecoli_yaml.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include @@ -466,7 +466,7 @@ parse_ec_node(struct enode_table *table, config = NULL; /* freed */ if (help != NULL) { - if (ec_keyval_set(ec_node_attrs(enode), "help", help, + if (ec_dict_set(ec_node_attrs(enode), "help", help, ec_free_func) < 0) { fprintf(stderr, "Failed to set help\n"); help = NULL; @@ -486,7 +486,7 @@ parse_ec_node(struct enode_table *table, value_dup = ec_strdup(value_str); if (value_dup == NULL) goto fail; - if (ec_keyval_set(ec_node_attrs(enode), key_str, + if (ec_dict_set(ec_node_attrs(enode), key_str, value_dup, ec_free_func) < 0) { value_dup = NULL; goto fail; diff --git a/src/meson.build b/src/meson.build index db350eb..cfd1d04 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,8 +7,9 @@ libecoli_sources = [ 'ecoli_assert.c', 'ecoli_complete.c', 'ecoli_config.c', + 'ecoli_dict.c', 'ecoli_init.c', - 'ecoli_keyval.c', + 'ecoli_htable.c', 'ecoli_log.c', 'ecoli_malloc.c', 'ecoli_murmurhash.c', -- 2.20.1