/* * Copyright (c) 2015-2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** \file * \brief: NFA Graph Builder: used by Glushkov construction to construct an * NGHolder from a parsed expression. */ #include "ng_builder.h" #include "grey.h" #include "ng.h" #include "ng_util.h" #include "ue2common.h" #include "compiler/compiler.h" // for ParsedExpression #include "util/compile_error.h" #include using namespace std; namespace ue2 { namespace { /** Concrete implementation of NFABuilder interface. */ class NFABuilderImpl : public NFABuilder { public: NFABuilderImpl(ReportManager &rm, const Grey &grey, const ParsedExpression &expr); ~NFABuilderImpl() override; Position makePositions(size_t nPositions) override; Position getStart() const override; Position getStartDotStar() const override; Position getAccept() const override; Position getAcceptEOD() const override; bool isSpecialState(Position p) const override; void setNodeReportID(Position position, int offsetAdjust) override; void addCharReach(Position position, const CharReach &cr) override; void setAssertFlag(Position position, u32 flag) override; u32 getAssertFlag(Position position) override; void addVertex(Position p) override; void addEdge(Position start, Position end) override; bool hasEdge(Position start, Position end) const override; u32 numVertices() const override { return vertIdx; } void cloneRegion(Position first, Position last, unsigned posOffset) override; BuiltExpression getGraph() override; private: /** fetch a vertex given its Position ID. */ NFAVertex getVertex(Position pos) const; /** \brief Internal convenience function to add an edge (u, v). */ pair addEdge(NFAVertex u, NFAVertex v); /** \brief We use the ReportManager to hand out new internal reports. */ ReportManager &rm; /** \brief Greybox: used for resource limits. */ const Grey &grey; /** \brief Underlying graph. */ unique_ptr graph; /** \brief Underlying expression info. */ ExpressionInfo expr; /** \brief mapping from position to vertex. Use \ref getVertex for access. * */ vector id2vertex; /** \brief Index of next vertex. */ u32 vertIdx; }; // class NFABuilderImpl } // namespace NFABuilderImpl::NFABuilderImpl(ReportManager &rm_in, const Grey &grey_in, const ParsedExpression &parsed) : rm(rm_in), grey(grey_in), graph(std::make_unique()), expr(parsed.expr), vertIdx(N_SPECIALS) { // Reserve space for a reasonably-sized NFA id2vertex.reserve(64); id2vertex.resize(N_SPECIALS); id2vertex[NODE_START] = graph->start; id2vertex[NODE_START_DOTSTAR] = graph->startDs; id2vertex[NODE_ACCEPT] = graph->accept; id2vertex[NODE_ACCEPT_EOD] = graph->acceptEod; } NFABuilderImpl::~NFABuilderImpl() { // empty } NFAVertex NFABuilderImpl::getVertex(Position pos) const { assert(id2vertex.size() >= pos); const NFAVertex v = id2vertex[pos]; assert(v != NGHolder::null_vertex()); assert((*graph)[v].index == pos); return v; } void NFABuilderImpl::addVertex(Position pos) { // Enforce resource limit. if (pos > grey.limitGraphVertices) { throw CompileError("Pattern too large."); } NFAVertex v = add_vertex(*graph); if (id2vertex.size() <= pos) { id2vertex.resize(pos + 1); } id2vertex[pos] = v; (*graph)[v].index = pos; } BuiltExpression NFABuilderImpl::getGraph() { DEBUG_PRINTF("built graph has %zu vertices and %zu edges\n", num_vertices(*graph), num_edges(*graph)); if (num_edges(*graph) > grey.limitGraphEdges) { throw CompileError("Pattern too large."); } if (num_vertices(*graph) > grey.limitGraphVertices) { throw CompileError("Pattern too large."); } return { expr, std::move(graph) }; } void NFABuilderImpl::setNodeReportID(Position pos, int offsetAdjust) { Report ir = rm.getBasicInternalReport(expr, offsetAdjust); DEBUG_PRINTF("setting report id on %u = (%u, %d, %u)\n", pos, expr.report, offsetAdjust, ir.ekey); NFAVertex v = getVertex(pos); auto &reports = (*graph)[v].reports; reports.clear(); reports.insert(rm.getInternalId(ir)); } void NFABuilderImpl::addCharReach(Position pos, const CharReach &cr) { NFAVertex v = getVertex(pos); (*graph)[v].char_reach |= cr; } void NFABuilderImpl::setAssertFlag(Position pos, u32 flag) { NFAVertex v = getVertex(pos); (*graph)[v].assert_flags |= flag; } u32 NFABuilderImpl::getAssertFlag(Position pos) { NFAVertex v = getVertex(pos); return (*graph)[v].assert_flags; } pair NFABuilderImpl::addEdge(NFAVertex u, NFAVertex v) { // assert that the edge doesn't already exist assert(edge(u, v, *graph).second == false); return add_edge(u, v, *graph); } void NFABuilderImpl::addEdge(Position startPos, Position endPos) { DEBUG_PRINTF("%u -> %u\n", startPos, endPos); assert(startPos < vertIdx); assert(endPos < vertIdx); NFAVertex u = getVertex(startPos); NFAVertex v = getVertex(endPos); if ((u == graph->start || u == graph->startDs) && v == graph->startDs) { /* standard special -> special edges already exist */ assert(edge(u, v, *graph).second == true); return; } assert(edge(u, v, *graph).second == false); addEdge(u, v); } bool NFABuilderImpl::hasEdge(Position startPos, Position endPos) const { return edge(getVertex(startPos), getVertex(endPos), *graph).second; } Position NFABuilderImpl::getStart() const { return NODE_START; } Position NFABuilderImpl::getStartDotStar() const { return NODE_START_DOTSTAR; } Position NFABuilderImpl::getAccept() const { return NODE_ACCEPT; } Position NFABuilderImpl::getAcceptEOD() const { return NODE_ACCEPT_EOD; } bool NFABuilderImpl::isSpecialState(Position p) const { return (p == NODE_START || p == NODE_START_DOTSTAR || p == NODE_ACCEPT || p == NODE_ACCEPT_EOD); } Position NFABuilderImpl::makePositions(size_t nPositions) { Position base = vertIdx; for (size_t i = 0; i < nPositions; i++) { addVertex(vertIdx++); } DEBUG_PRINTF("built %zu positions from base %u\n", nPositions, base); return base; } void NFABuilderImpl::cloneRegion(Position first, Position last, unsigned posOffset) { NGHolder &g = *graph; assert(posOffset > 0); // walk the nodes between first and last and copy their vertex properties DEBUG_PRINTF("cloning nodes in [%u, %u], offset %u\n", first, last, posOffset); for (Position i = first; i <= last; ++i) { NFAVertex orig = getVertex(i); Position destIdx = i + posOffset; assert(destIdx < vertIdx); NFAVertex dest = getVertex(destIdx); g[dest] = g[orig]; // all properties g[dest].index = destIdx; } } unique_ptr makeNFABuilder(ReportManager &rm, const CompileContext &cc, const ParsedExpression &expr) { return std::make_unique(rm, cc.grey, expr); } NFABuilder::~NFABuilder() { } } // namespace ue2