2 * Copyright (c) 2014, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include <murphy/common.h>
37 #include "c5-decision-tree.h"
40 #define ATTRIBUTE_MAX 32
41 #define ENUM_MAX 16384
42 #define ENUM_BUCKETS 16
46 typedef enum state_e state_t;
47 typedef struct conf_iter_s conf_iter_t;
48 typedef struct attr_value_iter_s attr_value_iter_t;
49 typedef struct value_list_item_s value_list_item_t;
50 typedef struct value_list_s value_list_t;
61 mrp_decision_conf_t *conf;
63 mrp_decision_attr_t *attrs[0];
67 struct attr_value_iter_s {
68 mrp_decision_attr_t *attr;
70 mrp_decision_attr_value_desc_t descs[0];
73 struct value_list_item_s {
80 value_list_item_t *items;
83 static bool conf_finish(mrp_decision_conf_t *, const char *);
84 static conf_iter_t *conf_iterator(mrp_decision_conf_t *);
86 static mrp_decision_attr_t *attr_create(mrp_decision_conf_t*,const char*,int);
87 static void attr_destroy(void *, void *);
88 static void attr_add_value(mrp_decision_attr_t *, const char *);
89 static attr_value_iter_t *attr_value_iterator(mrp_decision_attr_t *);
90 static size_t attr_print(mrp_decision_attr_t *, bool, char *, size_t);
92 static void value_destroy(void *, void *);
94 static bool tree_parse(mrp_decision_conf_t *, FILE *, size_t *, char *,
95 mrp_decision_node_t **, int);
96 static bool tree_parse_terminal_node(mrp_decision_conf_t *, FILE *, size_t *,
97 char *, bool, mrp_decision_node_t **, int);
98 static bool tree_parse_test_node(mrp_decision_conf_t *, FILE *, size_t *,
99 char *, int, mrp_decision_node_t **, int);
103 static bool property(char **, char **, char **);
104 static bool list_item(char **, char **);
105 static bool identifier(char **, char **, char *);
106 static bool whitespace(char **);
107 static bool quoted(char **, char **);
109 static size_t print_node(mrp_decision_conf_t *, mrp_decision_node_t *,
110 conf_iter_t *, char *, size_t, char *);
111 static size_t print_bitmask(mrp_decision_attr_t *, mrp_decision_bitmask_t,
116 mrp_decision_conf_t *mrp_decision_conf_create_from_file(const char *stem)
129 mrp_decision_conf_t *conf;
130 mrp_decision_attr_t *attr;
131 mrp_htbl_config_t aconf;
134 if (!stem || !stem[0])
137 snprintf(filnam, sizeof(filnam), "%s.names", stem);
138 if (!(f = fopen(filnam, "r"))) {
139 mrp_log_error("gam-resource-manager: failed to open file '%s': %s",
140 filnam, strerror(errno));
144 if (!(conf = mrp_allocz(sizeof(mrp_decision_conf_t)))) {
145 mrp_log_error("gam-resource-manager: can't allocate memory for "
146 "'%s' decision configuration", stem);
150 aconf.nentry = ATTRIBUTE_MAX;
151 aconf.comp = mrp_string_comp;
152 aconf.hash = mrp_string_hash;
153 aconf.free = attr_destroy;
154 aconf.nbucket = ATTRIBUTE_MAX;
156 conf->stem = mrp_strdup(stem);
158 conf->attrs = mrp_htbl_create(&aconf);
161 mrp_log_error("gam-resource-manager: failed to create attribute "
162 "hash for '%s' decision configuration", stem);
163 mrp_decision_conf_destroy(conf);
175 while ((linlen = getline(&buf, &n, f)) >= 0) {
185 if (!identifier(&p, &name, &sep) || sep != '.')
187 mrp_debug("decision = '%s'", name);
188 snprintf(decision, sizeof(decision), "%s", name);
194 if (!identifier(&p, &name, &sep) || sep != ':')
196 mrp_debug("name = '%s'", name);
197 if (!(attr = attr_create(conf, name, id++)))
204 if (!identifier(&p, &value, &sep) ||
205 (sep != ',' && sep != '.' && sep != 0))
207 mrp_debug("value = '%s'", value);
208 if (!strcmp(name, "continuous") && sep != '.')
210 attr_add_value(attr, value);
225 } /* while getline */
227 if (linlen < 0 && !feof(f)) {
228 mrp_log_error("gam-resource-manager: error during reading "
229 "'%s' file: %s", filnam, strerror(errno));
230 mrp_decision_conf_destroy(conf);
236 if (!conf_finish(conf, decision)) {
237 mrp_decision_conf_destroy(conf);
241 mrp_log_info("mrp-resource-manager: successfully loaded "
242 "decision configuration from file '%s'", filnam);
247 mrp_log_error("gam-resource-manager: error in file '%s' line %zu",
251 mrp_decision_conf_destroy(conf);
255 void mrp_decision_conf_destroy(mrp_decision_conf_t *conf)
258 mrp_log_info("mrp-resource-manager: going to unload "
259 "decision configuration '%s'", conf->stem);
261 if (conf->decision_attr && conf->decision_names)
262 mrp_free(conf->decision_names);
264 mrp_htbl_destroy(conf->attrs, TRUE);
266 mrp_free((void *)conf->stem);
267 mrp_free((void *)conf);
271 bool mrp_decision_set_attr_offset(mrp_decision_conf_t *conf,
272 const char *attr_name,
275 mrp_decision_attr_t *attr;
277 if (!conf || !attr_name)
280 if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
283 attr->offset = offset;
288 ssize_t mrp_decision_attr_list(mrp_decision_conf_t *conf,
289 const char **buf, size_t len)
294 if (!conf || !buf || len < 1)
297 if ((int)len < conf->nattr)
300 if (!(it = conf_iterator(conf)))
303 for (i = j = 0, n = it->nattr; i < n; i++) {
304 if (conf->decision_attr != it->attrs[i])
305 buf[j++] = it->attrs[i]->name;
314 ssize_t mrp_decision_attr_value_list(mrp_decision_conf_t *conf,
315 const char *attr_name,
316 mrp_decision_attr_value_desc_t *buf,
319 mrp_decision_attr_t *attr;
320 attr_value_iter_t *it;
325 if (!conf || !attr_name || !buf || (buf_len = len) < 1)
328 if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
331 if (buf_len < attr->nvalue)
334 if (!(it = attr_value_iterator(attr)))
337 actual_len = it->ndesc;
339 size = sizeof(mrp_decision_attr_value_desc_t) * actual_len;
340 memcpy(buf, it->descs, size);
342 if (buf_len > actual_len) {
343 size = sizeof(mrp_decision_attr_value_desc_t) * (buf_len - it->ndesc);
344 memset(buf + it->ndesc, 0, size);
354 const char *mrp_decision_name(mrp_decision_conf_t *conf, int32_t decision)
356 if (!conf || decision < 0 || decision > conf->decision_attr->nvalue)
359 return conf->decision_names[decision];
363 int32_t mrp_decision_value_max(mrp_decision_conf_t *conf)
365 if (!conf || !conf->decision_attr)
368 return conf->decision_attr->nvalue;
371 int32_t mrp_decision_get_integer_attr_value(mrp_decision_conf_t *conf,
372 const char *attr_name,
373 const char *value_name,
376 mrp_decision_attr_t *attr;
377 mrp_decision_value_t *value;
382 if (!conf || !attr_name || !value_name)
385 if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
388 if (attr->value_type != MRP_DECISION_VALUE_INTEGER)
391 if (!(value = mrp_htbl_lookup(attr->values, (void *)value_name)))
397 return value->integer;
401 const char *mrp_decision_get_integer_attr_name(mrp_decision_conf_t *conf,
402 const char *attr_name,
406 mrp_decision_attr_t *attr;
407 attr_value_iter_t *it;
408 const char *value_name;
414 if (!conf || !attr_name)
417 if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
418 return "<unknown attribute>";
420 if (attr->value_type != MRP_DECISION_VALUE_INTEGER)
421 return "<not integer attribute>";
423 if (!(it = attr_value_iterator(attr)))
426 for (i = 0, value_name = "<unknown value>"; i < it->ndesc; i++) {
427 if (it->descs[i].value == value) {
428 value_name = it->descs[i].name;
433 if (i < it->ndesc && error)
440 size_t mrp_decision_conf_print(mrp_decision_conf_t *conf, char *buf,size_t len)
443 mrp_decision_attr_t *attr;
450 if (!(it = conf_iterator(conf)))
451 p += snprintf(p, e-p, "<error>");
453 p += snprintf(p, e-p, "attributes for '%s'\n", conf->stem);
455 for (i = 0; i < it->nattr; i++) {
457 p += attr_print(attr, (attr == conf->decision_attr), p, e-p);
468 static bool conf_finish(mrp_decision_conf_t *conf,
469 const char *decision_attr_name)
471 mrp_decision_attr_t *attr;
472 attr_value_iter_t *it;
473 mrp_decision_attr_value_desc_t *dsc;
478 if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)decision_attr_name))) {
479 mrp_log_error("gam-resoure-manager: can't find decision attribute "
480 "'%s' for '%s'", decision_attr_name, conf->stem);
484 if (attr->nvalue < 1) {
485 mrp_log_error("gam-resource-manager: attribute '%s' in '%s' is "
486 "not suitable for decisions", attr->name, conf->stem);
491 size = sizeof(const char *) * n;
493 if (!(it = attr_value_iterator(attr)) || !(tbl = mrp_allocz(size))) {
495 mrp_log_error("gam-resource-manager: can't allocate memory to "
496 "finalize '%s' decision", conf->stem);
500 for (i = 0; i < n; i++) {
503 if (i != dsc->value) {
504 mrp_log_error("gam-resource-manager: internal error: decision "
505 "values of '%s' are non-continous for '%s' decisions",
506 attr->name, conf->stem);
517 conf->decision_attr = attr;
518 conf->decision_names = tbl;
524 static int conf_iter_cb(void *key, void *object, void *user_data)
526 conf_iter_t *it = (conf_iter_t *)user_data;
527 mrp_decision_attr_t *attr = (mrp_decision_attr_t *)object;
531 if (it->nattr >= it->conf->nattr) {
532 mrp_log_error("gam-resource-manager: detected inconsitency while "
533 "iterating configuration of '%s'", it->conf->stem);
536 it->attrs[it->nattr++] = attr;
539 return MRP_HTBL_ITER_MORE;
542 static conf_iter_t *conf_iterator(mrp_decision_conf_t *conf)
546 mrp_decision_attr_t *tmp;
552 size = sizeof(conf_iter_t) + (sizeof(void *) * conf->nattr);
554 if ((it = mrp_allocz(size))) {
557 mrp_htbl_foreach(conf->attrs, conf_iter_cb, it);
559 for (i = 0; i < it->nattr - 1; i++) {
560 for (j = i + 1; j < it->nattr; j++) {
561 if (it->attrs[i]->id > it->attrs[j]->id) {
563 it->attrs[i] = it->attrs[j];
575 static mrp_decision_attr_t *attr_create(mrp_decision_conf_t *conf,
576 const char *name, int id)
578 mrp_decision_attr_t *attr;
583 if ((attr = mrp_allocz(sizeof(mrp_decision_attr_t)))) {
584 attr->name = mrp_strdup(name);
586 attr->attr_type = MRP_DECISION_ATTR_CONTINUOUS;
587 attr->value_type = MRP_DECISION_VALUE_INTEGER;
592 if (!mrp_htbl_insert(conf->attrs, (void *)attr->name, attr)) {
593 attr_destroy((void *)attr->name, (void *)attr);
603 static void attr_destroy(void *key, void *object)
605 mrp_decision_attr_t *attr = (mrp_decision_attr_t *)object;
610 mrp_htbl_destroy(attr->values, TRUE);
612 mrp_free((void *)attr->name);
616 static void attr_add_value(mrp_decision_attr_t *attr, const char *name)
618 mrp_htbl_config_t vconf;
619 mrp_decision_value_t *value;
623 if (attr->attr_type == MRP_DECISION_ATTR_CONTINUOUS) {
624 vconf.nentry = ENUM_MAX;
625 vconf.comp = mrp_string_comp;
626 vconf.hash = mrp_string_hash;
627 vconf.free = value_destroy;
628 vconf.nbucket = ENUM_BUCKETS;
630 attr->attr_type = MRP_DECISION_ATTR_ENUM;
632 attr->values = mrp_htbl_create(&vconf);
635 if (!(key = mrp_strdup(name)) || !(value = mrp_allocz(sizeof(*value))))
636 mrp_free((void *)key);
638 value->integer = attr->nvalue++;
639 mrp_htbl_insert(attr->values, key, value);
644 static int attr_value_iter_cb(void *key, void *object, void *user_data)
646 attr_value_iter_t *it = (attr_value_iter_t *)user_data;
647 const char *name = (const char *)key;
648 mrp_decision_value_t *value = (mrp_decision_value_t *)object;
649 mrp_decision_attr_value_desc_t *desc;
651 if (it->ndesc >= it->attr->nvalue) {
652 mrp_log_error("gam-resource-manager: detected inconsitency while "
653 "iterating decision attribute '%s' for '%s'",
654 name, it->attr->name);
657 desc = it->descs + it->ndesc++;
659 desc->value = value->integer;
662 return MRP_HTBL_ITER_MORE;
665 static attr_value_iter_t *attr_value_iterator(mrp_decision_attr_t *attr)
667 attr_value_iter_t *it;
668 mrp_decision_attr_value_desc_t tmp;
676 (sizeof(mrp_decision_attr_value_desc_t) * attr->nvalue);
678 if ((it = mrp_allocz(size))) {
681 mrp_htbl_foreach(attr->values, attr_value_iter_cb, it);
683 for (i = 0; i < it->ndesc - 1; i++) {
684 for (j = i + 1; j < it->ndesc; j++) {
685 if (it->descs[i].value > it->descs[j].value) {
687 it->descs[i] = it->descs[j];
698 static size_t attr_print(mrp_decision_attr_t *attr, bool decision,
699 char *buf, size_t len)
701 #define PRINT(args...) \
702 do { if (p < e) p += snprintf(p, e-p, args); } while (0)
704 attr_value_iter_t *it;
705 mrp_decision_attr_value_desc_t *dsc;
714 snprintf(nambuf, sizeof(nambuf), "%2d %s:", attr->id, attr->name);
715 PRINT(" %c %-24s @%03zu ", decision ? '*':' ', nambuf, attr->offset);
717 switch (attr->attr_type) {
719 case MRP_DECISION_ATTR_ENUM:
720 if (!(it = attr_value_iterator(attr)))
724 for (i = 0; i < it->ndesc; i++) {
726 sep = (i == 0) ? "" : ((i % 10) ?
729 PRINT("%s[%d (%s)]", sep, dsc->value, dsc->name);
737 case MRP_DECISION_ATTR_CONTINUOUS:
738 PRINT("continuous\n");
742 PRINT("<unsupported attribute type %d>\n", attr->attr_type);
752 static void value_destroy(void *key, void *object)
759 mrp_decision_node_t *mrp_decision_tree_create_from_file(
760 mrp_decision_conf_t *conf,
769 mrp_decision_node_t *root, *node;
770 mrp_decision_value_type_t vtype;
776 snprintf(filnam, sizeof(filnam), "%s.tree", stem);
777 if (!(f = fopen(filnam, "r"))) {
778 printf("failed to open file '%s': %s\n", filnam, strerror(errno));
782 vtype = conf->decision_attr->value_type;
784 if (!(root = mrp_decision_tree_create(stem, vtype))) {
785 mrp_log_error("gam-resource-manager: failed to create "
786 "decision tree for '%s'", stem);
794 while ((linlen = getline(&buf, &n, f)) >= 0) {
800 if (!strncmp(p, "type", 4)) {
801 if (!(tree_parse(conf, f, &lineno, buf, &node, 0)))
803 if (!(mrp_decision_add_node_to_root(root, node)))
807 else if (property(&p, &name, &value)) {
808 if (!strcmp(name, "id")) {
809 mrp_debug("id: %s", value);
811 else if (!strcmp(name, "entries")) {
812 mrp_debug("entries: %s", value);
823 if (linlen < 0 && !feof(f)) {
824 mrp_log_error("gam-resource-manager: error during reading "
825 "'%s' file: %s", filnam, strerror(errno));
831 mrp_log_info("mrp-resource-manager: successfully loaded "
832 "decision tree from file '%s'", filnam);
837 mrp_log_error("gam-resource-manager: error in file '%s' line %zu",
840 mrp_log_error("gam-resource-manager: failed to parse '%s' file",
844 mrp_decision_tree_destroy(root);
845 mrp_decision_tree_destroy(node);
849 static const char *indent(int depth)
851 static char buf[1024];
852 size_t l = depth * 3;
858 static bool tree_parse(mrp_decision_conf_t *conf,
859 FILE *f, size_t *lineno,
861 mrp_decision_node_t **node,
865 char *name, *value, *e;
868 if (!property(&p, &name, &value) || strcmp(name, "type"))
871 type = strtol(value, &e, 10);
873 if (e == value || *e)
878 return tree_parse_terminal_node(conf, f, lineno, p, false, node, depth);
883 return tree_parse_test_node(conf, f, lineno, p, type, node, depth);
892 static bool tree_parse_terminal_node(mrp_decision_conf_t *conf,
893 FILE *f, size_t *lineno,
896 mrp_decision_node_t **node,
901 mrp_decision_value_t *vptr;
911 has_decision = false;
915 if (!property(&p, &name, &value))
918 if (!strcmp(name, "class")) {
919 if (!(vptr = mrp_htbl_lookup(conf->decision_attr->values, value)))
922 decision_name = value;
923 decision = vptr->integer;
927 if (!strcmp(name, "freq")) {
935 if (!need_empty && node) {
936 mrp_debug("%sterminal: %d/%s", indent(depth),
937 decision, decision_name);
938 if (!(*node = mrp_decision_create_terminal_node(vptr)))
942 if (need_empty && !has_cases)
949 static bool tree_parse_test_node(mrp_decision_conf_t *conf,
950 FILE *f, size_t *lineno,
953 mrp_decision_node_t **node,
956 mrp_decision_node_t *child;
959 mrp_decision_attr_t *attr;
965 value_list_t *lists, *l;
966 value_list_item_t *iv;
967 mrp_decision_value_t *av;
968 mrp_decision_value_type_t testval_type;
969 mrp_decision_value_t testval;
970 mrp_decision_condition_t testcond;
972 attr_value_iter_t *ait;
973 char valbuf[4096], *q;
988 testcond = MRP_DECISION_EQ;
989 testval_type = MRP_DECISION_VALUE_UNKNOWN;
990 memset(&testval, 0, sizeof(testval));
993 if (!property(&p, &name, &value))
996 if (!strcmp(name, "att")) {
1000 if (!(attr = mrp_htbl_lookup(conf->attrs, value)))
1001 goto finish_parsing;
1003 else if (!strcmp(name, "forks")) {
1005 goto finish_parsing;
1007 nbr = strtol(value, &e, 10);
1009 if (*e || e == value || nbr <= 0 || nbr > 100)
1010 goto finish_parsing;
1013 lists = mrp_allocz(sizeof(*lists) * nbr);
1015 else if (!strcmp(name, "elts")) {
1016 if (!attr || !lists || listidx >= nbr)
1017 goto finish_parsing;
1019 l = lists + listidx++;
1020 l->items = mrp_allocz(sizeof(l->items[0]) * attr->nvalue);
1023 if (l->size >= attr->nvalue)
1024 goto finish_parsing;
1025 if (!(av = mrp_htbl_lookup(attr->values, value)))
1026 goto finish_parsing;
1028 iv = l->items + l->size++;
1030 iv->value = av->integer;
1032 } while (list_item(&p, &value));
1037 } /* while property */
1039 if (attr && nbr > 0) {
1041 if (getline(&buf2, &n, f) < 0)
1042 goto finish_parsing;
1044 if (!tree_parse_terminal_node(conf, f,lineno,buf2, true, NULL, 0))
1045 goto finish_parsing;
1050 if (!(ait = attr_value_iterator(attr)))
1051 goto finish_parsing;
1054 mrp_debug("%stest/%d: '%s'", indent(depth), nbr, attr->name);
1056 if (!(*node = mrp_decision_create_test_node()))
1057 goto finish_parsing;
1059 for (i=0, buf2=NULL; i < nbr && getline(&buf2,&n,f) >= 0; i++) {
1065 testval_type = MRP_DECISION_VALUE_INTEGER;
1066 testval.integer = ait->descs[i].value;
1067 testcond = MRP_DECISION_EQ;
1068 snprintf(valbuf, sizeof(valbuf), "%s", ait->descs[i].name);
1075 testval_type = MRP_DECISION_VALUE_UNKNOWN;
1078 testcond = MRP_DECISION_EQ;
1079 testval_type = MRP_DECISION_VALUE_INTEGER;
1080 testval.integer = l->items[0].value;
1081 snprintf(valbuf, sizeof(valbuf), "%s", l->items[0].name);
1084 testcond = MRP_DECISION_IN;
1085 if (l->size <= (int)MRP_DECISION_BITMASK_WIDTH) {
1086 testval_type = MRP_DECISION_VALUE_BITMASK;
1087 testval.bitmask = 0;
1090 testval_type = MRP_DECISION_ARRAY |
1091 MRP_DECISION_VALUE_INTEGER;
1092 testval.array.size = l->size;
1093 size = sizeof(mrp_decision_value_t) * testval.array.size;
1094 testval.array.values = mrp_allocz(size);
1096 e = (q = valbuf) + sizeof(valbuf);
1097 for (j = 0; j < l->size; j++) {
1099 q += snprintf(q, e-q, "%s%s",
1100 j?",":"", l->items[j].name);
1102 k = l->items[j].value;
1103 if (testval_type == MRP_DECISION_VALUE_BITMASK)
1104 testval.bitmask |= MRP_DECISION_BIT(k);
1106 testval.array.values[j].integer = k;
1112 goto finish_parsing;
1115 switch (testval_type) {
1116 case MRP_DECISION_VALUE_BITMASK:
1117 snprintf(dbgbuf, sizeof(dbgbuf), " 0x%x", testval.bitmask);
1119 case MRP_DECISION_VALUE_INTEGER:
1120 snprintf(dbgbuf, sizeof(dbgbuf), " %d", testval.integer);
1125 mrp_debug("%s%s %s '%s'%s", indent(depth+1),
1126 mrp_decision_condition_str(testcond),
1127 mrp_decision_value_type_str(testval_type),
1130 if (!tree_parse(conf, f, lineno, buf2, &child, depth+2))
1131 goto finish_parsing;
1133 ok = mrp_decision_add_branch_to_test_node(*node, testcond, attr->id,
1134 testval_type, &testval,
1135 attr->offset, child);
1137 goto finish_parsing;
1140 if ((testval_type & MRP_DECISION_ARRAY))
1141 mrp_free(testval.array.values);
1142 testval_type = MRP_DECISION_VALUE_UNKNOWN;
1153 for (i = 0; i < nbr; i++)
1154 free(lists[i].items);
1157 if ((testval_type & MRP_DECISION_ARRAY))
1158 mrp_free(testval.array.values);
1159 mrp_decision_tree_destroy(child);
1168 static bool property(char **buf, char **name, char **value)
1173 if (identifier(&p, name, &term) &&
1185 static bool list_item(char **buf, char **name)
1194 if (quoted(&p, name)) {
1203 static bool identifier(char **buf, char **id, char *term)
1211 for (p = q; (c = *p); p++) {
1229 static bool whitespace(char **buf)
1233 for (p = *buf; (c = *p); p++) {
1234 if (c != ' ' && c != '\t' && c != '\n')
1242 static bool quoted(char **buf, char **string)
1253 for (p = q; (c = *p); p++) {
1256 if (c == '"' && p[-1] != '\\') {
1267 size_t mrp_decision_tree_print(mrp_decision_conf_t *conf,
1268 mrp_decision_node_t *node,
1269 char *buf, size_t len)
1274 e = (p = buf) + len;
1276 if (conf && node && buf && len > 0) {
1277 if (!(cit = conf_iterator(conf)))
1278 p += snprintf(p, e-p, "<error>\n");
1280 p += print_node(conf, node, cit, p, e-p, NULL);
1281 p += snprintf(p, e-p, "\n");
1290 static size_t print_node(mrp_decision_conf_t *conf,
1291 mrp_decision_node_t *node,
1293 char *buf, size_t len,
1296 #define PRINT(_args...) \
1297 do {if (p < e) p += snprintf(p,e-p, _args);} while(0)
1298 #define PRINT_VALUE(_t,_v) \
1299 do {if (p < e) p += mrp_decision_value_print(_t, _v, p,e-p);} while(0)
1300 #define PRINT_BITMASK(_a,_m) \
1301 do {if (p < e) p += print_bitmask(_a, _m, p, e-p);} while(0)
1302 #define PRINT_NODE(_p,_i) \
1303 do {if (p < e) p += print_node(conf, (_p)->node, cit, p,e-p, _i);} while(0)
1305 static char indent_buf[4096];
1306 static char *indent_end = indent_buf + sizeof(indent_buf);
1308 mrp_decision_root_node_t *root;
1309 mrp_decision_test_node_t *test;
1310 mrp_decision_terminal_node_t *term;
1311 mrp_decision_branch_t *branch;
1312 mrp_decision_attr_t *attr;
1313 const char *attr_name;
1315 attr_value_iter_t *ait;
1316 char *p, *e, *new_indent;
1325 indent = indent_buf;
1328 e = (p = buf) + len;
1330 switch (node->type) {
1332 case MRP_DECISION_ROOT_NODE:
1334 PRINT("root of %s (decision type: %s)", root->name,
1335 mrp_decision_value_type_str(root->decision_value_type));
1336 new_indent = indent;
1337 new_indent += snprintf(indent, indent_end-indent, "\n");
1338 PRINT_NODE(root, new_indent);
1342 case MRP_DECISION_TEST_NODE:
1345 for (i = 0; i < test->nbranch; i++) {
1346 branch = test->branches + i;
1347 if (branch->value_id < 0 || branch->value_id >= cit->nattr)
1348 PRINT("%s:...<invalid attribute>", indent_buf);
1350 attr = cit->attrs[branch->value_id];
1351 attr_name = attr ? attr->name : "<invalid attribute>";
1352 PRINT("%s:...%s %s ", indent_buf, attr_name,
1353 mrp_decision_condition_str(branch->condition));
1355 if (branch->value_type != MRP_DECISION_VALUE_INTEGER) {
1356 PRINT_VALUE(branch->value_type, &branch->value);
1357 if (branch->value_type == MRP_DECISION_VALUE_BITMASK) {
1359 PRINT_BITMASK(attr, branch->value.bitmask);
1364 value_idx = branch->value.integer;
1365 if (value_idx < 0 || value_idx >= attr->nvalue ||
1366 !(ait = attr_value_iterator(attr)))
1367 PRINT("<invalid attribute value>");
1369 PRINT("%d (%s)", value_idx, ait->descs[value_idx].name);
1374 new_indent = indent;
1375 new_indent += snprintf(new_indent, indent_end-indent, "%c ",
1376 i == test->nbranch-1 ? ' ':':');
1377 PRINT_NODE(branch, new_indent);
1383 case MRP_DECISION_TERMINAL_NODE:
1384 term = &node->terminal;
1385 decision = term->decision.integer;
1386 if (decision < 0 || decision >= conf->decision_attr->nvalue)
1387 PRINT(" => decision <invalid value>");
1389 PRINT(" => %s", conf->decision_names[decision]);
1393 PRINT("%s<unknown node type %d>\n", indent_buf, node->type);
1404 static size_t print_bitmask(mrp_decision_attr_t *attr,
1405 mrp_decision_bitmask_t bitmask,
1406 char *buf, size_t len)
1408 attr_value_iter_t *it;
1409 mrp_decision_bitmask_t m;
1414 if (!attr || !buf || len < 1)
1417 if (!(it = attr_value_iterator(attr)))
1420 e = (p = buf) + len;
1423 p += snprintf(p, e-p, "<empty>");
1425 for (i = 0, sep = ""; m && i < it->ndesc && p < e; i++, m >>= 1) {
1427 if (it->descs[i].value == i)
1430 for (j = 0; j < it->ndesc; j++) {
1431 if (it->descs[j].value == i)
1435 if (j < 0 || j >= it->ndesc)
1436 p += snprintf(p, e-p, "%s<unknown value %d>", sep, j);
1438 p += snprintf(p, e-p, "%s%s", sep, it->descs[j].name);