/** * Remove unuseful failure links to skip using an invalid transition * * @param ac_tree the matcher that holds the patterns * @param state the state where it should start (it's used recursively) * */ static void ib_ac_unlink_unuseful(ib_ac_t *ac_tree, ib_ac_state_t *state) { IB_FTRACE_INIT(); ib_ac_state_t *child = NULL; ib_ac_state_t *fail_state = NULL; ib_ac_state_t *found = NULL; for (child = state->child; child != NULL; child = child->sibling) { if (child->fail == NULL || child->fail->child == NULL || child->child == NULL) { continue; } for (fail_state = child->fail->child; fail_state != ac_tree->root && fail_state != NULL; fail_state = fail_state->sibling) { found = ib_ac_child_for_code(child, fail_state->letter); if (found == NULL) { break; } } if (found != NULL) { /* There's no transition in the fail state that will * success, since the fail state doesn't have any letter not * present at the goto() of the main state. So let's * change the fail state to parent. Consider that this is * different to the output links (they'll still valid) */ child->fail = ac_tree->root; /* printf("Removing invalid fails\n"); */ } } for (child = state->child; child != NULL; child = child->sibling) { if (child->child != NULL) { ib_ac_unlink_unuseful(ac_tree, child); } } IB_FTRACE_RET_VOID(); }
/** * Constructs fail links of branches (the failure transition function) * * @param ac_tree the ac tree matcher * * @return ib_status_t status of the operation */ static ib_status_t ib_ac_link_fail_states(ib_ac_t *ac_tree) { ib_status_t rc; ib_ac_state_t *child = NULL; ib_ac_state_t *state = NULL; ib_ac_state_t *goto_state = NULL; ib_list_t *iter_queue = NULL; if (ac_tree->flags & IB_AC_FLAG_PARSER_COMPILED) { return IB_OK; } ac_tree->root->pattern = 0; rc = ib_list_create(&iter_queue, ac_tree->mp); if (rc != IB_OK) { return rc; } ac_tree->root->fail = ac_tree->root; /* All first-level children will fail back to root state */ for (child = ac_tree->root->child; child != NULL; child = child->sibling) { child->fail = ac_tree->root; rc = ib_list_enqueue(iter_queue, (void *) child); if (rc != IB_OK) { return rc; } } while (ib_list_elements(iter_queue) > 0) { rc = ib_list_dequeue(iter_queue, (void *) &state); if (rc != IB_OK) { return rc; } state->fail = ac_tree->root; if (state->parent != ac_tree->root) { goto_state = ib_ac_child_for_code(state->parent->fail, state->letter); if (goto_state != NULL) { state->fail = goto_state; } } for (child = state->child; child != NULL; child = child->sibling) { rc = ib_list_enqueue(iter_queue, (void *) child); if (rc != IB_OK) { return rc; } } } /* Link common outputs of subpatterns present in the branch*/ ib_ac_link_outputs(ac_tree, ac_tree->root); /* Unlink invalid fail transitions. This guarantees that there will * be at least one letter with transition in each fail state*/ ib_ac_unlink_unuseful(ac_tree, ac_tree->root); if (ac_tree->root->child != NULL) { ib_ac_build_bintree(ac_tree, ac_tree->root); } ac_tree->flags |= IB_AC_FLAG_PARSER_COMPILED; return IB_OK; }