7845611264ff7268b1fc553da97a7402dd9cd925
[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)
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++;
135                 return;
136         }
137         node->free.refcnt = 1;
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                 count_references(child);
143         }
144 }
145
146 static void mark_freeable(struct ec_node *node, enum ec_node_free_state mark)
147 {
148         struct ec_node *child;
149         size_t i, n;
150
151         if (mark == node->free.state)
152                 return;
153
154         if (node->refcnt != node->free.refcnt)
155                 mark = EC_NODE_FREE_STATE_NOT_FREEABLE;
156         node->free.state = mark;
157
158         n = ec_node_get_children_count(node);
159         for (i = 0; i < n; i++) {
160                 child = ec_node_get_child(node, i);
161                 mark_freeable(child, mark);
162         }
163 }
164
165 static void reset_mark(struct ec_node *node)
166 {
167         struct ec_node *child;
168         size_t i, n;
169
170         if (node->free.state == EC_NODE_FREE_STATE_NONE)
171                 return;
172
173         node->free.state = EC_NODE_FREE_STATE_NONE;
174         node->free.refcnt = 0;
175
176         n = ec_node_get_children_count(node);
177         for (i = 0; i < n; i++) {
178                 child = ec_node_get_child(node, i);
179                 reset_mark(child);
180         }
181 }
182
183 /* free a node, taking care of loops in the node graph */
184 void ec_node_free(struct ec_node *node)
185 {
186         struct ec_node *child;
187         size_t i, n;
188
189         if (node == NULL)
190                 return;
191
192         assert(node->refcnt > 0);
193
194         if (node->free.state == EC_NODE_FREE_STATE_NONE &&
195                         node->refcnt != 1) {
196
197                 /* Traverse the node tree starting from this node, and for each
198                  * node, count the number of reachable references. Then, all
199                  * nodes whose reachable references == total reference are
200                  * marked as freeable, and other are marked as unfreeable. Any
201                  * node reachable from an unfreeable node is also marked as
202                  * unfreeable. */
203                 if (node->free.state == EC_NODE_FREE_STATE_NONE) {
204                         count_references(node);
205                         mark_freeable(node, EC_NODE_FREE_STATE_FREEABLE);
206                 }
207         }
208
209         if (node->free.state == EC_NODE_FREE_STATE_NOT_FREEABLE) {
210                 node->refcnt--;
211                 reset_mark(node);
212                 return;
213         }
214
215         if (node->free.state != EC_NODE_FREE_STATE_FREEING) {
216                 node->free.state = EC_NODE_FREE_STATE_FREEING;
217                 n = ec_node_get_children_count(node);
218                 for (i = 0; i < n; i++) {
219                         child = ec_node_get_child(node, i);
220                         ec_node_free(child);
221                 }
222         }
223
224         node->refcnt--;
225         if (node->refcnt != 0)
226                 return;
227
228         node->free.state = EC_NODE_FREE_STATE_NONE;
229         node->free.refcnt = 0;
230
231         if (node->type != NULL && node->type->free_priv != NULL)
232                 node->type->free_priv(node);
233         ec_free(node->id);
234         ec_free(node->desc);
235         ec_keyval_free(node->attrs);
236         ec_config_free(node->config);
237         ec_free(node);
238 }
239
240 struct ec_node *ec_node_clone(struct ec_node *node)
241 {
242         if (node != NULL)
243                 node->refcnt++;
244         return node;
245 }
246
247 size_t ec_node_get_children_count(const struct ec_node *node)
248 {
249         if (node->type->get_children_count == NULL)
250                 return 0;
251         return node->type->get_children_count(node);
252 }
253
254 struct ec_node *
255 ec_node_get_child(const struct ec_node *node, size_t i)
256 {
257         if (node->type->get_child == NULL)
258                 return NULL;
259         return node->type->get_child(node, i);
260 }
261
262 int
263 ec_node_set_config(struct ec_node *node, struct ec_config *config)
264 {
265         if (node->type->schema == NULL) {
266                 errno = EINVAL;
267                 goto fail;
268         }
269         if (ec_config_validate(config, node->type->schema,
270                                 node->type->schema_len) < 0)
271                 goto fail;
272         if (node->type->set_config != NULL) {
273                 if (node->type->set_config(node, config) < 0)
274                         goto fail;
275         }
276
277         ec_config_free(node->config);
278         node->config = config;
279
280         return 0;
281
282 fail:
283         ec_config_free(config);
284         return -1;
285 }
286
287 const struct ec_config *ec_node_get_config(struct ec_node *node)
288 {
289         return node->config;
290 }
291
292 struct ec_node *ec_node_find(struct ec_node *node, const char *id)
293 {
294         struct ec_node *child, *ret;
295         const char *node_id = ec_node_id(node);
296         size_t i, n;
297
298         if (id != NULL && node_id != NULL && !strcmp(node_id, id))
299                 return node;
300
301         n = ec_node_get_children_count(node);
302         for (i = 0; i < n; i++) {
303                 child = ec_node_get_child(node, i);
304                 ret = ec_node_find(child, id);
305                 if (ret != NULL)
306                         return ret;
307         }
308
309         return NULL;
310 }
311
312 const struct ec_node_type *ec_node_type(const struct ec_node *node)
313 {
314         return node->type;
315 }
316
317 struct ec_keyval *ec_node_attrs(const struct ec_node *node)
318 {
319         return node->attrs;
320 }
321
322 const char *ec_node_id(const struct ec_node *node)
323 {
324         return node->id;
325 }
326
327 static void __ec_node_dump(FILE *out,
328         const struct ec_node *node, size_t indent, struct ec_keyval *dict)
329 {
330         const char *id, *typename;
331         struct ec_node *child;
332         char buf[32];
333         size_t i, n;
334
335         id = ec_node_id(node);
336         typename = node->type->name;
337
338         snprintf(buf, sizeof(buf), "%p", node);
339         if (ec_keyval_has_key(dict, buf)) {
340                 fprintf(out, "%*s" "type=%s id=%s %p... (loop)\n",
341                         (int)indent * 4, "", typename, id, node);
342                 return;
343         }
344
345         ec_keyval_set(dict, buf, NULL, NULL);
346         fprintf(out, "%*s" "type=%s id=%s %p refs=%u free=(%d,%d)\n",
347                 (int)indent * 4, "", typename, id, node, node->refcnt,
348                 node->free.state, node->free.refcnt);
349
350         n = ec_node_get_children_count(node);
351         for (i = 0; i < n; i++) {
352                 child = ec_node_get_child(node, i);
353                 __ec_node_dump(out, child, indent + 1, dict);
354         }
355 }
356
357 void ec_node_dump(FILE *out, const struct ec_node *node)
358 {
359         struct ec_keyval *dict = NULL;
360
361         fprintf(out, "------------------- node dump:\n");
362
363         if (node == NULL) {
364                 fprintf(out, "node is NULL\n");
365                 return;
366         }
367
368         dict = ec_keyval();
369         if (dict == NULL)
370                 goto fail;
371
372         __ec_node_dump(out, node, 0, dict);
373
374         ec_keyval_free(dict);
375         return;
376
377 fail:
378         ec_keyval_free(dict);
379         EC_LOG(EC_LOG_ERR, "failed to dump node\n");
380 }
381
382 const char *ec_node_desc(const struct ec_node *node)
383 {
384         if (node->type->desc != NULL)
385                 return node->type->desc(node);
386
387         return node->desc;
388 }
389
390 int ec_node_check_type(const struct ec_node *node,
391                 const struct ec_node_type *type)
392 {
393         if (strcmp(node->type->name, type->name)) {
394                 errno = EINVAL;
395                 return -1;
396         }
397
398         return 0;
399 }
400
401 /* LCOV_EXCL_START */
402 static int ec_node_testcase(void)
403 {
404         struct ec_node *node = NULL, *expr = NULL;
405         struct ec_node *expr2 = NULL, *val = NULL, *op = NULL, *seq = NULL;
406         const struct ec_node *child;
407         const struct ec_node_type *type;
408         FILE *f = NULL;
409         char *buf = NULL;
410         size_t buflen = 0;
411         int testres = 0;
412         int ret;
413
414         node = EC_NODE_SEQ(EC_NO_ID,
415                         ec_node_str("id_x", "x"),
416                         ec_node_str("id_y", "y"));
417         if (node == NULL)
418                 goto fail;
419
420         ec_node_clone(node);
421         ec_node_free(node);
422
423         f = open_memstream(&buf, &buflen);
424         if (f == NULL)
425                 goto fail;
426         ec_node_dump(f, node);
427         ec_node_type_dump(f);
428         ec_node_dump(f, NULL);
429         fclose(f);
430         f = NULL;
431
432         testres |= EC_TEST_CHECK(
433                 strstr(buf, "type=seq id=no-id"), "bad dump\n");
434         testres |= EC_TEST_CHECK(
435                 strstr(buf, "type=str id=id_x") &&
436                 strstr(strstr(buf, "type=str id=id_x") + 1,
437                         "type=str id=id_y"),
438                 "bad dump\n");
439         free(buf);
440         buf = NULL;
441
442         testres |= EC_TEST_CHECK(
443                 !strcmp(ec_node_type(node)->name, "seq") &&
444                 !strcmp(ec_node_id(node), EC_NO_ID) &&
445                 !strcmp(ec_node_desc(node), "<seq>"),
446                 "bad child 0");
447
448         testres |= EC_TEST_CHECK(
449                 ec_node_get_children_count(node) == 2,
450                 "bad children count\n");
451         child = ec_node_get_child(node, 0);
452         testres |= EC_TEST_CHECK(child != NULL &&
453                 !strcmp(ec_node_type(child)->name, "str") &&
454                 !strcmp(ec_node_id(child), "id_x"),
455                 "bad child 0");
456         child = ec_node_get_child(node, 1);
457         testres |= EC_TEST_CHECK(child != NULL &&
458                 !strcmp(ec_node_type(child)->name, "str") &&
459                 !strcmp(ec_node_id(child), "id_y"),
460                 "bad child 1");
461         child = ec_node_get_child(node, 2);
462         testres |= EC_TEST_CHECK(child == NULL,
463                 "child 2 should be NULL");
464
465         child = ec_node_find(node, "id_x");
466         testres |= EC_TEST_CHECK(child != NULL &&
467                 !strcmp(ec_node_type(child)->name, "str") &&
468                 !strcmp(ec_node_id(child), "id_x") &&
469                 !strcmp(ec_node_desc(child), "x"),
470                 "bad child id_x");
471         child = ec_node_find(node, "id_dezdex");
472         testres |= EC_TEST_CHECK(child == NULL,
473                 "child with wrong id should be NULL");
474
475         ret = ec_keyval_set(ec_node_attrs(node), "key", "val", NULL);
476         testres |= EC_TEST_CHECK(ret == 0,
477                 "cannot set node attribute\n");
478
479         type = ec_node_type_lookup("seq");
480         testres |= EC_TEST_CHECK(type != NULL &&
481                 ec_node_check_type(node, type) == 0,
482                 "cannot get seq node type");
483         type = ec_node_type_lookup("str");
484         testres |= EC_TEST_CHECK(type != NULL &&
485                 ec_node_check_type(node, type) < 0,
486                 "node type should not be str");
487
488         ec_node_free(node);
489         node = NULL;
490
491         node = ec_node("deznuindez", EC_NO_ID);
492         testres |= EC_TEST_CHECK(node == NULL,
493                         "should not be able to create node\n");
494
495         /* test loop */
496         expr = ec_node("or", EC_NO_ID);
497         val = ec_node_int(EC_NO_ID, 0, 10, 0);
498         op = ec_node_str(EC_NO_ID, "!");
499         seq = EC_NODE_SEQ(EC_NO_ID,
500                         op,
501                         ec_node_clone(expr));
502         op = NULL;
503         if (expr == NULL || val == NULL || seq == NULL)
504                 goto fail;
505         if (ec_node_or_add(expr, ec_node_clone(seq)) < 0)
506                 goto fail;
507         ec_node_free(seq);
508         seq = NULL;
509         if (ec_node_or_add(expr, ec_node_clone(val)) < 0)
510                 goto fail;
511         ec_node_free(val);
512         val = NULL;
513
514         testres |= EC_TEST_CHECK_PARSE(expr, 1, "1");
515         testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1");
516         testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!");
517
518         ec_node_free(expr);
519         expr = NULL;
520
521         /* same loop test, but keep some refs (released later) */
522         expr = ec_node("or", EC_NO_ID);
523         ec_node_clone(expr);
524         expr2 = expr;
525         val = ec_node_int(EC_NO_ID, 0, 10, 0);
526         op = ec_node_str(EC_NO_ID, "!");
527         seq = EC_NODE_SEQ(EC_NO_ID,
528                         op,
529                         ec_node_clone(expr));
530         op = NULL;
531         if (expr == NULL || val == NULL || seq == NULL)
532                 goto fail;
533         if (ec_node_or_add(expr, ec_node_clone(seq)) < 0)
534                 goto fail;
535         ec_node_free(seq);
536         seq = NULL;
537         if (ec_node_or_add(expr, ec_node_clone(val)) < 0)
538                 goto fail;
539
540         testres |= EC_TEST_CHECK_PARSE(expr, 1, "1");
541         testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1");
542         testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!");
543
544         ec_node_free(expr2);
545         expr2 = NULL;
546         ec_node_free(val);
547         val = NULL;
548         ec_node_free(expr);
549         expr = NULL;
550
551         return testres;
552
553 fail:
554         ec_node_free(expr);
555         ec_node_free(expr2);
556         ec_node_free(val);
557         ec_node_free(seq);
558         ec_node_free(node);
559         if (f != NULL)
560                 fclose(f);
561         free(buf);
562
563         assert(errno != 0);
564         return -1;
565 }
566 /* LCOV_EXCL_STOP */
567
568 static struct ec_test ec_node_test = {
569         .name = "node",
570         .test = ec_node_testcase,
571 };
572
573 EC_TEST_REGISTER(ec_node_test);