diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2023-01-07 20:18:44 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2023-01-07 20:18:44 +0100 |
commit | e20bef69ccc4a85ef62359ee539c9db2dbe09127 (patch) | |
tree | 1b45ebbcb62cf8d8da5f4e173e39dc0b5e26e770 | |
parent | d1cd5e5a63b2256eb71661f7438537e4ded7b01a (diff) |
Filter: Change linearization of branches in switch instruction
Most branching instructions (FI_CONDITION, FI_AND, FI_OR) linearize its
branches in a recursive way, while FI_SWITCH branches are linearized
from parser even before the switch instruction is allocated.
Change linearization of FI_SWITCH branches to make it similar to other
branching instructions. This also fixes an issue with constant
switch evaluation, where linearized branch is mistaken for
non-linearized during switch construction.
Thanks to Jiten Kumar Pathy for the bugreport.
-rw-r--r-- | filter/config.Y | 7 | ||||
-rw-r--r-- | filter/data.h | 1 | ||||
-rw-r--r-- | filter/f-inst.c | 25 | ||||
-rw-r--r-- | filter/tree.c | 20 |
4 files changed, 46 insertions, 7 deletions
diff --git a/filter/config.Y b/filter/config.Y index 68ee1a84..1d9d9aa9 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -676,16 +676,15 @@ switch_body: /* EMPTY */ { $$ = NULL; } | switch_body switch_items ':' cmds_scoped { /* Fill data fields */ struct f_tree *t; - struct f_line *line = f_linearize($4, 0); for (t = $2; t; t = t->left) - t->data = line; + t->data = $4; $$ = f_merge_items($1, $2); } | switch_body ELSECOL cmds_scoped { struct f_tree *t = f_new_tree(); t->from.type = t->to.type = T_VOID; t->right = t; - t->data = f_linearize($3, 0); + t->data = $3; $$ = f_merge_items($1, t); } ; @@ -972,7 +971,7 @@ cmd: } | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); } | CASE term '{' switch_body '}' { - $$ = f_new_inst(FI_SWITCH, $2, build_tree($4)); + $$ = f_new_inst(FI_SWITCH, $2, $4); } | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); } diff --git a/filter/data.h b/filter/data.h index 5edeaedb..700609e9 100644 --- a/filter/data.h +++ b/filter/data.h @@ -198,6 +198,7 @@ struct f_trie_walk_state struct f_tree *f_new_tree(void); struct f_tree *build_tree(struct f_tree *); const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val); +const struct f_tree *find_tree_linear(const struct f_tree *t, const struct f_val *val); int same_tree(const struct f_tree *t0, const struct f_tree *t2); int tree_node_count(const struct f_tree *t); void tree_format(const struct f_tree *t, buffer *buf); diff --git a/filter/f-inst.c b/filter/f-inst.c index 9a3a22ab..2d2a30e4 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -1285,14 +1285,33 @@ FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree); + FID_LINEARIZE_BODY() + /* Linearize all branches in switch */ + struct f_inst *last_inst = NULL; + struct f_line *last_line = NULL; + for (struct f_tree *t = whati->tree; t; t = t->left) + { + if (t->data != last_inst) + { + last_inst = t->data; + last_line = f_linearize(t->data, 0); + } + + t->data = last_line; + } + + /* Balance the tree */ + item->tree = build_tree(whati->tree); + FID_ITERATE_BODY() - tree_walk(whati->tree, f_add_tree_lines, fit); + tree_walk(whati->tree, f_add_tree_lines, fit); FID_INTERPRET_BODY() - const struct f_tree *t = find_tree(tree, &v1); + /* In parse-time use find_tree_linear(), in runtime use find_tree() */ + const struct f_tree *t = FID_HIC(,find_tree,find_tree_linear)(tree, &v1); if (!t) { v1.type = T_VOID; - t = find_tree(tree, &v1); + t = FID_HIC(,find_tree,find_tree_linear)(tree, &v1); if (!t) { debug( "No else statement?\n"); FID_HIC(,break,return NULL); diff --git a/filter/tree.c b/filter/tree.c index 97bf7dae..25cf93e4 100644 --- a/filter/tree.c +++ b/filter/tree.c @@ -38,6 +38,26 @@ find_tree(const struct f_tree *t, const struct f_val *val) return find_tree(t->left, val); } +/** + * find_tree_linear + * @t: tree to search in + * @val: value to find + * + * Search for given value in the degenerated linear tree, which is generated by + * parser before build_tree() is applied. The tree is not sorted and all nodes + * are linked by left ptr. + */ +const struct f_tree * +find_tree_linear(const struct f_tree *t, const struct f_val *val) +{ + for (; t; t = t->left) + if ((val_compare(&(t->from), val) != 1) && + (val_compare(&(t->to), val) != -1)) + return t; + + return NULL; +} + static struct f_tree * build_tree_rec(struct f_tree **buf, int l, int h) { |