void Detector::handleMessage(cMessage *msg) { if (msg->arrivedOn("clock")) { // Schedule evaluation just a bit later, so that we have all sketches scheduleAt( simTime() + simtime_t(getParentModule()->par("interval")) * .8, msg); } else if (msg->isSelfMessage()) { evaluateCores(); delete msg; clearReports(); } else if (msg->arrivedOn("graphServerIn")) { updateCores(check_and_cast<CoresUpdate*>(msg)); } else if (msg->arrivedOn("reportsIn")) { updateReports(check_and_cast<Report*>(msg)); } else { delete msg; } }
void handleExtendedParams(ReportManager &rm, NGWrapper &g, UNUSED const CompileContext &cc) { if (!hasExtParams(g)) { return; } depth minWidth = findMinWidth(g); depth maxWidth = findMaxWidth(g); bool is_anchored = !has_proper_successor(g.startDs, g) && out_degree(g.start, g); bool has_offset_adj = hasOffsetAdjustments(rm, g); DEBUG_PRINTF("minWidth=%s, maxWidth=%s, anchored=%d, offset_adj=%d\n", minWidth.str().c_str(), maxWidth.str().c_str(), is_anchored, has_offset_adj); DepthMinMax match_depths = findMatchLengths(rm, g); DEBUG_PRINTF("match depths %s\n", match_depths.str().c_str()); if (is_anchored && maxWidth.is_finite() && g.min_offset > maxWidth) { ostringstream oss; oss << "Expression is anchored and cannot satisfy min_offset=" << g.min_offset << " as it can only produce matches of length " << maxWidth << " bytes at most."; throw CompileError(g.expressionIndex, oss.str()); } if (minWidth > g.max_offset) { ostringstream oss; oss << "Expression has max_offset=" << g.max_offset << " but requires " << minWidth << " bytes to match."; throw CompileError(g.expressionIndex, oss.str()); } if (maxWidth.is_finite() && match_depths.max < g.min_length) { ostringstream oss; oss << "Expression has min_length=" << g.min_length << " but can " "only produce matches of length " << match_depths.max << " bytes at most."; throw CompileError(g.expressionIndex, oss.str()); } if (g.min_length && g.min_length <= match_depths.min) { DEBUG_PRINTF("min_length=%llu constraint is unnecessary\n", g.min_length); g.min_length = 0; } if (!hasExtParams(g)) { return; } pruneVacuousEdges(g); pruneUnmatchable(g, rm); if (!has_offset_adj) { pruneExtUnreachable(g); } // We may have removed all the edges to accept, in which case this // expression cannot match. if (in_degree(g.accept, g) == 0 && in_degree(g.acceptEod, g) == 1) { throw CompileError(g.expressionIndex, "Extended parameter " "constraints can not be satisfied for any match from " "this expression."); } // Remove reports on vertices without an edge to accept (which have been // pruned above). clearReports(g); // Recalc. minWidth = findMinWidth(g); maxWidth = findMaxWidth(g); is_anchored = proper_out_degree(g.startDs, g) == 0 && out_degree(g.start, g); has_offset_adj = hasOffsetAdjustments(rm, g); // If the pattern is completely anchored and has a min_length set, this can // be converted to a min_offset. if (g.min_length && (g.min_offset <= g.min_length) && is_anchored) { DEBUG_PRINTF("converting min_length to min_offset=%llu for " "anchored case\n", g.min_length); g.min_offset = g.min_length; g.min_length = 0; } if (g.min_offset && g.min_offset <= minWidth && !has_offset_adj) { DEBUG_PRINTF("min_offset=%llu constraint is unnecessary\n", g.min_offset); g.min_offset = 0; } if (!hasExtParams(g)) { return; } // If the pattern has a min_length and is of "ratchet" form with one // unbounded repeat, that repeat can become a bounded repeat. // e.g. /foo.*bar/{min_length=100} --> /foo.{94,}bar/ if (g.min_length && transformMinLengthToRepeat(rm, g)) { DEBUG_PRINTF("converted min_length to bounded repeat\n"); // recalc minWidth = findMinWidth(g); } // If the pattern is unanchored, has a max_offset and has not asked for // SOM, we can use that knowledge to anchor it which will limit its // lifespan. Note that we can't use this transformation if there's a // min_length, as it's currently handled using "sly SOM". // Note that it is possible to handle graphs that have a combination of // anchored and unanchored paths, but it's too tricky for the moment. if (g.max_offset != MAX_OFFSET && !g.som && !g.min_length && !has_offset_adj && isUnanchored(g)) { if (anchorPatternWithBoundedRepeat(g, minWidth, maxWidth)) { DEBUG_PRINTF("minWidth=%s, maxWidth=%s\n", minWidth.str().c_str(), maxWidth.str().c_str()); if (minWidth == maxWidth) { // For a fixed width pattern, we can retire the offsets as they // are implicit in the graph now. g.min_offset = 0; g.max_offset = MAX_OFFSET; } } } //dumpGraph("final.dot", g.g); if (!hasExtParams(g)) { return; } set<NFAVertex> done; updateReportBounds(rm, g, g.accept, done); updateReportBounds(rm, g, g.acceptEod, done); }
/** If the pattern has a min_length and is of "ratchet" form with one unbounded * repeat, that repeat can become a bounded repeat. * * /foo.*bar/{min_length=100} --> /foo.{94,}bar/ */ static bool transformMinLengthToRepeat(const ReportManager &rm, NGWrapper &g) { assert(g.min_length); if (g.min_length > MAX_MINLENGTH_TO_CONVERT) { return false; } // If the pattern has virtual starts, we probably don't want to touch it. if (hasVirtualStarts(g)) { DEBUG_PRINTF("virtual starts, bailing\n"); return false; } // The graph must contain a single cyclic vertex (other than startDs), and // that vertex can have one pred and one successor. NFAVertex cyclic = findSingleCyclic(g); if (cyclic == NGHolder::null_vertex()) { return false; } NGHolder::adjacency_iterator ai, ae; tie(ai, ae) = adjacent_vertices(g.start, g); if (*ai == g.startDs) { ++ai; } NFAVertex v = *ai; if (++ai != ae) { DEBUG_PRINTF("more than one initial vertex\n"); return false; } u32 width = 0; // Walk from the start vertex to the cyclic state and ensure we have a // chain of vertices. while (v != cyclic) { DEBUG_PRINTF("vertex %u\n", g[v].index); width++; tie(ai, ae) = adjacent_vertices(v, g); set<NFAVertex> succ(ai, ae); if (contains(succ, cyclic)) { if (succ.size() == 1) { v = cyclic; } else if (succ.size() == 2) { // Cyclic and jump edge. succ.erase(cyclic); NFAVertex v2 = *succ.begin(); if (!edge(cyclic, v2, g).second) { DEBUG_PRINTF("bad form\n"); return false; } v = cyclic; } else { DEBUG_PRINTF("bad form\n"); return false; } } else { if (succ.size() != 1) { DEBUG_PRINTF("bad form\n"); return false; } v = *succ.begin(); } } // Check the cyclic state is A-OK. v = getSoleDestVertex(g, cyclic); if (v == NGHolder::null_vertex()) { DEBUG_PRINTF("cyclic has more than one successor\n"); return false; } // Walk from the cyclic state to an accept and ensure we have a chain of // vertices. while (!is_any_accept(v, g)) { DEBUG_PRINTF("vertex %u\n", g[v].index); width++; tie(ai, ae) = adjacent_vertices(v, g); set<NFAVertex> succ(ai, ae); if (succ.size() != 1) { DEBUG_PRINTF("bad form\n"); return false; } v = *succ.begin(); } int offsetAdjust = 0; if (!hasOffsetAdjust(rm, g, &offsetAdjust)) { return false; } DEBUG_PRINTF("adjusting width by %d\n", offsetAdjust); width += offsetAdjust; DEBUG_PRINTF("width=%u, vertex %u is cyclic\n", width, g[cyclic].index); if (width >= g.min_length) { DEBUG_PRINTF("min_length=%llu is guaranteed, as width=%u\n", g.min_length, width); g.min_length = 0; return true; } vector<NFAVertex> preds; vector<NFAEdge> dead; for (auto u : inv_adjacent_vertices_range(cyclic, g)) { DEBUG_PRINTF("pred %u\n", g[u].index); if (u == cyclic) { continue; } preds.push_back(u); // We want to delete the out-edges of each predecessor, but need to // make sure we don't delete the startDs self loop. for (const auto &e : out_edges_range(u, g)) { if (target(e, g) != g.startDs) { dead.push_back(e); } } } remove_edges(dead, g); assert(!preds.empty()); const CharReach &cr = g[cyclic].char_reach; for (u32 i = 0; i < g.min_length - width - 1; ++i) { v = add_vertex(g); g[v].char_reach = cr; for (auto u : preds) { add_edge(u, v, g); } preds.clear(); preds.push_back(v); } assert(!preds.empty()); for (auto u : preds) { add_edge(u, cyclic, g); } g.renumberVertices(); g.renumberEdges(); clearReports(g); g.min_length = 0; return true; }
void Detector::finish() { clearReports(); }