free children in free_priv
[protos/libecoli.git] / lib / ecoli_node.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <errno.h>
11
12 #include <ecoli_malloc.h>
13 #include <ecoli_string.h>
14 #include <ecoli_strvec.h>
15 #include <ecoli_keyval.h>
16 #include <ecoli_log.h>
17 #include <ecoli_config.h>
18 #include <ecoli_test.h>
19 #include <ecoli_node.h>
20
21 #include <ecoli_node_str.h>
22 #include <ecoli_node_seq.h>
23 #include <ecoli_node_or.h>
24 #include <ecoli_node_int.h>
25
26 EC_LOG_TYPE_REGISTER(node);
27
28 static struct ec_node_type_list node_type_list =
29         TAILQ_HEAD_INITIALIZER(node_type_list);
30
31 const struct ec_node_type *
32 ec_node_type_lookup(const char *name)
33 {
34         struct ec_node_type *type;
35
36         TAILQ_FOREACH(type, &node_type_list, next) {
37                 if (!strcmp(name, type->name))
38                         return type;
39         }
40
41         errno = ENOENT;
42         return NULL;
43 }
44
45 int ec_node_type_register(struct ec_node_type *type)
46 {
47         EC_CHECK_ARG(type->size >= sizeof(struct ec_node), -1, EINVAL);
48
49         if (ec_node_type_lookup(type->name) != NULL) {
50                 errno = EEXIST;
51                 return -1;
52         }
53
54         TAILQ_INSERT_TAIL(&node_type_list, type, next);
55
56         return 0;
57 }
58
59 void ec_node_type_dump(FILE *out)
60 {
61         struct ec_node_type *type;
62
63         TAILQ_FOREACH(type, &node_type_list, next)
64                 fprintf(out, "%s\n", type->name);
65 }
66
67 struct ec_node *__ec_node(const struct ec_node_type *type, const char *id)
68 {
69         struct ec_node *node = NULL;
70
71         EC_LOG(EC_LOG_DEBUG, "create node type=%s id=%s\n",
72                 type->name, id);
73         if (id == NULL) {
74                 errno = EINVAL;
75                 goto fail;
76         }
77
78         node = ec_calloc(1, type->size);
79         if (node == NULL)
80                 goto fail;
81
82         node->type = type;
83         node->refcnt = 1;
84
85         node->id = ec_strdup(id);
86         if (node->id == NULL)
87                 goto fail;
88
89         if (ec_asprintf(&node->desc, "<%s>", type->name) < 0)
90                 goto fail;
91
92         node->attrs = ec_keyval();
93         if (node->attrs == NULL)
94                 goto fail;
95
96         if (type->init_priv != NULL) {
97                 if (type->init_priv(node) < 0)
98                         goto fail;
99         }
100
101         return node;
102
103  fail:
104         if (node != NULL) {
105                 ec_keyval_free(node->attrs);
106                 ec_free(node->desc);
107                 ec_free(node->id);
108         }
109         ec_free(node);
110
111         return NULL;
112 }
113
114 struct ec_node *ec_node(const char *typename, const char *id)
115 {
116         const struct ec_node_type *type;
117
118         type = ec_node_type_lookup(typename);
119         if (type == NULL) {
120                 EC_LOG(EC_LOG_ERR, "type=%s does not exist\n",
121                         typename);
122                 return NULL;
123         }
124
125         return __ec_node(type, id);
126 }
127
128 static void count_references(struct ec_node *node, unsigned int refs)
129 {
130         struct ec_node *child;
131         size_t i, n;
132
133         if (node->free.state == EC_NODE_FREE_STATE_TRAVERSED) {
134                 node->free.refcnt += refs;
135                 return;
136         }
137         node->free.refcnt = refs;
138         node->free.state = EC_NODE_FREE_STATE_TRAVERSED;
139         n = ec_node_get_children_count(node);
140         for (i = 0; i < n; i++) {
141                 child = ec_node_get_child(node, i);
142                 refs = ec_node_get_child_refs(node, i);
143                 count_references(child, refs);
144         }
145 }
146
147 static void mark_freeable(struct ec_node *node, enum ec_node_free_state mark)
148 {
149         struct ec_node *child;
150         size_t i, n;
151
152         if (mark == node->free.state)
153                 return;
154
155         if (node->refcnt > node->free.refcnt)
156                 mark = EC_NODE_FREE_STATE_NOT_FREEABLE;
157         assert(node->refcnt >= node->free.refcnt);
158         node->free.state = mark;
159
160         n = ec_node_get_children_count(node);
161         for (i = 0; i < n; i++) {
162                 child = ec_node_get_child(node, i);
163                 mark_freeable(child, mark);
164         }
165 }
166
167 static void reset_mark(struct ec_node *node)
168 {
169         struct ec_node *child;
170         size_t i, n;
171
172         if (node->free.state == EC_NODE_FREE_STATE_NONE)
173                 return;
174
175         node->free.state = EC_NODE_FREE_STATE_NONE;
176         node->free.refcnt = 0;
177
178         n = ec_node_get_children_count(node);
179         for (i = 0; i < n; i++) {
180                 child = ec_node_get_child(node, i);
181                 reset_mark(child);
182         }
183 }
184
185 /* free a node, taking care of loops in the node graph */
186 void ec_node_free(struct ec_node *node)
187 {
188         size_t n;
189
190         if (node == NULL)
191                 return;
192
193         assert(node->refcnt > 0);
194
195         if (node->free.state == EC_NODE_FREE_STATE_NONE &&
196                         node->refcnt != 1) {
197
198                 /* Traverse the node tree starting from this node, and for each
199                  * node, count the number of reachable references. Then, all
200                  * nodes whose reachable references == total reference are
201                  * marked as freeable, and other are marked as unfreeable. Any
202                  * node reachable from an unfreeable node is also marked as
203                  * unfreeable. */
204                 if (node->free.state == EC_NODE_FREE_STATE_NONE) {
205                         count_references(node, 1);
206                         mark_freeable(node, EC_NODE_FREE_STATE_FREEABLE);
207                 }
208         }
209
210         if (node->free.state == EC_NODE_FREE_STATE_NOT_FREEABLE) {
211                 node->refcnt--;
212                 reset_mark(node);
213                 return;
214         }
215
216         if (node->free.state != EC_NODE_FREE_STATE_FREEING) {
217                 node->free.state = EC_NODE_FREE_STATE_FREEING;
218                 n = ec_node_get_children_count(node);
219                 /* children should be freed by free_priv() */
220                 assert(n == 0 || node->type->free_priv != NULL);
221                 if (node->type->free_priv != NULL)
222                         node->type->free_priv(node);
223         }
224
225         node->refcnt--;
226         if (node->refcnt != 0)
227                 return;
228
229         node->free.state = EC_NODE_FREE_STATE_NONE;
230         node->free.refcnt = 0;
231
232         ec_free(node->id);
233         ec_free(node->desc);
234         ec_keyval_free(node->attrs);
235         ec_config_free(node->config);
236         ec_free(node);
237 }
238
239 struct ec_node *ec_node_clone(struct ec_node *node)
240 {
241         if (node != NULL)
242                 node->refcnt++;
243         return node;
244 }
245
246 size_t ec_node_get_children_count(const struct ec_node *node)
247 {
248         if (node->type->get_children_count == NULL)
249                 return 0;
250         return node->type->get_children_count(node);
251 }
252
253 struct ec_node *
254 ec_node_get_child(const struct ec_node *node, size_t i)
255 {
256         if (node->type->get_child == NULL)
257                 return NULL;
258         return node->type->get_child(node, i);
259 }
260
261 unsigned int
262 ec_node_get_child_refs(const struct ec_node *node, size_t i)
263 {
264         if (node->type->get_child_refs == NULL)
265                 return 1;
266         return node->type->get_child_refs(node, i);
267 }
268
269 int
270 ec_node_set_config(struct ec_node *node, struct ec_config *config)
271 {
272         if (node->type->schema == NULL) {
273                 errno = EINVAL;
274                 goto fail;
275         }
276         if (ec_config_validate(config, node->type->schema,
277                                 node->type->schema_len) < 0)
278                 goto fail;
279         if (node->type->set_config != NULL) {
280                 if (node->type->set_config(node, config) < 0)
281                         goto fail;
282         }
283
284         ec_config_free(node->config);
285         node->config = config;
286
287         return 0;
288
289 fail:
290         ec_config_free(config);
291         return -1;
292 }
293
294 const struct ec_config *ec_node_get_config(struct ec_node *node)
295 {
296         return node->config;
297 }
298
299 struct ec_node *ec_node_find(struct ec_node *node, const char *id)
300 {
301         struct ec_node *child, *ret;
302         const char *node_id = ec_node_id(node);
303         size_t i, n;
304
305         if (id != NULL && node_id != NULL && !strcmp(node_id, id))
306                 return node;
307
308         n = ec_node_get_children_count(node);
309         for (i = 0; i < n; i++) {
310                 child = ec_node_get_child(node, i);
311                 ret = ec_node_find(child, id);
312                 if (ret != NULL)
313                         return ret;
314         }
315
316         return NULL;
317 }
318
319 const struct ec_node_type *ec_node_type(const struct ec_node *node)
320 {
321         return node->type;
322 }
323
324 struct ec_keyval *ec_node_attrs(const struct ec_node *node)
325 {
326         return node->attrs;
327 }
328
329 const char *ec_node_id(const struct ec_node *node)
330 {
331         return node->id;
332 }
333
334 static void __ec_node_dump(FILE *out,
335         const struct ec_node *node, size_t indent, struct ec_keyval *dict)
336 {
337         const char *id, *typename;
338         struct ec_node *child;
339         char buf[32];
340         size_t i, n;
341
342         id = ec_node_id(node);
343         typename = node->type->name;
344
345         snprintf(buf, sizeof(buf), "%p", node);
346         if (ec_keyval_has_key(dict, buf)) {
347                 fprintf(out, "%*s" "type=%s id=%s %p... (loop)\n",
348                         (int)indent * 4, "", typename, id, node);
349                 return;
350         }
351
352         ec_keyval_set(dict, buf, NULL, NULL);
353         fprintf(out, "%*s" "type=%s id=%s %p refs=%u free_state=%d free_refs=%d\n",
354                 (int)indent * 4, "", typename, id, node, node->refcnt,
355                 node->free.state, node->free.refcnt);
356
357         n = ec_node_get_children_count(node);
358         for (i = 0; i < n; i++) {
359                 child = ec_node_get_child(node, i);
360                 __ec_node_dump(out, child, indent + 1, dict);
361         }
362 }
363
364 void ec_node_dump(FILE *out, const struct ec_node *node)
365 {
366         struct ec_keyval *dict = NULL;
367
368         fprintf(out, "------------------- node dump:\n");
369
370         if (node == NULL) {
371                 fprintf(out, "node is NULL\n");
372                 return;
373         }
374
375         dict = ec_keyval();
376         if (dict == NULL)
377                 goto fail;
378
379         __ec_node_dump(out, node, 0, dict);
380
381         ec_keyval_free(dict);
382         return;
383
384 fail:
385         ec_keyval_free(dict);
386         EC_LOG(EC_LOG_ERR, "failed to dump node\n");
387 }
388
389 const char *ec_node_desc(const struct ec_node *node)
390 {
391         if (node->type->desc != NULL)
392                 return node->type->desc(node);
393
394         return node->desc;
395 }
396
397 int ec_node_check_type(const struct ec_node *node,
398                 const struct ec_node_type *type)
399 {
400         if (strcmp(node->type->name, type->name)) {
401                 errno = EINVAL;
402                 return -1;
403         }
404
405         return 0;
406 }
407
408 /* LCOV_EXCL_START */
409 static int ec_node_testcase(void)
410 {
411         struct ec_node *node = NULL, *expr = NULL;
412         struct ec_node *expr2 = NULL, *val = NULL, *op = NULL, *seq = NULL;
413         const struct ec_node *child;
414         const struct ec_node_type *type;
415         FILE *f = NULL;
416         char *buf = NULL;
417         size_t buflen = 0;
418         int testres = 0;
419         int ret;
420
421         node = EC_NODE_SEQ(EC_NO_ID,
422                         ec_node_str("id_x", "x"),
423                         ec_node_str("id_y", "y"));
424         if (node == NULL)
425                 goto fail;
426
427         ec_node_clone(node);
428         ec_node_free(node);
429
430         f = open_memstream(&buf, &buflen);
431         if (f == NULL)
432                 goto fail;
433         ec_node_dump(f, node);
434         ec_node_type_dump(f);
435         ec_node_dump(f, NULL);
436         fclose(f);
437         f = NULL;
438
439         testres |= EC_TEST_CHECK(
440                 strstr(buf, "type=seq id=no-id"), "bad dump\n");
441         testres |= EC_TEST_CHECK(
442                 strstr(buf, "type=str id=id_x") &&
443                 strstr(strstr(buf, "type=str id=id_x") + 1,
444                         "type=str id=id_y"),
445                 "bad dump\n");
446         free(buf);
447         buf = NULL;
448
449         testres |= EC_TEST_CHECK(
450                 !strcmp(ec_node_type(node)->name, "seq") &&
451                 !strcmp(ec_node_id(node), EC_NO_ID) &&
452                 !strcmp(ec_node_desc(node), "<seq>"),
453                 "bad child 0");
454
455         testres |= EC_TEST_CHECK(
456                 ec_node_get_children_count(node) == 2,
457                 "bad children count\n");
458         child = ec_node_get_child(node, 0);
459         testres |= EC_TEST_CHECK(child != NULL &&
460                 !strcmp(ec_node_type(child)->name, "str") &&
461                 !strcmp(ec_node_id(child), "id_x"),
462                 "bad child 0");
463         child = ec_node_get_child(node, 1);
464         testres |= EC_TEST_CHECK(child != NULL &&
465                 !strcmp(ec_node_type(child)->name, "str") &&
466                 !strcmp(ec_node_id(child), "id_y"),
467                 "bad child 1");
468         child = ec_node_get_child(node, 2);
469         testres |= EC_TEST_CHECK(child == NULL,
470                 "child 2 should be NULL");
471
472         child = ec_node_find(node, "id_x");
473         testres |= EC_TEST_CHECK(child != NULL &&
474                 !strcmp(ec_node_type(child)->name, "str") &&
475                 !strcmp(ec_node_id(child), "id_x") &&
476                 !strcmp(ec_node_desc(child), "x"),
477                 "bad child id_x");
478         child = ec_node_find(node, "id_dezdex");
479         testres |= EC_TEST_CHECK(child == NULL,
480                 "child with wrong id should be NULL");
481
482         ret = ec_keyval_set(ec_node_attrs(node), "key", "val", NULL);
483         testres |= EC_TEST_CHECK(ret == 0,
484                 "cannot set node attribute\n");
485
486         type = ec_node_type_lookup("seq");
487         testres |= EC_TEST_CHECK(type != NULL &&
488                 ec_node_check_type(node, type) == 0,
489                 "cannot get seq node type");
490         type = ec_node_type_lookup("str");
491         testres |= EC_TEST_CHECK(type != NULL &&
492                 ec_node_check_type(node, type) < 0,
493                 "node type should not be str");
494
495         ec_node_free(node);
496         node = NULL;
497
498         node = ec_node("deznuindez", EC_NO_ID);
499         testres |= EC_TEST_CHECK(node == NULL,
500                         "should not be able to create node\n");
501
502         /* test loop */
503         expr = ec_node("or", EC_NO_ID);
504         val = ec_node_int(EC_NO_ID, 0, 10, 0);
505         op = ec_node_str(EC_NO_ID, "!");
506         seq = EC_NODE_SEQ(EC_NO_ID,
507                         op,
508                         ec_node_clone(expr));
509         op = NULL;
510         if (expr == NULL || val == NULL || seq == NULL)
511                 goto fail;
512         if (ec_node_or_add(expr, ec_node_clone(seq)) < 0)
513                 goto fail;
514         ec_node_free(seq);
515         seq = NULL;
516         if (ec_node_or_add(expr, ec_node_clone(val)) < 0)
517                 goto fail;
518         ec_node_free(val);
519         val = NULL;
520
521         testres |= EC_TEST_CHECK_PARSE(expr, 1, "1");
522         testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1");
523         testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!");
524
525         ec_node_free(expr);
526         expr = NULL;
527
528         /* same loop test, but keep some refs (released later) */
529         expr = ec_node("or", EC_NO_ID);
530         ec_node_clone(expr);
531         expr2 = expr;
532         val = ec_node_int(EC_NO_ID, 0, 10, 0);
533         op = ec_node_str(EC_NO_ID, "!");
534         seq = EC_NODE_SEQ(EC_NO_ID,
535                         op,
536                         ec_node_clone(expr));
537         op = NULL;
538         if (expr == NULL || val == NULL || seq == NULL)
539                 goto fail;
540         if (ec_node_or_add(expr, ec_node_clone(seq)) < 0)
541                 goto fail;
542         ec_node_free(seq);
543         seq = NULL;
544         if (ec_node_or_add(expr, ec_node_clone(val)) < 0)
545                 goto fail;
546
547         testres |= EC_TEST_CHECK_PARSE(expr, 1, "1");
548         testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1");
549         testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!");
550
551         ec_node_free(expr2);
552         expr2 = NULL;
553         ec_node_free(val);
554         val = NULL;
555         ec_node_free(expr);
556         expr = NULL;
557
558         return testres;
559
560 fail:
561         ec_node_free(expr);
562         ec_node_free(expr2);
563         ec_node_free(val);
564         ec_node_free(seq);
565         ec_node_free(node);
566         if (f != NULL)
567                 fclose(f);
568         free(buf);
569
570         assert(errno != 0);
571         return -1;
572 }
573 /* LCOV_EXCL_STOP */
574
575 static struct ec_test ec_node_test = {
576         .name = "node",
577         .test = ec_node_testcase,
578 };
579
580 EC_TEST_REGISTER(ec_node_test);