data_accessor_t::edges_range data_accessor_t::get_incident_edges(vertex_id_t vertex_id, edge_type_t edges_type) const { details::raw_vertex_t * raw_v = raw_vertex_by_id(vertex_id); if (raw_v == NULL) { return edges_range(details::edges_iterator(), details::edges_iterator()); } raw_edge_id_t eid_begin; raw_edge_id_t eid_end; if (edges_type == EDGE_REVERSED) { eid_begin = raw_v->transposed_edges_begin_id; eid_end = raw_v->transposed_edges_end_id; } else // edges_type == EDGE_NORMAL or wrong edges_type { eid_begin = raw_v->edges_begin_id; eid_end = raw_v->edges_end_id; } return edges_range(details::edges_iterator(this, vertex_id, eid_begin), details::edges_iterator(this, vertex_id, eid_end)); }
/** Remove vacuous edges in graphs where the min_offset or min_length * constraints dictate that they can never produce a match. */ static void pruneVacuousEdges(NGWrapper &g) { if (!g.min_length && !g.min_offset) { return; } vector<NFAEdge> dead; for (const auto &e : edges_range(g)) { const NFAVertex u = source(e, g); const NFAVertex v = target(e, g); // Special case: Crudely remove vacuous edges from start in graphs with a // min_offset. if (g.min_offset && u == g.start && is_any_accept(v, g)) { DEBUG_PRINTF("vacuous edge in graph with min_offset!\n"); dead.push_back(e); continue; } // If a min_length is set, vacuous edges can be removed. if (g.min_length && is_any_start(u, g) && is_any_accept(v, g)) { DEBUG_PRINTF("vacuous edge in graph with min_length!\n"); dead.push_back(e); continue; } } if (dead.empty()) { return; } remove_edges(dead, g); pruneUseless(g); }
static void dump_graph(const GoughGraph &g, const string &base, const Grey &grey) { stringstream ss; ss << grey.dumpPath << "gough_" << base << ".dot"; FILE *f = fopen(ss.str().c_str(), "w"); fprintf(f, "digraph NFA {\n"); fprintf(f, "rankdir=LR;\n"); fprintf(f, "size=\"11.5,8\"\n"); fprintf(f, "node [ shape = circle ];\n"); fprintf(f, "START [style=invis];\n"); for (auto v : vertices_range(g)) { fprintf(f, "%s [ width = 1, fixedsize = true, fontsize = 12, ", dump_name(g[v]).c_str()); if (!g[v].reports.empty() || !g[v].reports_eod.empty()) { fprintf(f, "shape = doublecircle "); } fprintf(f, "label = \"%u\"];\n", g[v].state_id); } for (const auto &e : edges_range(g)) { GoughVertex s = source(e, g); GoughVertex t = target(e, g); fprintf(f, "%s -> %s\n", dump_name(g[s]).c_str(), dump_name(g[t]).c_str()); } fprintf(f, "}\n"); fclose(f); }
static void gather_vars(const GoughGraph &g, vector<const GoughSSAVar *> *vars, map<const GoughSSAVar *, string> *names, map<const GoughSSAVar *, string> *src_label, set<const GoughSSAVar *> *reporters) { for (auto v : vertices_range(g)) { for (const auto &r : g[v].reports) { reporters->insert(r.second); } for (const auto &r : g[v].reports_eod) { reporters->insert(r.second); } for (u32 i = 0; i < g[v].vars.size(); i++) { const GoughSSAVar *vp = g[v].vars[i].get(); stringstream ss; ss << dump_name(g[v]) << "_" << i; vars->push_back(vp); names->insert(make_pair(vp, ss.str())); src_label->insert(make_pair(vp, dump_name(g[v]))); } } for (const auto &e : edges_range(g)) { for (u32 i = 0; i < g[e].vars.size(); i++) { const GoughSSAVar *vp = g[e].vars[i].get(); stringstream ss; ss << dump_name(g, e) << "_" << i; vars->push_back(vp); names->insert(make_pair(vp, ss.str())); src_label->insert(make_pair(vp, dump_name(g, e))); } } }
static void all_vars(const GoughGraph &g, vector<GoughSSAVar *> *out) { for (auto v : vertices_range(g)) { push_back_all_raw(out, g[v].vars); } for (const auto &e : edges_range(g)) { push_back_all_raw(out, g[e].vars); } }
/* crude, deterministic assignment of symbolic register slots. * returns number of slots given out */ static u32 initial_slots(const GoughGraph &g) { u32 next_slot = 0; for (auto v : vertices_range(g)) { set_initial_slots(g[v].vars, &next_slot); } for (const auto &e : edges_range(g)) { set_initial_slots(g[e].vars, &next_slot); } return next_slot; }
static void dump_var_mapping(const GoughGraph &g, const string &base, const Grey &grey) { stringstream ss; ss << grey.dumpPath << "gough_" << base << "_vars.txt"; FILE *f = fopen(ss.str().c_str(), "w"); for (auto v : vertices_range(g)) { set<const GoughSSAVar *> used = uses(g[v]); if (g[v].vars.empty() && used.empty()) { continue; } fprintf(f, "%s\n", dump_name(g[v]).c_str()); for (u32 i = 0; i < g[v].vars.size(); i++) { const GoughSSAVar *vp = g[v].vars[i].get(); fprintf(f, "\t%u: slot %u\n", i, vp->slot); } if (!used.empty()) { fprintf(f, "\tuses:"); vector<u32> used_id; for (const GoughSSAVar *var : used) { used_id.push_back(var->slot); } for (const u32 &id : used_id) { fprintf(f, " %u", id); } fprintf(f, "\n"); } } for (const auto &e : edges_range(g)) { set<const GoughSSAVar *> used = uses(g[e]); if (g[e].vars.empty() && used.empty()) { continue; } fprintf(f, "%s\n", dump_name(g, e).c_str()); for (u32 i = 0; i < g[e].vars.size(); i++) { const GoughSSAVar *vp = g[e].vars[i].get(); fprintf(f, "\t%u: slot %u\n", i, vp->slot); } if (!used.empty()) { fprintf(f, "\tuses:"); vector<u32> used_id; for (const GoughSSAVar *var : used) { used_id.push_back(var->slot); } for (const u32 &id : used_id) { fprintf(f, " %u", id); } fprintf(f, "\n"); } } fclose(f); }
static void pruneExtUnreachable(NGWrapper &g) { vector<NFAVertexBidiDepth> depths; calcDepths(g, depths); vector<NFAEdge> dead; for (const auto &e : edges_range(g)) { if (isEdgePrunable(g, depths, e)) { DEBUG_PRINTF("pruning\n"); dead.push_back(e); } } if (dead.empty()) { return; } remove_edges(dead, g); pruneUseless(g); }
static void update_local_slots(GoughGraph &g, set<GoughSSAVar *> &locals, u32 local_base) { DEBUG_PRINTF("%zu local variables\n", locals.size()); /* local variables only occur on edges (joins are never local) */ u32 allocated_count = 0; for (const auto &e : edges_range(g)) { u32 next_slot = local_base; for (auto &var : g[e].vars) { if (contains(locals, var.get())) { DEBUG_PRINTF("updating slot %u using local %u\n", var->slot, next_slot); var->slot = next_slot++; allocated_count++; } } } assert(allocated_count == locals.size()); }
/** \brief Convert temporary assert vertices (from construction method) to * edge-based flags. * * Remove the horrors that are the temporary assert vertices which arise from * our construction method. Allows the rest of our code base to live in * blissful ignorance of their existence. */ void removeAssertVertices(ReportManager &rm, NGWrapper &g) { size_t num = 0; DEBUG_PRINTF("before: graph has %zu vertices\n", num_vertices(g)); // Sweep over the graph and ascertain that we do actually have vertices // with assertion flags set. Otherwise, we're done. if (!hasAssertVertices(g)) { DEBUG_PRINTF("no assert vertices, done\n"); return; } u32 assert_edge_count = 0; // Build a cache of (u, v) vertex pairs to edge descriptors. edge_cache_t edge_cache; for (const auto &e : edges_range(g)) { edge_cache[make_pair(source(e, g), target(e, g))] = e; } for (auto v : vertices_range(g)) { if (g[v].assert_flags & WORDBOUNDARY_FLAGS) { replaceAssertVertex(g, v, edge_cache, assert_edge_count); num++; } } checkForMultilineStart(rm, g); if (num) { DEBUG_PRINTF("resolved %zu assert vertices\n", num); pruneUseless(g); pruneEmptyVertices(g); g.renumberVertices(); g.renumberEdges(); } DEBUG_PRINTF("after: graph has %zu vertices\n", num_vertices(g)); assert(!hasAssertVertices(g)); }
static NFAVertex findSingleCyclic(const NGHolder &g) { NFAVertex v = NGHolder::null_vertex(); for (const auto &e : edges_range(g)) { if (source(e, g) == target(e, g)) { if (source(e, g) == g.startDs) { continue; } if (v != NGHolder::null_vertex()) { // More than one cyclic vertex. return NGHolder::null_vertex(); } v = source(e, g); } } if (v != NGHolder::null_vertex()) { DEBUG_PRINTF("cyclic is %u\n", g[v].index); assert(!is_special(v, g)); } return v; }
static never_inline void fill_aux(const GoughGraph &g, GoughGraphAux *aux) { for (auto v : vertices_range(g)) { for (const auto &var : g[v].vars) { aux->containing_v[var.get()] = v; DEBUG_PRINTF("%u is on vertex %u\n", var->slot, g[v].state_id); } for (GoughSSAVar *var : g[v].reports | map_values) { aux->reporters[var].insert(v); } for (GoughSSAVar *var : g[v].reports_eod | map_values) { aux->reporters[var].insert(v); } } for (const auto &e : edges_range(g)) { for (const auto &var : g[e].vars) { aux->containing_e[var.get()] = e; DEBUG_PRINTF("%u is on edge %u->%u\n", var->slot, g[source(e, g)].state_id, g[target(e, g)].state_id); } } }
static never_inline void mergeNfa(NGHolder &dest, vector<NFAVertex> &destStateMap, ue2::unordered_map<NFAVertex, u32> &dest_state_ids, NGHolder &vic, vector<NFAVertex> &vicStateMap, size_t common_len) { map<NFAVertex, NFAVertex> vmap; // vic -> dest vmap[vic.start] = dest.start; vmap[vic.startDs] = dest.startDs; vmap[vic.accept] = dest.accept; vmap[vic.acceptEod] = dest.acceptEod; vmap[nullptr] = nullptr; u32 stateNum = countStates(dest, dest_state_ids); // For vertices in the common len, add to vmap and merge in the reports, if // any. for (u32 i = 0; i < common_len; i++) { NFAVertex v_old = vicStateMap[i], v = destStateMap[i]; vmap[v_old] = v; const auto &reports = vic[v_old].reports; dest[v].reports.insert(reports.begin(), reports.end()); } // Add in vertices beyond the common len, giving them state numbers // starting at stateNum. for (u32 i = common_len; i < vicStateMap.size(); i++) { NFAVertex v_old = vicStateMap[i]; if (is_special(v_old, vic)) { // Dest already has start vertices, just merge the reports. u32 idx = vic[v_old].index; NFAVertex v = dest.getSpecialVertex(idx); const auto &reports = vic[v_old].reports; dest[v].reports.insert(reports.begin(), reports.end()); continue; } NFAVertex v = add_vertex(vic[v_old], dest); dest_state_ids[v] = stateNum++; vmap[v_old] = v; } /* add edges */ DEBUG_PRINTF("common_len=%zu\n", common_len); for (const auto &e : edges_range(vic)) { NFAVertex u_old = source(e, vic), v_old = target(e, vic); NFAVertex u = vmap[u_old], v = vmap[v_old]; bool uspecial = is_special(u, dest); bool vspecial = is_special(v, dest); // Skip stylised edges that are already present. if (uspecial && vspecial && edge(u, v, dest).second) { continue; } // We're in the common region if v's state ID is low enough, unless v // is a special (an accept), in which case we use u's state ID. assert(contains(dest_state_ids, v)); bool in_common_region = dest_state_ids.at(v) < common_len; if (vspecial && dest_state_ids.at(u) < common_len) { in_common_region = true; } DEBUG_PRINTF("adding idx=%u (state %u) -> idx=%u (state %u)%s\n", dest[u].index, dest_state_ids.at(u), dest[v].index, dest_state_ids.at(v), in_common_region ? " [common]" : ""); if (in_common_region) { if (!is_special(v, dest)) { DEBUG_PRINTF("skipping common edge\n"); assert(edge(u, v, dest).second); // Should never merge edges with different top values. assert(vic[e].top == dest[edge(u, v, dest).first].top); continue; } else { assert(is_any_accept(v, dest)); // If the edge exists in both graphs, skip it. if (edge(u, v, dest).second) { DEBUG_PRINTF("skipping common edge to accept\n"); continue; } } } assert(!edge(u, v, dest).second); add_edge(u, v, vic[e], dest); } dest.renumberEdges(); dest.renumberVertices(); }
static u32 findMaxInfixMatches(const NGHolder &h, const set<ue2_literal> &lits) { DEBUG_PRINTF("h=%p, %zu literals\n", &h, lits.size()); //dumpGraph("infix.dot", h.g); if (!onlyOneTop(h)) { DEBUG_PRINTF("more than one top!n"); return NO_MATCH_LIMIT; } // Indices of vertices that could terminate any of the literals in 'lits'. set<u32> terms; for (const auto &s : lits) { DEBUG_PRINTF("lit s='%s'\n", escapeString(s).c_str()); if (s.empty()) { // Likely an anchored case, be conservative here. return NO_MATCH_LIMIT; } for (auto v : vertices_range(h)) { if (is_special(v, h)) { continue; } if (couldEndLiteral(s, v, h)) { u32 idx = h[v].index; DEBUG_PRINTF("vertex %u could terminate lit\n", idx); terms.insert(idx); } } } if (terms.empty()) { DEBUG_PRINTF("literals cannot match inside infix\n"); return 0; } NGHolder g; cloneHolder(g, h); vector<NFAVertex> dead; // The set of all edges in the graph is used for existence checks in contractVertex. ue2::unordered_set<pair<NFAVertex, NFAVertex>> all_edges; for (const auto &e : edges_range(g)) { all_edges.emplace(source(e, g), target(e, g)); } for (auto v : vertices_range(g)) { if (is_special(v, g)) { continue; } if (contains(terms, g[v].index)) { continue; } contractVertex(g, v, all_edges); dead.push_back(v); } remove_vertices(dead, g); //dumpGraph("relaxed.dot", g.g); depth maxWidth = findMaxWidth(g); DEBUG_PRINTF("maxWidth=%s\n", maxWidth.str().c_str()); assert(maxWidth.is_reachable()); if (maxWidth.is_infinite()) { // Cycle detected, so we can likely squeeze an unlimited number of // matches into this graph. return NO_MATCH_LIMIT; } assert(terms.size() >= maxWidth); return maxWidth; }