From: Olivier Matz Date: Thu, 19 Jul 2018 18:30:20 +0000 (+0200) Subject: add get_child_refs, tests are ok X-Git-Url: http://git.droids-corp.org/?p=protos%2Flibecoli.git;a=commitdiff_plain;h=eaaf443bad651bd0b01f0bde3668a0f7d7f2dff0 add get_child_refs, tests are ok --- diff --git a/lib/ecoli_config.c b/lib/ecoli_config.c index aa2d7e3..f8a45d7 100644 --- a/lib/ecoli_config.c +++ b/lib/ecoli_config.c @@ -621,7 +621,7 @@ int ec_config_list_add(struct ec_config *list, struct ec_config *value) { - if (list == NULL || list->type != EC_CONFIG_TYPE_LIST) { + if (list == NULL || list->type != EC_CONFIG_TYPE_LIST || value == NULL) { errno = EINVAL; goto fail; } diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c index 7845611..484e25f 100644 --- a/lib/ecoli_node.c +++ b/lib/ecoli_node.c @@ -125,21 +125,22 @@ struct ec_node *ec_node(const char *typename, const char *id) return __ec_node(type, id); } -static void count_references(struct ec_node *node) +static void count_references(struct ec_node *node, unsigned int refs) { struct ec_node *child; size_t i, n; if (node->free.state == EC_NODE_FREE_STATE_TRAVERSED) { - node->free.refcnt++; + node->free.refcnt += refs; return; } - node->free.refcnt = 1; + node->free.refcnt = refs; node->free.state = EC_NODE_FREE_STATE_TRAVERSED; n = ec_node_get_children_count(node); for (i = 0; i < n; i++) { child = ec_node_get_child(node, i); - count_references(child); + refs = ec_node_get_child_refs(node, i); + count_references(child, refs); } } @@ -151,8 +152,9 @@ static void mark_freeable(struct ec_node *node, enum ec_node_free_state mark) if (mark == node->free.state) return; - if (node->refcnt != node->free.refcnt) + if (node->refcnt > node->free.refcnt) mark = EC_NODE_FREE_STATE_NOT_FREEABLE; + assert(node->refcnt >= node->free.refcnt); node->free.state = mark; n = ec_node_get_children_count(node); @@ -201,7 +203,7 @@ void ec_node_free(struct ec_node *node) * node reachable from an unfreeable node is also marked as * unfreeable. */ if (node->free.state == EC_NODE_FREE_STATE_NONE) { - count_references(node); + count_references(node, 1); mark_freeable(node, EC_NODE_FREE_STATE_FREEABLE); } } @@ -259,6 +261,14 @@ ec_node_get_child(const struct ec_node *node, size_t i) return node->type->get_child(node, i); } +unsigned int +ec_node_get_child_refs(const struct ec_node *node, size_t i) +{ + if (node->type->get_child_refs == NULL) + return 1; + return node->type->get_child_refs(node, i); +} + int ec_node_set_config(struct ec_node *node, struct ec_config *config) { @@ -343,7 +353,7 @@ static void __ec_node_dump(FILE *out, } ec_keyval_set(dict, buf, NULL, NULL); - fprintf(out, "%*s" "type=%s id=%s %p refs=%u free=(%d,%d)\n", + 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); diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h index 9bf459d..67652ab 100644 --- a/lib/ecoli_node.h +++ b/lib/ecoli_node.h @@ -85,6 +85,8 @@ typedef void (*ec_node_free_priv_t)(struct ec_node *); typedef size_t (*ec_node_get_children_count_t)(const struct ec_node *); typedef struct ec_node * (*ec_node_get_child_t)(const struct ec_node *, size_t i); +typedef unsigned int (*ec_node_get_child_refs_t)(const struct ec_node *, + size_t i); /** * A structure describing a node type. @@ -104,6 +106,7 @@ struct ec_node_type { ec_node_free_priv_t free_priv; ec_node_get_children_count_t get_children_count; ec_node_get_child_t get_child; + ec_node_get_child_refs_t get_child_refs; }; /** @@ -176,8 +179,8 @@ const struct ec_config *ec_node_get_config(struct ec_node *node); size_t ec_node_get_children_count(const struct ec_node *node); struct ec_node * ec_node_get_child(const struct ec_node *node, size_t i); -int ec_node_add_child(struct ec_node *node, struct ec_node *child); -int ec_node_del_child(struct ec_node *node, struct ec_node *child); +unsigned int +ec_node_get_child_refs(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); diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c index 1750cab..d298c14 100644 --- a/lib/ecoli_node_cmd.c +++ b/lib/ecoli_node_cmd.c @@ -413,6 +413,8 @@ static void ec_node_cmd_free_priv(struct ec_node *gen_node) struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; size_t i; + /* node->cmd is freed automatically, because it is returned + * by ec_node_cmd_get_child() */ ec_free(node->cmd_str); ec_node_free(node->expr); ec_node_free(node->parser); diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c index ed7325f..92dc73e 100644 --- a/lib/ecoli_node_seq.c +++ b/lib/ecoli_node_seq.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -161,6 +162,68 @@ static void ec_node_seq_free_priv(struct ec_node *gen_node) ec_free(node->table); } +static const struct ec_config_schema ec_node_seq_subschema[] = { + { + .desc = "A child node which is part of the sequence.", + .type = EC_CONFIG_TYPE_NODE, + }, +}; + +static const struct ec_config_schema ec_node_seq_schema[] = { + { + .key = "children", + .desc = "The list of children nodes, to be parsed in sequence.", + .type = EC_CONFIG_TYPE_LIST, + .subschema = ec_node_seq_subschema, + .subschema_len = EC_COUNT_OF(ec_node_seq_subschema), + }, +}; + +static int ec_node_seq_set_config(struct ec_node *gen_node, + const struct ec_config *config) +{ + struct ec_node_seq *node = (struct ec_node_seq *)gen_node; + const struct ec_config *children = NULL, *child; + struct ec_node **table = NULL; + size_t n, i; + + children = ec_config_dict_get(config, "children"); + if (children == NULL) { + errno = EINVAL; + goto fail; + } + + n = 0; + TAILQ_FOREACH(child, &children->list, next) + n++; + + table = ec_malloc(n * sizeof(*table)); + if (table == NULL) + goto fail; + + n = 0; + TAILQ_FOREACH(child, &children->list, next) { + table[n] = ec_node_clone(child->node); + n++; + } + + for (i = 0; i < node->len; i++) + ec_node_free(node->table[i]); + ec_free(node->table); + node->table = table; + node->len = n; + + return 0; + +fail: + if (table != NULL) { + for (i = 0; i < n; i++) + ec_node_free(table[i]); + } + ec_free(table); + return -1; +} + static size_t ec_node_seq_get_children_count(const struct ec_node *gen_node) { @@ -179,14 +242,29 @@ ec_node_seq_get_child(const struct ec_node *gen_node, size_t i) return node->table[i]; } +static unsigned int +ec_node_seq_get_child_refs(const struct ec_node *gen_node, size_t i) +{ + (void)gen_node; + (void)i; + + /* each child node is referenced twice: once in the config and + * once in the node->table[] */ + return 2; +} + static struct ec_node_type ec_node_seq_type = { .name = "seq", + .schema = ec_node_seq_schema, + .schema_len = EC_COUNT_OF(ec_node_seq_schema), + .set_config = ec_node_seq_set_config, .parse = ec_node_seq_parse, .complete = ec_node_seq_complete, .size = sizeof(struct ec_node_seq), .free_priv = ec_node_seq_free_priv, .get_children_count = ec_node_seq_get_children_count, .get_child = ec_node_seq_get_child, + .get_child_refs = ec_node_seq_get_child_refs, }; EC_NODE_TYPE_REGISTER(ec_node_seq_type); @@ -194,69 +272,110 @@ EC_NODE_TYPE_REGISTER(ec_node_seq_type); int ec_node_seq_add(struct ec_node *gen_node, struct ec_node *child) { struct ec_node_seq *node = (struct ec_node_seq *)gen_node; - struct ec_node **table; + const struct ec_config *cur_config = NULL; + struct ec_config *config = NULL, *children; + int ret; assert(node != NULL); - if (child == NULL) { - errno = EINVAL; - goto fail; - } + /* XXX factorize this code in a helper */ if (ec_node_check_type(gen_node, &ec_node_seq_type) < 0) goto fail; - table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); - if (table == NULL) + cur_config = ec_node_get_config(gen_node); + if (cur_config == NULL) + config = ec_config_dict(); + else + config = ec_config_dup(cur_config); + if (config == NULL) goto fail; - node->table = table; - table[node->len] = child; - node->len++; + children = ec_config_dict_get(config, "children"); + if (children == NULL) { + children = ec_config_list(); + if (children == NULL) + goto fail; + + if (ec_config_dict_set(config, "children", children) < 0) + goto fail; /* children list is freed on error */ + } + + if (ec_config_list_add(children, ec_config_node(child)) < 0) { + child = NULL; + goto fail; + } + + ret = ec_node_set_config(gen_node, config); + config = NULL; /* freed */ + if (ret < 0) + goto fail; return 0; fail: + ec_config_free(config); ec_node_free(child); return -1; } struct ec_node *__ec_node_seq(const char *id, ...) { + struct ec_config *config = NULL, *children = NULL; struct ec_node *gen_node = NULL; - struct ec_node_seq *node = NULL; struct ec_node *child; va_list ap; - int fail = 0; + int ret; va_start(ap, id); + child = va_arg(ap, struct ec_node *); gen_node = __ec_node(&ec_node_seq_type, id); - node = (struct ec_node_seq *)gen_node; - if (node == NULL) - fail = 1;; - - for (child = va_arg(ap, struct ec_node *); - child != EC_NODE_ENDLIST; - child = va_arg(ap, struct ec_node *)) { - - /* on error, don't quit the loop to avoid leaks */ - if (fail == 1 || child == NULL || - ec_node_seq_add(&node->gen, child) < 0) { - fail = 1; - ec_node_free(child); + if (gen_node == NULL) + goto fail_free_children; + + config = ec_config_dict(); + if (config == NULL) + goto fail_free_children; + + children = ec_config_list(); + if (children == NULL) + goto fail_free_children; + + for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *)) { + if (child == NULL) + goto fail_free_children; + + if (ec_config_list_add(children, ec_config_node(child)) < 0) { + child = NULL; + goto fail_free_children; } } - if (fail == 1) + if (ec_config_dict_set(config, "children", children) < 0) { + children = NULL; /* freed */ + goto fail; + } + children = NULL; + + ret = ec_node_set_config(gen_node, config); + config = NULL; /* freed */ + if (ret < 0) goto fail; va_end(ap); + return gen_node; +fail_free_children: + for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *)) + ec_node_free(child); fail: - ec_node_free(gen_node); /* will also free children */ + ec_node_free(gen_node); /* will also free added children */ + ec_config_free(children); + ec_config_free(config); va_end(ap); + return NULL; } @@ -281,6 +400,10 @@ static int ec_node_seq_testcase(void) testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "barx"); testres |= EC_TEST_CHECK_PARSE(node, -1, "bar", "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "", "foo"); + + testres |= (ec_node_seq_add(node, ec_node_str(EC_NO_ID, "grr")) < 0); + testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "bar", "grr"); + ec_node_free(node); /* test completion */ diff --git a/lib/main.c b/lib/main.c index 2a08cdd..1b2b7ef 100644 --- a/lib/main.c +++ b/lib/main.c @@ -142,6 +142,7 @@ static struct debug_alloc_hdr_list debug_alloc_hdr_list = struct debug_alloc_hdr { TAILQ_ENTRY(debug_alloc_hdr) next; const char *file; + unsigned int seq; unsigned int line; size_t size; void *stack[STACK_SZ]; @@ -171,6 +172,7 @@ static void *debug_malloc(size_t size, const char *file, unsigned int line) if (hdr == NULL) { ret = NULL; } else { + hdr->seq = malloc_seq; hdr->file = file; hdr->line = line; hdr->size = size; @@ -286,6 +288,7 @@ static void *debug_realloc(void *ptr, size_t size, const char *file, } if (hdr != NULL) { + hdr->seq = malloc_seq; hdr->file = file; hdr->line = line; hdr->size = size; @@ -318,8 +321,8 @@ static int debug_alloc_dump_leaks(void) TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) { EC_LOG(EC_LOG_ERR, - "%s:%d: error: memory leak size=%zd ptr=%p\n", - hdr->file, hdr->line, hdr->size, hdr + 1); + "%s:%d: error: memory leak seq=%u size=%zd ptr=%p\n", + hdr->file, hdr->line, hdr->seq, hdr->size, hdr + 1); buffer = backtrace_symbols(hdr->stack, hdr->stacklen); if (buffer == NULL) { for (i = 0; i < hdr->stacklen; i++)