Initial commit of Hyperscan

This commit is contained in:
Matthew Barr
2015-10-20 09:13:35 +11:00
commit 904e436f11
610 changed files with 213627 additions and 0 deletions

131
src/nfa/accel.c Normal file
View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2015, 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.
*/
#include "accel.h"
#include "shufti.h"
#include "truffle.h"
#include "vermicelli.h"
#include "ue2common.h"
const u8 *run_accel(const union AccelAux *accel, const u8 *c, const u8 *c_end) {
assert(ISALIGNED_N(accel, alignof(union AccelAux)));
const u8 *rv;
switch (accel->accel_type) {
case ACCEL_NONE:
DEBUG_PRINTF("accel none %p %p\n", c, c_end);
return c;
case ACCEL_VERM:
DEBUG_PRINTF("accel verm %p %p\n", c, c_end);
if (c + 15 >= c_end) {
return c;
}
rv = vermicelliExec(accel->verm.c, 0, c, c_end);
break;
case ACCEL_VERM_NOCASE:
DEBUG_PRINTF("accel verm nc %p %p\n", c, c_end);
if (c + 15 >= c_end) {
return c;
}
rv = vermicelliExec(accel->verm.c, 1, c, c_end);
break;
case ACCEL_DVERM:
DEBUG_PRINTF("accel dverm %p %p\n", c, c_end);
if (c + 16 + 1 >= c_end) {
return c;
}
/* need to stop one early to get an accurate end state */
rv = vermicelliDoubleExec(accel->dverm.c1, accel->dverm.c2, 0, c,
c_end - 1);
break;
case ACCEL_DVERM_NOCASE:
DEBUG_PRINTF("accel dverm nc %p %p\n", c, c_end);
if (c + 16 + 1 >= c_end) {
return c;
}
/* need to stop one early to get an accurate end state */
rv = vermicelliDoubleExec(accel->dverm.c1, accel->dverm.c2, 1, c,
c_end - 1);
break;
case ACCEL_SHUFTI:
DEBUG_PRINTF("accel shufti %p %p\n", c, c_end);
if (c + 15 >= c_end) {
return c;
}
rv = shuftiExec(accel->shufti.lo, accel->shufti.hi, c, c_end);
break;
case ACCEL_TRUFFLE:
DEBUG_PRINTF("accel Truffle %p %p\n", c, c_end);
if (c + 15 >= c_end) {
return c;
}
rv = truffleExec(accel->truffle.mask1, accel->truffle.mask2, c, c_end);
break;
case ACCEL_DSHUFTI:
DEBUG_PRINTF("accel dshufti %p %p\n", c, c_end);
if (c + 15 + 1 >= c_end) {
return c;
}
/* need to stop one early to get an accurate end state */
rv = shuftiDoubleExec(accel->dshufti.lo1,
accel->dshufti.hi1,
accel->dshufti.lo2,
accel->dshufti.hi2, c, c_end - 1);
break;
case ACCEL_RED_TAPE:
DEBUG_PRINTF("accel red tape %p %p\n", c, c_end);
rv = c_end;
break;
default:
assert(!"not here");
return c;
}
DEBUG_PRINTF("adjusting for offset %u\n", accel->generic.offset);
/* adjust offset to take into account the offset */
rv = MAX(c + accel->generic.offset, rv);
rv -= accel->generic.offset;
return rv;
}

112
src/nfa/accel.h Normal file
View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2015, 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 Acceleration: data structures and common definitions.
*/
#ifndef ACCEL_H
#define ACCEL_H
#include "ue2common.h"
/* run time defs */
#define BAD_ACCEL_DIST 4
#define SMALL_ACCEL_PENALTY 8
#define BIG_ACCEL_PENALTY 32
/// Minimum length of the scan buffer for us to attempt acceleration.
#define ACCEL_MIN_LEN 16
enum AccelType {
ACCEL_NONE,
ACCEL_VERM,
ACCEL_VERM_NOCASE,
ACCEL_DVERM,
ACCEL_DVERM_NOCASE,
ACCEL_RVERM,
ACCEL_RVERM_NOCASE,
ACCEL_RDVERM,
ACCEL_RDVERM_NOCASE,
ACCEL_REOD,
ACCEL_REOD_NOCASE,
ACCEL_RDEOD,
ACCEL_RDEOD_NOCASE,
ACCEL_SHUFTI,
ACCEL_DSHUFTI,
ACCEL_TRUFFLE,
ACCEL_RED_TAPE
};
/** \brief Structure for accel framework. */
union AccelAux {
u8 accel_type;
struct {
u8 accel_type;
u8 offset;
} generic;
struct {
u8 accel_type;
u8 offset;
u8 c; // uppercase if nocase
} verm;
struct {
u8 accel_type;
u8 offset;
u8 c1; // uppercase if nocase
u8 c2; // uppercase if nocase
} dverm;
struct {
u8 accel_type;
u8 offset;
m128 lo;
m128 hi;
} shufti;
struct {
u8 accel_type;
u8 offset;
m128 lo1;
m128 hi1;
m128 lo2;
m128 hi2;
} dshufti;
struct {
u8 accel_type;
u8 offset;
m128 mask1;
m128 mask2;
} truffle;
};
/**
* Runs the specified acceleration scheme between c and c_end, returns a point
* such that the acceleration scheme does not match before.
*/
const u8 *run_accel(const union AccelAux *accel, const u8 *c, const u8 *c_end);
#endif

152
src/nfa/accel_dump.cpp Normal file
View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2015, 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 Acceleration: dump code.
*/
#include "config.h"
#include "accel.h"
#include "accel_dump.h"
#include "shufticompile.h"
#include "trufflecompile.h"
#include "ue2common.h"
#include "util/charreach.h"
#include "util/dump_charclass.h"
#include "util/dump_mask.h"
#include <cstdio>
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
namespace ue2 {
static
const char *accelName(u8 accel_type) {
switch (accel_type) {
case ACCEL_NONE:
return "none";
case ACCEL_VERM:
return "vermicelli";
case ACCEL_VERM_NOCASE:
return "vermicelli nocase";
case ACCEL_DVERM:
return "double-vermicelli";
case ACCEL_DVERM_NOCASE:
return "double-vermicelli nocase";
case ACCEL_RVERM:
return "reverse vermicelli";
case ACCEL_RVERM_NOCASE:
return "reverse vermicelli nocase";
case ACCEL_RDVERM:
return "reverse double-vermicelli";
case ACCEL_RDVERM_NOCASE:
return "reverse double-vermicelli nocase";
case ACCEL_REOD:
return "reverse eod";
case ACCEL_REOD_NOCASE:
return "reverse eod nocase";
case ACCEL_RDEOD:
return "reverse double-eod";
case ACCEL_RDEOD_NOCASE:
return "reverse double-eod nocase";
case ACCEL_SHUFTI:
return "shufti";
case ACCEL_DSHUFTI:
return "double-shufti";
case ACCEL_TRUFFLE:
return "truffle";
case ACCEL_RED_TAPE:
return "red tape";
default:
return "unknown!";
}
}
void dumpAccelInfo(FILE *f, const AccelAux &accel) {
fprintf(f, " %s", accelName(accel.accel_type));
if (accel.generic.offset) {
fprintf(f, "+%hhu", accel.generic.offset);
}
switch (accel.accel_type) {
case ACCEL_VERM:
case ACCEL_VERM_NOCASE:
case ACCEL_RVERM:
case ACCEL_RVERM_NOCASE:
fprintf(f, " [\\x%02hhx]\n", accel.verm.c);
break;
case ACCEL_DVERM:
case ACCEL_DVERM_NOCASE:
case ACCEL_RDVERM:
case ACCEL_RDVERM_NOCASE:
fprintf(f, " [\\x%02hhx\\x%02hhx]\n", accel.dverm.c1, accel.dverm.c2);
break;
case ACCEL_SHUFTI: {
fprintf(f, "\n");
fprintf(f, "lo %s\n",
dumpMask((const u8 *)&accel.shufti.lo, 128).c_str());
fprintf(f, "hi %s\n",
dumpMask((const u8 *)&accel.shufti.hi, 128).c_str());
CharReach cr = shufti2cr(accel.shufti.lo, accel.shufti.hi);
fprintf(f, "count %zu class %s\n", cr.count(),
describeClass(cr).c_str());
break;
}
case ACCEL_DSHUFTI:
fprintf(f, "\n");
fprintf(f, "lo1 %s\n",
dumpMask((const u8 *)&accel.dshufti.lo1, 128).c_str());
fprintf(f, "hi1 %s\n",
dumpMask((const u8 *)&accel.dshufti.hi1, 128).c_str());
fprintf(f, "lo2 %s\n",
dumpMask((const u8 *)&accel.dshufti.lo2, 128).c_str());
fprintf(f, "hi2 %s\n",
dumpMask((const u8 *)&accel.dshufti.hi2, 128).c_str());
break;
case ACCEL_TRUFFLE: {
fprintf(f, "\n");
fprintf(f, "lo %s\n",
dumpMask((const u8 *)&accel.truffle.mask1, 128).c_str());
fprintf(f, "hi %s\n",
dumpMask((const u8 *)&accel.truffle.mask2, 128).c_str());
CharReach cr = truffle2cr(accel.truffle.mask1, accel.truffle.mask2);
fprintf(f, "count %zu class %s\n", cr.count(),
describeClass(cr).c_str());
break;
}
default:
fprintf(f, "\n");
break;
}
}
} // namespace ue2

49
src/nfa/accel_dump.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 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 Acceleration: dump code.
*/
#ifndef ACCEL_DUMP_H
#define ACCEL_DUMP_H
#if defined(DUMP_SUPPORT)
#include <cstdio>
union AccelAux;
namespace ue2 {
void dumpAccelInfo(FILE *f, const AccelAux &accel);
} // namespace ue2
#endif // DUMP_SUPPORT
#endif // ACCEL_DUMP_H

191
src/nfa/accelcompile.cpp Normal file
View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2015, 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.
*/
#include "accel.h"
#include "accelcompile.h"
#include "shufticompile.h"
#include "trufflecompile.h"
#include "nfagraph/ng_limex_accel.h" /* for constants */
#include "util/bitutils.h"
#include "util/verify_types.h"
#include <map>
#include <set>
#include <vector>
using namespace std;
namespace ue2 {
static
void buildAccelSingle(const AccelInfo &info, AccelAux *aux) {
assert(aux->accel_type == ACCEL_NONE);
if (info.single_stops.all()) {
return;
}
size_t outs = info.single_stops.count();
DEBUG_PRINTF("%zu outs\n", outs);
assert(outs && outs < 256);
u32 offset = info.single_offset;
if (outs == 1) {
aux->accel_type = ACCEL_VERM;
aux->verm.offset = offset;
aux->verm.c = info.single_stops.find_first();
DEBUG_PRINTF("building vermicelli caseful for 0x%02hhx\n", aux->verm.c);
return;
}
if (outs == 2 && info.single_stops.isCaselessChar()) {
aux->accel_type = ACCEL_VERM_NOCASE;
aux->verm.offset = offset;
aux->verm.c = info.single_stops.find_first() & CASE_CLEAR;
DEBUG_PRINTF("building vermicelli caseless for 0x%02hhx\n",
aux->verm.c);
return;
}
DEBUG_PRINTF("attempting shufti for %zu chars\n", outs);
if (-1 != shuftiBuildMasks(info.single_stops, &aux->shufti.lo,
&aux->shufti.hi)) {
aux->accel_type = ACCEL_SHUFTI;
aux->shufti.offset = offset;
DEBUG_PRINTF("shufti built OK\n");
return;
} else {
DEBUG_PRINTF("shufti build failed, falling through\n");
}
if (outs <= ACCEL_MAX_STOP_CHAR) {
DEBUG_PRINTF("building Truffle for %zu chars\n", outs);
aux->accel_type = ACCEL_TRUFFLE;
aux->truffle.offset = offset;
truffleBuildMasks(info.single_stops, &aux->truffle.mask1,
&aux->truffle.mask2);
return;
}
DEBUG_PRINTF("unable to accelerate case with %zu outs\n", outs);
}
static
bool isCaselessDouble(const flat_set<pair<u8, u8>> &stop) {
// test for vector containing <A,Z> <A,z> <a,Z> <a,z>
if (stop.size() != 4) {
return false;
}
const u8 a = stop.begin()->first & CASE_CLEAR;
const u8 b = stop.begin()->second & CASE_CLEAR;
flat_set<pair<u8, u8>>::const_iterator it, ite;
for (it = stop.begin(), ite = stop.end(); it != ite; ++it) {
if ((it->first & CASE_CLEAR) != a || (it->second & CASE_CLEAR) != b) {
return false;
}
}
return true;
}
static
void buildAccelDouble(const AccelInfo &info, AccelAux *aux) {
size_t outs1 = info.double_stop1.count();
size_t outs2 = info.double_stop2.size();
u8 offset = verify_u8(info.double_offset);
DEBUG_PRINTF("outs1=%zu, outs2=%zu\n", outs1, outs2);
assert(aux->accel_type == ACCEL_NONE);
if (!outs2) {
/* no double byte accel available */
return;
}
// double-byte accel
if (outs1 == 0 && outs2 == 1) {
aux->accel_type = ACCEL_DVERM;
aux->dverm.offset = offset;
aux->dverm.c1 = info.double_stop2.begin()->first;
aux->dverm.c2 = info.double_stop2.begin()->second;
DEBUG_PRINTF("building double-vermicelli caseful for 0x%02hhx%02hhx\n",
aux->dverm.c1, aux->dverm.c2);
return;
}
if (outs1 == 0 && isCaselessDouble(info.double_stop2)) {
aux->accel_type = ACCEL_DVERM_NOCASE;
aux->dverm.offset = offset;
aux->dverm.c1 = info.double_stop2.begin()->first & CASE_CLEAR;
aux->dverm.c2 = info.double_stop2.begin()->second & CASE_CLEAR;
DEBUG_PRINTF("building double-vermicelli caseless for 0x%02hhx%02hhx\n",
aux->dverm.c1, aux->dverm.c2);
return;
}
if (outs1 + outs2 <= 8) {
if (outs1 < outs2 && outs1 <= 2) { // Heuristic from UE-438.
DEBUG_PRINTF("building double-shufti for %zu one-byte and %zu"
" two-byte literals\n", outs1, outs2);
aux->accel_type = ACCEL_DSHUFTI;
aux->dshufti.offset = offset;
shuftiBuildDoubleMasks(info.double_stop1, info.double_stop2,
&aux->dshufti.lo1,
&aux->dshufti.hi1,
&aux->dshufti.lo2,
&aux->dshufti.hi2);
return;
}
}
// drop back to attempt single-byte accel
DEBUG_PRINTF("dropping back to single-byte acceleration\n");
aux->accel_type = ACCEL_NONE;
}
bool buildAccelAux(const AccelInfo &info, AccelAux *aux) {
assert(aux->accel_type == ACCEL_NONE);
if (info.single_stops.none()) {
DEBUG_PRINTF("picked red tape\n");
aux->accel_type = ACCEL_RED_TAPE;
aux->generic.offset = info.single_offset;
} else {
buildAccelDouble(info, aux);
}
if (aux->accel_type == ACCEL_NONE) {
buildAccelSingle(info, aux);
}
assert(aux->accel_type == ACCEL_NONE
|| aux->generic.offset == info.single_offset
|| aux->generic.offset == info.double_offset);
return aux->accel_type != ACCEL_NONE;
}
} // namespace ue2

56
src/nfa/accelcompile.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef ACCEL_COMPILE_H
#define ACCEL_COMPILE_H
#include "ue2common.h"
#include "util/charreach.h"
#include "util/ue2_containers.h"
union AccelAux;
namespace ue2 {
struct AccelInfo {
AccelInfo() : single_offset(0U), double_offset(0U),
single_stops(CharReach::dot()) {}
u32 single_offset; /**< offset correction to apply to single schemes */
u32 double_offset; /**< offset correction to apply to double schemes */
CharReach double_stop1; /**< single-byte accel stop literals for double
* schemes */
flat_set<std::pair<u8, u8>> double_stop2; /**< double-byte accel stop
* literals */
CharReach single_stops; /**< escapes for single byte acceleration */
};
bool buildAccelAux(const AccelInfo &info, AccelAux *aux);
} // namespace ue2
#endif

76
src/nfa/callback.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2015, 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 Callback definitions, used at runtime.
*/
#ifndef NFA_CALLBACK_H
#define NFA_CALLBACK_H
#include "ue2common.h"
/** \brief The type for an NFA callback.
*
* This is a function that takes as arguments the current offset where the
* match occurs, the id of the match and the context pointer that was passed
* into the NFA API function that executed the NFA.
*
* The offset where the match occurs will be the offset after the character
* that caused the match. Thus, if we have a buffer containing 'abc', then a
* pattern that matches an empty string will have an offset of 0, a pattern
* that matches 'a' will have an offset of 1, and a pattern that matches 'abc'
* will have an offset of 3, which will be a value that is 'beyond' the size of
* the buffer. That is, if we have n characters in the buffer, there are n+1
* different potential offsets for matches.
*
* This function should return an int - currently the possible return values
* are 0, which means 'stop running the engine' or non-zero, which means
* 'continue matching'.
*/
typedef int (*NfaCallback)(u64a offset, ReportID id, void *context);
/** \brief The type for an NFA callback which also tracks start of match.
*
* see \ref NfaCallback
*/
typedef int (*SomNfaCallback)(u64a from_offset, u64a to_offset, ReportID id,
void *context);
/**
* standard \ref NfaCallback return value indicating that engine execution
* should continue. (any non-zero value will serve this purpose)
*/
#define MO_CONTINUE_MATCHING 1
/**
* \ref NfaCallback return value indicating that engine execution should halt.
*/
#define MO_HALT_MATCHING 0
#endif // NFA_CALLBACK_H

1016
src/nfa/castle.c Normal file

File diff suppressed because it is too large Load Diff

64
src/nfa/castle.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef NFA_CASTLE_H
#define NFA_CASTLE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ue2common.h"
struct mq;
struct NFA;
char nfaExecCastle0_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecCastle0_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecCastle0_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecCastle0_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecCastle0_inAccept(const struct NFA *n, ReportID report,
struct mq *q);
char nfaExecCastle0_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecCastle0_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecCastle0_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecCastle0_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecCastle0_testEOD NFA_API_NO_IMPL
#define nfaExecCastle0_B_Reverse NFA_API_NO_IMPL
#define nfaExecCastle0_zombie_status NFA_API_NO_IMPL
#ifdef __cplusplus
}
#endif // __cplusplus
#endif

116
src/nfa/castle_dump.cpp Normal file
View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2015, 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 Castle: multi-tenant repeat engine, dump code.
*/
#include "config.h"
#include "castle_dump.h"
#include "castle_internal.h"
#include "nfa_dump_internal.h"
#include "nfa_internal.h"
#include "shufticompile.h"
#include "trufflecompile.h"
#include "util/charreach.h"
#include "util/dump_charclass.h"
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
namespace ue2 {
void nfaExecCastle0_dumpDot(const struct NFA *, FILE *) {
// No GraphViz output for Castles.
}
static
void dumpTextSubCastle(const SubCastle &sub, FILE *f) {
const RepeatInfo *info =
(const RepeatInfo *)((const char *)&sub + sub.repeatInfoOffset);
fprintf(f, " repeat model: %s\n", repeatTypeName(info->type));
fprintf(f, " repeat bounds: {%u, %u}\n", info->repeatMin,
info->repeatMax);
fprintf(f, " min period: %u\n", info->minPeriod);
fprintf(f, " report: %u\n", sub.report);
fprintf(f, " full state offset: %u\n", sub.fullStateOffset);
fprintf(f, " stream state offset: %u\n", sub.streamStateOffset);
fprintf(f, "\n");
}
void nfaExecCastle0_dumpText(const struct NFA *nfa, FILE *f) {
const Castle *c = (const Castle *)getImplNfa(nfa);
fprintf(f, "Castle multi-tenant repeat engine\n");
fprintf(f, "\n");
fprintf(f, "Number of repeat tenants: %u\n", c->numRepeats);
fprintf(f, "Scan type: ");
switch (c->type) {
case CASTLE_DOT:
fprintf(f, "dot\n");
break;
case CASTLE_VERM:
fprintf(f, "verm, scanning for 0x%02x\n", c->u.verm.c);
break;
case CASTLE_NVERM:
fprintf(f, "negated verm, scanning for 0x%02x\n", c->u.verm.c);
break;
case CASTLE_SHUFTI: {
const CharReach cr = shufti2cr(c->u.shuf.mask_lo, c->u.shuf.mask_hi);
fprintf(f, "shufti, scanning for %s (%zu chars)\n",
describeClass(cr).c_str(), cr.count());
break;
}
case CASTLE_TRUFFLE: {
const CharReach cr = truffle2cr(c->u.truffle.mask1, c->u.truffle.mask2);
fprintf(f, "truffle, scanning for %s (%zu chars)\n",
describeClass(cr).c_str(), cr.count());
break;
}
default:
fprintf(f, "unknown type %u\n", c->type);
break;
}
fprintf(f, "\n");
dumpTextReverse(nfa, f);
fprintf(f, "\n");
const SubCastle *sub =
(const SubCastle *)((const char *)c + sizeof(Castle));
for (u32 i = 0; i < c->numRepeats; i++) {
fprintf(f, "Sub %u:\n", i);
dumpTextSubCastle(sub[i], f);
}
}
} // namespace ue2

47
src/nfa/castle_dump.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef CASTLE_DUMP_H
#define CASTLE_DUMP_H
#if defined(DUMP_SUPPORT)
#include <cstdio>
struct NFA;
namespace ue2 {
void nfaExecCastle0_dumpDot(const NFA *nfa, FILE *file);
void nfaExecCastle0_dumpText(const NFA *nfa, FILE *file);
} // namespace ue2
#endif // DUMP_SUPPORT
#endif

101
src/nfa/castle_internal.h Normal file
View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2015, 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 Castle: multi-tenant repeat engine, data structures.
*/
#ifndef NFA_CASTLE_INTERNAL_H
#define NFA_CASTLE_INTERNAL_H
#include "ue2common.h"
#include "repeat_internal.h"
struct SubCastle {
ReportID report; //!< report to raise on match
u32 fullStateOffset; //!< offset within full state (scratch)
u32 streamStateOffset; //!< offset within stream state
u32 repeatInfoOffset; //!< offset of RepeatInfo structure
// relative to the start of SubCastle
char exclusive; //!< exclusive info of this SubCastle
};
#define CASTLE_DOT 0
#define CASTLE_VERM 1
#define CASTLE_NVERM 2
#define CASTLE_SHUFTI 3
#define CASTLE_TRUFFLE 4
/**
* \brief Castle engine structure.
*
* A Castle is a collection of repeats that all share the same character
* reachability.
*
* The whole engine is laid out in memory as:
*
* - struct NFA
* - struct Castle
* - struct SubCastle[numRepeats]
* - tables for sparse model repeats
*
* Castle stores an "active repeats" multibit in stream state, followed by the
* packed repeat state for each SubCastle. If all SubCastles are mutual
* exclusive, we store current active SubCastle id instead of "active repeats"
* multibit in stream state. If there are both exclusive and non-exclusive
* SubCastle groups, we use an active id for the exclusive group and a multibit
* for the non-exclusive group.
*
* In full state (stored in scratch space) it stores a temporary multibit over
* the repeats (used by \ref castleMatchLoop), followed by the repeat control
* blocks for each SubCastle. If all SubCastles are mutual exclusive, we only
* need to store the repeat control blocks for each SubCastle.
*/
struct ALIGN_AVX_DIRECTIVE Castle {
u32 numRepeats;
u8 type; //!< tells us which scanning mechanism (below) to use
char exclusive; //!< tells us if there are mutual exclusive SubCastles
char pureExclusive; //!< tells us if all SubCastles are mutual exclusive
u8 activeIdxSize; //!< number of bytes in stream state to store
// active SubCastle id for exclusive mode
union {
struct {
char c;
} verm;
struct {
m128 mask_lo;
m128 mask_hi;
} shuf;
struct {
m128 mask1;
m128 mask2;
} truffle;
} u;
};
#endif // NFA_CASTLE_INTERNAL_H

1029
src/nfa/castlecompile.cpp Normal file

File diff suppressed because it is too large Load Diff

146
src/nfa/castlecompile.h Normal file
View File

@@ -0,0 +1,146 @@
/*
* Copyright (c) 2015, 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 Castle: multi-tenant repeat engine, compiler code.
*/
#ifndef NFA_CASTLECOMPILE_H
#define NFA_CASTLECOMPILE_H
#include "nfa_kind.h"
#include "ue2common.h"
#include "nfagraph/ng_repeat.h"
#include "util/alloc.h"
#include "util/depth.h"
#include <map>
#include <memory>
#include <set>
#include <vector>
struct NFA;
namespace ue2 {
class CharReach;
class NGHolder;
struct CompileContext;
/**
* \brief Prototype for a Castle engine: contains at least one CastleRepeat.
*
* Currently, all repeats in a Castle must have the same character
* reachability.
*
* A CastleProto is converted into a single NFA, with each top triggering a
* unique repeat. A CastleProto can contain at most CastleProto::max_occupancy
* elements.
*/
struct CastleProto {
static constexpr size_t max_occupancy = 65536; // arbitrary limit
explicit CastleProto(const PureRepeat &pr);
const CharReach &reach() const;
u32 add(const PureRepeat &pr);
/**
* \brief Merge in the given repeat, returning the top used.
*
* If the repeat already exists in this castle, we will re-use (and return)
* the old top. If it doesn't, it will be added and assigned a new top.
* Returns \ref max_occupancy if capacity would be exceeded.
*/
u32 merge(const PureRepeat &pr);
/** \brief Mapping from unique top id to repeat. */
std::map<u32, PureRepeat> repeats;
};
std::set<ReportID> all_reports(const CastleProto &proto);
depth findMinWidth(const CastleProto &proto);
depth findMaxWidth(const CastleProto &proto);
/**
* \brief Remap tops to be contiguous.
*
* Remap the tops in the given CastleProto so that they're contiguous in the
* range [0 .. N-1].
*/
void remapCastleTops(CastleProto &proto, std::map<u32, u32> &top_map);
/**
* \brief Construct an NFA from a CastleProto.
*
* NOTE: Tops must be contiguous, i.e. \ref remapCastleTops must have been run
* first.
*/
ue2::aligned_unique_ptr<NFA>
buildCastle(const CastleProto &proto,
const std::map<u32, std::vector<std::vector<CharReach>>> &triggers,
const CompileContext &cc);
/**
* \brief Merge two CastleProto prototypes together, if possible.
*
* Returns true if merge of all repeats in c2 into c1 succeeds, and fills
* mapping with the repeat indices.
*/
bool mergeCastle(CastleProto &c1, const CastleProto &c2,
std::map<u32, u32> &top_map);
/**
* \brief True if the two castles are identical with respect to the reports
* given; i.e. the same tops lead to the same repeats, just with report1 in c1
* and report2 in c2.
*
* Repeats leading to other reports are ignored.
*/
bool is_equal(const CastleProto &c1, ReportID report1, const CastleProto &c2,
ReportID report2);
/**
* \brief True if the two castles given are identical.
*/
bool is_equal(const CastleProto &c1, const CastleProto &c2);
/**
* \brief True if the given castle contains more than a single instance of any
* of the reports in the given set.
*/
bool requiresDedupe(const CastleProto &proto, const std::set<ReportID> &reports);
/**
* \brief Build an NGHolder from a CastleProto.
*/
std::unique_ptr<NGHolder> makeHolder(const CastleProto &castle, nfa_kind kind,
const CompileContext &cc);
} // namespace ue2
#endif // NFA_CASTLECOMPILE_H

351
src/nfa/dfa_min.cpp Normal file
View File

@@ -0,0 +1,351 @@
/*
* Copyright (c) 2015, 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 Build code for DFA minimization
*/
/**
* /Summary of the Hopcrofts algorithm/
* partition := {F, Q \ F};
* work_queue := {F};
* while (work_queue is not empty) do
* choose and remove a set A from work_queue
* for each c in . do
* let X be the set of states for which a transition on c
* leads to a state in A
* for each set Y in partition for which X . Y is nonempty and
* Y \ X is nonempty do
* replace Y in partition by the two sets X . Y and Y \ X
* if Y is in work_queue
* replace Y in work_queue by the same two sets
* else
* if |X . Y| <= |Y \ X|
* add X . Y to work_queue
* else
* add Y \ X to work_queue
* end;
* end;
* end;
*/
#include "dfa_min.h"
#include "grey.h"
#include "nfa/rdfa.h"
#include "nfagraph/ng_mcclellan.h"
#include "ue2common.h"
#include "util/partitioned_set.h"
#include "util/container.h"
#include "util/ue2_containers.h"
#include <algorithm>
#include <functional>
#include <map>
#include <set>
#include <vector>
#include <iterator>
#include <boost/core/noncopyable.hpp>
#include <boost/dynamic_bitset.hpp>
using namespace std;
namespace ue2 {
namespace {
struct hopcroft_state_info {
vector<vector<dstate_id_t> > prev;
};
struct DFA_components : boost::noncopyable {
dstate_id_t nstates;
size_t inp_size;
set<size_t> work_queue;
/*Partition contains reduced states*/
partitioned_set<dstate_id_t> partition;
vector<hopcroft_state_info> states;
explicit DFA_components(const raw_dfa &rdfa);
};
} //namespace
/**
* create_map:
* Creates an initial partitioning and work_queue.
* Initial partition contains {accepting states..., Non-accepting states}
* Initial work_queue contains accepting state subsets
*
* The initial partitioning needs to distinguish between the different
* reporting behaviours (unlike standard hopcroft) --> more than one subset
* possible for the accepting states.
*
* Look for accepting states in both reports and reports_eod.
* Creates a map with a key(reports, reports_eod) and an id.
* Reports of each state are searched against the map and
* added to the corresponding id -> partition[id] and work_queue[id].
* Non Accept states are added to partition[id+1].
*/
static
vector<size_t> create_map(const raw_dfa &rdfa, set<size_t> &work_queue) {
using ReportKey = pair<flat_set<ReportID>, flat_set<ReportID>>;
map<ReportKey, size_t> subset_map;
vector<size_t> state_to_subset(rdfa.states.size(), INVALID_SUBSET);
for (size_t i = 0; i < rdfa.states.size(); i++) {
if (!rdfa.states[i].reports.empty() ||
!rdfa.states[i].reports_eod.empty()) {
ReportKey key(rdfa.states[i].reports, rdfa.states[i].reports_eod);
if (contains(subset_map, key)) {
state_to_subset[i] = subset_map[key];
} else {
size_t sub = subset_map.size();
subset_map[key] = sub;
state_to_subset[i] = sub;
work_queue.insert(sub);
}
}
}
/* handle non accepts */
size_t non_accept_sub = subset_map.size();
for (size_t i = 0; i < state_to_subset.size(); i++) {
if (state_to_subset[i] == INVALID_SUBSET) {
state_to_subset[i] = non_accept_sub;
}
}
return state_to_subset;
}
DFA_components::DFA_components(const raw_dfa &rdfa)
: nstates(rdfa.states.size()),
inp_size(rdfa.states[nstates - 1].next.size()),
partition(create_map(rdfa, work_queue)) {
/* initializing states */
for (size_t i = 0; i < nstates; i++) {
states.push_back(hopcroft_state_info());
states.back().prev.resize(inp_size);
}
for (size_t i = 0; i < nstates; i++) { // i is the previous state
for (size_t j = 0; j < inp_size; j++) {
/* Creating X_table */
dstate_id_t present_state = rdfa.states[i].next[j];
states[present_state].prev[j].push_back(i);
DEBUG_PRINTF("rdfa.states[%zu].next[%zu] %hu \n", i, j,
rdfa.states[i].next[j]);
}
}
}
/**
* choose and remove a set A from work_queue.
*/
static
void get_work_item(DFA_components &mdfa, ue2::flat_set<dstate_id_t> &A) {
A.clear();
assert(!mdfa.work_queue.empty());
set<size_t>::iterator pt = mdfa.work_queue.begin();
insert(&A, mdfa.partition[*pt]);
mdfa.work_queue.erase(pt);
}
/**
* X is the set of states for which a transition on the input leads to a state
* in A.
*/
static
void create_X(const DFA_components &mdfa, const ue2::flat_set<dstate_id_t> &A,
size_t inp, ue2::flat_set<dstate_id_t> &X) {
X.clear();
for (dstate_id_t id : A) {
insert(&X, mdfa.states[id].prev[inp]);
}
}
/**
* For a split set X, each subset S (given by part_index) in the partition, two
* sets are created: v_inter (X intersection S) and v_sub (S - X).
*
* For each subset S in the partition that could be split (v_inter is nonempty
* and v_sub is nonempty):
* - replace S in partition by the two sets v_inter and v_sub.
* - if S is in work_queue:
* - replace S in work_queue by the two subsets.
* - else:
* - replace S in work_queue by the smaller of the two sets.
*/
static
void split_and_replace_set(const size_t part_index, DFA_components &mdfa,
const ue2::flat_set<dstate_id_t> &splitter) {
/* singleton sets cannot be split */
if (mdfa.partition[part_index].size() == 1) {
return;
}
size_t small_index = mdfa.partition.split(part_index, splitter);
if (small_index == INVALID_SUBSET) {
/* the set could not be split */
return;
}
/* larger subset remains at the input subset index, if the input subset was
* already in the work queue then the larger subset will remain there. */
mdfa.work_queue.insert(small_index);
}
/**
* The complete Hopcrofts algorithm is implemented in this function.
* Choose and remove a set tray from work_queue
* For each input- X is created.
* For each subset in the partition, split_and_replace_sets are called with the
* split set.
*/
static
void dfa_min(DFA_components &mdfa) {
ue2::flat_set<dstate_id_t> A, X;
vector<size_t> cand_subsets;
while (!mdfa.work_queue.empty()) {
get_work_item(mdfa, A);
for (size_t inp = 0; inp < mdfa.inp_size; inp++) {
create_X(mdfa, A, inp, X);
if (X.empty()) {
continue;
}
/* we only need to consider subsets with at least one member in X for
* splitting */
cand_subsets.clear();
mdfa.partition.find_overlapping(X, &cand_subsets);
for (size_t sub : cand_subsets) {
split_and_replace_set(sub, mdfa, X);
}
}
}
}
/**
* Creating new dfa table
* Map ordering contains key being an equivalence classes first state
* and the value being the equivalence class index.
* Eq_state[i] tells us new state id the equivalence class located at
* partition[i].
*/
static
void mapping_new_states(const DFA_components &mdfa,
vector<dstate_id_t> &old_to_new,
raw_dfa &rdfa) {
const size_t num_partitions = mdfa.partition.size();
// Mapping from equiv class's first state to equiv class index.
map<dstate_id_t, size_t> ordering;
// New state id for each equiv class.
vector<dstate_id_t> eq_state(num_partitions);
for (size_t i = 0; i < num_partitions; i++) {
ordering[*mdfa.partition[i].begin()] = i;
}
dstate_id_t new_id = 0;
for (const auto &m : ordering) {
eq_state[m.second] = new_id++;
}
for (size_t t = 0; t < mdfa.partition.size(); t++) {
for (dstate_id_t id : mdfa.partition[t]) {
old_to_new[id] = eq_state[t];
}
}
vector<dstate> new_states;
new_states.reserve(num_partitions);
for (size_t i = 0; i < mdfa.nstates; i++) {
if (contains(ordering, i)) {
new_states.push_back(rdfa.states[i]);
}
}
rdfa.states.swap(new_states);
}
static
void renumber_new_states(const DFA_components &mdfa,
const vector<dstate_id_t> &old_to_new,
raw_dfa &rdfa) {
for (size_t i = 0; i < mdfa.partition.size(); i++) {
for (size_t j = 0; j < mdfa.inp_size; j++) {
dstate_id_t output = rdfa.states[i].next[j];
rdfa.states[i].next[j] = old_to_new[output];
}
dstate_id_t dad = rdfa.states[i].daddy;
rdfa.states[i].daddy = old_to_new[dad];
}
rdfa.start_floating = old_to_new[rdfa.start_floating];
rdfa.start_anchored = old_to_new[rdfa.start_anchored];
}
static
void new_dfa(raw_dfa &rdfa, const DFA_components &mdfa) {
if (mdfa.partition.size() != mdfa.nstates) {
vector<dstate_id_t> old_to_new(mdfa.nstates);
mapping_new_states(mdfa, old_to_new, rdfa);
renumber_new_states(mdfa, old_to_new, rdfa);
}
}
/**
* MAIN FUNCTION
*/
void minimize_hopcroft(raw_dfa &rdfa, const Grey &grey) {
if (!grey.minimizeDFA) {
return;
}
UNUSED const size_t states_before = rdfa.states.size();
DFA_components mdfa(rdfa);
dfa_min(mdfa);
new_dfa(rdfa, mdfa);
DEBUG_PRINTF("reduced from %zu to %zu states\n", states_before,
rdfa.states.size());
}
} // namespace ue2

45
src/nfa/dfa_min.h Normal file
View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2015, 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 Build code for McClellan DFA.
*/
#ifndef DFA_MIN_H
#define DFA_MIN_H
namespace ue2 {
struct raw_dfa;
struct Grey;
void minimize_hopcroft(raw_dfa &rdfa, const Grey &grey);
} // namespace ue2
#endif

1153
src/nfa/gough.c Normal file

File diff suppressed because it is too large Load Diff

82
src/nfa/gough.h Normal file
View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef GOUGH_H
#define GOUGH_H
#include "callback.h"
#include "ue2common.h"
struct NFA;
struct mq;
// 8-bit Gough
char nfaExecGough8_testEOD(const struct NFA *nfa, const char *state,
const char *streamState, u64a offset,
NfaCallback callback, SomNfaCallback som_cb,
void *context);
char nfaExecGough8_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecGough8_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecGough8_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecGough8_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecGough8_inAccept(const struct NFA *n, ReportID report, struct mq *q);
char nfaExecGough8_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecGough8_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecGough8_queueCompressState(const struct NFA *nfa, const struct mq *q,
s64a loc);
char nfaExecGough8_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecGough8_B_Reverse NFA_API_NO_IMPL
#define nfaExecGough8_zombie_status NFA_API_NO_IMPL
// 16-bit Gough
char nfaExecGough16_testEOD(const struct NFA *nfa, const char *state,
const char *streamState, u64a offset,
NfaCallback callback, SomNfaCallback som_cb,
void *context);
char nfaExecGough16_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecGough16_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecGough16_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecGough16_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecGough16_inAccept(const struct NFA *n, ReportID report, struct mq *q);
char nfaExecGough16_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecGough16_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecGough16_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecGough16_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecGough16_B_Reverse NFA_API_NO_IMPL
#define nfaExecGough16_zombie_status NFA_API_NO_IMPL
#endif

134
src/nfa/gough_internal.h Normal file
View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef GOUGH_INTERNAL_H
#define GOUGH_INTERNAL_H
#include "accel.h"
#include "mcclellan_internal.h"
#include "ue2common.h"
#define INVALID_SLOT (~0U)
#define GOUGH_INS_END 0
#define GOUGH_INS_MOV 1
#define GOUGH_INS_NEW 2
#define GOUGH_INS_MIN 3
/* todo: add instructions targeting acc reg? */
struct gough_ins {
u32 op; /* u32 to avoid padding */
u32 dest;
u32 src; /* for GOUGH_INS_NEW, this specifies the adjustment to apply to the
* current offset */
};
/*
* HAPPY FUN ASCII ART TIME
*
* ----
* | | struct NFA
* ----
* ~~~~ normal(ish) mcclellan engine
* ~~~~
* ~~~~
* ~~~~
* ~~~~
* ~~~~
* ~~~~
* ~~~~
* ---- = m->haig_offset
* | | } struct gough_info
* ----
* | | }
* | | } edge prog table -> provides the offset of the start of the program
* | | } to run when the edge is taken. 0 indicates no
* | | } work to do
* ---- = h->top_prog_offset
* | | }
* | | } top prog table -> provides the offset of the start of the program
* | | } to run when a top is taken from this state. 0
* | | } indicates nothing to do
* ---- = h->prog_base_offset
* | | }
* | | } programs to run
* | | }
* | | }
* ----
*/
struct gough_info {
u32 top_prog_offset; /**< offset to the base of the top prog table */
u32 prog_base_offset; /**< not used at runtime */
u32 stream_som_loc_count; /**< number of som locs in the stream state */
u8 stream_som_loc_width; /**< number of bytes per som loc */
};
static really_inline
const struct gough_info *get_gough(const struct mcclellan *m) {
assert(m->haig_offset);
const char *n = (const char *)m - sizeof(struct NFA);
return (const struct gough_info *)(n + m->haig_offset);
}
static really_inline
const u32 *get_gough_top_offsets(const struct mcclellan *m) {
const struct gough_info *g = get_gough(m);
if (!g->top_prog_offset) {
return NULL;
}
const char *n = (const char *)m - sizeof(struct NFA);
return (const u32 *)(n + g->top_prog_offset);
}
/* Gough state representation in scratch.
*
* During execution, gough tracks a number of variables containing potential
* starts of match. These are all stored in a large array of u64a slots.
*/
struct gough_som_info {
u64a slots[1]; /* 'flexible' member array */
};
struct gough_report {
ReportID r;
u32 som; /* som slot to report */
};
struct gough_report_list {
u32 count;
struct gough_report report[];
};
struct gough_accel {
union AccelAux accel;
u8 margin_dist;
u32 prog_offset;
};
#endif

1320
src/nfa/goughcompile.cpp Normal file

File diff suppressed because it is too large Load Diff

93
src/nfa/goughcompile.h Normal file
View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef GOUGHCOMPILE_H
#define GOUGHCOMPILE_H
#include "mcclellancompile.h"
#include "nfa_kind.h"
#include "ue2common.h"
#include "util/alloc.h"
#include "util/ue2_containers.h"
#include "util/order_check.h"
#include <map>
#include <memory>
#include <set>
#include <vector>
namespace ue2 {
#define CREATE_NEW_SOM (~0U)
/* dest nfa state -> som info for dest state is min of provided loc idx som
* info */
typedef flat_map<u32, std::vector<u32>> som_tran_info;
struct som_report {
som_report(ReportID r, u32 s) : report(r), slot(s) {}
ReportID report;
u32 slot;
bool operator<(const som_report &b) const {
const som_report &a = *this;
ORDER_CHECK(report);
ORDER_CHECK(slot);
return false;
}
};
struct dstate_som {
std::set<som_report> reports;
std::set<som_report> reports_eod;
som_tran_info preds; /* live nfa states mapped back to pred states */
};
struct raw_som_dfa : public raw_dfa {
raw_som_dfa(nfa_kind k, bool unordered_som_triggers_in)
: raw_dfa(k), unordered_som_triggers(unordered_som_triggers_in) {
assert(!unordered_som_triggers || is_triggered(kind));
}
std::vector<dstate_som> state_som;
u32 stream_som_loc_width;
bool unordered_som_triggers;
void stripExtraEodReports(void) override;
std::map<u32, u32> new_som_nfa_states; /* map nfa vertex id -> offset */
u32 trigger_nfa_state; /* for triggered cases, slot_id that contains a new
* som */
};
aligned_unique_ptr<NFA> goughCompile(raw_som_dfa &raw, u8 somPrecision,
const CompileContext &cc);
} // namespace ue2
#endif

View File

@@ -0,0 +1,281 @@
/*
* Copyright (c) 2015, 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.
*/
#include "goughcompile_internal.h"
#include "gough_internal.h"
#include "grey.h"
#include "mcclellancompile.h"
#include "util/container.h"
#include "util/graph.h"
#include "util/graph_range.h"
#include "ue2common.h"
#include <map>
#include <vector>
using namespace std;
namespace ue2 {
template<typename Graph>
void add_edge_if_not_selfloop(const typename Graph::vertex_descriptor &u,
const typename Graph::vertex_descriptor &v,
Graph &g) {
if (u != v) {
add_edge(u, v, g);
}
}
static
bool can_accel_over_selfloop(const GoughVertexProps &vp, const GoughEdge &e,
const GoughEdgeProps &ep, u32 *margin) {
if (vp.vars.empty() && ep.vars.empty()) {
/* if we update no som information, then it is trivial to accelerate */
*margin = 0;
return true;
}
/* if the effect of running a self loop stabilises after a small number of
* iterations, it is possible to accelerate over the state and only then run
* the block N times. To model this we create a graph which shows how the
* value for a variable at the end of a self loop block is related to values
* at the start */
typedef boost::adjacency_list<boost::vecS, boost::vecS,
boost::bidirectionalS> basic_graph;
typedef basic_graph::vertex_descriptor basic_vertex;
basic_graph bg;
map<const GoughSSAVar *, basic_vertex> verts;
/* create verts */
for (const auto &var : ep.vars) {
verts[var.get()] = add_vertex(bg);
}
for (const auto &var : vp.vars) {
verts[var.get()] = add_vertex(bg);
}
/* wire edges */
set<basic_vertex> done;
for (const auto &var : ep.vars) {
assert(contains(verts, var.get()));
basic_vertex v = verts[var.get()];
for (GoughSSAVar *pred : var->get_inputs()) {
if (!contains(verts, pred)) {
continue;
}
basic_vertex u = verts[pred];
if (contains(done, u)) { /* u has already taken on new values this
* iteration */
for (auto p : inv_adjacent_vertices_range(u, bg)) {
add_edge_if_not_selfloop(p, v, bg);
}
} else {
add_edge_if_not_selfloop(u, v, bg);
}
}
done.insert(v);
}
for (const auto &var : vp.vars) {
GoughSSAVar *pred = var->get_input(e);
assert(contains(verts, var.get()));
basic_vertex v = verts[var.get()];
if (!contains(verts, pred)) {
continue;
}
basic_vertex u = verts[pred];
if (contains(done, u)) { /* u has already taken on new values this
* iteration */
for (auto p : inv_adjacent_vertices_range(u, bg)) {
add_edge_if_not_selfloop(p, v, bg);
}
} else {
add_edge_if_not_selfloop(u, v, bg);
}
/* do not add v to done as all joins happen in parallel */
}
/* check for loops - non self loops may prevent settling */
if (!is_dag(bg)) {
DEBUG_PRINTF("can not %u accel as large loops\n", vp.state_id);
return false;
}
*margin = num_vertices(bg); /* TODO: be less conservative */
if (*margin > 50) {
return false;
}
return true;
}
static
bool verify_neighbour(const GoughGraph &g, GoughVertex u,
const map<gough_edge_id, vector<gough_ins> > &blocks,
const set<GoughVertex> &succs,
const vector<gough_ins> &block_sl) {
for (const auto &e : out_edges_range(u, g)) {
if (!g[e].reach.any()) { /* ignore top edges */
continue;
}
GoughVertex t = target(e, g);
if (!contains(succs, t)) { /* must be an escape string */
continue;
}
if (!contains(blocks, gough_edge_id(g, e))) {
return false;
}
if (blocks.at(gough_edge_id(g, e)) != block_sl) {
return false;
}
}
return true;
}
static
bool verify_neighbour_no_block(const GoughGraph &g, GoughVertex u,
const map<gough_edge_id, vector<gough_ins> > &blocks,
const set<GoughVertex> &succs) {
for (const auto &e : out_edges_range(u, g)) {
if (!g[e].reach.any()) { /* ignore top edges */
continue;
}
GoughVertex t = target(e, g);
if (!contains(succs, t)) { /* must be an escape string */
continue;
}
if (contains(blocks, gough_edge_id(g, e))) {
return false;
}
}
return true;
}
/* Checks the som aspects of allowing two byte accel - it is expected that the
* mcclellan logic will identify escape strings.
*
* For 2 byte acceleration to be correct we require that any non-escape sequence
* characters xy from the accel state has the same effect as just the character
* of y.
*
* The current way of ensuring this is to require:
* (a) all edges out of the cyclic state behave identically to the cyclic self
* loop edge
* (b) edges out of the neighbouring state which do not correspond to escape
* string behave identical to the cyclic state edges.
*
* TODO: these restrictions could be relaxed by looking at the effect on
* relevant (live?) vars only, allowing additions to the escape string set, and
* considering one byte escapes.
*/
static
bool allow_two_byte_accel(const GoughGraph &g,
const map<gough_edge_id, vector<gough_ins> > &blocks,
GoughVertex v, const GoughEdge &self_loop) {
if (contains(blocks, gough_edge_id(g, self_loop))) {
DEBUG_PRINTF("edge plan on self loop\n");
const auto &block_sl = blocks.at(gough_edge_id(g, self_loop));
set<GoughVertex> succs;
for (const auto &e : out_edges_range(v, g)) {
if (g[e].reach.none()) { /* ignore top edges */
continue;
}
gough_edge_id ged(g, e);
if (!contains(blocks, ged) || blocks.at(ged) != block_sl) {
DEBUG_PRINTF("different out-edge behaviour\n");
return false;
}
succs.insert(target(e, g));
}
for (auto w : adjacent_vertices_range(v, g)) {
if (w != v && !verify_neighbour(g, w, blocks, succs, block_sl)) {
return false;
}
}
} else {
DEBUG_PRINTF("no edge plan on self loop\n");
set<GoughVertex> succs;
for (const auto &e : out_edges_range(v, g)) {
if (g[e].reach.none()) { /* ignore top edges */
continue;
}
gough_edge_id ged(g, e);
if (contains(blocks, ged)) {
DEBUG_PRINTF("different out-edge behaviour\n");
return false;
}
succs.insert(target(e, g));
for (auto w : adjacent_vertices_range(v, g)) {
if (w != v && !verify_neighbour_no_block(g, w, blocks, succs)) {
return false;
}
}
}
}
DEBUG_PRINTF("allowing two byte accel for %u\n", g[v].state_id);
return true;
}
void find_allowed_accel_states(const GoughGraph &g,
const map<gough_edge_id, vector<gough_ins> > &blocks,
map<dstate_id_t, gough_accel_state_info> *out) {
for (auto v : vertices_range(g)) {
GoughEdge e;
if (!find_normal_self_loop(v, g, &e)) {
continue; /* not accelerable */
}
u32 margin = 0;
if (!can_accel_over_selfloop(g[v], e, g[e], &margin)) {
continue; /* not accelerable */
}
bool tba = allow_two_byte_accel(g, blocks, v, e);
out->emplace(g[v].state_id, gough_accel_state_info(margin, tba));
}
}
} // namespace ue2

View File

@@ -0,0 +1,334 @@
/*
* Copyright (c) 2015, 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.
*/
#include "config.h"
#include "goughcompile_dump.h"
#include "goughcompile_internal.h"
#include "grey.h"
#include "util/container.h"
#include "util/graph_range.h"
#include <string>
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
using namespace std;
namespace ue2 {
string dump_name(const GoughVertexProps &vp) {
stringstream ss;
ss << "vertex_" << vp.state_id;
return ss.str();
}
static
string dump_name(const GoughGraph &g, const GoughEdge &e) {
stringstream ss;
ss << "edge_" << g[source(e, g)].state_id << "_"
<< g[target(e, g)].state_id;
return ss.str();
}
string dump_name(const gough_edge_id &e) {
stringstream ss;
ss << "edge_" << e.src << "_" << e.dest;
return ss.str();
}
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
set<const GoughSSAVar *> uses(const GoughVertexProps &vp) {
set<const GoughSSAVar *> rv;
for (const auto &r : vp.reports) {
if (r.second) {
rv.insert(r.second);
}
}
for (const auto &r : vp.reports_eod) {
if (r.second) {
rv.insert(r.second);
}
}
for (const auto &var : vp.vars) {
insert(&rv, var->get_inputs());
}
return rv;
}
static
set<const GoughSSAVar *> uses(const GoughEdgeProps &ep) {
set<const GoughSSAVar *> rv;
for (const auto &var : ep.vars) {
insert(&rv, var->get_inputs());
}
return rv;
}
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 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 dump_vars(const GoughGraph &g, const string &base, const Grey &grey) {
FILE *f;
{
stringstream ss;
ss << grey.dumpPath << "gough_" << base << "_vars.dot";
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");
vector<const GoughSSAVar *> vars;
map<const GoughSSAVar *, string> names;
map<const GoughSSAVar *, string> src_label;
set<const GoughSSAVar *> reporters;
gather_vars(g, &vars, &names, &src_label, &reporters);
for (const GoughSSAVar *vp : vars) {
fprintf(f, "%s [ width = 1, fixedsize = true, fontsize = 12, ",
names[vp].c_str());
fprintf(f, "label = \"%s\\n", src_label[vp].c_str());
if (dynamic_cast<const GoughSSAVarMin *>(vp)) {
fprintf(f, "MIN");
} else if (dynamic_cast<const GoughSSAVarJoin *>(vp)) {
fprintf(f, "JOIN");
} else if (dynamic_cast<const GoughSSAVarNew *>(vp)) {
fprintf(f, "NEW");
} else {
fprintf(f, "???");
}
fprintf(f, "\"];\n");
}
for (const GoughSSAVar *vp : reporters) {
if (vp) {
fprintf(f, "%s [ shape = doublecircle]\n", names[vp].c_str());
} else {
fprintf(f, "eps [ label = \"eps\" shape = doublecircle]\n");
}
}
for (const GoughSSAVar *vp : vars) {
const flat_set<GoughSSAVar *> &inputs = vp->get_inputs();
for (const GoughSSAVar *v_in : inputs) {
fprintf(f, "%s -> %s\n", names[v_in].c_str(), names[vp].c_str());
}
}
fprintf(f, "}\n");
fclose(f);
}
void dump(const GoughGraph &g, const string &base, const Grey &grey) {
if (!grey.dumpFlags) {
return;
}
dump_graph(g, base, grey);
dump_var_mapping(g, base, grey);
dump_vars(g, base, grey);
}
static
void dump_block(FILE *f, const gough_edge_id &e,
const vector<gough_ins> &block) {
fprintf(f, "%s:\n", dump_name(e).c_str());
for (const gough_ins &ins : block) {
fprintf(f, "\t");
switch (ins.op) {
case GOUGH_INS_END:
fprintf(f, "END");
break;
case GOUGH_INS_MOV:
fprintf(f, "MOV %u %u", ins.dest, ins.src);
break;
case GOUGH_INS_NEW:
fprintf(f, "NEW %u (+%u)", ins.dest, ins.src);
break;
case GOUGH_INS_MIN:
fprintf(f, "MIN %u %u", ins.dest, ins.src);
break;
default:
fprintf(f, "<UNKNOWN>");
break;
}
fprintf(f, "\n");
}
}
void dump_blocks(const map<gough_edge_id, vector<gough_ins> > &blocks,
const string &base, const Grey &grey) {
if (!grey.dumpFlags) {
return;
}
FILE *f;
{
stringstream ss;
ss << grey.dumpPath << "gough_" << base << "_programs.txt";
f = fopen(ss.str().c_str(), "w");
}
for (const auto &m : blocks) {
dump_block(f, m.first, m.second);
}
fclose(f);
}
} // namespace ue2

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef GOUGHCOMPILE_DUMP_H
#define GOUGHCOMPILE_DUMP_H
#include "goughcompile_internal.h"
#include <map>
#include <string>
namespace ue2 {
struct Grey;
#ifdef DUMP_SUPPORT
std::string dump_name(const GoughVertexProps &vp);
std::string dump_name(const gough_edge_id &e);
void dump(const GoughGraph &g, const std::string &base, const Grey &grey);
void dump_blocks(const std::map<gough_edge_id, std::vector<gough_ins> > &blocks,
const std::string &base, const Grey &grey);
#else
static UNUSED
void dump(UNUSED const GoughGraph &g, UNUSED const std::string &base,
UNUSED const Grey &grey) {
}
static UNUSED
void dump_blocks(
UNUSED const std::map<gough_edge_id, std::vector<gough_ins> > &blocks,
UNUSED const std::string &base, UNUSED const Grey &grey) {
}
#endif
} // namespace ue2
#endif

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef GOUGHCOMPILE_INTERNAL_H
#define GOUGHCOMPILE_INTERNAL_H
#include "gough_internal.h"
#include "mcclellancompile.h"
#include "ue2common.h"
#include "util/charreach.h"
#include "util/order_check.h"
#include "util/ue2_containers.h"
#include <map>
#include <memory>
#include <set>
#include <vector>
#include <boost/core/noncopyable.hpp>
#include <boost/graph/adjacency_list.hpp>
namespace ue2 {
struct Grey;
struct GoughSSAVar;
struct GoughSSAVarJoin;
struct GoughVertexProps {
GoughVertexProps() {}
explicit GoughVertexProps(u32 state_in) : state_id(state_in) {}
u32 state_id = ~0U;
std::vector<std::shared_ptr<GoughSSAVarJoin> > vars; /* owns variables */
std::vector<std::pair<ReportID, GoughSSAVar *> > reports; /**< report som,
som variable */
std::vector<std::pair<ReportID, GoughSSAVar *> > reports_eod;
};
struct GoughEdgeProps {
GoughEdgeProps(void) : top(false) {}
bool top;
CharReach reach;
std::vector<std::shared_ptr<GoughSSAVar> > vars; /* owns variables */
};
struct GoughGraphProps {
boost::adjacency_list_traits<boost::vecS, boost::vecS>::vertex_descriptor
initial_vertex; /* for triggered nfas, dead state;
* for others start anchored or start floating
*/
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
GoughVertexProps, GoughEdgeProps, GoughGraphProps> GoughGraph;
typedef GoughGraph::vertex_descriptor GoughVertex;
typedef GoughGraph::edge_descriptor GoughEdge;
struct gough_edge_id {
gough_edge_id(const GoughGraph &g, const GoughEdge &e)
: src(g[source(e, g)].state_id), dest(g[target(e, g)].state_id),
first_char(g[e].reach.find_first()) {}
bool operator<(const gough_edge_id &b) const {
const gough_edge_id &a = *this;
ORDER_CHECK(src);
ORDER_CHECK(dest);
ORDER_CHECK(first_char);
return false;
}
const u32 src;
const u32 dest;
const u32 first_char; /* ~0U if only top */
};
struct GoughSSAVarWithInputs;
struct GoughSSAVarMin;
struct GoughSSAVarJoin;
struct GoughSSAVar : boost::noncopyable {
GoughSSAVar(void) : seen(false), slot(INVALID_SLOT) {}
virtual ~GoughSSAVar();
const ue2::flat_set<GoughSSAVar *> &get_inputs() const {
return inputs;
}
const ue2::flat_set<GoughSSAVarWithInputs *> &get_outputs() const {
return outputs;
}
virtual void replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) = 0;
virtual void generate(std::vector<gough_ins> *out) const = 0;
bool seen; /* for temp use by remove_dead alg */
u32 slot;
void clear_outputs();
/** remove all inputs and outputs of the vertex, call before
* removing vertex */
virtual void clear_all() {
clear_outputs();
}
protected:
ue2::flat_set<GoughSSAVar *> inputs;
ue2::flat_set<GoughSSAVarWithInputs *> outputs;
friend struct GoughSSAVarWithInputs;
friend struct GoughSSAVarMin;
friend struct GoughSSAVarJoin;
};
struct GoughSSAVarNew : public GoughSSAVar {
explicit GoughSSAVarNew(u32 adjust_in) : adjust(adjust_in) {}
void replace_input(GoughSSAVar *, GoughSSAVar *) override {
assert(0);
}
void generate(std::vector<gough_ins> *out) const override;
const u32 adjust;
};
struct GoughSSAVarWithInputs : public GoughSSAVar {
GoughSSAVarWithInputs(void) {}
void replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) override = 0;
virtual void clear_inputs() = 0;
void clear_all() override;
protected:
virtual void remove_input_raw(GoughSSAVar *v) = 0;
friend struct GoughSSAVar;
};
struct GoughSSAVarMin : public GoughSSAVarWithInputs {
GoughSSAVarMin(void) {}
void generate(std::vector<gough_ins> *out) const override;
void clear_inputs() override;
void replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) override;
virtual void add_input(GoughSSAVar *v) {
inputs.insert(v);
v->outputs.insert(this);
}
protected:
void remove_input_raw(GoughSSAVar *v) override;
};
struct GoughSSAVarJoin : public GoughSSAVarWithInputs {
GoughSSAVarJoin(void) {}
/* dummy; all joins at a point must be generated simultaneously */
void generate(std::vector<gough_ins> *out) const override;
GoughSSAVar *get_input(const GoughEdge &prev) const;
void clear_inputs() override;
void replace_input(GoughSSAVar *old_v, GoughSSAVar *new_v) override;
void add_input(GoughSSAVar *v, GoughEdge prev);
const ue2::flat_set<GoughEdge> &get_edges_for_input(GoughSSAVar *input)
const;
const std::map<GoughSSAVar *, ue2::flat_set<GoughEdge> > &get_input_map()
const;
protected:
void remove_input_raw(GoughSSAVar *v) override;
private:
std::map<GoughSSAVar *, ue2::flat_set<GoughEdge>> input_map;
};
struct gough_accel_state_info {
u32 margin;
bool two_byte;
gough_accel_state_info(u32 margin_in, bool two_byte_in)
: margin(margin_in), two_byte(two_byte_in) {
}
};
u32 assign_slots(GoughGraph &g, const Grey &grey);
void find_allowed_accel_states(const GoughGraph &g,
const std::map<gough_edge_id, std::vector<gough_ins> > &blocks,
std::map<dstate_id_t, gough_accel_state_info> *out);
bool find_normal_self_loop(GoughVertex v, const GoughGraph &g, GoughEdge *out);
} // namespace ue2
// Note: C structure, can't be in namespace ue2
static inline
bool operator==(const gough_ins &a, const gough_ins &b) {
return a.op == b.op && a.dest == b.dest && a.src == b.src;
}
static inline
bool operator<(const gough_ins &a, const gough_ins &b) {
return std::tie(a.op, a.src, a.dest) < std::tie(b.op, b.src, b.dest);
}
#endif

View File

@@ -0,0 +1,502 @@
/*
* Copyright (c) 2015, 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.
*/
#include "goughcompile.h"
#include "goughcompile_dump.h"
#include "goughcompile_internal.h"
#include "gough_internal.h"
#include "grey.h"
#include "util/container.h"
#include "util/graph.h"
#include "util/graph_range.h"
#include "util/order_check.h"
#include "util/ue2_containers.h"
#include "ue2common.h"
#include <algorithm>
#include <boost/graph/depth_first_search.hpp>
#include <boost/range/adaptor/map.hpp>
using namespace std;
using boost::adaptors::map_values;
namespace ue2 {
template<typename VarP, typename VarQ>
void push_back_all_raw(vector<VarP> *out, const vector<VarQ> &in) {
for (const auto &var : in) {
out->push_back(var.get());
}
}
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);
}
}
namespace {
struct GoughGraphAux {
map<const GoughSSAVar *, GoughVertex> containing_v;
map<const GoughSSAVar *, GoughEdge> containing_e;
map<const GoughSSAVar *, set<GoughVertex> > reporters;
};
}
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
bool is_block_local(const GoughGraph &cfg, GoughSSAVar *var,
const GoughGraphAux &aux) {
/* if var used as a report, it cannot be considered block local */
if (contains(aux.reporters, var)) {
return false;
}
/* (useful) vertex/join vars never local - they are terminal in blocks
* and so should be read by another block. */
if (!contains(aux.containing_e, var)) {
return false;
}
/* for other cases, require that all uses of var are later in the same edge
* or on the target AND if on target it is sole on flow coming from the
* edge in question. */
const GoughEdge &e = aux.containing_e.at(var);
GoughVertex t = target(e, cfg);
size_t seen_outputs = 0;
const flat_set<GoughSSAVarWithInputs *> &out = var->get_outputs();
bool seen_var = false;
for (const auto &e_var : cfg[e].vars) {
if (seen_var) {
GoughSSAVarWithInputs *w
= dynamic_cast<GoughSSAVarWithInputs *>(e_var.get());
if (contains(out, w)) {
seen_outputs++;
}
} else {
seen_var = var == e_var.get();
}
}
assert(seen_var);
for (const auto &t_var : cfg[t].vars) {
if (contains(out, t_var.get())) {
seen_outputs++;
const flat_set<GoughEdge> &flow = t_var->get_edges_for_input(var);
if (flow.size() != 1 || *flow.begin() != e) {
/* this var is used by the target join var BUT on a different
* flow, so this is not a block local variable */
return false;
}
}
}
assert(seen_outputs <= out.size());
return seen_outputs == out.size();
}
static
void handle_pending_edge(const GoughGraph &g, const GoughEdge &e,
GoughSSAVar *start, set<GoughVertex> &pending_vertex,
set<const GoughSSAVar *> &rv) {
const vector<shared_ptr<GoughSSAVar> > &vars = g[e].vars;
bool marking = !start;
DEBUG_PRINTF(" ---checking edge %u->%u %s %zu\n", g[source(e, g)].state_id,
g[target(e, g)].state_id, marking ? "full" : "partial",
vars.size());
for (auto it = vars.rbegin(); it != vars.rend(); ++it) {
GoughSSAVar *var = it->get();
if (contains(rv, var)) {
DEBUG_PRINTF("somebody has already processed this vertex [%u]\n",
var->slot);
return;
}
if (var == start) {
assert(!marking);
marking = true;
continue;
}
if (marking) {
rv.insert(var);
}
}
assert(marking);
GoughVertex s = source(e, g);
for (const auto &var : g[s].vars) {
DEBUG_PRINTF("interferes %u\n", var->slot);
rv.insert(var.get());
}
pending_vertex.insert(s);
}
static
void handle_pending_vars(GoughSSAVar *def, const GoughGraph &g,
const GoughGraphAux &aux,
const flat_set<GoughSSAVarWithInputs *> &pending_var,
set<GoughVertex> &pending_vertex,
set<const GoughSSAVar *> &rv) {
for (GoughSSAVarWithInputs *var : pending_var) {
if (contains(aux.containing_v, var)) {
/* def is used by join vertex, value only needs to be live on some
* incoming edges */
GoughSSAVarJoin *vj = (GoughSSAVarJoin *)var;
const flat_set<GoughEdge> &live_edges
= vj->get_edges_for_input(def);
for (const auto &e : live_edges) {
handle_pending_edge(g, e, nullptr, pending_vertex, rv);
}
continue;
}
const GoughEdge &e = aux.containing_e.at(var);
handle_pending_edge(g, e, var, pending_vertex, rv);
}
}
static
void handle_pending_vertex(GoughVertex def_v, const GoughGraph &g,
GoughVertex current,
set<GoughVertex> &pending_vertex,
set<const GoughSSAVar *> &rv) {
DEBUG_PRINTF("---checking vertex %u\n", g[current].state_id);
if (def_v == current) {
DEBUG_PRINTF("contains target vertex\n");
return; /* we have reached def */
}
for (const auto &e : in_edges_range(current, g)) {
handle_pending_edge(g, e, nullptr, pending_vertex, rv);
}
}
static
void handle_pending_vertices(GoughSSAVar *def, const GoughGraph &g,
const GoughGraphAux &aux,
set<GoughVertex> &pending_vertex,
set<const GoughSSAVar *> &rv) {
if (pending_vertex.empty()) {
return;
}
GoughVertex def_v = GoughGraph::null_vertex();
if (contains(aux.containing_v, def)) {
def_v = aux.containing_v.at(def);
}
ue2::unordered_set<GoughVertex> done;
while (!pending_vertex.empty()) {
GoughVertex current = *pending_vertex.begin();
pending_vertex.erase(current);
if (contains(done, current)) {
continue;
}
done.insert(current);
handle_pending_vertex(def_v, g, current, pending_vertex, rv);
}
}
/* returns set of labels that the given def is live at */
static never_inline
set<const GoughSSAVar *> live_during(GoughSSAVar *def, const GoughGraph &g,
const GoughGraphAux &aux) {
DEBUG_PRINTF("checking who is defined during %u lifetime\n", def->slot);
set<GoughVertex> pending_vertex;
set<const GoughSSAVar *> rv;
rv.insert(def);
if (contains(aux.reporters, def)) {
DEBUG_PRINTF("--> gets reported\n");
const set<GoughVertex> &reporters = aux.reporters.at(def);
for (auto v : reporters) {
pending_vertex.insert(v);
for (const auto &var : g[v].vars) {
DEBUG_PRINTF("interferes %u\n", var->slot);
rv.insert(var.get());
}
}
}
handle_pending_vars(def, g, aux, def->get_outputs(), pending_vertex, rv);
handle_pending_vertices(def, g, aux, pending_vertex, rv);
rv.erase(def);
return rv;
}
template<typename VarP>
void set_initial_slots(const vector<VarP> &vars, u32 *next_slot) {
for (auto &var : vars) {
assert(var->slot == INVALID_SLOT);
var->slot = (*next_slot)++;
}
}
/* 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;
}
#define NO_COLOUR (~0U)
static
u32 available_colour(const flat_set<u32> &bad_colours) {
u32 rv = 0;
for (const u32 &colour : bad_colours) {
if (colour != rv) {
assert(colour > rv);
break;
}
rv = colour + 1;
}
assert(rv != NO_COLOUR);
return rv;
}
static
void poison_colours(const set<const GoughSSAVar *> &live, u32 c,
const vector<u32> &colour_map,
vector<flat_set<u32> > *bad_colour) {
for (const GoughSSAVar *var : live) {
u32 var_index = var->slot;
if (colour_map[var_index] != NO_COLOUR) {
assert(c != colour_map[var_index]);
} else {
(*bad_colour)[var_index].insert(c);
}
}
}
static
void find_bad_due_to_live(const set<const GoughSSAVar *> &live,
const vector<u32> &colour_map, flat_set<u32> *out) {
for (const GoughSSAVar *var : live) {
u32 var_index = var->slot;
if (colour_map[var_index] != NO_COLOUR) {
out->insert(colour_map[var_index]);
}
}
}
static
void sequential_vertex_colouring(const GoughGraph &g, const GoughGraphAux &aux,
const vector<GoughSSAVar *> &order,
vector<u32> &colour_map) {
assert(order.size() < NO_COLOUR);
colour_map.clear();
colour_map.resize(order.size(), NO_COLOUR);
vector<u32> temp(order.size(), ~0U);
vector<flat_set<u32> > bad_colour(order.size());
for (GoughSSAVar *var : order) {
u32 var_index = var->slot;
if (is_block_local(g, var, aux)) {
DEBUG_PRINTF("%u is block local\n", var_index);
/* ignore variable whose lifetime is limited to their local block
* there is no need to assign stream state to these variables */
continue;
}
assert(colour_map[var_index] == NO_COLOUR);
set<const GoughSSAVar *> live = live_during(var, g, aux);
flat_set<u32> &local_bad = bad_colour[var_index];
find_bad_due_to_live(live, colour_map, &local_bad);
DEBUG_PRINTF("colouring %u\n", var_index);
u32 c = available_colour(local_bad);
colour_map[var_index] = c;
assert(!contains(bad_colour[var_index], c));
poison_colours(live, c, colour_map, &bad_colour);
flat_set<u32> temp_set;
local_bad.swap(temp_set);
DEBUG_PRINTF(" %u coloured %u\n", var_index, c);
}
}
template<typename VarP>
void add_to_dom_ordering(const vector<VarP> &vars,
vector<GoughSSAVar *> *out) {
for (const auto &var : vars) {
out->push_back(var.get());
}
}
namespace {
class FinishVisitor : public boost::default_dfs_visitor {
public:
explicit FinishVisitor(vector<GoughVertex> *o) : out(o) {}
void finish_vertex(const GoughVertex v, const GoughGraph &) {
out->push_back(v);
}
vector<GoughVertex> *out;
};
}
static
void find_dom_ordering(const GoughGraph &cfg, vector<GoughSSAVar *> *out) {
vector<GoughVertex> g_order;
/* due to construction quirks, default vertex order provides entry points */
depth_first_search(cfg, visitor(FinishVisitor(&g_order))
.root_vertex(cfg[boost::graph_bundle].initial_vertex));
for (auto it = g_order.rbegin(); it != g_order.rend(); ++it) {
add_to_dom_ordering(cfg[*it].vars, out);
for (const auto &e : out_edges_range(*it, cfg)) {
add_to_dom_ordering(cfg[e].vars, out);
}
}
}
static
void create_slot_mapping(const GoughGraph &cfg, UNUSED u32 old_slot_count,
vector<u32> *old_new) {
/* Interference graphs from SSA form are chordal -> optimally colourable in
* poly time.
*
* Chordal graphs can be coloured by walking in perfect elimination order.
* If the SSA CFG is iterated over in a way that respects dominance
* relationship, the interference graph will be iterated in a perfect
* elimination order.
*
* We can avoid creating the full interference graph and use liveness
* information as we iterate over the definitions to perform the colouring.
*
* See S Hack various 2006-
*/
vector<GoughSSAVar *> dom_order;
GoughGraphAux aux;
fill_aux(cfg, &aux);
find_dom_ordering(cfg, &dom_order);
assert(dom_order.size() == old_slot_count);
sequential_vertex_colouring(cfg, aux, dom_order, *old_new);
}
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());
}
static never_inline
u32 update_slots(GoughGraph &g, const vector<u32> &old_new,
UNUSED u32 old_slot_count) {
vector<GoughSSAVar *> vars;
set<GoughSSAVar *> locals;
all_vars(g, &vars);
u32 slot_count = 0;
for (GoughSSAVar *v : vars) {
assert(v->slot < old_new.size());
DEBUG_PRINTF("updating slot %u to %u\n", v->slot, old_new[v->slot]);
if (old_new[v->slot] != NO_COLOUR) { /* not local, assign final slot */
v->slot = old_new[v->slot];
ENSURE_AT_LEAST(&slot_count, v->slot + 1);
} else {
locals.insert(v);
}
}
assert(slot_count <= old_slot_count);
DEBUG_PRINTF("reduce stream slots from %u to %u\n", old_slot_count,
slot_count);
update_local_slots(g, locals, slot_count);
return slot_count;
}
u32 assign_slots(GoughGraph &cfg, const Grey &grey) {
u32 slot_count = initial_slots(cfg);
if (!grey.goughRegisterAllocate) {
return slot_count;
}
dump(cfg, "slots_pre", grey);
vector<u32> old_new;
create_slot_mapping(cfg, slot_count, &old_new);
slot_count = update_slots(cfg, old_new, slot_count);
return slot_count;
}
} // namespace ue2

349
src/nfa/goughdump.cpp Normal file
View File

@@ -0,0 +1,349 @@
/*
* Copyright (c) 2015, 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.
*/
#include "config.h"
#include "goughdump.h"
#include "gough_internal.h"
#include "mcclellandump.h"
#include "nfa_dump_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/charreach.h"
#include "util/dump_charclass.h"
#include "util/unaligned.h"
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
using namespace std;
namespace ue2 {
static
void goughGetTransitions(const NFA *n, u16 s, u16 *t) {
assert(isGoughType(n->type));
const mcclellan *m = (const mcclellan *)getImplNfa(n);
const mstate_aux *aux = getAux(n, s);
const u32 as = m->alphaShift;
const char *sher_base
= (const char *)m - sizeof(struct NFA) + m->sherman_offset;
if (n->type == GOUGH_NFA_8) {
const u8 *succ_table = (const u8 *)((const char *)m + sizeof(mcclellan));
for (u16 c = 0; c < N_CHARS; c++) {
t[c] = succ_table[((u32)s << as) + m->remap[c]];
}
} else {
u16 base_s = s;
if (s >= m->sherman_limit) {
const char *state_base
= findShermanState(m, sher_base, m->sherman_limit, s);
base_s = *(const u16 *)(state_base + SHERMAN_DADDY_OFFSET);
}
const u16 *succ_table = (const u16 *)((const char *)m
+ sizeof(mcclellan));
for (u16 c = 0; c < N_CHARS; c++) {
const u8 *addr
= (const u8*)(succ_table + (((u32)base_s << as) + m->remap[c]));
t[c] = unaligned_load_u16(addr);
t[c] &= STATE_MASK;
}
if (s >= m->sherman_limit) {
const char *state_base
= findShermanState(m, sher_base, m->sherman_limit, s);
u8 len = *(const u8 *)(SHERMAN_LEN_OFFSET + state_base);
const u8 *chars = (const u8 *)state_base + SHERMAN_CHARS_OFFSET;
const u16 *states
= (const u16 *)(state_base + SHERMAN_STATES_OFFSET(len));
for (u8 i = 0; i < len; i++) {
for (u16 c = 0; c < N_CHARS; c++) {
if (m->remap[c] != chars[i]) {
t[c] = unaligned_load_u16((const u8*)&states[i])
& STATE_MASK;
}
}
}
}
}
t[TOP] = aux->top & STATE_MASK;
}
static
void describeNode(const NFA *n, const mcclellan *m, u16 i, FILE *f) {
const mstate_aux *aux = getAux(n, i);
bool isSherman = m->sherman_limit && i >= m->sherman_limit;
const char *sher_base
= (const char *)m - sizeof(NFA) + m->sherman_offset;
fprintf(f, "%u [ width = 1, fixedsize = true, fontsize = 12, "
"label = \"%u%s\" ]; \n", i, i, isSherman ? "w":"");
if (aux->accel_offset) {
dumpAccelDot(f, i,
&((const gough_accel *)((const char *)m + aux->accel_offset))->accel);
}
if (aux->accept_eod) {
fprintf(f, "%u [ color = darkorchid ];\n", i);
}
if (aux->accept) {
fprintf(f, "%u [ shape = doublecircle ];\n", i);
}
if (aux->top && aux->top != i) {
fprintf(f, "%u -> %u [color = darkgoldenrod weight=0.1 ]\n", i,
aux->top);
}
if (i == m->start_anchored) {
fprintf(f, "STARTA -> %u [color = blue ]\n", i);
}
if (i == m->start_floating) {
fprintf(f, "STARTF -> %u [color = red ]\n", i);
}
if (isSherman) {
const char *sherman_state
= findShermanState(m, sher_base, m->sherman_limit, i);
fprintf(f, "%u [ fillcolor = lightblue style=filled ];\n", i);
u16 daddy = *(const u16 *)(sherman_state + SHERMAN_DADDY_OFFSET);
if (daddy) {
fprintf(f, "%u -> %u [ color=royalblue style=dashed weight=0.1]\n",
i, daddy);
}
}
}
static
void dump_program(FILE *f, const pair<u32, u32> &e, const gough_ins *prog) {
fprintf(f, "edge_%u_%u:\n", e.first, e.second);
for (const gough_ins *it = prog;; ++it) {
fprintf(f, "\t");
u32 s = it->src;
u32 d = it->dest;
switch (it->op) {
case GOUGH_INS_END:
fprintf(f, "END");
fprintf(f, "\n");
return;
case GOUGH_INS_MOV:
fprintf(f, "MOV %u %u", d, s);
break;
case GOUGH_INS_NEW:
fprintf(f, "NEW-%u %u", s, d);
break;
case GOUGH_INS_MIN:
fprintf(f, "MIN %u %u", d, s);
break;
default:
fprintf(f, "<UNKNOWN>");
fprintf(f, "\n");
return;
}
fprintf(f, "\n");
}
}
static
void dump_programs(FILE *f, const NFA *nfa,
const set<pair<pair<u32, u32>, u32 > > &prog_dump) {
fprintf(f, "Edge Programs\n");
fprintf(f, "-------------\n");
for (set<pair<pair<u32, u32>, u32 > >::const_iterator it
= prog_dump.begin(); it != prog_dump.end(); ++it) {
assert(it->second);
const gough_ins *p = (const gough_ins *)((const u8 *)nfa + it->second);
dump_program(f, it->first, p);
}
}
static
void dumpTransitions(const NFA *nfa, FILE *f,
set<pair<pair<u32, u32>, u32 > > *prog_dump) {
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
const gough_info *g = get_gough(m);
u32 alphaSize = 1U << m->alphaShift;
const u32 *prog_offset_table = (const u32 *)(g + 1);
for (u16 i = 0; i < m->state_count; i++) {
fprintf(f, "%05hu", i);
const mstate_aux *aux = getAux(nfa, i);
if (aux->accel_offset) {
dumpAccelText(f, (const union AccelAux *)((const char *)m +
aux->accel_offset));
}
u16 trans[ALPHABET_SIZE];
goughGetTransitions(nfa, i, trans);
int rstart = 0;
u16 prev = 0xffff;
for (int j = 0; j < N_CHARS; j++) {
u16 curr = trans[j];
if (curr == prev) {
continue;
}
if (prev != 0xffff) {
if (j == rstart + 1) {
fprintf(f, " %02x->%hu", rstart, prev);
} else {
fprintf(f, " [%02x - %02x]->%hu", rstart, j - 1, prev);
}
}
prev = curr;
rstart = j;
u32 edge_index = i * alphaSize + m->remap[j];
u32 prog_offset = prog_offset_table[edge_index];
if (prog_offset) {
prog_dump->insert(make_pair(make_pair((u32)i, (u32)trans[j]),
prog_offset));
}
}
if (N_CHARS == rstart + 1) {
fprintf(f, " %02x->%hu", rstart, prev);
} else {
fprintf(f, " [%02x - %02x]->%hu", rstart, N_CHARS - 1, prev);
}
fprintf(f, " TOP->%hu\n", trans[TOP]);
fprintf(f, "\n");
}
fprintf(f, "\n");
}
void nfaExecGough8_dumpDot(const struct NFA *nfa, FILE *f) {
assert(nfa->type == GOUGH_NFA_8);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
dumpDotPreambleDfa(f);
for (u16 i = 1; i < m->state_count; i++) {
describeNode(nfa, m, i, f);
u16 t[ALPHABET_SIZE];
goughGetTransitions(nfa, i, t);
describeEdge(f, t, i);
}
fprintf(f, "}\n");
}
void nfaExecGough8_dumpText(const struct NFA *nfa, FILE *f) {
assert(nfa->type == GOUGH_NFA_8);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
fprintf(f, "gough 8\n");
fprintf(f, "report: %u, states %u, length %u\n", m->arb_report,
m->state_count, m->length);
fprintf(f, "astart: %hu, fstart %hu\n", m->start_anchored,
m->start_floating);
fprintf(f, "accel_limit: %hu, accept_limit %hu\n", m->accel_limit_8,
m->accept_limit_8);
fprintf(f, "\n");
describeAlphabet(f, m);
set<pair<pair<u32, u32>, u32 > > prog_dump;
dumpTransitions(nfa, f, &prog_dump);
dump_programs(f, nfa, prog_dump);
dumpTextReverse(nfa, f);
}
void nfaExecGough16_dumpDot(const struct NFA *nfa, FILE *f) {
assert(nfa->type == GOUGH_NFA_16);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
dumpDotPreambleDfa(f);
for (u16 i = 1; i < m->state_count; i++) {
describeNode(nfa, m, i, f);
u16 t[ALPHABET_SIZE];
goughGetTransitions(nfa, i, t);
describeEdge(f, t, i);
}
fprintf(f, "}\n");
}
void nfaExecGough16_dumpText(const struct NFA *nfa, FILE *f) {
assert(nfa->type == GOUGH_NFA_16);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
// const gough_info *h = get_gough(m);
fprintf(f, "gough 16\n");
fprintf(f, "report: %u, states: %u, length: %u\n", m->arb_report,
m->state_count, m->length);
fprintf(f, "astart: %hu, fstart: %hu\n", m->start_anchored,
m->start_floating);
fprintf(f, "single accept: %d\n", !!(int)m->flags & MCCLELLAN_FLAG_SINGLE);
fprintf(f, "sherman_limit: %u, sherman_end: %u\n", m->sherman_limit,
m->sherman_end);
describeAlphabet(f, m);
set<pair<pair<u32, u32>, u32 > > prog_dump;
dumpTransitions(nfa, f, &prog_dump);
dump_programs(f, nfa, prog_dump);
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
} // namespace ue2

49
src/nfa/goughdump.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef GOUGH_DUMP_H
#define GOUGH_DUMP_H
#ifdef DUMP_SUPPORT
#include "ue2common.h"
struct NFA;
namespace ue2 {
void nfaExecGough8_dumpDot(const NFA *nfa, FILE *file);
void nfaExecGough16_dumpDot(const NFA *nfa, FILE *file);
void nfaExecGough8_dumpText(const NFA *nfa, FILE *file);
void nfaExecGough16_dumpText(const NFA *nfa, FILE *file);
} // namespace ue2
#endif
#endif

527
src/nfa/lbr.c Normal file
View File

@@ -0,0 +1,527 @@
/*
* Copyright (c) 2015, 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 Large Bounded Repeat (LBR) engine: runtime code.
*/
#include "lbr.h"
#include "lbr_internal.h"
#include "nfa_api.h"
#include "nfa_api_queue.h"
#include "nfa_internal.h"
#include "repeat.h"
#include "repeat_internal.h"
#include "shufti.h"
#include "truffle.h"
#include "vermicelli.h"
#include "util/partial_store.h"
#include "util/unaligned.h"
/** \brief Sentinel value used to indicate that a repeat is dead/empty/unused.
* * */
#define REPEAT_DEAD 0xffffffffffffffffull
enum MatchMode {
CALLBACK_OUTPUT,
STOP_AT_MATCH,
};
static really_inline
const struct RepeatInfo *getRepeatInfo(const struct lbr_common *l) {
const struct RepeatInfo *repeatInfo =
(const struct RepeatInfo *)((const char *)l + l->repeatInfoOffset);
return repeatInfo;
}
static really_inline
void lbrCompressState(const struct lbr_common *l, u64a offset,
const struct lbr_state *lstate, char *stream_state) {
assert(l && lstate && stream_state);
assert(ISALIGNED(lstate));
const struct RepeatInfo *info = getRepeatInfo(l);
repeatPack(stream_state, info, &lstate->ctrl, offset);
}
static really_inline
void lbrExpandState(const struct lbr_common *l, u64a offset,
const char *stream_state, struct lbr_state *lstate) {
assert(l && stream_state && lstate);
assert(ISALIGNED(lstate));
const struct RepeatInfo *info = getRepeatInfo(l);
repeatUnpack(stream_state, info, offset, &lstate->ctrl);
}
static really_inline
void clearRepeat(const struct RepeatInfo *info, struct lbr_state *lstate) {
assert(info && lstate);
DEBUG_PRINTF("clear repeat at %p\n", lstate);
switch ((enum RepeatType)info->type) {
case REPEAT_RING:
lstate->ctrl.ring.offset = REPEAT_DEAD;
break;
case REPEAT_RANGE:
lstate->ctrl.range.offset = REPEAT_DEAD;
break;
case REPEAT_FIRST:
case REPEAT_LAST:
lstate->ctrl.offset.offset = REPEAT_DEAD;
break;
case REPEAT_BITMAP:
lstate->ctrl.bitmap.offset = REPEAT_DEAD;
break;
case REPEAT_SPARSE_OPTIMAL_P:
lstate->ctrl.ring.offset = REPEAT_DEAD;
break;
case REPEAT_TRAILER:
lstate->ctrl.trailer.offset = REPEAT_DEAD;
break;
default:
assert(0);
break;
}
}
static really_inline
char repeatIsDead(const struct RepeatInfo *info,
const struct lbr_state *lstate) {
assert(info && lstate);
switch ((enum RepeatType)info->type) {
case REPEAT_RING:
return lstate->ctrl.ring.offset == REPEAT_DEAD;
case REPEAT_RANGE:
return lstate->ctrl.range.offset == REPEAT_DEAD;
case REPEAT_FIRST:
case REPEAT_LAST:
return lstate->ctrl.offset.offset == REPEAT_DEAD;
case REPEAT_BITMAP:
return lstate->ctrl.bitmap.offset == REPEAT_DEAD;
case REPEAT_SPARSE_OPTIMAL_P:
return lstate->ctrl.ring.offset == REPEAT_DEAD;
case REPEAT_TRAILER:
return lstate->ctrl.trailer.offset == REPEAT_DEAD;
}
assert(0);
return 1;
}
/** Returns true if the LBR can produce matches at offsets greater than the
* given one. TODO: can this be combined with lbrIsActive? */
static really_inline
char lbrIsAlive(const struct lbr_common *l, const struct lbr_state *lstate,
const char *state, u64a offset) {
assert(l && lstate && state);
const struct RepeatInfo *info = getRepeatInfo(l);
if (repeatIsDead(info, lstate)) {
DEBUG_PRINTF("repeat is dead\n");
return 0;
}
if (info->repeatMax == REPEAT_INF) {
DEBUG_PRINTF("active repeat with inf max bound, alive\n");
return 1;
}
assert(info->repeatMax < REPEAT_INF);
const char *repeatState = state + info->packedCtrlSize;
u64a lastTop = repeatLastTop(info, &lstate->ctrl, repeatState);
if (offset < lastTop + info->repeatMax) {
DEBUG_PRINTF("alive, as we can still produce matches after %llu\n",
offset);
return 1;
}
DEBUG_PRINTF("dead\n");
return 0;
}
/** Returns true if the LBR is matching at the given offset or it could produce
* a match in the future. */
static really_inline
char lbrIsActive(const struct lbr_common *l, const struct lbr_state *lstate,
const char *state, u64a offset) {
assert(l && lstate && state);
const struct RepeatInfo *info = getRepeatInfo(l);
assert(!repeatIsDead(info, lstate)); // Guaranteed by caller.
const char *repeatState = state + info->packedCtrlSize;
if (repeatHasMatch(info, &lstate->ctrl, repeatState, offset) ==
REPEAT_MATCH) {
DEBUG_PRINTF("currently matching\n");
return 1;
}
u64a i = repeatNextMatch(info, &lstate->ctrl, repeatState, offset);
if (i != 0) {
DEBUG_PRINTF("active, next match is at %llu\n", i);
return 1;
}
DEBUG_PRINTF("no more matches\n");
return 0;
}
static really_inline
void lbrTop(const struct lbr_common *l, struct lbr_state *lstate, char *state,
u64a offset) {
assert(l && lstate && state);
DEBUG_PRINTF("top at %llu\n", offset);
const struct RepeatInfo *info = getRepeatInfo(l);
char *repeatState = state + info->packedCtrlSize;
char is_alive = !repeatIsDead(info, lstate);
if (is_alive) {
// Ignore duplicate TOPs.
u64a last = repeatLastTop(info, &lstate->ctrl, repeatState);
assert(last <= offset);
if (last == offset) {
return;
}
}
repeatStore(info, &lstate->ctrl, repeatState, offset, is_alive);
}
static really_inline
char lbrInAccept(const struct lbr_common *l, const struct lbr_state *lstate,
const char *state, u64a offset, ReportID report) {
assert(l && lstate && state);
DEBUG_PRINTF("offset=%llu, report=%u\n", offset, report);
if (report != l->report) {
DEBUG_PRINTF("report=%u is not LBR report %u\n", report, l->report);
return 0;
}
const struct RepeatInfo *info = getRepeatInfo(l);
assert(!repeatIsDead(info, lstate)); // Guaranteed by caller.
const char *repeatState = state + info->packedCtrlSize;
return repeatHasMatch(info, &lstate->ctrl, repeatState, offset) ==
REPEAT_MATCH;
}
static really_inline
char lbrFindMatch(const struct lbr_common *l, const u64a begin, const u64a end,
const struct lbr_state *lstate, const char *state,
size_t *mloc) {
DEBUG_PRINTF("begin=%llu, end=%llu\n", begin, end);
assert(begin <= end);
if (begin == end) {
return 0;
}
const struct RepeatInfo *info = getRepeatInfo(l);
const char *repeatState = state + info->packedCtrlSize;
u64a i = repeatNextMatch(info, &lstate->ctrl, repeatState, begin);
if (i == 0) {
DEBUG_PRINTF("no more matches\n");
return 0;
}
if (i > end) {
DEBUG_PRINTF("next match at %llu is beyond the horizon\n", i);
return 0;
}
DEBUG_PRINTF("stop at match at %llu\n", i);
assert(mloc);
*mloc = i - begin;
return 1;
}
static really_inline
char lbrMatchLoop(const struct lbr_common *l, const u64a begin, const u64a end,
const struct lbr_state *lstate, const char *state,
NfaCallback cb, void *ctx) {
DEBUG_PRINTF("begin=%llu, end=%llu\n", begin, end);
assert(begin <= end);
if (begin == end) {
return MO_CONTINUE_MATCHING;
}
const struct RepeatInfo *info = getRepeatInfo(l);
const char *repeatState = state + info->packedCtrlSize;
u64a i = begin;
for (;;) {
i = repeatNextMatch(info, &lstate->ctrl, repeatState, i);
if (i == 0) {
DEBUG_PRINTF("no more matches\n");
return MO_CONTINUE_MATCHING;
}
if (i > end) {
DEBUG_PRINTF("next match at %llu is beyond the horizon\n", i);
return MO_CONTINUE_MATCHING;
}
DEBUG_PRINTF("firing match at %llu\n", i);
if (cb(i, l->report, ctx) == MO_HALT_MATCHING) {
return MO_HALT_MATCHING;
}
}
assert(0);
return MO_CONTINUE_MATCHING;
}
static really_inline
char lbrRevScanDot(UNUSED const struct NFA *nfa, UNUSED const u8 *buf,
UNUSED size_t begin, UNUSED size_t end,
UNUSED size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Dot);
// Nothing can kill a dot!
return 0;
}
static really_inline
char lbrRevScanVerm(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end, size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Verm);
const struct lbr_verm *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = rvermicelliExec(l->c, 0, buf + begin, buf + end);
if (ptr == buf + begin - 1) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
assert((char)*ptr == l->c);
return 1;
}
static really_inline
char lbrRevScanNVerm(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end, size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_NVerm);
const struct lbr_verm *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = rnvermicelliExec(l->c, 0, buf + begin, buf + end);
if (ptr == buf + begin - 1) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
assert((char)*ptr != l->c);
return 1;
}
static really_inline
char lbrRevScanShuf(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end,
size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Shuf);
const struct lbr_shuf *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = rshuftiExec(l->mask_lo, l->mask_hi, buf + begin, buf + end);
if (ptr == buf + begin - 1) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
return 1;
}
static really_inline
char lbrRevScanTruf(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end,
size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Truf);
const struct lbr_truf *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = rtruffleExec(l->mask1, l->mask2, buf + begin, buf + end);
if (ptr == buf + begin - 1) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
return 1;
}
static really_inline
char lbrFwdScanDot(UNUSED const struct NFA *nfa, UNUSED const u8 *buf,
UNUSED size_t begin, UNUSED size_t end,
UNUSED size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Dot);
// Nothing can kill a dot!
return 0;
}
static really_inline
char lbrFwdScanVerm(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end, size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Verm);
const struct lbr_verm *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = vermicelliExec(l->c, 0, buf + begin, buf + end);
if (ptr == buf + end) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
assert((char)*ptr == l->c);
return 1;
}
static really_inline
char lbrFwdScanNVerm(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end, size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_NVerm);
const struct lbr_verm *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = nvermicelliExec(l->c, 0, buf + begin, buf + end);
if (ptr == buf + end) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
assert((char)*ptr != l->c);
return 1;
}
static really_inline
char lbrFwdScanShuf(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end,
size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Shuf);
const struct lbr_shuf *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = shuftiExec(l->mask_lo, l->mask_hi, buf + begin, buf + end);
if (ptr == buf + end) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
return 1;
}
static really_inline
char lbrFwdScanTruf(const struct NFA *nfa, const u8 *buf,
size_t begin, size_t end,
size_t *loc) {
assert(begin <= end);
assert(nfa->type == LBR_NFA_Truf);
const struct lbr_truf *l = getImplNfa(nfa);
if (begin == end) {
return 0;
}
const u8 *ptr = truffleExec(l->mask1, l->mask2, buf + begin, buf + end);
if (ptr == buf + end) {
DEBUG_PRINTF("no escape found\n");
return 0;
}
assert(loc);
*loc = (size_t)(ptr - buf);
DEBUG_PRINTF("escape found at offset %zu\n", *loc);
return 1;
}
#define ENGINE_ROOT_NAME Dot
#include "lbr_common_impl.h"
#define ENGINE_ROOT_NAME Verm
#include "lbr_common_impl.h"
#define ENGINE_ROOT_NAME NVerm
#include "lbr_common_impl.h"
#define ENGINE_ROOT_NAME Shuf
#include "lbr_common_impl.h"
#define ENGINE_ROOT_NAME Truf
#include "lbr_common_impl.h"

145
src/nfa/lbr.h Normal file
View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef LBR_H
#define LBR_H
#include "ue2common.h"
struct mq;
struct NFA;
#ifdef __cplusplus
extern "C"
{
#endif
// LBR Dot
char nfaExecLbrDot_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrDot_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrDot_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecLbrDot_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecLbrDot_inAccept(const struct NFA *n, ReportID report, struct mq *q);
char nfaExecLbrDot_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecLbrDot_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecLbrDot_queueCompressState(const struct NFA *nfa, const struct mq *q,
s64a loc);
char nfaExecLbrDot_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecLbrDot_testEOD NFA_API_NO_IMPL
#define nfaExecLbrDot_B_Reverse NFA_API_NO_IMPL
#define nfaExecLbrDot_zombie_status NFA_API_NO_IMPL
// LBR Verm
char nfaExecLbrVerm_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrVerm_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrVerm_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecLbrVerm_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecLbrVerm_inAccept(const struct NFA *n, ReportID report,
struct mq *q);
char nfaExecLbrVerm_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecLbrVerm_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecLbrVerm_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecLbrVerm_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecLbrVerm_testEOD NFA_API_NO_IMPL
#define nfaExecLbrVerm_B_Reverse NFA_API_NO_IMPL
#define nfaExecLbrVerm_zombie_status NFA_API_NO_IMPL
// LBR Negated Verm
char nfaExecLbrNVerm_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrNVerm_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrNVerm_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecLbrNVerm_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecLbrNVerm_inAccept(const struct NFA *n, ReportID report,
struct mq *q);
char nfaExecLbrNVerm_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecLbrNVerm_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecLbrNVerm_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecLbrNVerm_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecLbrNVerm_testEOD NFA_API_NO_IMPL
#define nfaExecLbrNVerm_B_Reverse NFA_API_NO_IMPL
#define nfaExecLbrNVerm_zombie_status NFA_API_NO_IMPL
// LBR Shuf
char nfaExecLbrShuf_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrShuf_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrShuf_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecLbrShuf_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecLbrShuf_inAccept(const struct NFA *n, ReportID report,
struct mq *q);
char nfaExecLbrShuf_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecLbrShuf_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecLbrShuf_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecLbrShuf_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecLbrShuf_testEOD NFA_API_NO_IMPL
#define nfaExecLbrShuf_B_Reverse NFA_API_NO_IMPL
#define nfaExecLbrShuf_zombie_status NFA_API_NO_IMPL
// LBR Truffle
char nfaExecLbrTruf_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrTruf_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecLbrTruf_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecLbrTruf_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecLbrTruf_inAccept(const struct NFA *n, ReportID report,
struct mq *q);
char nfaExecLbrTruf_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecLbrTruf_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecLbrTruf_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecLbrTruf_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecLbrTruf_testEOD NFA_API_NO_IMPL
#define nfaExecLbrTruf_B_Reverse NFA_API_NO_IMPL
#define nfaExecLbrTruf_zombie_status NFA_API_NO_IMPL
#ifdef __cplusplus
}
#endif
#endif

453
src/nfa/lbr_common_impl.h Normal file
View File

@@ -0,0 +1,453 @@
/*
* Copyright (c) 2015, 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 Large Bounded Repeat (LBR) engine: runtime impl X-macros.
*/
#include "util/join.h"
#define ENGINE_EXEC_NAME JOIN(nfaExecLbr, ENGINE_ROOT_NAME)
#define EXEC_FN JOIN(lbrExec, ENGINE_ROOT_NAME)
#define FWDSCAN_FN JOIN(lbrFwdScan, ENGINE_ROOT_NAME)
#define REVSCAN_FN JOIN(lbrRevScan, ENGINE_ROOT_NAME)
char JOIN(ENGINE_EXEC_NAME, _queueCompressState)(const struct NFA *nfa,
const struct mq *q, s64a loc) {
assert(nfa && q);
assert(isLbrType(nfa->type));
DEBUG_PRINTF("entry, q->offset=%llu, loc=%lld\n", q->offset, loc);
const struct lbr_common *l = getImplNfa(nfa);
const struct lbr_state *lstate = (const struct lbr_state *)q->state;
u64a offset = q->offset + loc;
lbrCompressState(l, offset, lstate, q->streamState);
return 0;
}
char JOIN(ENGINE_EXEC_NAME, _expandState)(const struct NFA *nfa, void *dest,
const void *src, u64a offset,
UNUSED u8 key) {
assert(nfa);
assert(isLbrType(nfa->type));
DEBUG_PRINTF("entry, offset=%llu\n", offset);
const struct lbr_common *l = getImplNfa(nfa);
struct lbr_state *lstate = (struct lbr_state *)dest;
lbrExpandState(l, offset, src, lstate);
return 0;
}
char JOIN(ENGINE_EXEC_NAME, _reportCurrent)(const struct NFA *nfa,
struct mq *q) {
assert(nfa && q);
assert(isLbrType(nfa->type));
const struct lbr_common *l = getImplNfa(nfa);
u64a offset = q_cur_offset(q);
DEBUG_PRINTF("firing match %u at %llu\n", l->report, offset);
q->cb(offset, l->report, q->context);
return 0;
}
char JOIN(ENGINE_EXEC_NAME, _inAccept)(const struct NFA *nfa,
ReportID report, struct mq *q) {
assert(nfa && q);
assert(isLbrType(nfa->type));
DEBUG_PRINTF("entry\n");
const struct lbr_common *l = getImplNfa(nfa);
const struct RepeatInfo *info = getRepeatInfo(l);
const struct lbr_state *lstate = (const struct lbr_state *)q->state;
if (repeatIsDead(info, lstate)) {
DEBUG_PRINTF("repeat is dead\n");
return 0;
}
u64a offset = q->offset + q_last_loc(q);
return lbrInAccept(l, lstate, q->streamState, offset, report);
}
char JOIN(ENGINE_EXEC_NAME, _queueInitState)(const struct NFA *nfa,
struct mq *q) {
assert(nfa && q);
assert(isLbrType(nfa->type));
DEBUG_PRINTF("entry\n");
const struct lbr_common *l = getImplNfa(nfa);
const struct RepeatInfo *info = getRepeatInfo(l);
assert(q->state);
struct lbr_state *lstate = (struct lbr_state *)q->state;
assert(ISALIGNED(lstate));
lstate->lastEscape = 0;
clearRepeat(info, lstate);
return 0;
}
char JOIN(ENGINE_EXEC_NAME, _initCompressedState)(const struct NFA *nfa,
u64a offset,
void *state, UNUSED u8 key) {
assert(nfa && state);
assert(isLbrType(nfa->type));
DEBUG_PRINTF("entry\n");
const struct lbr_common *l = getImplNfa(nfa);
const struct RepeatInfo *info = getRepeatInfo(l);
struct lbr_state lstate; // temp control block on stack.
clearRepeat(info, &lstate);
lbrTop(l, &lstate, state, offset);
lbrCompressState(l, offset, &lstate, state);
return 1; // LBR is alive
}
// FIXME: this function could be much simpler for a Dot LBR, as all it needs to
// do is find the next top.
static really_inline
char JOIN(ENGINE_EXEC_NAME, _TopScan)(const struct NFA *nfa, struct mq *q,
s64a end) {
const struct lbr_common *l = getImplNfa(nfa);
const struct RepeatInfo *info = getRepeatInfo(l);
const u64a offset = q->offset;
struct lbr_state *lstate = (struct lbr_state *)q->state;
assert(ISALIGNED(lstate));
assert(repeatIsDead(info, lstate));
assert(q->cur < q->end);
DEBUG_PRINTF("entry, end=%lld, offset=%llu, lastEscape=%llu\n", end,
offset, lstate->lastEscape);
while (1) {
// Find the next top with location >= the last escape we saw.
for (; q->cur < q->end && q_cur_loc(q) <= end; q->cur++) {
enum mqe_event t = q_cur_type(q);
if ((t == MQE_TOP || t == MQE_TOP_FIRST) &&
q_cur_offset(q) >= lstate->lastEscape) {
goto found_top;
}
DEBUG_PRINTF("skip event type=%d offset=%lld\n", t, q_cur_offset(q));
}
// No more tops, we're done.
break;
found_top:;
assert(q->cur < q->end);
u64a sp = q_cur_offset(q);
u64a first_match = sp + info->repeatMin;
DEBUG_PRINTF("first possible match is at %llu\n", first_match);
u64a ep = MIN(MIN(end, (s64a)q->length) + offset, first_match);
if (ep > sp && sp >= offset) {
size_t eloc;
DEBUG_PRINTF("rev b%llu e%llu/%zu\n", sp - offset, ep - offset,
q->length);
assert(ep - offset <= q->length);
if (REVSCAN_FN(nfa, q->buffer, sp - offset, ep - offset, &eloc)) {
DEBUG_PRINTF("escape found at %llu\n", offset + eloc);
lstate->lastEscape = eloc;
q->cur++;
continue;
}
}
lbrTop(l, lstate, q->streamState, sp);
return 1;
}
DEBUG_PRINTF("exhausted queue\n");
return 0;
}
static really_inline
char JOIN(ENGINE_EXEC_NAME, _Q_i)(const struct NFA *nfa, struct mq *q,
s64a end, enum MatchMode mode) {
assert(nfa && q);
assert(isLbrType(nfa->type));
const struct lbr_common *l = getImplNfa(nfa);
const struct RepeatInfo *info = getRepeatInfo(l);
struct lbr_state *lstate = (struct lbr_state *)q->state;
assert(ISALIGNED(lstate));
if (q->report_current) {
DEBUG_PRINTF("report_current: fire match at %llu\n", q_cur_offset(q));
int rv = q->cb(q_cur_offset(q), l->report, q->context);
q->report_current = 0;
if (rv == MO_HALT_MATCHING) {
return MO_HALT_MATCHING;
}
}
if (q->cur == q->end) {
return 1;
}
assert(q->cur + 1 < q->end); /* require at least two items */
assert(q_cur_type(q) == MQE_START);
u64a sp = q_cur_offset(q);
q->cur++;
DEBUG_PRINTF("sp=%llu, abs_end=%llu\n", sp, end + q->offset);
while (q->cur < q->end) {
DEBUG_PRINTF("q item type=%d offset=%llu\n", q_cur_type(q),
q_cur_offset(q));
assert(sp >= q->offset); // not in history
if (repeatIsDead(info, lstate)) {
DEBUG_PRINTF("repeat is currently dead, skipping scan\n");
goto scan_done;
}
u64a ep = q_cur_offset(q);
ep = MIN(ep, q->offset + end);
if (sp < ep) {
size_t eloc = 0;
char escape_found = 0;
DEBUG_PRINTF("scanning from sp=%llu to ep=%llu\n", sp, ep);
assert(sp >= q->offset && ep >= q->offset);
if (FWDSCAN_FN(nfa, q->buffer, sp - q->offset, ep - q->offset, &eloc)) {
escape_found = 1;
ep = q->offset + eloc;
DEBUG_PRINTF("escape found at %llu\n", ep);
assert(ep >= sp);
}
assert(sp <= ep);
if (mode == STOP_AT_MATCH) {
size_t mloc;
if (lbrFindMatch(l, sp, ep, lstate, q->streamState, &mloc)) {
DEBUG_PRINTF("storing match at %llu\n", sp + mloc);
q->cur--;
assert(q->cur < MAX_MQE_LEN);
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = (s64a)(sp - q->offset) + mloc;
return MO_MATCHES_PENDING;
}
} else {
assert(mode == CALLBACK_OUTPUT);
char rv = lbrMatchLoop(l, sp, ep, lstate, q->streamState, q->cb,
q->context);
if (rv == MO_HALT_MATCHING) {
return MO_HALT_MATCHING;
}
assert(rv == MO_CONTINUE_MATCHING);
}
if (escape_found) {
DEBUG_PRINTF("clearing repeat due to escape\n");
clearRepeat(info, lstate);
}
}
scan_done:
if (q_cur_loc(q) > end) {
q->cur--;
assert(q->cur < MAX_MQE_LEN);
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = end;
return MO_ALIVE;
}
if (repeatIsDead(info, lstate)) {
if (!JOIN(ENGINE_EXEC_NAME, _TopScan)(nfa, q, end)) {
assert(repeatIsDead(info, lstate));
if (q->cur < q->end && q_cur_loc(q) > end) {
q->cur--;
assert(q->cur < MAX_MQE_LEN);
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = end;
return MO_ALIVE;
}
return 0;
}
DEBUG_PRINTF("cur offset = %llu\n", q_cur_offset(q));
} else {
switch (q_cur_type(q)) {
case MQE_TOP:
case MQE_TOP_FIRST:
lbrTop(l, lstate, q->streamState, q_cur_offset(q));
break;
case MQE_START:
case MQE_END:
break;
default:
DEBUG_PRINTF("unhandled event %d!\n", q_cur_type(q));
assert(0);
break;
}
}
sp = q_cur_offset(q);
q->cur++;
}
return lbrIsAlive(l, lstate, q->streamState, sp);
}
char JOIN(ENGINE_EXEC_NAME, _Q)(const struct NFA *nfa, struct mq *q, s64a end) {
DEBUG_PRINTF("entry, offset=%llu, end=%lld\n", q->offset, end);
return JOIN(ENGINE_EXEC_NAME, _Q_i)(nfa, q, end, CALLBACK_OUTPUT);
}
char JOIN(ENGINE_EXEC_NAME, _Q2)(const struct NFA *nfa, struct mq *q, s64a end) {
DEBUG_PRINTF("entry, offset=%llu, end=%lld\n", q->offset, end);
return JOIN(ENGINE_EXEC_NAME, _Q_i)(nfa, q, end, STOP_AT_MATCH);
}
static really_inline
void JOIN(ENGINE_EXEC_NAME, _StreamSilent)(const struct NFA *nfa, struct mq *q,
const u8 *buf, size_t length) {
const struct lbr_common *l = getImplNfa(nfa);
const struct RepeatInfo *info = getRepeatInfo(l);
struct lbr_state *lstate = (struct lbr_state *)q->state;
assert(ISALIGNED(lstate));
assert(!repeatIsDead(info, lstate));
// This call doesn't produce matches, so we elide the lbrMatchLoop call
// entirely and just do escape scans to maintain the repeat.
size_t eloc = 0;
char escaped = FWDSCAN_FN(nfa, buf, 0, length, &eloc);
if (escaped) {
assert(eloc < length);
DEBUG_PRINTF("escape found at %zu, clearing repeat\n", eloc);
clearRepeat(info, lstate);
}
}
// Rose infix path.
char JOIN(ENGINE_EXEC_NAME, _QR)(const struct NFA *nfa, struct mq *q,
ReportID report) {
assert(nfa && q);
assert(isLbrType(nfa->type));
if (q->cur == q->end) {
return 1;
}
assert(q->cur + 1 < q->end); /* require at least two items */
assert(q_cur_type(q) == MQE_START);
u64a sp = q_cur_offset(q);
q->cur++;
DEBUG_PRINTF("sp=%llu\n", sp);
const struct lbr_common *l = getImplNfa(nfa);
const struct RepeatInfo *info = getRepeatInfo(l);
struct lbr_state *lstate = (struct lbr_state *)q->state;
assert(ISALIGNED(lstate));
const s64a lastLoc = q_last_loc(q);
while (q->cur < q->end) {
DEBUG_PRINTF("q item type=%d offset=%llu\n", q_cur_type(q),
q_cur_offset(q));
if (repeatIsDead(info, lstate)) {
DEBUG_PRINTF("repeat is dead\n");
goto scan_done;
}
u64a ep = q_cur_offset(q);
if (sp < q->offset) {
DEBUG_PRINTF("HISTORY BUFFER SCAN\n");
assert(q->offset - sp <= q->hlength);
u64a local_ep = MIN(q->offset, ep);
const u8 *ptr = q->history + q->hlength + sp - q->offset;
JOIN(ENGINE_EXEC_NAME, _StreamSilent)(nfa, q, ptr, local_ep - sp);
sp = local_ep;
}
if (repeatIsDead(info, lstate)) {
DEBUG_PRINTF("repeat is dead\n");
goto scan_done;
}
if (sp < ep) {
DEBUG_PRINTF("MAIN BUFFER SCAN\n");
assert(ep - q->offset <= q->length);
const u8 *ptr = q->buffer + sp - q->offset;
JOIN(ENGINE_EXEC_NAME, _StreamSilent)(nfa, q, ptr, ep - sp);
}
if (repeatIsDead(info, lstate)) {
scan_done:
if (!JOIN(ENGINE_EXEC_NAME, _TopScan)(nfa, q, lastLoc)) {
assert(repeatIsDead(info, lstate));
assert(q->cur == q->end);
return 0;
}
} else {
switch (q_cur_type(q)) {
case MQE_TOP:
case MQE_TOP_FIRST:
lbrTop(l, lstate, q->streamState, q_cur_offset(q));
break;
case MQE_START:
case MQE_END:
break;
default:
DEBUG_PRINTF("unhandled event %d!\n", q_cur_type(q));
assert(0);
break;
}
}
sp = q_cur_offset(q);
q->cur++;
}
if (repeatIsDead(info, lstate)) {
DEBUG_PRINTF("repeat is dead\n");
return 0;
}
if (lbrInAccept(l, lstate, q->streamState, sp, report)) {
return MO_MATCHES_PENDING;
}
return lbrIsActive(l, lstate, q->streamState, sp);
}
#undef ENGINE_EXEC_NAME
#undef EXEC_FN
#undef FWDSCAN_FN
#undef REVSCAN_FN
#undef ENGINE_ROOT_NAME

142
src/nfa/lbr_dump.cpp Normal file
View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2015, 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 Large Bounded Repeat (LBR): dump code.
*/
#include "config.h"
#include "lbr_dump.h"
#include "lbr_internal.h"
#include "nfa_dump_internal.h"
#include "nfa_internal.h"
#include "repeat_internal.h"
#include "shufticompile.h"
#include "trufflecompile.h"
#include "util/charreach.h"
#include "util/dump_charclass.h"
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
namespace ue2 {
void nfaExecLbrDot_dumpDot(UNUSED const NFA *nfa, UNUSED FILE *f) {
// No impl
}
void nfaExecLbrVerm_dumpDot(UNUSED const NFA *nfa, UNUSED FILE *f) {
// No impl
}
void nfaExecLbrNVerm_dumpDot(UNUSED const NFA *nfa, UNUSED FILE *f) {
// No impl
}
void nfaExecLbrShuf_dumpDot(UNUSED const NFA *nfa, UNUSED FILE *f) {
// No impl
}
void nfaExecLbrTruf_dumpDot(UNUSED const NFA *nfa, UNUSED FILE *f) {
// No impl
}
static
void lbrDumpCommon(const lbr_common *lc, FILE *f) {
const RepeatInfo *info
= (const RepeatInfo *)((const char *)lc + lc->repeatInfoOffset);
fprintf(f, "Limited Bounded Repeat\n");
fprintf(f, "\n");
fprintf(f, "repeat model: %s\n", repeatTypeName(info->type));
fprintf(f, "repeat bounds: {%u, %u}\n", info->repeatMin,
info->repeatMax);
fprintf(f, "report id: %u\n", lc->report);
fprintf(f, "\n");
fprintf(f, "min period: %u\n", info->minPeriod);
}
void nfaExecLbrDot_dumpText(const NFA *nfa, FILE *f) {
assert(nfa);
assert(nfa->type == LBR_NFA_Dot);
const lbr_dot *ld = (const lbr_dot *)getImplNfa(nfa);
lbrDumpCommon(&ld->common, f);
fprintf(f, "DOT model\n");
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
void nfaExecLbrVerm_dumpText(const NFA *nfa, FILE *f) {
assert(nfa);
assert(nfa->type == LBR_NFA_Verm);
const lbr_verm *lv = (const lbr_verm *)getImplNfa(nfa);
lbrDumpCommon(&lv->common, f);
fprintf(f, "VERM model, scanning for 0x%02x\n", lv->c);
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
void nfaExecLbrNVerm_dumpText(const NFA *nfa, FILE *f) {
assert(nfa);
assert(nfa->type == LBR_NFA_NVerm);
const lbr_verm *lv = (const lbr_verm *)getImplNfa(nfa);
lbrDumpCommon(&lv->common, f);
fprintf(f, "NEGATED VERM model, scanning for 0x%02x\n", lv->c);
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
void nfaExecLbrShuf_dumpText(const NFA *nfa, FILE *f) {
assert(nfa);
assert(nfa->type == LBR_NFA_Shuf);
const lbr_shuf *ls = (const lbr_shuf *)getImplNfa(nfa);
lbrDumpCommon(&ls->common, f);
CharReach cr = shufti2cr(ls->mask_lo, ls->mask_hi);
fprintf(f, "SHUF model, scanning for: %s (%zu chars)\n",
describeClass(cr, 20, CC_OUT_TEXT).c_str(), cr.count());
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
void nfaExecLbrTruf_dumpText(const NFA *nfa, FILE *f) {
assert(nfa);
assert(nfa->type == LBR_NFA_Truf);
const lbr_truf *lt = (const lbr_truf *)getImplNfa(nfa);
lbrDumpCommon(&lt->common, f);
CharReach cr = truffle2cr(lt->mask1, lt->mask2);
fprintf(f, "TRUFFLE model, scanning for: %s (%zu chars)\n",
describeClass(cr, 20, CC_OUT_TEXT).c_str(), cr.count());
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
} // namespace ue2

55
src/nfa/lbr_dump.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef LBR_DUMP_H
#define LBR_DUMP_H
#ifdef DUMP_SUPPORT
#include <cstdio>
struct NFA;
namespace ue2 {
void nfaExecLbrDot_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecLbrVerm_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecLbrNVerm_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecLbrShuf_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecLbrTruf_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecLbrDot_dumpText(const struct NFA *nfa, FILE *file);
void nfaExecLbrVerm_dumpText(const struct NFA *nfa, FILE *file);
void nfaExecLbrNVerm_dumpText(const struct NFA *nfa, FILE *file);
void nfaExecLbrTruf_dumpText(const struct NFA *nfa, FILE *file);
void nfaExecLbrShuf_dumpText(const struct NFA *nfa, FILE *file);
} // namespace ue2
#endif
#endif

82
src/nfa/lbr_internal.h Normal file
View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, 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 Large Bounded Repeat (LBR): data structures.
*/
#ifndef LBR_INTERNAL_H
#define LBR_INTERNAL_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "repeat_internal.h"
/** \brief Common LBR header. */
struct lbr_common {
u32 repeatInfoOffset; //!< offset of RepeatInfo structure relative
// to the start of lbr_common
ReportID report; //!< report to raise on match
};
struct lbr_dot {
struct lbr_common common;
};
struct lbr_verm {
struct lbr_common common;
char c; //!< escape char
};
struct lbr_shuf {
struct lbr_common common;
m128 mask_lo; //!< shufti lo mask for escape chars
m128 mask_hi; //!< shufti hi mask for escape chars
};
struct lbr_truf {
struct lbr_common common;
m128 mask1;
m128 mask2;
};
/** \brief Uncompressed ("full") state structure used by the LBR. This is
* stored in scratch, not in stream state. */
struct lbr_state {
u64a lastEscape; //!< \brief offset of last escape seen.
union RepeatControl ctrl; //!< \brief repeat control block. */
};
#ifdef __cplusplus
}
#endif
#endif // LBR_INTERNAL_H

121
src/nfa/limex.h Normal file
View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef LIMEX_H
#define LIMEX_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "nfa_api.h"
#if defined(DUMP_SUPPORT) && defined(__cplusplus)
#define GENERATE_NFA_DUMP_DECL(gf_name) \
} /* extern "C" */ \
namespace ue2 { \
void gf_name##_dumpDot(const struct NFA *nfa, FILE *file); \
void gf_name##_dumpText(const struct NFA *nfa, FILE *file); \
} /* namespace ue2 */ \
extern "C" {
#else
#define GENERATE_NFA_DUMP_DECL(gf_name)
#endif
#define GENERATE_NFA_DECL(gf_name) \
char gf_name##_testEOD(const struct NFA *nfa, const char *state, \
const char *streamState, u64a offset, \
NfaCallback callback, SomNfaCallback som_cb, \
void *context); \
char gf_name##_Q(const struct NFA *n, struct mq *q, s64a end); \
char gf_name##_Q2(const struct NFA *n, struct mq *q, s64a end); \
char gf_name##_QR(const struct NFA *n, struct mq *q, ReportID report); \
char gf_name##_reportCurrent(const struct NFA *n, struct mq *q); \
char gf_name##_inAccept(const struct NFA *n, ReportID report, \
struct mq *q); \
char gf_name##_queueInitState(const struct NFA *n, struct mq *q); \
char gf_name##_initCompressedState(const struct NFA *n, u64a offset, \
void *state, u8 key); \
char gf_name##_B_Reverse(const struct NFA *n, u64a offset, const u8 *buf, \
size_t buflen, const u8 *hbuf, size_t hlen, \
struct hs_scratch *scratch, NfaCallback cb, \
void *context); \
char gf_name##_queueCompressState(const struct NFA *nfa, \
const struct mq *q, s64a loc); \
char gf_name##_expandState(const struct NFA *nfa, void *dest, \
const void *src, u64a offset, u8 key); \
enum nfa_zombie_status gf_name##_zombie_status(const struct NFA *nfa, \
struct mq *q, s64a loc); \
GENERATE_NFA_DUMP_DECL(gf_name)
GENERATE_NFA_DECL(nfaExecLimEx32_1)
GENERATE_NFA_DECL(nfaExecLimEx32_2)
GENERATE_NFA_DECL(nfaExecLimEx32_3)
GENERATE_NFA_DECL(nfaExecLimEx32_4)
GENERATE_NFA_DECL(nfaExecLimEx32_5)
GENERATE_NFA_DECL(nfaExecLimEx32_6)
GENERATE_NFA_DECL(nfaExecLimEx32_7)
GENERATE_NFA_DECL(nfaExecLimEx128_1)
GENERATE_NFA_DECL(nfaExecLimEx128_2)
GENERATE_NFA_DECL(nfaExecLimEx128_3)
GENERATE_NFA_DECL(nfaExecLimEx128_4)
GENERATE_NFA_DECL(nfaExecLimEx128_5)
GENERATE_NFA_DECL(nfaExecLimEx128_6)
GENERATE_NFA_DECL(nfaExecLimEx128_7)
GENERATE_NFA_DECL(nfaExecLimEx256_1)
GENERATE_NFA_DECL(nfaExecLimEx256_2)
GENERATE_NFA_DECL(nfaExecLimEx256_3)
GENERATE_NFA_DECL(nfaExecLimEx256_4)
GENERATE_NFA_DECL(nfaExecLimEx256_5)
GENERATE_NFA_DECL(nfaExecLimEx256_6)
GENERATE_NFA_DECL(nfaExecLimEx256_7)
GENERATE_NFA_DECL(nfaExecLimEx384_1)
GENERATE_NFA_DECL(nfaExecLimEx384_2)
GENERATE_NFA_DECL(nfaExecLimEx384_3)
GENERATE_NFA_DECL(nfaExecLimEx384_4)
GENERATE_NFA_DECL(nfaExecLimEx384_5)
GENERATE_NFA_DECL(nfaExecLimEx384_6)
GENERATE_NFA_DECL(nfaExecLimEx384_7)
GENERATE_NFA_DECL(nfaExecLimEx512_1)
GENERATE_NFA_DECL(nfaExecLimEx512_2)
GENERATE_NFA_DECL(nfaExecLimEx512_3)
GENERATE_NFA_DECL(nfaExecLimEx512_4)
GENERATE_NFA_DECL(nfaExecLimEx512_5)
GENERATE_NFA_DECL(nfaExecLimEx512_6)
GENERATE_NFA_DECL(nfaExecLimEx512_7)
#undef GENERATE_NFA_DECL
#undef GENERATE_NFA_DUMP_DECL
#ifdef __cplusplus
}
#endif
#endif

229
src/nfa/limex_accel.c Normal file
View File

@@ -0,0 +1,229 @@
/*
* Copyright (c) 2015, 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 Limex NFA: acceleration runtime.
*/
#include "limex_accel.h"
#include "accel.h"
#include "limex_internal.h"
#include "limex_limits.h"
#include "nfa_internal.h"
#include "shufti.h"
#include "truffle.h"
#include "ue2common.h"
#include "vermicelli.h"
#include "util/bitutils.h"
#include "util/shuffle.h"
#include "util/simd_utils.h"
#include "util/simd_utils_ssse3.h"
#include "util/shuffle_ssse3.h"
static
const u8 *accelScan(const union AccelAux *aux, const u8 *ptr, const u8 *end) {
assert(ISALIGNED(aux)); // must be SIMD aligned for shufti
assert(end > ptr);
assert(end - ptr >= 16); // must be at least 16 bytes to scan
const u8 *start = ptr;
u8 offset;
switch (aux->accel_type) {
case ACCEL_VERM:
DEBUG_PRINTF("single vermicelli for 0x%02hhx\n", aux->verm.c);
offset = aux->verm.offset;
ptr = vermicelliExec(aux->verm.c, 0, ptr, end);
break;
case ACCEL_VERM_NOCASE:
DEBUG_PRINTF("single vermicelli-nocase for 0x%02hhx\n", aux->verm.c);
offset = aux->verm.offset;
ptr = vermicelliExec(aux->verm.c, 1, ptr, end);
break;
case ACCEL_DVERM:
DEBUG_PRINTF("double vermicelli for 0x%02hhx%02hhx\n",
aux->dverm.c1, aux->dverm.c2);
offset = aux->dverm.offset;
ptr = vermicelliDoubleExec(aux->dverm.c1, aux->dverm.c2, 0, ptr, end);
break;
case ACCEL_DVERM_NOCASE:
DEBUG_PRINTF("double vermicelli-nocase for 0x%02hhx%02hhx\n",
aux->dverm.c1, aux->dverm.c2);
offset = aux->dverm.offset;
ptr = vermicelliDoubleExec(aux->dverm.c1, aux->dverm.c2,
1, ptr, end);
break;
case ACCEL_SHUFTI:
DEBUG_PRINTF("single shufti\n");
offset = aux->shufti.offset;
ptr = shuftiExec(aux->shufti.lo, aux->shufti.hi, ptr, end);
break;
case ACCEL_DSHUFTI:
DEBUG_PRINTF("double shufti\n");
offset = aux->dshufti.offset;
ptr = shuftiDoubleExec(aux->dshufti.lo1, aux->dshufti.hi1,
aux->dshufti.lo2, aux->dshufti.hi2, ptr, end);
break;
case ACCEL_TRUFFLE:
DEBUG_PRINTF("truffle shuffle\n");
offset = aux->truffle.offset;
ptr = truffleExec(aux->truffle.mask1, aux->truffle.mask2, ptr, end);
break;
case ACCEL_RED_TAPE:
ptr = end; /* there is no escape */
offset = aux->generic.offset;
break;
default:
/* no acceleration, fall through and return current ptr */
offset = 0;
break;
}
if (offset) {
ptr -= offset;
if (ptr < start) {
return start;
}
}
return ptr;
}
static really_inline
size_t accelScanWrapper(const u8 *accelTable, const union AccelAux *aux,
const u8 *input, u32 idx, size_t i, size_t end) {
assert(accelTable);
assert(aux);
DEBUG_PRINTF("shuffle returned %u -> aux %u\n", idx, accelTable[idx]);
assert(idx < (1 << NFA_MAX_ACCEL_STATES));
if (!idx) {
return end;
}
u8 aux_idx = accelTable[idx];
if (!aux_idx) {
assert(aux[0].accel_type == ACCEL_NONE);
DEBUG_PRINTF("no accel, bailing\n");
return i;
}
aux = aux + aux_idx;
const u8 *ptr = accelScan(aux, &input[i], &input[end]);
assert(ptr >= &input[i]);
size_t j = (size_t)(ptr - input);
DEBUG_PRINTF("accel skipped %zu of %zu chars\n", (j - i), (end - i));
DEBUG_PRINTF("returning j=%zu (i=%zu, end=%zu)\n", j, i, end);
return j;
}
size_t doAccel32(u32 s, u32 accel, const u8 *accelTable,
const union AccelAux *aux, const u8 *input, size_t i,
size_t end) {
u32 idx = shuffleDynamic32(s, accel);
return accelScanWrapper(accelTable, aux, input, idx, i, end);
}
size_t doAccel128(const m128 *state, const struct LimExNFA128 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end) {
u32 idx;
m128 s = *state;
DEBUG_PRINTF("using PSHUFB for 128-bit shuffle\n");
m128 accelPerm = limex->accelPermute;
m128 accelComp = limex->accelCompare;
idx = shufflePshufb128(s, accelPerm, accelComp);
return accelScanWrapper(accelTable, aux, input, idx, i, end);
}
size_t doAccel256(const m256 *state, const struct LimExNFA256 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end) {
u32 idx;
m256 s = *state;
DEBUG_PRINTF("using PSHUFB for 256-bit shuffle\n");
m256 accelPerm = limex->accelPermute;
m256 accelComp = limex->accelCompare;
#if !defined(__AVX2__)
u32 idx1 = shufflePshufb128(s.lo, accelPerm.lo, accelComp.lo);
u32 idx2 = shufflePshufb128(s.hi, accelPerm.hi, accelComp.hi);
#else
// TODO: learn you some avx2 shuffles for great good
u32 idx1 = shufflePshufb128(movdq_lo(s), movdq_lo(accelPerm),
movdq_lo(accelComp));
u32 idx2 = shufflePshufb128(movdq_hi(s), movdq_hi(accelPerm),
movdq_hi(accelComp));
#endif
assert((idx1 & idx2) == 0); // should be no shared bits
idx = idx1 | idx2;
return accelScanWrapper(accelTable, aux, input, idx, i, end);
}
size_t doAccel384(const m384 *state, const struct LimExNFA384 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end) {
u32 idx;
m384 s = *state;
DEBUG_PRINTF("using PSHUFB for 384-bit shuffle\n");
m384 accelPerm = limex->accelPermute;
m384 accelComp = limex->accelCompare;
u32 idx1 = shufflePshufb128(s.lo, accelPerm.lo, accelComp.lo);
u32 idx2 = shufflePshufb128(s.mid, accelPerm.mid, accelComp.mid);
u32 idx3 = shufflePshufb128(s.hi, accelPerm.hi, accelComp.hi);
assert((idx1 & idx2 & idx3) == 0); // should be no shared bits
idx = idx1 | idx2 | idx3;
return accelScanWrapper(accelTable, aux, input, idx, i, end);
}
size_t doAccel512(const m512 *state, const struct LimExNFA512 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end) {
u32 idx;
m512 s = *state;
DEBUG_PRINTF("using PSHUFB for 512-bit shuffle\n");
m512 accelPerm = limex->accelPermute;
m512 accelComp = limex->accelCompare;
#if !defined(__AVX2__)
u32 idx1 = shufflePshufb128(s.lo.lo, accelPerm.lo.lo, accelComp.lo.lo);
u32 idx2 = shufflePshufb128(s.lo.hi, accelPerm.lo.hi, accelComp.lo.hi);
u32 idx3 = shufflePshufb128(s.hi.lo, accelPerm.hi.lo, accelComp.hi.lo);
u32 idx4 = shufflePshufb128(s.hi.hi, accelPerm.hi.hi, accelComp.hi.hi);
#else
u32 idx1 = shufflePshufb128(movdq_lo(s.lo), movdq_lo(accelPerm.lo),
movdq_lo(accelComp.lo));
u32 idx2 = shufflePshufb128(movdq_hi(s.lo), movdq_hi(accelPerm.lo),
movdq_hi(accelComp.lo));
u32 idx3 = shufflePshufb128(movdq_lo(s.hi), movdq_lo(accelPerm.hi),
movdq_lo(accelComp.hi));
u32 idx4 = shufflePshufb128(movdq_hi(s.hi), movdq_hi(accelPerm.hi),
movdq_hi(accelComp.hi));
#endif
assert((idx1 & idx2 & idx3 & idx4) == 0); // should be no shared bits
idx = idx1 | idx2 | idx3 | idx4;
return accelScanWrapper(accelTable, aux, input, idx, i, end);
}

68
src/nfa/limex_accel.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2015, 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 Limex NFA: acceleration runtime.
*
* For the SIMD types (128 bits and above), we pass a pointer to the
* implementation NFA structure instead of three masks: otherwise we spend all
* our time building stack frames.
*/
#ifndef LIMEX_ACCEL_H
#define LIMEX_ACCEL_H
#include "util/simd_utils.h" // for m128 etc
union AccelAux;
struct LimExNFA128;
struct LimExNFA256;
struct LimExNFA384;
struct LimExNFA512;
size_t doAccel32(u32 s, u32 accel, const u8 *accelTable,
const union AccelAux *aux, const u8 *input, size_t i,
size_t end);
size_t doAccel128(const m128 *s, const struct LimExNFA128 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end);
size_t doAccel256(const m256 *s, const struct LimExNFA256 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end);
size_t doAccel384(const m384 *s, const struct LimExNFA384 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end);
size_t doAccel512(const m512 *s, const struct LimExNFA512 *limex,
const u8 *accelTable, const union AccelAux *aux,
const u8 *input, size_t i, size_t end);
#endif

404
src/nfa/limex_common_impl.h Normal file
View File

@@ -0,0 +1,404 @@
/*
* Copyright (c) 2015, 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.
*/
#include "repeat.h"
#include "util/join.h"
/* impl of limex functions which depend only on state size */
#if !defined(SIZE) || !defined(STATE_T) || !defined(INLINE_ATTR)
# error Must define SIZE and STATE_T and INLINE_ATTR in includer.
#endif
#define IMPL_NFA_T JOIN(struct LimExNFA, SIZE)
#define TESTEOD_FN JOIN(moNfaTestEod, SIZE)
#define TESTEOD_REV_FN JOIN(moNfaRevTestEod, SIZE)
#define LIMEX_INACCEPT_FN JOIN(limexInAccept, SIZE)
#define EXPIRE_ESTATE_FN JOIN(limexExpireExtendedState, SIZE)
#define REPORTCURRENT_FN JOIN(moNfaReportCurrent, SIZE)
#define INITIAL_FN JOIN(moNfaInitial, SIZE)
#define TOP_FN JOIN(moNfaTop, SIZE)
#define TOPN_FN JOIN(moNfaTopN, SIZE)
#define PROCESS_ACCEPTS_FN JOIN(moProcessAccepts, SIZE)
#define PROCESS_ACCEPTS_NOSQUASH_FN JOIN(moProcessAcceptsNoSquash, SIZE)
#define CONTEXT_T JOIN(NFAContext, SIZE)
#define ONES_STATE JOIN(ones_, STATE_T)
#define LOAD_STATE JOIN(load_, STATE_T)
#define STORE_STATE JOIN(store_, STATE_T)
#define AND_STATE JOIN(and_, STATE_T)
#define OR_STATE JOIN(or_, STATE_T)
#define ANDNOT_STATE JOIN(andnot_, STATE_T)
#define CLEARBIT_STATE JOIN(clearbit_, STATE_T)
#define TESTBIT_STATE JOIN(testbit_, STATE_T)
#define ISNONZERO_STATE JOIN(isNonZero_, STATE_T)
#define ISZERO_STATE JOIN(isZero_, STATE_T)
#define SQUASH_UNTUG_BR_FN JOIN(lazyTug, SIZE)
#define GET_NFA_REPEAT_INFO_FN JOIN(getNfaRepeatInfo, SIZE)
static really_inline
void SQUASH_UNTUG_BR_FN(const IMPL_NFA_T *limex,
const union RepeatControl *repeat_ctrl,
const char *repeat_state, u64a offset,
STATE_T *accstate) {
// switch off cyclic tug-accepts which aren't tuggable right now.
/* TODO: might be nice to work which br to examine based on accstate rather
* than iterating overall br */
if (!limex->repeatCount) {
return;
}
assert(repeat_ctrl);
assert(repeat_state);
for (u32 i = 0; i < limex->repeatCount; i++) {
const struct NFARepeatInfo *info = GET_NFA_REPEAT_INFO_FN(limex, i);
u32 cyclicState = info->cyclicState;
if (!TESTBIT_STATE(accstate, cyclicState)) {
continue;
}
DEBUG_PRINTF("repeat %u (cyclic state %u) is active\n", i, cyclicState);
DEBUG_PRINTF("checking if offset %llu would match\n", offset);
const union RepeatControl *ctrl = repeat_ctrl + i;
const char *state = repeat_state + info->stateOffset;
const struct RepeatInfo *repeat = getRepeatInfo(info);
if (repeatHasMatch(repeat, ctrl, state, offset) != REPEAT_MATCH) {
DEBUG_PRINTF("not ready to accept yet\n");
CLEARBIT_STATE(accstate, cyclicState);
}
}
}
static never_inline
char PROCESS_ACCEPTS_FN(const IMPL_NFA_T *limex, STATE_T *s,
const struct NFAAccept *acceptTable, u32 acceptCount,
u64a offset, NfaCallback callback, void *context) {
assert(s);
assert(limex);
assert(callback);
assert(acceptCount);
// We have squash masks we might have to apply after firing reports.
STATE_T squash = ONES_STATE;
const STATE_T *squashMasks = (const STATE_T *)
((const char *)limex + limex->squashOffset);
for (u32 i = 0; i < acceptCount; i++) {
const struct NFAAccept *a = &acceptTable[i];
if (TESTBIT_STATE(s, a->state)) {
DEBUG_PRINTF("state %u is on, firing report id=%u, offset=%llu\n",
a->state, a->externalId, offset);
int rv = callback(offset, a->externalId, context);
if (unlikely(rv == MO_HALT_MATCHING)) {
return 1;
}
if (a->squash != MO_INVALID_IDX) {
assert(a->squash < limex->squashCount);
const STATE_T *sq = &squashMasks[a->squash];
DEBUG_PRINTF("squash mask %u @ %p\n", a->squash, sq);
squash = AND_STATE(squash, LOAD_STATE(sq));
}
}
}
STORE_STATE(s, AND_STATE(LOAD_STATE(s), squash));
return 0;
}
static never_inline
char PROCESS_ACCEPTS_NOSQUASH_FN(const STATE_T *s,
const struct NFAAccept *acceptTable,
u32 acceptCount, u64a offset,
NfaCallback callback, void *context) {
assert(s);
assert(callback);
assert(acceptCount);
for (u32 i = 0; i < acceptCount; i++) {
const struct NFAAccept *a = &acceptTable[i];
if (TESTBIT_STATE(s, a->state)) {
DEBUG_PRINTF("state %u is on, firing report id=%u, offset=%llu\n",
a->state, a->externalId, offset);
int rv = callback(offset, a->externalId, context);
if (unlikely(rv == MO_HALT_MATCHING)) {
return 1;
}
}
}
return 0;
}
// Run EOD accepts.
static really_inline
char TESTEOD_FN(const IMPL_NFA_T *limex, const STATE_T *s,
const union RepeatControl *repeat_ctrl,
const char *repeat_state, u64a offset, char do_br,
NfaCallback callback, void *context) {
assert(limex && s);
// There may not be any EOD accepts in this NFA.
if (!limex->acceptEodCount) {
return MO_CONTINUE_MATCHING;
}
const STATE_T acceptEodMask = LOAD_STATE(&limex->acceptAtEOD);
STATE_T foundAccepts = AND_STATE(LOAD_STATE(s), acceptEodMask);
if (do_br) {
SQUASH_UNTUG_BR_FN(limex, repeat_ctrl, repeat_state,
offset + 1 /* EOD 'symbol' */, &foundAccepts);
} else {
assert(!limex->repeatCount);
}
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
const struct NFAAccept *acceptEodTable = getAcceptEodTable(limex);
if (PROCESS_ACCEPTS_NOSQUASH_FN(&foundAccepts, acceptEodTable,
limex->acceptEodCount, offset, callback,
context)) {
return MO_HALT_MATCHING;
}
}
return MO_CONTINUE_MATCHING;
}
static really_inline
char TESTEOD_REV_FN(const IMPL_NFA_T *limex, const STATE_T *s, u64a offset,
NfaCallback callback, void *context) {
assert(limex && s);
// There may not be any EOD accepts in this NFA.
if (!limex->acceptEodCount) {
return MO_CONTINUE_MATCHING;
}
STATE_T acceptEodMask = LOAD_STATE(&limex->acceptAtEOD);
STATE_T foundAccepts = AND_STATE(LOAD_STATE(s), acceptEodMask);
assert(!limex->repeatCount);
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
const struct NFAAccept *acceptEodTable = getAcceptEodTable(limex);
if (PROCESS_ACCEPTS_NOSQUASH_FN(&foundAccepts, acceptEodTable,
limex->acceptEodCount, offset, callback,
context)) {
return MO_HALT_MATCHING;
}
}
return MO_CONTINUE_MATCHING;
}
// Run accepts corresponding to current state.
static really_inline
char REPORTCURRENT_FN(const IMPL_NFA_T *limex, const struct mq *q) {
assert(limex && q);
assert(q->state);
assert(q_cur_type(q) == MQE_START);
STATE_T s = LOAD_STATE(q->state);
STATE_T acceptMask = LOAD_STATE(&limex->accept);
STATE_T foundAccepts = AND_STATE(s, acceptMask);
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
DEBUG_PRINTF("found accepts\n");
DEBUG_PRINTF("for nfa %p\n", limex);
const struct NFAAccept *acceptTable = getAcceptTable(limex);
u64a offset = q_cur_offset(q);
if (PROCESS_ACCEPTS_NOSQUASH_FN(&foundAccepts, acceptTable,
limex->acceptCount, offset, q->cb,
q->context)) {
return MO_HALT_MATCHING;
}
}
return MO_CONTINUE_MATCHING;
}
static really_inline
STATE_T INITIAL_FN(const IMPL_NFA_T *impl, char onlyDs) {
return LOAD_STATE(onlyDs ? &impl->initDS : &impl->init);
}
static really_inline
STATE_T TOP_FN(const IMPL_NFA_T *impl, char onlyDs, STATE_T state) {
return OR_STATE(INITIAL_FN(impl, onlyDs), state);
}
static really_inline
STATE_T TOPN_FN(const IMPL_NFA_T *limex, STATE_T state, u32 n) {
assert(n < limex->topCount);
const STATE_T *topsptr =
(const STATE_T *)((const char *)limex + limex->topOffset);
STATE_T top = LOAD_STATE(&topsptr[n]);
return OR_STATE(top, state);
}
static really_inline
void EXPIRE_ESTATE_FN(const IMPL_NFA_T *limex, struct CONTEXT_T *ctx,
u64a offset) {
assert(limex);
assert(ctx);
if (!limex->repeatCount) {
return;
}
DEBUG_PRINTF("expire estate at offset %llu\n", offset);
const STATE_T cyclics =
AND_STATE(LOAD_STATE(&ctx->s), LOAD_STATE(&limex->repeatCyclicMask));
if (ISZERO_STATE(cyclics)) {
DEBUG_PRINTF("no cyclic states are on\n");
return;
}
for (u32 i = 0; i < limex->repeatCount; i++) {
const struct NFARepeatInfo *info = GET_NFA_REPEAT_INFO_FN(limex, i);
u32 cyclicState = info->cyclicState;
if (!TESTBIT_STATE(&cyclics, cyclicState)) {
continue;
}
DEBUG_PRINTF("repeat %u (cyclic state %u) is active\n", i,
cyclicState);
const struct RepeatInfo *repeat = getRepeatInfo(info);
if (repeat->repeatMax == REPEAT_INF) {
continue; // can't expire
}
const union RepeatControl *repeat_ctrl = ctx->repeat_ctrl + i;
const char *repeat_state = ctx->repeat_state + info->stateOffset;
u64a last_top = repeatLastTop(repeat, repeat_ctrl, repeat_state);
assert(repeat->repeatMax < REPEAT_INF);
DEBUG_PRINTF("offset %llu, last_top %llu repeatMax %u\n", offset,
last_top, repeat->repeatMax);
u64a adj = 0;
/* if the cycle's tugs are active at repeat max, it is still alive */
if (TESTBIT_STATE((const STATE_T *)&limex->accept, cyclicState) ||
TESTBIT_STATE((const STATE_T *)&limex->acceptAtEOD, cyclicState)) {
DEBUG_PRINTF("lazy tug possible - may still be inspected\n");
adj = 1;
} else {
const STATE_T *tug_mask =
(const STATE_T *)((const char *)info + info->tugMaskOffset);
if (ISNONZERO_STATE(AND_STATE(ctx->s, LOAD_STATE(tug_mask)))) {
DEBUG_PRINTF("tug possible - may still be inspected\n");
adj = 1;
}
}
if (offset >= last_top + repeat->repeatMax + adj) {
DEBUG_PRINTF("repeat state is stale, squashing state %u\n",
cyclicState);
CLEARBIT_STATE(&ctx->s, cyclicState);
}
}
}
// Specialised inAccept call: LimEx NFAs with the "lazy tug" optimisation (see
// UE-1636) need to guard cyclic tug-accepts as well.
static really_inline
char LIMEX_INACCEPT_FN(const IMPL_NFA_T *limex, STATE_T state,
union RepeatControl *repeat_ctrl, char *repeat_state,
u64a offset, ReportID report) {
assert(limex);
const STATE_T acceptMask = LOAD_STATE(&limex->accept);
STATE_T accstate = AND_STATE(state, acceptMask);
// Are we in an accept state?
if (ISZERO_STATE(accstate)) {
DEBUG_PRINTF("no accept states are on\n");
return 0;
}
SQUASH_UNTUG_BR_FN(limex, repeat_ctrl, repeat_state, offset, &accstate);
DEBUG_PRINTF("looking for report %u\n", report);
#ifdef DEBUG
DEBUG_PRINTF("accept states that are on: ");
for (u32 i = 0; i < sizeof(STATE_T) * 8; i++) {
if (TESTBIT_STATE(&accstate, i)) printf("%u ", i);
}
printf("\n");
#endif
// Does one of our states match the given report ID?
const struct NFAAccept *acceptTable = getAcceptTable(limex);
for (u32 i = 0; i < limex->acceptCount; i++) {
const struct NFAAccept *a = &acceptTable[i];
DEBUG_PRINTF("checking idx=%u, externalId=%u\n", a->state,
a->externalId);
if (a->externalId == report && TESTBIT_STATE(&accstate, a->state)) {
DEBUG_PRINTF("report is on!\n");
return 1;
}
}
return 0;
}
#undef TESTEOD_FN
#undef TESTEOD_REV_FN
#undef REPORTCURRENT_FN
#undef EXPIRE_ESTATE_FN
#undef LIMEX_INACCEPT_FN
#undef INITIAL_FN
#undef TOP_FN
#undef TOPN_FN
#undef CONTEXT_T
#undef IMPL_NFA_T
#undef ONES_STATE
#undef LOAD_STATE
#undef STORE_STATE
#undef AND_STATE
#undef OR_STATE
#undef ANDNOT_STATE
#undef CLEARBIT_STATE
#undef TESTBIT_STATE
#undef ISNONZERO_STATE
#undef ISZERO_STATE
#undef PROCESS_ACCEPTS_FN
#undef PROCESS_ACCEPTS_NOSQUASH_FN
#undef SQUASH_UNTUG_BR_FN
#undef GET_NFA_REPEAT_INFO_FN
#undef SIZE
#undef STATE_T
#undef INLINE_ATTR

2179
src/nfa/limex_compile.cpp Normal file

File diff suppressed because it is too large Load Diff

99
src/nfa/limex_compile.h Normal file
View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2015, 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 Main NFA build code.
*/
#ifndef LIMEX_COMPILE_H
#define LIMEX_COMPILE_H
#include <map>
#include <memory>
#include <vector>
#include "ue2common.h"
#include "nfagraph/ng_holder.h"
#include "nfagraph/ng_squash.h" // for NFAStateSet
#include "util/alloc.h"
#include "util/ue2_containers.h"
struct NFA;
namespace ue2 {
struct BoundedRepeatData;
struct CompileContext;
/** \brief Construct a LimEx NFA from an NGHolder.
*
* \param g Input NFA graph. Must have state IDs assigned.
* \param repeats Bounded repeat information, if any.
* \param reportSquashMap Single-match mode squash map.
* \param squashMap More general squash map.
* \param tops Tops and their start vertices,
* \param zombies The set of zombifying states.
* \param do_accel Calculate acceleration schemes.
* \param stateCompression Allow (and calculate masks for) state compression.
* \param hint If not INVALID_NFA, this allows a particular LimEx NFA model
to be requested.
* \param cc Compile context.
* \return a built NFA, or nullptr if no NFA could be constructed for this
* graph.
*/
aligned_unique_ptr<NFA> generate(NGHolder &g,
const ue2::unordered_map<NFAVertex, u32> &states,
const std::vector<BoundedRepeatData> &repeats,
const std::map<NFAVertex, NFAStateSet> &reportSquashMap,
const std::map<NFAVertex, NFAStateSet> &squashMap,
const std::map<u32, NFAVertex> &tops,
const std::set<NFAVertex> &zombies,
bool do_accel,
bool stateCompression,
u32 hint,
const CompileContext &cc);
/**
* \brief For a given graph, count the number of accel states it will have in
* an implementation.
*
* \return the number of accel states, or NFA_MAX_ACCEL_STATES + 1 if an
* implementation would not be constructible.
*/
u32 countAccelStates(NGHolder &h,
const ue2::unordered_map<NFAVertex, u32> &states,
const std::vector<BoundedRepeatData> &repeats,
const std::map<NFAVertex, NFAStateSet> &reportSquashMap,
const std::map<NFAVertex, NFAStateSet> &squashMap,
const std::map<u32, NFAVertex> &tops,
const std::set<NFAVertex> &zombies,
const CompileContext &cc);
} // namespace ue2
#endif

76
src/nfa/limex_context.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2015, 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 Runtime context structures (NFAContext128 and friends) for the NFA.
*/
#ifndef LIMEX_CONTEXT_H
#define LIMEX_CONTEXT_H
#include "ue2common.h"
#include "callback.h"
#include "util/simd_utils.h" // for m128 etc
// Runtime context structures.
/* cached_estate/esucc etc...
*
* If the exception state matches the cached_estate we will apply
* the or in the cached_esucc to the successor states rather than processing
* the exceptions.
*
* If the current exception state is a superset of the cached_estate, the
* cache is NOT used at all.
*
* The cache is updated when we see a different cacheable estate.
*/
#define GEN_CONTEXT_STRUCT(nsize, ntype) \
struct ALIGN_CL_DIRECTIVE NFAContext##nsize { \
ntype s; /**< state bitvector (on entry/exit) */ \
ntype local_succ; /**< used by exception handling for large models */ \
ntype cached_estate; /* inited to 0 */ \
ntype cached_esucc; \
char cached_br; /**< cached_estate contains a br state */ \
const ReportID *cached_reports; \
union RepeatControl *repeat_ctrl; \
char *repeat_state; \
NfaCallback callback; \
void *context; \
};
GEN_CONTEXT_STRUCT(32, u32)
GEN_CONTEXT_STRUCT(128, m128)
GEN_CONTEXT_STRUCT(256, m256)
GEN_CONTEXT_STRUCT(384, m384)
GEN_CONTEXT_STRUCT(512, m512)
#undef GEN_CONTEXT_STRUCT
#endif

497
src/nfa/limex_dump.cpp Normal file
View File

@@ -0,0 +1,497 @@
/*
* Copyright (c) 2015, 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.
*/
#include "config.h"
#include "limex.h"
#include "accel.h"
#include "accel_dump.h"
#include "limex_internal.h"
#include "nfa_dump_internal.h"
#include "ue2common.h"
#include "util/dump_charclass.h"
#include "util/dump_mask.h"
#include "util/charreach.h"
#include <algorithm>
#include <cstdio>
#include <cctype>
#include <sstream>
#include <vector>
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
using namespace std;
namespace ue2 {
template<typename T> struct limex_traits {};
template<> struct limex_traits<LimExNFA512> {
static const u32 size = 512;
typedef NFAException512 exception_type;
};
template<> struct limex_traits<LimExNFA384> {
static const u32 size = 384;
typedef NFAException384 exception_type;
};
template<> struct limex_traits<LimExNFA256> {
static const u32 size = 256;
typedef NFAException256 exception_type;
};
template<> struct limex_traits<LimExNFA128> {
static const u32 size = 128;
typedef NFAException128 exception_type;
};
template<> struct limex_traits<LimExNFA32> {
static const u32 size = 32;
typedef NFAException32 exception_type;
};
static
void dumpMask(FILE *f, const char *name, const u8 *mask, u32 mask_bits) {
fprintf(f, "MSK %-20s %s\n", name, dumpMask(mask, mask_bits).c_str());
}
template <typename limex_type>
static
void dumpRepeats(const limex_type *limex, u32 model_size, FILE *f) {
fprintf(f, "\n");
fprintf(f, "%u bounded repeats.\n", limex->repeatCount);
const char *base = (const char *)limex;
const u32 *repeatOffset = (const u32 *)(base + limex->repeatOffset);
for (u32 i = 0; i < limex->repeatCount; i++) {
const NFARepeatInfo *info =
(const NFARepeatInfo *)(base + repeatOffset[i]);
const RepeatInfo *repeat =
(const RepeatInfo *)((const char *)info + sizeof(*info));
fprintf(f, " repeat %u: %s {%u,%u} packedCtrlSize=%u, "
"stateSize=%u\n",
i, repeatTypeName(repeat->type), repeat->repeatMin,
repeat->repeatMax, repeat->packedCtrlSize, repeat->stateSize);
fprintf(f, " nfa state: stream offset %u\n", info->stateOffset);
fprintf(f, " ");
const u8 *tug_mask = (const u8 *)info + info->tugMaskOffset;
dumpMask(f, "tugs", tug_mask, model_size);
}
fprintf(f, "\n");
}
static
void dumpLimexReachMasks(u32 model_size, const u8 *reach, u32 reachCount,
FILE *f) {
for (u32 i = 0; i < reachCount; i++) {
char tmp_common[100];
const u8 *row = reach + (i * (model_size/8));
sprintf(tmp_common, "reach mask %u ", i);
dumpMask(f, tmp_common, row, model_size);
}
}
static
void dumpLimexReachMap(const u8 *reachMap, FILE *f) {
for (u32 i = 0; i < N_CHARS; i++) {
fprintf(f, "reach 0x%02x ", i);
if (!isprint(i)) {
fprintf(f, " ");
} else {
fprintf(f, "'%c'", (char)i);
}
fprintf(f, " -> mask %hhu\n", reachMap[i]);
}
}
template<typename limex_type>
static
const NFA *limex_to_nfa(const limex_type *limex) {
return (const NFA *)((const char *)limex - sizeof(NFA));
}
template<typename limex_type>
static
void dumpAccel(const limex_type *limex, FILE *f) {
fprintf(f, "\n");
fprintf(f, "%u acceleration schemes.\n", limex->accelCount);
if (!limex->accelCount) {
return;
}
u32 tableOffset = limex->accelTableOffset;
u32 auxOffset = limex->accelAuxOffset;
const u8 *accelTable = (const u8 *)((const char *)limex + tableOffset);
const AccelAux *aux = (const AccelAux *)((const char *)limex + auxOffset);
for (u32 i = 0; i < limex->accelCount; i++) {
fprintf(f, " accel %u (aux entry %u): ", i, accelTable[i]);
dumpAccelInfo(f, aux[accelTable[i]]);
}
}
template<typename limex_type>
static
void dumpAccepts(const limex_type *limex, FILE *f) {
u32 acceptCount = limex->acceptCount;
u32 acceptEodCount = limex->acceptEodCount;
fprintf(f, "\n%u accepts.\n", acceptCount);
const struct NFAAccept *accepts
= (const struct NFAAccept *)((const char *)limex + limex->acceptOffset);
for (u32 i = 0; i < acceptCount; i++) {
fprintf(f, " state %u fires report %u\n", accepts[i].state,
accepts[i].externalId);
}
fprintf(f, "\n%u accepts at EOD.\n", acceptEodCount);
accepts = (const struct NFAAccept *)((const char *)limex
+ limex->acceptEodOffset);
for (u32 i = 0; i < acceptEodCount; i++) {
fprintf(f, " state %u fires report %u\n", accepts[i].state,
accepts[i].externalId);
}
fprintf(f, "\n");
}
template<typename limex_type>
static
void dumpSquash(const limex_type *limex, FILE *f) {
u32 size = limex_traits<limex_type>::size;
// Dump squash masks, if there are any.
const u8 *squashMask = (const u8 *)limex + limex->squashOffset;
for (u32 i = 0; i < limex->squashCount; i++) {
std::ostringstream name;
name << "squash_" << i;
dumpMask(f, name.str().c_str(), squashMask, size);
squashMask += size / 8;
}
}
template<typename limex_type>
static
const typename limex_traits<limex_type>::exception_type *
getExceptionTable(const limex_type *limex) {
return (const typename limex_traits<limex_type>::exception_type *)
((const char *)limex + limex->exceptionOffset);
}
template<typename limex_type>
static
const ReportID *getReportList(const limex_type *limex) {
return (const ReportID *)((const char *)limex + limex->exReportOffset);
}
template<typename limex_type>
static
void dumpLimexExceptions(const limex_type *limex, FILE *f) {
const typename limex_traits<limex_type>::exception_type *e =
getExceptionTable(limex);
const ReportID *reports = getReportList(limex);
const u32 size = limex_traits<limex_type>::size;
fprintf(f, "\n");
for (u32 i = 0; i < limex->exceptionCount; i++) {
fprintf(f, "exception %u: hasSquash=%u, reports offset=%u\n",
i, e[i].hasSquash, e[i].reports);
switch (e[i].trigger) {
case LIMEX_TRIGGER_TUG: fprintf(f, " trigger: TUG\n"); break;
case LIMEX_TRIGGER_POS: fprintf(f, " trigger: POS\n"); break;
default: break;
}
dumpMask(f, "succ", (const u8 *)&e[i].successors, size);
dumpMask(f, "squash", (const u8 *)&e[i].squash, size);
fprintf(f, "reports: ");
if (e[i].reports == MO_INVALID_IDX) {
fprintf(f, " <none>\n");
} else {
const ReportID *r = reports + e[i].reports;
while (*r != MO_INVALID_IDX) {
fprintf(f, " %u", *r++);
}
fprintf(f, "\n");
}
}
}
template<typename limex_type>
static
void dumpLimexText(const limex_type *limex, FILE *f) {
u32 size = limex_traits<limex_type>::size;
dumpMask(f, "init", (const u8 *)&limex->init, size);
dumpMask(f, "init_dot_star", (const u8 *)&limex->initDS, size);
dumpMask(f, "accept", (const u8 *)&limex->accept, size);
dumpMask(f, "accept_at_eod", (const u8 *)&limex->acceptAtEOD, size);
dumpMask(f, "accel", (const u8 *)&limex->accel, size);
dumpMask(f, "accel_and_friends", (const u8 *)&limex->accel_and_friends,
size);
dumpMask(f, "compress_mask", (const u8 *)&limex->compressMask, size);
dumpMask(f, "emask", (const u8 *)&limex->exceptionMask, size);
dumpMask(f, "zombie", (const u8 *)&limex->zombieMask, size);
// Dump top masks, if there are any.
u32 topCount = limex->topCount;
const u8 *topMask = (const u8 *)limex + limex->topOffset;
for (u32 i = 0; i < topCount; i++) {
std::ostringstream name;
name << "top_" << i;
dumpMask(f, name.str().c_str(), topMask, size);
topMask += size / 8;
}
dumpSquash(limex, f);
dumpLimexReachMap(limex->reachMap, f);
dumpLimexReachMasks(size, (const u8 *)limex + sizeof(*limex) /* reach*/,
limex->reachSize, f);
dumpAccepts(limex, f);
dumpLimexExceptions<limex_type>(limex, f);
dumpAccel<limex_type>(limex, f);
dumpRepeats(limex, size, f);
dumpTextReverse(limex_to_nfa(limex), f);
}
static
bool testbit(const u8 *mask, UNUSED u32 mask_bits, u32 bit) {
u32 byte = bit / 8;
return mask[byte] & (1 << (bit % 8));
}
static
void setupReach(const u8 *reachMap, const u8 *reachBase, u32 size,
u32 state_count, vector<CharReach> *perStateReach) {
for (u32 i = 0; i < state_count; i++) {
perStateReach->push_back(CharReach());
for (u32 j = 0; j < N_CHARS; j++) {
u8 k = reachMap[j];
const u8 *r = reachBase + k * (size/8);
if (testbit(r, size, i)) {
perStateReach->back().set(j);
}
}
}
}
namespace {
struct nfa_labeller {
virtual ~nfa_labeller() {}
virtual void label_state(FILE *f, u32 state) const = 0;
};
template<typename limex_type>
struct limex_labeller : public nfa_labeller {
explicit limex_labeller(const limex_type *limex_in) : limex(limex_in) {}
void label_state(FILE *f, u32 state) const {
const typename limex_traits<limex_type>::exception_type *exceptions
= getExceptionTable(limex);
if (!testbit((const u8 *)&limex->exceptionMask,
limex_traits<limex_type>::size, state)) {
return;
}
u32 ex_index = limex->exceptionMap[state];
const typename limex_traits<limex_type>::exception_type *e
= &exceptions[ex_index];
switch (e->trigger) {
case LIMEX_TRIGGER_TUG: fprintf(f, "\\nTUG"); break;
case LIMEX_TRIGGER_POS: fprintf(f, "\\nPOS"); break;
default: break;
}
}
const limex_type *limex;
};
}
template<typename limex_type>
static
void dumpVertexDotInfo(const limex_type *limex, u32 state_count, FILE *f,
const nfa_labeller &labeller) {
u32 size = sizeof(limex->init) * 8;
const u8 *reach = (const u8 *)limex + sizeof(*limex);
vector<CharReach> perStateReach;
setupReach(limex->reachMap, reach, size, state_count, &perStateReach);
const u8 *topMask = (const u8 *)limex + limex->topOffset;
for (u32 state = 0; state < state_count; state++) {
fprintf(f, "%u [ width = 1, fixedsize = true, fontsize = 12, "
"label = \"%u\\n", state, state);
assert(perStateReach[state].any());
describeClass(f, perStateReach[state], 5, CC_OUT_DOT);
labeller.label_state(f, state);
// bung in another couple lines to push char class (the widest thing) up a bit
fprintf(f, "\\n\\n\" ];\n");
if (testbit((const u8 *)&limex->acceptAtEOD, size, state)) {
fprintf(f, "%u [ shape = box ];\n", state);
} else if (testbit((const u8 *)&limex->accept, size, state)) {
fprintf(f, "%u [ shape = doublecircle ];\n", state);
}
if (testbit((const u8 *)&limex->accel, size, state)) {
fprintf(f, "%u [ color = red style = diagonals];\n", state);
}
if (testbit((const u8 *)&limex->init, size, state)) {
fprintf(f, "START -> %u [ color = grey ];\n", state);
}
// vertex could be in a top mask.
for (u32 i = 0; i < limex->topCount; i++) {
const u8 *msk = topMask + i * (size / 8);
if (testbit(msk, size, state)) {
fprintf(f, "START -> %u [ color = grey, "
"label = \"TOP %u\" ];\n",
state, i);
}
}
}
}
template<typename limex_type>
static
void dumpExDotInfo(const limex_type *limex, u32 state, FILE *f) {
u32 size = limex_traits<limex_type>::size;
if (!testbit((const u8 *)&limex->exceptionMask, size, state)) {
return; /* not exceptional */
}
const typename limex_traits<limex_type>::exception_type *exceptions
= getExceptionTable(limex);
u32 ex_index = limex->exceptionMap[state];
const typename limex_traits<limex_type>::exception_type *e
= &exceptions[ex_index];
u32 state_count = limex_to_nfa(limex)->nPositions;
for (u32 j = 0; j < state_count; j++) {
if (testbit((const u8 *)&e->successors, size, j)) {
fprintf(f, "%u -> %u [color = blue];\n", state, j);
}
if (!testbit((const u8 *)&e->squash, size, j)) {
fprintf(f, "%u -> %u [color = grey style = dashed];\n", state, j);
}
}
if (e->trigger != LIMEX_TRIGGER_NONE) {
fprintf(f, "%u [color = forestgreen];\n", state);
} else {
fprintf(f, "%u [color = blue];\n", state);
}
}
template<typename limex_type>
static
void dumpLimDotInfo(const limex_type *limex, u32 state, FILE *f) {
for (u32 j = 0; j < MAX_MAX_SHIFT; j++) {
if (testbit((const u8 *)&limex->shift[j],
limex_traits<limex_type>::size, state)) {
fprintf(f, "%u -> %u;\n", state, state + j);
}
}
}
#define DUMP_TEXT_FN(ddf_u, ddf_n, ddf_s) \
void nfaExecLimEx##ddf_n##_##ddf_s##_dumpText(const NFA *nfa, FILE *f) { \
dumpLimexText((const LimExNFA##ddf_n *)getImplNfa(nfa), f); \
}
#define DUMP_DOT_FN(ddf_u, ddf_n, ddf_s) \
void nfaExecLimEx##ddf_n##_##ddf_s##_dumpDot(const NFA *nfa, FILE *f) { \
const LimExNFA##ddf_n *limex = \
(const LimExNFA##ddf_n *)getImplNfa(nfa); \
\
dumpDotPreamble(f); \
u32 state_count = nfa->nPositions; \
dumpVertexDotInfo(limex, state_count, f, \
limex_labeller<LimExNFA##ddf_n>(limex)); \
for (u32 i = 0; i < state_count; i++) { \
dumpLimDotInfo(limex, i, f); \
dumpExDotInfo(limex, i, f); \
} \
\
dumpDotTrailer(f); \
}
#define LIMEX_DUMP_FNS(ntype, size, shifts) \
DUMP_TEXT_FN(ntype, size, shifts) \
DUMP_DOT_FN(ntype, size, shifts)
LIMEX_DUMP_FNS(u32, 32, 1)
LIMEX_DUMP_FNS(u32, 32, 2)
LIMEX_DUMP_FNS(u32, 32, 3)
LIMEX_DUMP_FNS(u32, 32, 4)
LIMEX_DUMP_FNS(u32, 32, 5)
LIMEX_DUMP_FNS(u32, 32, 6)
LIMEX_DUMP_FNS(u32, 32, 7)
LIMEX_DUMP_FNS(m128, 128, 1)
LIMEX_DUMP_FNS(m128, 128, 2)
LIMEX_DUMP_FNS(m128, 128, 3)
LIMEX_DUMP_FNS(m128, 128, 4)
LIMEX_DUMP_FNS(m128, 128, 5)
LIMEX_DUMP_FNS(m128, 128, 6)
LIMEX_DUMP_FNS(m128, 128, 7)
LIMEX_DUMP_FNS(m256, 256, 1)
LIMEX_DUMP_FNS(m256, 256, 2)
LIMEX_DUMP_FNS(m256, 256, 3)
LIMEX_DUMP_FNS(m256, 256, 4)
LIMEX_DUMP_FNS(m256, 256, 5)
LIMEX_DUMP_FNS(m256, 256, 6)
LIMEX_DUMP_FNS(m256, 256, 7)
LIMEX_DUMP_FNS(m384, 384, 1)
LIMEX_DUMP_FNS(m384, 384, 2)
LIMEX_DUMP_FNS(m384, 384, 3)
LIMEX_DUMP_FNS(m384, 384, 4)
LIMEX_DUMP_FNS(m384, 384, 5)
LIMEX_DUMP_FNS(m384, 384, 6)
LIMEX_DUMP_FNS(m384, 384, 7)
LIMEX_DUMP_FNS(m512, 512, 1)
LIMEX_DUMP_FNS(m512, 512, 2)
LIMEX_DUMP_FNS(m512, 512, 3)
LIMEX_DUMP_FNS(m512, 512, 4)
LIMEX_DUMP_FNS(m512, 512, 5)
LIMEX_DUMP_FNS(m512, 512, 6)
LIMEX_DUMP_FNS(m512, 512, 7)
} // namespace ue2

330
src/nfa/limex_exceptional.h Normal file
View File

@@ -0,0 +1,330 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: runtime exception processing code.
*
* X-macro generic impl, included into the various LimEx model implementations.
*/
#if !defined(SIZE) || !defined(STATE_T)
# error Must define SIZE and STATE_T in includer.
#endif
#include "config.h"
#include "limex_ring.h"
#include "util/join.h"
#include "util/uniform_ops.h"
#define PE_FN JOIN(processExceptional, SIZE)
#define RUN_EXCEPTION_FN JOIN(runException, SIZE)
#define ZERO_STATE JOIN(zero_, STATE_T)
#define LOAD_STATE JOIN(load_, STATE_T)
#define STORE_STATE JOIN(store_, STATE_T)
#define AND_STATE JOIN(and_, STATE_T)
#define EQ_STATE(a, b) (!JOIN(noteq_, STATE_T)((a), (b)))
#define OR_STATE JOIN(or_, STATE_T)
#define TESTBIT_STATE JOIN(testbit_, STATE_T)
#define EXCEPTION_T JOIN(struct NFAException, SIZE)
#define CONTEXT_T JOIN(NFAContext, SIZE)
#define IMPL_NFA_T JOIN(LimExNFA, SIZE)
#define GET_NFA_REPEAT_INFO_FN JOIN(getNfaRepeatInfo, SIZE)
#ifdef ESTATE_ON_STACK
#define ESTATE_ARG STATE_T estate
#else
#define ESTATE_ARG const STATE_T *estatep
#define estate LOAD_STATE(estatep)
#endif
#ifdef STATE_ON_STACK
#define STATE_ARG_NAME s
#define STATE_ARG STATE_T STATE_ARG_NAME
#define STATE_ARG_P &s
#else
#define STATE_ARG_NAME sp
#define STATE_ARG const STATE_T *STATE_ARG_NAME
#define STATE_ARG_P sp
#endif
#ifndef STATE_ON_STACK
#define BIG_MODEL
#endif
#ifdef ARCH_64_BIT
#define CHUNK_T u64a
#define FIND_AND_CLEAR_FN findAndClearLSB_64
#else
#define CHUNK_T u32
#define FIND_AND_CLEAR_FN findAndClearLSB_32
#endif
/** \brief Process a single exception. Returns 1 if exception handling should
* continue, 0 if an accept callback has instructed us to halt. */
static really_inline
int RUN_EXCEPTION_FN(const EXCEPTION_T *e, STATE_ARG,
STATE_T *succ,
#ifndef BIG_MODEL
STATE_T *local_succ,
#endif
const struct IMPL_NFA_T *limex,
const ReportID *exReports,
u64a offset,
struct CONTEXT_T *ctx,
struct proto_cache *new_cache,
enum CacheResult *cacheable,
char in_rev,
const char flags) {
assert(e);
#ifdef DEBUG_EXCEPTIONS
printf("EXCEPTION e=%p reports=%u trigger=", e, e->reports);
if (e->trigger == LIMEX_TRIGGER_NONE) {
printf("none");
} else if (e->trigger == LIMEX_TRIGGER_POS) {
printf("pos");
} else if (e->trigger == LIMEX_TRIGGER_TUG) {
printf("tug");
} else {
printf("unknown!");
}
printf("\n");
#endif
// Trigger exceptions, used in bounded repeats.
assert(!in_rev || e->trigger == LIMEX_TRIGGER_NONE);
if (!in_rev && e->trigger != LIMEX_TRIGGER_NONE) {
assert(e->repeatOffset != MO_INVALID_IDX);
const struct NFARepeatInfo *info =
(const struct NFARepeatInfo *)((const char *)limex +
e->repeatOffset);
const struct RepeatInfo *repeat = getRepeatInfo(info);
assert(ctx->repeat_ctrl && ctx->repeat_state);
union RepeatControl *repeat_ctrl = ctx->repeat_ctrl + info->ctrlIndex;
char *repeat_state = ctx->repeat_state + info->stateOffset;
if (e->trigger == LIMEX_TRIGGER_POS) {
char cyclic_on = TESTBIT_STATE(STATE_ARG_P, info->cyclicState);
processPosTrigger(repeat, repeat_ctrl, repeat_state, offset,
cyclic_on);
*cacheable = DO_NOT_CACHE_RESULT_AND_FLUSH_BR_ENTRIES;
} else {
assert(e->trigger == LIMEX_TRIGGER_TUG);
enum TriggerResult rv =
processTugTrigger(repeat, repeat_ctrl, repeat_state, offset);
if (rv == TRIGGER_FAIL) {
*cacheable = DO_NOT_CACHE_RESULT_AND_FLUSH_BR_ENTRIES;
DEBUG_PRINTF("tug found no valid matches in repeat state\n");
return 1; // continue
} else if (rv == TRIGGER_STALE) {
*cacheable = DO_NOT_CACHE_RESULT_AND_FLUSH_BR_ENTRIES;
DEBUG_PRINTF("stale history, squashing cyclic state\n");
assert(e->hasSquash == LIMEX_SQUASH_TUG);
STORE_STATE(succ, AND_STATE(LOAD_STATE(succ),
LOAD_STATE(&e->squash)));
return 1; // continue
} else if (rv == TRIGGER_SUCCESS_CACHE) {
new_cache->br = 1;
} else {
assert(rv == TRIGGER_SUCCESS);
*cacheable = DO_NOT_CACHE_RESULT_AND_FLUSH_BR_ENTRIES;
}
}
}
// Some exceptions fire accepts.
if (e->reports != MO_INVALID_IDX) {
if (flags & CALLBACK_OUTPUT) {
const ReportID *reports = exReports + e->reports;
if (unlikely(limexRunReports(reports, ctx->callback,
ctx->context, offset)
== MO_HALT_MATCHING)) {
DEBUG_PRINTF("callback instructed us to stop\n");
return 0; // halt
}
if (*cacheable == CACHE_RESULT) {
if (!new_cache->reports || new_cache->reports == reports) {
new_cache->reports = reports;
} else {
*cacheable = DO_NOT_CACHE_RESULT;
}
}
} else {
if ((flags & FIRST_BYTE) && *cacheable == CACHE_RESULT) {
*cacheable = DO_NOT_CACHE_RESULT;
} /* otherwise we can cache as we never care about accepts */
}
}
// Most exceptions have a set of successors to switch on. `local_succ' is
// ORed into `succ' at the end of the caller's loop.
#ifndef BIG_MODEL
*local_succ = OR_STATE(*local_succ, LOAD_STATE(&e->successors));
#else
STORE_STATE(&ctx->local_succ, OR_STATE(LOAD_STATE(&ctx->local_succ),
LOAD_STATE(&e->successors)));
#endif
// Some exceptions squash states behind them. Note that we squash states in
// 'succ', not local_succ.
if (e->hasSquash == LIMEX_SQUASH_CYCLIC ||
e->hasSquash == LIMEX_SQUASH_REPORT) {
STORE_STATE(succ, AND_STATE(LOAD_STATE(succ),
LOAD_STATE(&e->squash)));
if (*cacheable == CACHE_RESULT) {
*cacheable = DO_NOT_CACHE_RESULT;
}
}
return 1; // continue
}
#ifndef RUN_EXCEPTION_FN_ONLY
/** \brief Process all of the exceptions associated with the states in the \a estate. */
static really_inline
int PE_FN(STATE_ARG, ESTATE_ARG, u32 diffmask, STATE_T *succ,
const struct IMPL_NFA_T *limex,
const u32 *exceptionMap, const EXCEPTION_T *exceptions,
const ReportID *exReports,
u64a offset, struct CONTEXT_T *ctx, char in_rev, char flags) {
assert(diffmask > 0); // guaranteed by caller macro
if (EQ_STATE(estate, LOAD_STATE(&ctx->cached_estate))) {
DEBUG_PRINTF("using cached succ from previous state\n");
STORE_STATE(succ, OR_STATE(LOAD_STATE(succ), LOAD_STATE(&ctx->cached_esucc)));
if (ctx->cached_reports) {
if (unlikely(limexRunReports(ctx->cached_reports, ctx->callback,
ctx->context, offset)
== MO_HALT_MATCHING)) {
return PE_RV_HALT; // halt;
}
}
return 0;
}
#ifndef BIG_MODEL
STATE_T local_succ = ZERO_STATE;
#else
STORE_STATE(&ctx->local_succ, ZERO_STATE);
#endif
// A copy of the estate as an array of GPR-sized chunks.
CHUNK_T chunks[sizeof(STATE_T) / sizeof(CHUNK_T)];
#ifdef ESTATE_ON_STACK
memcpy(chunks, &estate, sizeof(STATE_T));
#else
memcpy(chunks, estatep, sizeof(STATE_T));
#endif
struct proto_cache new_cache = {0, NULL};
enum CacheResult cacheable = CACHE_RESULT;
do {
u32 t = findAndClearLSB_32(&diffmask);
#ifdef ARCH_64_BIT
t >>= 1; // Due to diffmask64, which leaves holes in the bitmask.
#endif
assert(t < ARRAY_LENGTH(chunks));
CHUNK_T word = chunks[t];
assert(word != 0);
u32 base = t * sizeof(CHUNK_T) * 8;
do {
u32 bit = FIND_AND_CLEAR_FN(&word) + base;
u32 idx = exceptionMap[bit];
const EXCEPTION_T *e = &exceptions[idx];
if (!RUN_EXCEPTION_FN(e, STATE_ARG_NAME, succ,
#ifndef BIG_MODEL
&local_succ,
#endif
limex, exReports, offset, ctx, &new_cache,
&cacheable, in_rev, flags)) {
return PE_RV_HALT;
}
} while (word);
} while (diffmask);
#ifndef BIG_MODEL
STORE_STATE(succ, OR_STATE(LOAD_STATE(succ), local_succ));
#else
STORE_STATE(succ, OR_STATE(LOAD_STATE(succ), ctx->local_succ));
#endif
if (cacheable == CACHE_RESULT) {
STORE_STATE(&ctx->cached_estate, estate);
#ifndef BIG_MODEL
ctx->cached_esucc = local_succ;
#else
STORE_STATE(&ctx->cached_esucc, LOAD_STATE(&ctx->local_succ));
#endif
ctx->cached_reports = new_cache.reports;
ctx->cached_br = new_cache.br;
} else if (cacheable == DO_NOT_CACHE_RESULT_AND_FLUSH_BR_ENTRIES) {
if (ctx->cached_br) {
STORE_STATE(&ctx->cached_estate, ZERO_STATE);
}
}
return 0;
}
#endif
#undef ZERO_STATE
#undef AND_STATE
#undef EQ_STATE
#undef OR_STATE
#undef TESTBIT_STATE
#undef LOAD_STATE
#undef STORE_STATE
#undef PE_FN
#undef RUN_EXCEPTION_FN
#undef CONTEXT_T
#undef EXCEPTION_T
#ifdef estate
#undef estate
#endif
#ifdef BIG_MODEL
#undef BIG_MODEL
#endif
#undef STATE_ARG
#undef STATE_ARG_NAME
#undef STATE_ARG_P
#undef CHUNK_T
#undef FIND_AND_CLEAR_FN
#undef IMPL_NFA_T
#undef GET_NFA_REPEAT_INFO_FN
// Parameters.
#undef SIZE
#undef STATE_T

204
src/nfa/limex_internal.h Normal file
View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 2015, 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
This file provides the internal structures and definitions required for the
real NFAs (aka limex NFAs );
Limex NFAs now have variable length in memory. They look like this:
LimExNFA structure
Fixed length, e.g. LimExNFA256.
Reachability table
Variable length array of state bitvectors, mapped into by
NFACommonXXX.reachMap.
Tops
Variable length array of state bitvectors, used for TOP_N events.
Acceleration structures
Variable length array of AccelAux structs.
Accepts
Variable length array of NFAAccept structs.
EOD Accepts
Variable length array of NFAAccept structs.
Exceptions
Variable length array of NFAExceptionXXX structs.
Repeat Structure Offsets
Array of u32 offsets that point at each "Repeat Structure" (below)
Repeat Structures
Variable length repeat structures, addressed via
NFAException32::repeatOffset etc.
The state associated with the NFA is split into:
-# The "traditional" NFA state as a bitvector. This is stored in the
first N bytes of the state space (length given in
NFACommonXXX.stateSize), and may be stored shrunk to CEIL(stateSize/8)
or compressed. If it is stored compressed, than the
LIMEX_FLAG_COMPRESS_STATE flag is set in NFACommonXXX.flags.
-# Extended NFA state, only used in some LimEx NFAs. This consists of a
variable length array of LimExNFAExtendedState structures, each with
pointers to a packed list of mmbit structures that follows them. Only
present when used.
The value of NFA.stateSize gives the total state size in bytes (the sum of
all the above).
*/
#ifndef LIMEX_INTERNAL_H
#define LIMEX_INTERNAL_H
#include "nfa_internal.h"
#include "repeat_internal.h"
// Constants
#define MAX_MAX_SHIFT 8 /**< largest maxshift used by a LimEx NFA */
#define LIMEX_FLAG_COMPRESS_STATE 1 /**< pack state into stream state */
#define LIMEX_FLAG_COMPRESS_MASKED 2 /**< use reach mask-based compression */
enum LimExTrigger {
LIMEX_TRIGGER_NONE = 0,
LIMEX_TRIGGER_POS = 1,
LIMEX_TRIGGER_TUG = 2
};
enum LimExSquash {
LIMEX_SQUASH_NONE = 0, //!< no squash for you!
LIMEX_SQUASH_CYCLIC = 1, //!< squash due to cyclic state
LIMEX_SQUASH_TUG = 2, //!< squash due to tug trigger with stale estate
LIMEX_SQUASH_REPORT = 3 //!< squash when report is raised
};
struct LimExNFABase {
u8 reachMap[N_CHARS];
u32 reachSize;
u32 accelCount;
u32 accelTableOffset;
u32 accelAuxCount;
u32 accelAuxOffset;
u32 acceptCount;
u32 acceptOffset;
u32 acceptEodCount;
u32 acceptEodOffset;
u32 exceptionCount;
u32 exceptionOffset;
u32 exReportOffset;
u32 repeatCount;
u32 repeatOffset;
};
/* uniform looking types for the macros */
typedef u8 u_8;
typedef u16 u_16;
typedef u32 u_32;
typedef u64a u_64;
typedef m128 u_128;
typedef m256 u_256;
typedef m384 u_384;
typedef m512 u_512;
#define CREATE_NFA_LIMEX(size) \
struct NFAException##size { \
u_##size squash; /**< mask of states to leave on */ \
u_##size successors; /**< mask of states to switch on */ \
u32 reports; /**< offset to start of reports list, or MO_INVALID_IDX */ \
u32 repeatOffset; /**< offset to NFARepeatInfo, or MO_INVALID_IDX */ \
u8 hasSquash; /**< from enum LimExSquash */ \
u8 trigger; /**< from enum LimExTrigger */ \
}; \
\
struct LimExNFA##size { /* MUST align with LimExNFABase */ \
u8 reachMap[N_CHARS]; /**< map of char -> entry in reach[] */ \
u32 reachSize; /**< number of reach masks */ \
u32 accelCount; /**< number of entries in accel table */ \
u32 accelTableOffset; /* rel. to start of LimExNFA */ \
u32 accelAuxCount; /**< number of entries in aux table */ \
u32 accelAuxOffset; /* rel. to start of LimExNFA */ \
u32 acceptCount; \
u32 acceptOffset; /* rel. to start of LimExNFA */ \
u32 acceptEodCount; \
u32 acceptEodOffset; /* rel. to start of LimExNFA */ \
u32 exceptionCount; \
u32 exceptionOffset; /* rel. to start of LimExNFA */ \
u32 exReportOffset; /* rel. to start of LimExNFA */ \
u32 repeatCount; \
u32 repeatOffset; \
u32 exceptionMap[size]; \
u32 squashOffset; /* rel. to start of LimExNFA; for accept squashing */ \
u32 squashCount; \
u32 topCount; \
u32 topOffset; /* rel. to start of LimExNFA */ \
u32 stateSize; /**< not including extended history */ \
u32 flags; \
u_##size init; \
u_##size initDS; \
u_##size accept; /**< mask of accept states */ \
u_##size acceptAtEOD; /**< mask of states that accept at EOD */ \
u_##size accel; /**< mask of accelerable states */ \
u_##size accelPermute; /**< pshufb permute mask (not GPR) */ \
u_##size accelCompare; /**< pshufb compare mask (not GPR) */ \
u_##size accel_and_friends; /**< mask of accelerable states + likely
* followers */ \
u_##size compressMask; /**< switch off before compress */ \
u_##size exceptionMask; \
u_##size repeatCyclicMask; \
u_##size shift[MAX_MAX_SHIFT]; \
u_##size zombieMask; /**< zombie if in any of the set states */ \
};
CREATE_NFA_LIMEX(32)
CREATE_NFA_LIMEX(128)
CREATE_NFA_LIMEX(256)
CREATE_NFA_LIMEX(384)
CREATE_NFA_LIMEX(512)
/** \brief Structure describing a bounded repeat within the LimEx NFA.
*
* This struct is followed in memory by:
*
* -# a RepeatInfo structure
* -# a variable-sized lookup table for REPEAT_SPARSE_OPTIMAL_P repeats
* -# a TUG mask
*/
struct NFARepeatInfo {
u32 cyclicState; //!< index of this repeat's cyclic state
u32 ctrlIndex; //!< index of this repeat's control block
u32 packedCtrlOffset; //!< offset to packed control block in stream state
u32 stateOffset; //!< offset to repeat state in stream state
u32 stateSize; //!< total size of packed stream state for this repeat
u32 tugMaskOffset; //!< offset to tug mask (rel. to NFARepeatInfo)
};
struct NFAAccept {
u32 state; //!< state ID of triggering state
ReportID externalId; //!< report ID to raise
u32 squash; //!< offset into masks, or MO_INVALID_IDX
};
#endif

36
src/nfa/limex_limits.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef LIMEX_LIMITS_H
#define LIMEX_LIMITS_H
#define NFA_MAX_STATES 512 /**< max states in an NFA */
#define NFA_MAX_ACCEL_STATES 8 /**< max accel states in a NFA */
#define NFA_MAX_TOP_MASKS 32 /**< max number of MQE_TOP_N event types */
#endif

163
src/nfa/limex_native.c Normal file
View File

@@ -0,0 +1,163 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: native GPR runtime implementations.
*/
//#define DEBUG
//#define DEBUG_INPUT
//#define DEBUG_EXCEPTIONS
#include "limex.h"
#include "accel.h"
#include "limex_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/bitutils.h"
// Common code
#define STATE_ON_STACK
#define ESTATE_ON_STACK
#include "limex_runtime.h"
// Other implementation code from X-Macro impl.
#define SIZE 32
#define STATE_T u32
#include "limex_state_impl.h"
#define SIZE 32
#define STATE_T u32
#define INLINE_ATTR really_inline
#include "limex_common_impl.h"
////////////////////////////////////////////////////////////////////////////
// LimEx NFA implementation code - general purpose registers
////////////////////////////////////////////////////////////////////////////
// Process exceptional states
#define SIZE 32
#define STATE_T u32
#define STATE_ON_STACK
#define ESTATE_ON_STACK
#define RUN_EXCEPTION_FN_ONLY
#include "limex_exceptional.h"
static really_inline
int processExceptional32(u32 s, u32 estate, UNUSED u32 diffmask, u32 *succ,
const struct LimExNFA32 *limex,
const u32 *exceptionMap,
const struct NFAException32 *exceptions,
const ReportID *exReports, u64a offset,
struct NFAContext32 *ctx, char in_rev, char flags) {
assert(estate != 0); // guaranteed by calling macro
if (estate == ctx->cached_estate) {
DEBUG_PRINTF("using cached succ from previous state\n");
*succ |= ctx->cached_esucc;
if (ctx->cached_reports) {
if (unlikely(limexRunReports(ctx->cached_reports, ctx->callback,
ctx->context, offset)
== MO_HALT_MATCHING)) {
return PE_RV_HALT; // halt;
}
}
return 0;
}
u32 orig_estate = estate; // for caching
u32 local_succ = 0;
struct proto_cache new_cache = {0, NULL};
enum CacheResult cacheable = CACHE_RESULT;
/* Note that only exception-states that consist of exceptions that _only_
* set successors (not fire accepts or squash states) are cacheable. */
do {
u32 bit = findAndClearLSB_32(&estate);
u32 idx = exceptionMap[bit];
const struct NFAException32 *e = &exceptions[idx];
if (!runException32(e, s, succ, &local_succ, limex, exReports, offset,
ctx, &new_cache, &cacheable, in_rev, flags)) {
return PE_RV_HALT;
}
} while (estate != 0);
*succ |= local_succ;
if (cacheable == CACHE_RESULT) {
ctx->cached_estate = orig_estate;
ctx->cached_esucc = local_succ;
ctx->cached_reports = new_cache.reports;
ctx->cached_br = new_cache.br;
} else if (cacheable == DO_NOT_CACHE_RESULT_AND_FLUSH_BR_ENTRIES) {
ctx->cached_estate = 0U;
}
return 0;
}
// 32-bit models.
#define SIZE 32
#define STATE_T u32
#define SHIFT 1
#include "limex_runtime_impl.h"
#define SIZE 32
#define STATE_T u32
#define SHIFT 2
#include "limex_runtime_impl.h"
#define SIZE 32
#define STATE_T u32
#define SHIFT 3
#include "limex_runtime_impl.h"
#define SIZE 32
#define STATE_T u32
#define SHIFT 4
#include "limex_runtime_impl.h"
#define SIZE 32
#define STATE_T u32
#define SHIFT 5
#include "limex_runtime_impl.h"
#define SIZE 32
#define STATE_T u32
#define SHIFT 6
#include "limex_runtime_impl.h"
#define SIZE 32
#define STATE_T u32
#define SHIFT 7
#include "limex_runtime_impl.h"

106
src/nfa/limex_ring.h Normal file
View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2015, 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 Bounded Repeat implementation for the LimEx NFA.
*/
#ifndef LIMEX_RING_H
#define LIMEX_RING_H
#include "ue2common.h"
#include "repeat.h"
#ifdef __cplusplus
extern "C"
{
#endif
/** \brief Return values from \ref processTugTrigger, used to provide feedback
* about a bounded repeat to the caller.
*
* TRIGGER_FAIL does not get cached as we prefer to use TRIGGER_STALE which
* allows the exception to squash the cyclic state as well. */
enum TriggerResult {
TRIGGER_FAIL, /**< no valid matches, but history still valid */
TRIGGER_SUCCESS, /**< valid match found */
TRIGGER_STALE, /**< no valid matches and history is invalid (stale) */
TRIGGER_SUCCESS_CACHE /**< valid match found; can cache as the repeat has no
upper bound. */
};
/** \brief Handle a TUG trigger: given an \p offset, returns whether a repeat
* matches or not. */
static really_inline
enum TriggerResult processTugTrigger(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const char *state, u64a offset) {
DEBUG_PRINTF("tug trigger, %s history, repeat={%u,%u}, offset=%llu, "
"ctrl=%p, state=%p\n",
repeatTypeName(info->type), info->repeatMin, info->repeatMax,
offset, ctrl, state);
assert(ISALIGNED(ctrl));
enum RepeatMatch rv = repeatHasMatch(info, ctrl, state, offset);
switch (rv) {
case REPEAT_NOMATCH:
return TRIGGER_FAIL;
case REPEAT_STALE:
return TRIGGER_STALE;
case REPEAT_MATCH:
if (info->repeatMax == REPEAT_INF) {
// {N,} repeats can be cached.
return TRIGGER_SUCCESS_CACHE;
} else {
return TRIGGER_SUCCESS;
}
}
assert(0); // unreachable
return TRIGGER_FAIL;
}
/** \brief Handle a POS trigger: stores a top in the repeat. */
static really_inline
void processPosTrigger(const struct RepeatInfo *info, union RepeatControl *ctrl,
char *state, u64a offset, char is_alive) {
DEBUG_PRINTF("pos trigger, %s history, repeat={%u,%u}, offset=%llu, "
"is_alive=%d\n", repeatTypeName(info->type),
info->repeatMin, info->repeatMax, offset, is_alive);
assert(ISALIGNED(ctrl));
repeatStore(info, ctrl, state, offset, is_alive);
}
#ifdef __cplusplus
}
#endif
#endif

207
src/nfa/limex_runtime.h Normal file
View File

@@ -0,0 +1,207 @@
/*
* Copyright (c) 2015, 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 Limex Execution Engine Or:
How I Learned To Stop Worrying And Love The Preprocessor
This file includes utility functions which do not depend on the state size or
shift masks directly.
*/
#ifndef LIMEX_RUNTIME_H
#define LIMEX_RUNTIME_H
#include "limex_accel.h"
#include "limex_context.h"
#include "limex_internal.h"
#include "nfa_api_util.h"
#include "nfa_internal.h"
#include "scratch.h"
#include "util/uniform_ops.h"
////////////////////////////////////////////////////////////////////////////
// LimEx NFA implementation code - common macros
////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_INPUT
#include <ctype.h>
#define DUMP_INPUT(index) DEBUG_PRINTF("input %p i=%zu: %02hhx (%c)\n", \
&input[index], index, input[index], \
isprint(input[index]) ? input[index] : ' ')
#else
#define DUMP_INPUT(index) do { } while(0)
#endif
#define NO_OUTPUT 0
#define CALLBACK_OUTPUT 1
#define FIRST_BYTE 16
enum CacheResult {
DO_NOT_CACHE_RESULT,
CACHE_RESULT,
DO_NOT_CACHE_RESULT_AND_FLUSH_BR_ENTRIES
};
struct proto_cache {
char br;
const ReportID *reports;
};
// Shift macros for Limited NFAs. Defined in terms of uniform ops.
#define NFA_EXEC_LIM_SHIFT(nels_type, nels_i) \
(JOIN(shift_, nels_type)( \
JOIN(and_, nels_type)(s, \
JOIN(load_, nels_type)(&limex->shift[nels_i])), \
nels_i))
// Calculate the (limited model) successors for a given max shift. Assumes
// LimExNFAxxx ptr in 'l', current state in 's' and successors in 'succ'.
#define NFA_EXEC_GET_LIM_SUCC(gls_type, gls_shift) \
do { \
succ = \
JOIN(and_, gls_type)(s, JOIN(load_, gls_type)(&limex->shift[0])); \
switch (gls_shift) { \
case 7: \
succ = JOIN(or_, gls_type)(succ, NFA_EXEC_LIM_SHIFT(gls_type, 7)); \
case 6: \
succ = JOIN(or_, gls_type)(succ, NFA_EXEC_LIM_SHIFT(gls_type, 6)); \
case 5: \
succ = JOIN(or_, gls_type)(succ, NFA_EXEC_LIM_SHIFT(gls_type, 5)); \
case 4: \
succ = JOIN(or_, gls_type)(succ, NFA_EXEC_LIM_SHIFT(gls_type, 4)); \
case 3: \
succ = JOIN(or_, gls_type)(succ, NFA_EXEC_LIM_SHIFT(gls_type, 3)); \
case 2: \
succ = JOIN(or_, gls_type)(succ, NFA_EXEC_LIM_SHIFT(gls_type, 2)); \
case 1: \
succ = JOIN(or_, gls_type)(succ, NFA_EXEC_LIM_SHIFT(gls_type, 1)); \
case 0: \
; \
} \
} while (0)
#define PE_RV_HALT 1
#ifdef STATE_ON_STACK
#define pass_state s
#else
#define pass_state &s
#endif
#ifdef ESTATE_ON_STACK
#define pass_estate estate
#else
#define pass_estate &estate
#endif
static really_inline
int limexRunReports(const ReportID *reports, NfaCallback callback,
void *context, u64a offset) {
assert(reports);
assert(callback);
for (; *reports != MO_INVALID_IDX; ++reports) {
DEBUG_PRINTF("firing report for id %u at offset %llu\n",
*reports, offset);
int rv = callback(offset, *reports, context);
if (rv == MO_HALT_MATCHING) {
return MO_HALT_MATCHING;
}
}
return MO_CONTINUE_MATCHING; // continue
}
/** \brief Return a (correctly typed) pointer to the exception table. */
#define getExceptionTable(exc_type, lim) \
((const exc_type *)((const char *)(lim) + (lim)->exceptionOffset))
/** \brief Return a pointer to the exceptional reports list. */
#define getExReports(lim) \
((const ReportID *)((const char *)(lim) + (lim)->exReportOffset))
/** \brief Return a pointer to the ordinary accepts table. */
#define getAcceptTable(lim) \
((const struct NFAAccept *)((const char *)(lim) + (lim)->acceptOffset))
/** \brief Return a pointer to the EOD accepts table. */
#define getAcceptEodTable(lim) \
((const struct NFAAccept *)((const char *)(lim) + (lim)->acceptEodOffset))
#define MAKE_GET_NFA_REPEAT_INFO(size) \
static really_inline const struct NFARepeatInfo *getNfaRepeatInfo##size( \
const struct LimExNFA##size *limex, unsigned num) { \
assert(num < limex->repeatCount); \
\
const char *base = (const char *)limex; \
const u32 *repeatOffset = (const u32 *)(base + limex->repeatOffset); \
assert(ISALIGNED(repeatOffset)); \
\
const struct NFARepeatInfo *info = \
(const struct NFARepeatInfo *)(base + repeatOffset[num]); \
assert(ISALIGNED(info)); \
return info; \
}
MAKE_GET_NFA_REPEAT_INFO(32)
MAKE_GET_NFA_REPEAT_INFO(128)
MAKE_GET_NFA_REPEAT_INFO(256)
MAKE_GET_NFA_REPEAT_INFO(384)
MAKE_GET_NFA_REPEAT_INFO(512)
static really_inline
const struct RepeatInfo *getRepeatInfo(const struct NFARepeatInfo *info) {
const struct RepeatInfo *repeat =
(const struct RepeatInfo *)((const char *)info + sizeof(*info));
assert(ISALIGNED(repeat));
return repeat;
}
static really_inline
union RepeatControl *getRepeatControlBase(char *state, size_t nfa_state_size) {
union RepeatControl *ctrl_base =
(union RepeatControl *)(state +
ROUNDUP_N(nfa_state_size,
alignof(union RepeatControl)));
assert(ISALIGNED(ctrl_base));
return ctrl_base;
}
static really_inline
const union RepeatControl *getRepeatControlBaseConst(const char *state,
size_t nfa_state_size) {
const union RepeatControl *ctrl_base =
(const union RepeatControl *)(state +
ROUNDUP_N(nfa_state_size,
alignof(union RepeatControl)));
assert(ISALIGNED(ctrl_base));
return ctrl_base;
}
#endif

View File

@@ -0,0 +1,948 @@
/*
* Copyright (c) 2015, 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.
*/
#include "util/join.h"
#include <string.h>
/** \file
* \brief Limex Execution Engine Or:
* How I Learned To Stop Worrying And Love The Preprocessor
*
* Version 2.0: now with X-Macros, so you get line numbers in your debugger.
*/
#if !defined(SIZE) || !defined(STATE_T) || !defined(SHIFT)
# error Must define SIZE and STATE_T and SHIFT in includer.
#endif
#define LIMEX_API_ROOT JOIN(JOIN(JOIN(nfaExecLimEx, SIZE), _), SHIFT)
#define IMPL_NFA_T JOIN(struct LimExNFA, SIZE)
#define TESTEOD_FN JOIN(moNfaTestEod, SIZE)
#define TESTEOD_REV_FN JOIN(moNfaRevTestEod, SIZE)
#define INITIAL_FN JOIN(moNfaInitial, SIZE)
#define TOP_FN JOIN(moNfaTop, SIZE)
#define TOPN_FN JOIN(moNfaTopN, SIZE)
#define REPORTCURRENT_FN JOIN(moNfaReportCurrent, SIZE)
#define COMPRESS_FN JOIN(moNfaCompressState, SIZE)
#define EXPAND_FN JOIN(moNfaExpandState, SIZE)
#define COMPRESS_REPEATS_FN JOIN(LIMEX_API_ROOT, _Compress_Repeats)
#define EXPAND_REPEATS_FN JOIN(LIMEX_API_ROOT, _Expand_Repeats)
#define PROCESS_ACCEPTS_FN JOIN(moProcessAccepts, SIZE)
#define PROCESS_ACCEPTS_NOSQUASH_FN JOIN(moProcessAcceptsNoSquash, SIZE)
#define GET_NFA_REPEAT_INFO_FN JOIN(getNfaRepeatInfo, SIZE)
#define RUN_ACCEL_FN JOIN(LIMEX_API_ROOT, _Run_Accel)
#define RUN_EXCEPTIONS_FN JOIN(LIMEX_API_ROOT, _Run_Exceptions)
#define REV_STREAM_FN JOIN(LIMEX_API_ROOT, _Rev_Stream)
#define STREAM_FN JOIN(LIMEX_API_ROOT, _Stream)
#define STREAMCB_FN JOIN(LIMEX_API_ROOT, _Stream_CB)
#define STREAMFIRST_FN JOIN(LIMEX_API_ROOT, _Stream_First)
#define STREAMSILENT_FN JOIN(LIMEX_API_ROOT, _Stream_Silent)
#define CONTEXT_T JOIN(NFAContext, SIZE)
#define EXCEPTION_T JOIN(struct NFAException, SIZE)
#define LOAD_STATE JOIN(load_, STATE_T)
#define STORE_STATE JOIN(store_, STATE_T)
#define AND_STATE JOIN(and_, STATE_T)
#define ANDNOT_STATE JOIN(andnot_, STATE_T)
#define OR_STATE JOIN(or_, STATE_T)
#define TESTBIT_STATE JOIN(testbit_, STATE_T)
#define ZERO_STATE JOIN(zero_, STATE_T)
#define ISNONZERO_STATE JOIN(isNonZero_, STATE_T)
#define ISZERO_STATE JOIN(isZero_, STATE_T)
#define NOTEQ_STATE JOIN(noteq_, STATE_T)
// Pick an appropriate diffrich function for this platform.
#ifdef ARCH_64_BIT
#define DIFFRICH_STATE JOIN(diffrich64_, STATE_T)
#else
#define DIFFRICH_STATE JOIN(diffrich_, STATE_T)
#endif
#define EXPIRE_ESTATE_FN JOIN(limexExpireExtendedState, SIZE)
#define SQUASH_UNTUG_BR_FN JOIN(lazyTug, SIZE)
// Acceleration and exception masks: we load them on the fly for really big
// models.
#if SIZE < 256
#define ACCEL_MASK accelMask
#define ACCEL_AND_FRIENDS_MASK accel_and_friendsMask
#define EXCEPTION_MASK exceptionMask
#else
#define ACCEL_MASK LOAD_STATE(&limex->accel)
#define ACCEL_AND_FRIENDS_MASK LOAD_STATE(&limex->accel_and_friends)
#define EXCEPTION_MASK LOAD_STATE(&limex->exceptionMask)
#endif
// Run exception processing, if necessary. Returns 0 if scanning should
// continue, 1 if an accept was fired and the user instructed us to halt.
static really_inline
char RUN_EXCEPTIONS_FN(const IMPL_NFA_T *limex, const EXCEPTION_T *exceptions,
const ReportID *exReports, const u32 *exceptionMap,
STATE_T s, const STATE_T emask, size_t i, u64a offset,
STATE_T *succ, u64a *final_loc, struct CONTEXT_T *ctx,
const char flags, const char in_rev,
const char first_match) {
STATE_T estate = AND_STATE(s, emask);
u32 diffmask = DIFFRICH_STATE(ZERO_STATE, estate);
if (likely(!diffmask)) {
return 0; // No exceptions to process.
}
if (first_match && i) {
STATE_T acceptMask = LOAD_STATE(&limex->accept);
STATE_T foundAccepts = AND_STATE(s, acceptMask);
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
DEBUG_PRINTF("first match at %zu\n", i);
DEBUG_PRINTF("for nfa %p\n", limex);
assert(final_loc);
STORE_STATE(&ctx->s, s);
*final_loc = i;
return 1; // Halt matching.
}
}
u64a callback_offset = i + offset;
char localflags = (!i && !in_rev) ? NO_OUTPUT | FIRST_BYTE : flags;
int rv = JOIN(processExceptional, SIZE)(
pass_state, pass_estate, diffmask, succ, limex, exceptionMap,
exceptions, exReports, callback_offset, ctx, in_rev, localflags);
if (rv == PE_RV_HALT) {
return 1; // Halt matching.
}
return 0;
}
static really_inline
size_t RUN_ACCEL_FN(const STATE_T s, UNUSED const STATE_T accelMask,
UNUSED const IMPL_NFA_T *limex, const u8 *accelTable,
const union AccelAux *accelAux, const u8 *input, size_t i,
size_t length) {
size_t j;
#if SIZE < 128
// For small cases, we pass the state by value.
j = JOIN(doAccel, SIZE)(s, accelMask, accelTable, accelAux, input, i,
length);
#else
j = JOIN(doAccel, SIZE)(&s, limex, accelTable, accelAux, input, i, length);
#endif
assert(j >= i);
assert(i <= length);
return j;
}
static really_inline
char STREAM_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
struct CONTEXT_T *ctx, u64a offset, const char flags,
u64a *final_loc, const char first_match) {
const STATE_T *reach = (const STATE_T *)((const char *)limex + sizeof(*limex));
#if SIZE < 256
const STATE_T accelMask = LOAD_STATE(&limex->accel);
const STATE_T accel_and_friendsMask = LOAD_STATE(&limex->accel_and_friends);
const STATE_T exceptionMask = LOAD_STATE(&limex->exceptionMask);
#endif
const u8 *accelTable = (const u8 *)((const char *)limex + limex->accelTableOffset);
const union AccelAux *accelAux =
(const union AccelAux *)((const char *)limex + limex->accelAuxOffset);
const EXCEPTION_T *exceptions = getExceptionTable(EXCEPTION_T, limex);
const ReportID *exReports = getExReports(limex);
const u32 *exceptionMap = limex->exceptionMap;
assert(ISALIGNED_CL(ctx));
assert(ISALIGNED_CL(&ctx->s));
STATE_T s = LOAD_STATE(&ctx->s);
STORE_STATE(&ctx->cached_estate, ZERO_STATE); /* TODO: understand why this is required */
/* assert(ISALIGNED_16(exceptions)); */
/* assert(ISALIGNED_16(reach)); */
size_t i = 0;
size_t min_accel_offset = 0;
if (!limex->accelCount || length < ACCEL_MIN_LEN) {
min_accel_offset = length;
goto without_accel;
} else {
goto with_accel;
}
without_accel:
for (; i != min_accel_offset; i++) {
DUMP_INPUT(i);
if (ISZERO_STATE(s)) {
DEBUG_PRINTF("no states are switched on, early exit\n");
STORE_STATE(&ctx->s, s);
return MO_CONTINUE_MATCHING;
}
u8 c = input[i];
STATE_T succ;
NFA_EXEC_GET_LIM_SUCC(STATE_T, SHIFT);
if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, exceptionMap, s,
EXCEPTION_MASK, i, offset, &succ, final_loc, ctx,
flags, 0, first_match)) {
return MO_HALT_MATCHING;
}
s = AND_STATE(succ, LOAD_STATE(&reach[limex->reachMap[c]]));
}
with_accel:
for (; i != length; i++) {
DUMP_INPUT(i);
if (i + 16 <= length &&
ISZERO_STATE(ANDNOT_STATE(ACCEL_AND_FRIENDS_MASK, s))) {
DEBUG_PRINTF("current states are all accelerable\n");
assert(i + 16 <= length);
size_t post_idx =
RUN_ACCEL_FN(s, ACCEL_MASK, limex, accelTable, accelAux, input,
i, length);
if (post_idx != i) {
/* squashing any friends as they may no longer be valid;
* offset back off should ensure they weren't doing anything
* important */
s = AND_STATE(ACCEL_MASK, s);
}
if (i && post_idx < min_accel_offset + BAD_ACCEL_DIST) {
min_accel_offset = post_idx + BIG_ACCEL_PENALTY;
} else {
min_accel_offset = post_idx + SMALL_ACCEL_PENALTY;
}
if (min_accel_offset >= length - ACCEL_MIN_LEN) {
min_accel_offset = length;
}
DEBUG_PRINTF("advanced %zd, next accel chance in %zd/%zd\n",
post_idx - i, min_accel_offset - post_idx,
length - post_idx);
i = post_idx;
if (i == length) {
break; /* all chars eaten, break out of loop */
}
goto without_accel;
}
u8 c = input[i];
STATE_T succ;
NFA_EXEC_GET_LIM_SUCC(STATE_T, SHIFT);
if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, exceptionMap, s,
EXCEPTION_MASK, i, offset, &succ, final_loc, ctx,
flags, 0, first_match)) {
return MO_HALT_MATCHING;
}
s = AND_STATE(succ, LOAD_STATE(&reach[limex->reachMap[c]]));
}
STORE_STATE(&ctx->s, s);
if ((first_match || (flags & CALLBACK_OUTPUT)) && limex->acceptCount) {
STATE_T acceptMask = LOAD_STATE(&limex->accept);
const struct NFAAccept *acceptTable = getAcceptTable(limex);
const u32 acceptCount = limex->acceptCount;
STATE_T foundAccepts = AND_STATE(s, acceptMask);
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
if (first_match) {
STORE_STATE(&ctx->s, s);
assert(final_loc);
*final_loc = length;
return MO_HALT_MATCHING;
} else if (PROCESS_ACCEPTS_FN(limex, &ctx->s, acceptTable,
acceptCount, offset + length,
ctx->callback, ctx->context)) {
return MO_HALT_MATCHING;
}
}
}
if (first_match) {
assert(final_loc);
*final_loc = length;
}
return MO_CONTINUE_MATCHING;
}
static never_inline
char REV_STREAM_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
struct CONTEXT_T *ctx, u64a offset) {
const STATE_T *reach = (const STATE_T *)((const char *)limex + sizeof(*limex));
#if SIZE < 256
const STATE_T exceptionMask = LOAD_STATE(&limex->exceptionMask);
#endif
const EXCEPTION_T *exceptions = getExceptionTable(EXCEPTION_T, limex);
const ReportID *exReports = getExReports(limex);
const u32 *exceptionMap = limex->exceptionMap;
STATE_T s = LOAD_STATE(&ctx->s);
STORE_STATE(&ctx->cached_estate, ZERO_STATE); /* TODO: understand why this is required */
/* assert(ISALIGNED_16(exceptions)); */
/* assert(ISALIGNED_16(reach)); */
const char flags = CALLBACK_OUTPUT;
u64a *final_loc = NULL;
for (size_t i = length; i != 0; i--) {
DUMP_INPUT(i-1);
if (ISZERO_STATE(s)) {
DEBUG_PRINTF("no states are switched on, early exit\n");
STORE_STATE(&ctx->s, s);
return MO_CONTINUE_MATCHING;
}
u8 c = input[i-1];
STATE_T succ;
NFA_EXEC_GET_LIM_SUCC(STATE_T, SHIFT);
if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, exceptionMap, s,
EXCEPTION_MASK, i, offset, &succ, final_loc, ctx,
flags, 1, 0)) {
return MO_HALT_MATCHING;
}
s = AND_STATE(succ, reach[limex->reachMap[c]]);
}
STORE_STATE(&ctx->s, s);
STATE_T acceptMask = LOAD_STATE(&limex->accept);
const struct NFAAccept *acceptTable = getAcceptTable(limex);
const u32 acceptCount = limex->acceptCount;
assert(flags & CALLBACK_OUTPUT);
if (acceptCount) {
STATE_T foundAccepts = AND_STATE(s, acceptMask);
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
if (PROCESS_ACCEPTS_NOSQUASH_FN(&ctx->s, acceptTable, acceptCount,
offset, ctx->callback,
ctx->context)) {
return MO_HALT_MATCHING;
}
}
}
return MO_CONTINUE_MATCHING;
}
static really_inline
void COMPRESS_REPEATS_FN(const IMPL_NFA_T *limex, void *dest, const void *src,
u64a offset) {
if (!limex->repeatCount) {
return;
}
// Note: we compress all repeats, as they may have *just* had their
// cyclic states switched off a moment ago. TODO: is this required
const union RepeatControl *ctrl =
getRepeatControlBaseConst((const char *)src, sizeof(STATE_T));
char *state_base = (char *)dest + limex->stateSize;
for (u32 i = 0; i < limex->repeatCount; i++) {
const struct NFARepeatInfo *info = GET_NFA_REPEAT_INFO_FN(limex, i);
const struct RepeatInfo *repeat = getRepeatInfo(info);
repeatPack(state_base + info->packedCtrlOffset, repeat, &ctrl[i],
offset);
}
}
char JOIN(LIMEX_API_ROOT, _queueCompressState)(const struct NFA *n,
const struct mq *q,
s64a loc) {
void *dest = q->streamState;
const void *src = q->state;
u8 key = queue_prev_byte(q, loc);
const IMPL_NFA_T *limex = getImplNfa(n);
COMPRESS_FN(limex, dest, src, key);
COMPRESS_REPEATS_FN(limex, dest, src, q->offset + loc);
return 0;
}
static really_inline
void EXPAND_REPEATS_FN(const IMPL_NFA_T *limex, void *dest, const void *src,
u64a offset) {
if (!limex->repeatCount) {
return;
}
// Note: we expand all repeats, as they may have *just* had their
// cyclic states switched off a moment ago. TODO: is this required?
union RepeatControl *ctrl =
getRepeatControlBase((char *)dest, sizeof(STATE_T));
const char *state_base = (const char *)src + limex->stateSize;
for (u32 i = 0; i < limex->repeatCount; i++) {
const struct NFARepeatInfo *info = GET_NFA_REPEAT_INFO_FN(limex, i);
const struct RepeatInfo *repeat = getRepeatInfo(info);
repeatUnpack(state_base + info->packedCtrlOffset, repeat, offset,
&ctrl[i]);
}
}
char JOIN(LIMEX_API_ROOT, _expandState)(const struct NFA *n, void *dest,
const void *src, u64a offset,
u8 key) {
const IMPL_NFA_T *limex = getImplNfa(n);
EXPAND_FN(limex, dest, src, key);
EXPAND_REPEATS_FN(limex, dest, src, offset);
return 0;
}
char JOIN(LIMEX_API_ROOT, _queueInitState)(const struct NFA *n,
struct mq *q) {
STORE_STATE(q->state, ZERO_STATE);
// Zero every bounded repeat control block in state.
const IMPL_NFA_T *limex = getImplNfa(n);
union RepeatControl *ctrl = getRepeatControlBase(q->state, sizeof(STATE_T));
for (u32 i = 0; i < limex->repeatCount; i++) {
memset(&ctrl[i], 0, sizeof(*ctrl));
}
return 0;
}
char JOIN(LIMEX_API_ROOT, _initCompressedState)(const struct NFA *n,
u64a offset, void *state,
u8 key) {
const IMPL_NFA_T *limex = getImplNfa(n);
STATE_T s = INITIAL_FN(limex, !!offset);
if (ISZERO_STATE(s)) {
DEBUG_PRINTF("state went to zero\n");
return 0;
}
// NFA is still active, compress its state and ship it out.
COMPRESS_FN(limex, state, &s, key);
// Zero every packed bounded repeat control block in stream state.
char *repeat_region = (char *)state + limex->stateSize;
for (u32 i = 0; i < limex->repeatCount; i++) {
const struct NFARepeatInfo *info = GET_NFA_REPEAT_INFO_FN(limex, i);
const struct RepeatInfo *repeat = getRepeatInfo(info);
memset(repeat_region + info->packedCtrlOffset, 0,
repeat->packedCtrlSize);
}
return 1;
}
// Helper for history buffer scans, which catch up the NFA state but don't emit
// matches.
static never_inline
void STREAMSILENT_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
struct CONTEXT_T *ctx, u64a offset) {
const char first_match = 0;
UNUSED char rv = STREAM_FN(limex, input, length, ctx, offset, NO_OUTPUT,
NULL, first_match);
assert(rv != MO_HALT_MATCHING);
}
static never_inline
char STREAMCB_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
struct CONTEXT_T *ctx, u64a offset) {
const char first_match = 0;
assert(ISALIGNED_CL(ctx));
return STREAM_FN(limex, input, length, ctx, offset, CALLBACK_OUTPUT, NULL,
first_match);
}
static never_inline
char STREAMFIRST_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
struct CONTEXT_T *ctx, u64a offset, u64a *final_loc) {
const char first_match = 1; // Run to first match and stop, no callbacks.
return STREAM_FN(limex, input, length, ctx, offset, NO_OUTPUT, final_loc,
first_match);
}
// Common code for handling the current event on the queue.
static really_inline
void JOIN(LIMEX_API_ROOT, _HandleEvent)(const IMPL_NFA_T *limex,
struct mq *q, struct CONTEXT_T *ctx,
u64a sp) {
#define DEFINE_CASE(ee) \
case ee: \
DEBUG_PRINTF(#ee "\n");
u32 e = q->items[q->cur].type;
switch (e) {
DEFINE_CASE(MQE_TOP)
STORE_STATE(&ctx->s, TOP_FN(limex, !!sp, LOAD_STATE(&ctx->s)));
break;
DEFINE_CASE(MQE_START)
break;
DEFINE_CASE(MQE_END)
break;
default:
assert(e >= MQE_TOP_FIRST);
assert(e < MQE_INVALID);
DEBUG_PRINTF("MQE_TOP + %d\n", ((int)e - MQE_TOP_FIRST));
STORE_STATE(&ctx->s,
TOPN_FN(limex, LOAD_STATE(&ctx->s), e - MQE_TOP_FIRST));
}
#undef DEFINE_CASE
}
// "Classic" queue call, used by outfixes
char JOIN(LIMEX_API_ROOT, _Q)(const struct NFA *n, struct mq *q, s64a end) {
const IMPL_NFA_T *limex = getImplNfa(n);
if (q->report_current) {
char rv = REPORTCURRENT_FN(limex, q);
q->report_current = 0;
if (rv == MO_HALT_MATCHING) {
return MO_HALT_MATCHING;
}
}
if (q->cur == q->end) {
return 1;
}
assert(q->cur + 1 < q->end); /* require at least two items */
struct CONTEXT_T *ctx = q->scratch->nfaContext;
assert(ISALIGNED_CL(ctx));
ctx->repeat_ctrl = getRepeatControlBase(q->state, sizeof(STATE_T));
ctx->repeat_state = q->streamState + limex->stateSize;
ctx->callback = q->cb;
ctx->context = q->context;
STORE_STATE(&ctx->cached_estate, ZERO_STATE);
STORE_STATE(&ctx->cached_esucc, ZERO_STATE);
assert(q->items[q->cur].location >= 0);
DEBUG_PRINTF("LOAD STATE\n");
STORE_STATE(&ctx->s, LOAD_STATE(q->state));
assert(q->items[q->cur].type == MQE_START);
u64a offset = q->offset;
u64a sp = offset + q->items[q->cur].location;
u64a end_abs = offset + end;
q->cur++;
while (q->cur < q->end && sp <= end_abs) {
u64a ep = offset + q->items[q->cur].location;
ep = MIN(ep, end_abs);
assert(ep >= sp);
assert(sp >= offset); // We no longer do history buffer scans here.
if (sp >= ep) {
goto scan_done;
}
/* do main buffer region */
DEBUG_PRINTF("MAIN BUFFER SCAN\n");
assert(ep - offset <= q->length);
if (STREAMCB_FN(limex, q->buffer + sp - offset, ep - sp, ctx, sp)
== MO_HALT_MATCHING) {
STORE_STATE(q->state, ZERO_STATE);
return 0;
}
DEBUG_PRINTF("SCAN DONE\n");
scan_done:
sp = ep;
if (sp != offset + q->items[q->cur].location) {
assert(q->cur);
DEBUG_PRINTF("bail: sp = %llu end_abs == %llu offset == %llu\n",
sp, end_abs, offset);
assert(sp == end_abs);
q->cur--;
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = sp - offset;
DEBUG_PRINTF("bailing q->cur %u q->end %u\n", q->cur, q->end);
STORE_STATE(q->state, LOAD_STATE(&ctx->s));
return MO_ALIVE;
}
JOIN(LIMEX_API_ROOT, _HandleEvent)(limex, q, ctx, sp);
q->cur++;
}
EXPIRE_ESTATE_FN(limex, ctx, sp);
DEBUG_PRINTF("END\n");
STORE_STATE(q->state, LOAD_STATE(&ctx->s));
if (q->cur != q->end) {
q->cur--;
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = sp - offset;
return MO_ALIVE;
}
return ISNONZERO_STATE(LOAD_STATE(&ctx->s));
}
/* used by suffix execution in Rose */
char JOIN(LIMEX_API_ROOT, _Q2)(const struct NFA *n, struct mq *q, s64a end) {
const IMPL_NFA_T *limex = getImplNfa(n);
if (q->report_current) {
char rv = REPORTCURRENT_FN(limex, q);
q->report_current = 0;
if (rv == MO_HALT_MATCHING) {
return MO_HALT_MATCHING;
}
}
if (q->cur == q->end) {
return 1;
}
assert(q->cur + 1 < q->end); /* require at least two items */
struct CONTEXT_T *ctx = q->scratch->nfaContext;
assert(ISALIGNED_CL(ctx));
ctx->repeat_ctrl = getRepeatControlBase(q->state, sizeof(STATE_T));
ctx->repeat_state = q->streamState + limex->stateSize;
ctx->callback = q->cb;
ctx->context = q->context;
STORE_STATE(&ctx->cached_estate, ZERO_STATE);
STORE_STATE(&ctx->cached_esucc, ZERO_STATE);
DEBUG_PRINTF("LOAD STATE\n");
STORE_STATE(&ctx->s, LOAD_STATE(q->state));
assert(q->items[q->cur].type == MQE_START);
u64a offset = q->offset;
u64a sp = offset + q->items[q->cur].location;
u64a end_abs = offset + end;
q->cur++;
while (q->cur < q->end && sp <= end_abs) {
u64a ep = offset + q->items[q->cur].location;
DEBUG_PRINTF("sp = %llu, ep = %llu, end_abs = %llu\n",
sp, ep, end_abs);
ep = MIN(ep, end_abs);
assert(ep >= sp);
assert(sp >= offset); // We no longer do history buffer scans here.
if (sp >= ep) {
goto scan_done;
}
/* do main buffer region */
u64a final_look = 0;
assert(ep - offset <= q->length);
if (STREAMFIRST_FN(limex, q->buffer + sp - offset, ep - sp, ctx, sp,
&final_look) == MO_HALT_MATCHING) {
DEBUG_PRINTF("final_look:%llu sp:%llu end_abs:%llu offset:%llu\n",
final_look, sp, end_abs, offset);
assert(q->cur);
q->cur--;
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = sp + final_look - offset;
STORE_STATE(q->state, LOAD_STATE(&ctx->s));
return MO_MATCHES_PENDING;
}
scan_done:
sp = ep;
if (sp != offset + q->items[q->cur].location) {
assert(q->cur);
DEBUG_PRINTF("bail: sp = %llu end_abs == %llu offset == %llu\n",
sp, end_abs, offset);
assert(sp == end_abs);
q->cur--;
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = sp - offset;
DEBUG_PRINTF("bailing q->cur %u q->end %u\n", q->cur, q->end);
STORE_STATE(q->state, LOAD_STATE(&ctx->s));
return MO_ALIVE;
}
JOIN(LIMEX_API_ROOT, _HandleEvent)(limex, q, ctx, sp);
q->cur++;
}
EXPIRE_ESTATE_FN(limex, ctx, sp);
DEBUG_PRINTF("END\n");
STORE_STATE(q->state, LOAD_STATE(&ctx->s));
if (q->cur != q->end) {
q->cur--;
q->items[q->cur].type = MQE_START;
q->items[q->cur].location = sp - offset;
return MO_ALIVE;
}
return ISNONZERO_STATE(LOAD_STATE(&ctx->s));
}
// Used for execution Rose prefix/infixes.
char JOIN(LIMEX_API_ROOT, _QR)(const struct NFA *n, struct mq *q,
ReportID report) {
const IMPL_NFA_T *limex = getImplNfa(n);
if (q->cur == q->end) {
return 1;
}
assert(q->cur + 1 < q->end); /* require at least two items */
struct CONTEXT_T *ctx = q->scratch->nfaContext;
ctx->repeat_ctrl = getRepeatControlBase(q->state, sizeof(STATE_T));
ctx->repeat_state = q->streamState + limex->stateSize;
ctx->callback = NULL;
ctx->context = NULL;
STORE_STATE(&ctx->cached_estate, ZERO_STATE);
STORE_STATE(&ctx->cached_esucc, ZERO_STATE);
DEBUG_PRINTF("LOAD STATE\n");
STORE_STATE(&ctx->s, LOAD_STATE(q->state));
assert(q->items[q->cur].type == MQE_START);
u64a offset = q->offset;
u64a sp = offset + q->items[q->cur].location;
q->cur++;
while (q->cur < q->end) {
u64a ep = offset + q->items[q->cur].location;
if (n->maxWidth) {
if (ep - sp > n->maxWidth) {
sp = ep - n->maxWidth;
STORE_STATE(&ctx->s, INITIAL_FN(limex, !!sp));
}
}
assert(ep >= sp);
if (sp < offset) {
DEBUG_PRINTF("HISTORY BUFFER SCAN\n");
assert(offset - sp <= q->hlength);
u64a local_ep = MIN(offset, ep);
/* we are starting inside the history buffer */
STREAMSILENT_FN(limex, q->history + q->hlength + sp - offset,
local_ep - sp, ctx, sp);
sp = local_ep;
}
if (sp >= ep) {
goto scan_done;
}
/* do main buffer region */
DEBUG_PRINTF("MAIN BUFFER SCAN\n");
assert(ep - offset <= q->length);
STREAMSILENT_FN(limex, q->buffer + sp - offset, ep - sp, ctx, sp);
DEBUG_PRINTF("SCAN DONE\n");
scan_done:
sp = ep;
JOIN(LIMEX_API_ROOT, _HandleEvent)(limex, q, ctx, sp);
q->cur++;
}
EXPIRE_ESTATE_FN(limex, ctx, sp);
DEBUG_PRINTF("END, nfa is %s\n",
ISNONZERO_STATE(ctx->s) ? "still alive" : "dead");
STORE_STATE(q->state, LOAD_STATE(&ctx->s));
if (JOIN(limexInAccept, SIZE)(limex, LOAD_STATE(&ctx->s), ctx->repeat_ctrl,
ctx->repeat_state, sp + 1, report)) {
return MO_MATCHES_PENDING;
}
return ISNONZERO_STATE(LOAD_STATE(&ctx->s));
}
char JOIN(LIMEX_API_ROOT, _testEOD)(const struct NFA *n, const char *state,
const char *streamState, u64a offset,
NfaCallback callback,
UNUSED SomNfaCallback som_callback,
void *context) {
assert(n && state);
const IMPL_NFA_T *limex = getImplNfa(n);
const STATE_T *sptr = (const STATE_T *)state;
const union RepeatControl *repeat_ctrl =
getRepeatControlBaseConst(state, sizeof(STATE_T));
const char *repeat_state = streamState + limex->stateSize;
return TESTEOD_FN(limex, sptr, repeat_ctrl, repeat_state, offset, 1,
callback, context);
}
char JOIN(LIMEX_API_ROOT, _reportCurrent)(const struct NFA *n, struct mq *q) {
const IMPL_NFA_T *limex = getImplNfa(n);
REPORTCURRENT_FN(limex, q);
return 1;
}
// Block mode reverse scan.
char JOIN(LIMEX_API_ROOT, _B_Reverse)(const struct NFA *n, u64a offset,
const u8 *buf, size_t buflen,
const u8 *hbuf, size_t hlen,
struct hs_scratch *scratch,
NfaCallback cb, void *context) {
assert(buf || hbuf);
assert(buflen || hlen);
/* This may be called INSIDE another NFA, so we need a separate
* context --> Hence the nfaContextSom */
struct CONTEXT_T *ctx = scratch->nfaContextSom;
ctx->repeat_ctrl = NULL;
ctx->repeat_state = NULL;
ctx->callback = cb;
ctx->context = context;
STORE_STATE(&ctx->cached_estate, ZERO_STATE);
STORE_STATE(&ctx->cached_esucc, ZERO_STATE);
const IMPL_NFA_T *limex = getImplNfa(n);
STORE_STATE(&ctx->s, INITIAL_FN(limex, 0)); // always anchored
// 'buf' may be null, for example when we're scanning at EOD time.
if (buflen) {
assert(buf);
DEBUG_PRINTF("MAIN BUFFER SCAN, %zu bytes\n", buflen);
offset -= buflen;
REV_STREAM_FN(limex, buf, buflen, ctx, offset);
}
if (hlen) {
assert(hbuf);
DEBUG_PRINTF("HISTORY BUFFER SCAN, %zu bytes\n", hlen);
offset -= hlen;
REV_STREAM_FN(limex, hbuf, hlen, ctx, offset);
}
if (offset == 0 && ISNONZERO_STATE(LOAD_STATE(&ctx->s))) {
TESTEOD_REV_FN(limex, &ctx->s, offset, cb, context);
}
// NOTE: return value is unused.
return 0;
}
char JOIN(LIMEX_API_ROOT, _inAccept)(const struct NFA *nfa,
ReportID report, struct mq *q) {
assert(nfa && q);
assert(q->state && q->streamState);
const IMPL_NFA_T *limex = getImplNfa(nfa);
union RepeatControl *repeat_ctrl =
getRepeatControlBase(q->state, sizeof(STATE_T));
char *repeat_state = q->streamState + limex->stateSize;
STATE_T state = LOAD_STATE(q->state);
u64a offset = q->offset + q_last_loc(q) + 1;
return JOIN(limexInAccept, SIZE)(limex, state, repeat_ctrl, repeat_state,
offset, report);
}
enum nfa_zombie_status JOIN(LIMEX_API_ROOT, _zombie_status)(
const struct NFA *nfa,
struct mq *q,
s64a loc) {
assert(nfa->flags & NFA_ZOMBIE);
const IMPL_NFA_T *limex = getImplNfa(nfa);
STATE_T state = LOAD_STATE(q->state);
STATE_T zmask = LOAD_STATE(&limex->zombieMask);
if (limex->repeatCount) {
u64a offset = q->offset + loc + 1;
union RepeatControl *repeat_ctrl =
getRepeatControlBase(q->state, sizeof(STATE_T));
char *repeat_state = q->streamState + limex->stateSize;
SQUASH_UNTUG_BR_FN(limex, repeat_ctrl, repeat_state, offset, &state);
}
if (ISNONZERO_STATE(AND_STATE(state, zmask))) {
return NFA_ZOMBIE_ALWAYS_YES;
}
return NFA_ZOMBIE_NO;
}
#undef TESTEOD_FN
#undef TESTEOD_REV_FN
#undef INITIAL_FN
#undef TOP_FN
#undef TOPN_FN
#undef REPORTCURRENT_FN
#undef COMPRESS_FN
#undef EXPAND_FN
#undef COMPRESS_REPEATS_FN
#undef EXPAND_REPEATS_FN
#undef PROCESS_ACCEPTS_FN
#undef PROCESS_ACCEPTS_NOSQUASH_FN
#undef GET_NFA_REPEAT_INFO_FN
#undef RUN_ACCEL_FN
#undef RUN_EXCEPTIONS_FN
#undef REV_STREAM_FN
#undef STREAM_FN
#undef STREAMCB_FN
#undef STREAMFIRST_FN
#undef STREAMSILENT_FN
#undef CONTEXT_T
#undef EXCEPTION_T
#undef LOAD_STATE
#undef STORE_STATE
#undef AND_STATE
#undef ANDNOT_STATE
#undef OR_STATE
#undef TESTBIT_STATE
#undef ZERO_STATE
#undef ISNONZERO_STATE
#undef ISZERO_STATE
#undef NOTEQ_STATE
#undef DIFFRICH_STATE
#undef INLINE_ATTR_INT
#undef IMPL_NFA_T
#undef SQUASH_UNTUG_BR_FN
#undef ACCEL_MASK
#undef ACCEL_AND_FRIENDS_MASK
#undef EXCEPTION_MASK
// Parameters.
#undef SIZE
#undef STATE_T
#undef SHIFT
#undef LIMEX_API_ROOT

97
src/nfa/limex_simd128.c Normal file
View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: 128-bit SIMD runtime implementations.
*/
//#define DEBUG_INPUT
//#define DEBUG_EXCEPTIONS
#include "limex.h"
#include "accel.h"
#include "limex_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
// Common code
#define STATE_ON_STACK
#define ESTATE_ON_STACK
#include "limex_runtime.h"
#define SIZE 128
#define STATE_T m128
#include "limex_exceptional.h"
#define SIZE 128
#define STATE_T m128
#include "limex_state_impl.h"
#define SIZE 128
#define STATE_T m128
#define INLINE_ATTR really_inline
#include "limex_common_impl.h"
#define SIZE 128
#define STATE_T m128
#define SHIFT 1
#include "limex_runtime_impl.h"
#define SIZE 128
#define STATE_T m128
#define SHIFT 2
#include "limex_runtime_impl.h"
#define SIZE 128
#define STATE_T m128
#define SHIFT 3
#include "limex_runtime_impl.h"
#define SIZE 128
#define STATE_T m128
#define SHIFT 4
#include "limex_runtime_impl.h"
#define SIZE 128
#define STATE_T m128
#define SHIFT 5
#include "limex_runtime_impl.h"
#define SIZE 128
#define STATE_T m128
#define SHIFT 6
#include "limex_runtime_impl.h"
#define SIZE 128
#define STATE_T m128
#define SHIFT 7
#include "limex_runtime_impl.h"

94
src/nfa/limex_simd256.c Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: 256-bit SIMD runtime implementations.
*/
//#define DEBUG_INPUT
//#define DEBUG_EXCEPTIONS
#include "limex.h"
#include "accel.h"
#include "limex_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
// Common code
#include "limex_runtime.h"
#define SIZE 256
#define STATE_T m256
#include "limex_exceptional.h"
#define SIZE 256
#define STATE_T m256
#include "limex_state_impl.h"
#define SIZE 256
#define STATE_T m256
#define INLINE_ATTR really_inline
#include "limex_common_impl.h"
#define SIZE 256
#define STATE_T m256
#define SHIFT 1
#include "limex_runtime_impl.h"
#define SIZE 256
#define STATE_T m256
#define SHIFT 2
#include "limex_runtime_impl.h"
#define SIZE 256
#define STATE_T m256
#define SHIFT 3
#include "limex_runtime_impl.h"
#define SIZE 256
#define STATE_T m256
#define SHIFT 4
#include "limex_runtime_impl.h"
#define SIZE 256
#define STATE_T m256
#define SHIFT 5
#include "limex_runtime_impl.h"
#define SIZE 256
#define STATE_T m256
#define SHIFT 6
#include "limex_runtime_impl.h"
#define SIZE 256
#define STATE_T m256
#define SHIFT 7
#include "limex_runtime_impl.h"

94
src/nfa/limex_simd384.c Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: 384-bit SIMD runtime implementations.
*/
//#define DEBUG_INPUT
//#define DEBUG_EXCEPTIONS
#include "limex.h"
#include "accel.h"
#include "limex_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
// Common code
#include "limex_runtime.h"
#define SIZE 384
#define STATE_T m384
#include "limex_exceptional.h"
#define SIZE 384
#define STATE_T m384
#include "limex_state_impl.h"
#define SIZE 384
#define STATE_T m384
#define INLINE_ATTR really_inline
#include "limex_common_impl.h"
#define SIZE 384
#define STATE_T m384
#define SHIFT 1
#include "limex_runtime_impl.h"
#define SIZE 384
#define STATE_T m384
#define SHIFT 2
#include "limex_runtime_impl.h"
#define SIZE 384
#define STATE_T m384
#define SHIFT 3
#include "limex_runtime_impl.h"
#define SIZE 384
#define STATE_T m384
#define SHIFT 4
#include "limex_runtime_impl.h"
#define SIZE 384
#define STATE_T m384
#define SHIFT 5
#include "limex_runtime_impl.h"
#define SIZE 384
#define STATE_T m384
#define SHIFT 6
#include "limex_runtime_impl.h"
#define SIZE 384
#define STATE_T m384
#define SHIFT 7
#include "limex_runtime_impl.h"

74
src/nfa/limex_simd512a.c Normal file
View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: 512-bit SIMD runtime implementations.
*/
//#define DEBUG_INPUT
//#define DEBUG_EXCEPTIONS
#include "limex.h"
#include "accel.h"
#include "limex_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
// Common code
#include "limex_runtime.h"
#define SIZE 512
#define STATE_T m512
#include "limex_exceptional.h"
#define SIZE 512
#define STATE_T m512
#include "limex_state_impl.h"
#define SIZE 512
#define STATE_T m512
#define INLINE_ATTR really_inline
#include "limex_common_impl.h"
#define SIZE 512
#define STATE_T m512
#define SHIFT 2
#include "limex_runtime_impl.h"
#define SIZE 512
#define STATE_T m512
#define SHIFT 1
#include "limex_runtime_impl.h"
#define SIZE 512
#define STATE_T m512
#define SHIFT 3
#include "limex_runtime_impl.h"

69
src/nfa/limex_simd512b.c Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: 512-bit SIMD runtime implementations.
*/
//#define DEBUG_INPUT
//#define DEBUG_EXCEPTIONS
#include "limex.h"
#include "accel.h"
#include "limex_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
// Common code
#include "limex_runtime.h"
#define SIZE 512
#define STATE_T m512
#include "limex_exceptional.h"
#define SIZE 512
#define STATE_T m512
#include "limex_state_impl.h"
#define SIZE 512
#define STATE_T m512
#define INLINE_ATTR really_inline
#include "limex_common_impl.h"
#define SIZE 512
#define STATE_T m512
#define SHIFT 4
#include "limex_runtime_impl.h"
#define SIZE 512
#define STATE_T m512
#define SHIFT 5
#include "limex_runtime_impl.h"

69
src/nfa/limex_simd512c.c Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2015, 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 LimEx NFA: 512-bit SIMD runtime implementations.
*/
//#define DEBUG_INPUT
//#define DEBUG_EXCEPTIONS
#include "limex.h"
#include "accel.h"
#include "limex_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
// Common code
#include "limex_runtime.h"
#define SIZE 512
#define STATE_T m512
#include "limex_exceptional.h"
#define SIZE 512
#define STATE_T m512
#include "limex_state_impl.h"
#define SIZE 512
#define STATE_T m512
#define INLINE_ATTR really_inline
#include "limex_common_impl.h"
#define SIZE 512
#define STATE_T m512
#define SHIFT 6
#include "limex_runtime_impl.h"
#define SIZE 512
#define STATE_T m512
#define SHIFT 7
#include "limex_runtime_impl.h"

144
src/nfa/limex_state_impl.h Normal file
View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2015, 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 stream state handling.
*/
#include "util/join.h"
#include "util/partial_store.h"
#include "util/state_compress.h"
#include <string.h>
#if !defined(SIZE) || !defined(STATE_T)
# error Must define SIZE and STATE_T in includer.
#endif
#define IMPL_NFA_T JOIN(struct LimExNFA, SIZE)
#define COMMON_T JOIN(NFACommon, SIZE)
#define REACHMASK_FN JOIN(moNfaReachMask, SIZE)
#define COMPRESS_FN JOIN(moNfaCompressState, SIZE)
#define EXPAND_FN JOIN(moNfaExpandState, SIZE)
#define COMPRESSED_STORE_FN JOIN(storecompressed, SIZE)
#define COMPRESSED_LOAD_FN JOIN(loadcompressed, SIZE)
#define PARTIAL_STORE_FN JOIN(partial_store_, STATE_T)
#define PARTIAL_LOAD_FN JOIN(partial_load_, STATE_T)
#define LOAD_STATE JOIN(load_, STATE_T)
#define STORE_STATE JOIN(store_, STATE_T)
#define OR_STATE JOIN(or_, STATE_T)
#define AND_STATE JOIN(and_, STATE_T)
#define ISZERO_STATE JOIN(isZero_, STATE_T)
static really_inline
const STATE_T *REACHMASK_FN(const IMPL_NFA_T *limex, const u8 key) {
const STATE_T *reach
= (const STATE_T *)((const char *)limex + sizeof(*limex));
assert(ISALIGNED_N(reach, alignof(STATE_T)));
return &reach[limex->reachMap[key]];
}
static really_inline
void COMPRESS_FN(const IMPL_NFA_T *limex, u8 *dest, const STATE_T *src,
u8 key) {
assert(ISALIGNED_N(src, alignof(STATE_T)));
STATE_T a_src = LOAD_STATE(src);
DEBUG_PRINTF("compress state: %p -> %p\n", src, dest);
if (!(limex->flags & LIMEX_FLAG_COMPRESS_STATE)) {
// No key-based compression, just a partial store.
DEBUG_PRINTF("store state into %u bytes\n", limex->stateSize);
PARTIAL_STORE_FN(dest, a_src, limex->stateSize);
} else {
DEBUG_PRINTF("compress state, key=%hhx\n", key);
const STATE_T *reachmask = REACHMASK_FN(limex, key);
// Masked compression means that we mask off the initDs states and
// provide a shortcut for the all-zeroes case. Note that these must be
// switched on in the EXPAND call below.
if (limex->flags & LIMEX_FLAG_COMPRESS_MASKED) {
STATE_T s = AND_STATE(LOAD_STATE(&limex->compressMask), a_src);
if (ISZERO_STATE(s)) {
DEBUG_PRINTF("after compression mask, all states are zero\n");
memset(dest, 0, limex->stateSize);
return;
}
STATE_T mask = AND_STATE(LOAD_STATE(&limex->compressMask),
LOAD_STATE(reachmask));
COMPRESSED_STORE_FN(dest, &s, &mask, limex->stateSize);
} else {
COMPRESSED_STORE_FN(dest, src, reachmask, limex->stateSize);
}
}
}
static really_inline
void EXPAND_FN(const IMPL_NFA_T *limex, STATE_T *dest, const u8 *src,
u8 key) {
assert(ISALIGNED_N(dest, alignof(STATE_T)));
DEBUG_PRINTF("expand state: %p -> %p\n", src, dest);
if (!(limex->flags & LIMEX_FLAG_COMPRESS_STATE)) {
// No key-based compression, just a partial load.
DEBUG_PRINTF("load state from %u bytes\n", limex->stateSize);
*dest = PARTIAL_LOAD_FN(src, limex->stateSize);
} else {
DEBUG_PRINTF("expand state, key=%hhx\n", key);
const STATE_T *reachmask = REACHMASK_FN(limex, key);
if (limex->flags & LIMEX_FLAG_COMPRESS_MASKED) {
STATE_T mask = AND_STATE(LOAD_STATE(&limex->compressMask),
LOAD_STATE(reachmask));
COMPRESSED_LOAD_FN(dest, src, &mask, limex->stateSize);
STORE_STATE(dest, OR_STATE(LOAD_STATE(&limex->initDS),
LOAD_STATE(dest)));
} else {
COMPRESSED_LOAD_FN(dest, src, reachmask, limex->stateSize);
}
}
}
#undef IMPL_NFA_T
#undef COMMON_T
#undef REACHMASK_FN
#undef COMPRESS_FN
#undef EXPAND_FN
#undef COMPRESSED_STORE_FN
#undef COMPRESSED_LOAD_FN
#undef PARTIAL_STORE_FN
#undef PARTIAL_LOAD_FN
#undef LOAD_STATE
#undef STORE_STATE
#undef OR_STATE
#undef AND_STATE
#undef ISZERO_STATE
#undef SIZE
#undef STATE_T

1121
src/nfa/mcclellan.c Normal file

File diff suppressed because it is too large Load Diff

109
src/nfa/mcclellan.h Normal file
View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MCCLELLAN_H
#define MCCLELLAN_H
#include "callback.h"
#include "ue2common.h"
struct mq;
struct NFA;
// 8-bit McClellan
char nfaExecMcClellan8_testEOD(const struct NFA *nfa, const char *state,
const char *streamState, u64a offset,
NfaCallback callback, SomNfaCallback som_cb,
void *context);
char nfaExecMcClellan8_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecMcClellan8_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecMcClellan8_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecMcClellan8_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecMcClellan8_inAccept(const struct NFA *n, ReportID report,
struct mq *q);
char nfaExecMcClellan8_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecMcClellan8_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecMcClellan8_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecMcClellan8_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecMcClellan8_B_Reverse NFA_API_NO_IMPL
#define nfaExecMcClellan8_zombie_status NFA_API_NO_IMPL
// 16-bit McClellan
char nfaExecMcClellan16_testEOD(const struct NFA *nfa, const char *state,
const char *streamState, u64a offset,
NfaCallback callback, SomNfaCallback som_cb,
void *context);
char nfaExecMcClellan16_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecMcClellan16_Q2(const struct NFA *n, struct mq *q, s64a end);
char nfaExecMcClellan16_QR(const struct NFA *n, struct mq *q, ReportID report);
char nfaExecMcClellan16_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecMcClellan16_inAccept(const struct NFA *n, ReportID report,
struct mq *q);
char nfaExecMcClellan16_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecMcClellan16_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecMcClellan16_queueCompressState(const struct NFA *nfa,
const struct mq *q, s64a loc);
char nfaExecMcClellan16_expandState(const struct NFA *nfa, void *dest,
const void *src, u64a offset, u8 key);
#define nfaExecMcClellan16_B_Reverse NFA_API_NO_IMPL
#define nfaExecMcClellan16_zombie_status NFA_API_NO_IMPL
/**
* Simple streaming mode calls:
* - always uses the anchored start state regardless if top is set regardless of
* start_off
* - never checks eod
*/
void nfaExecMcClellan8_SimpStream(const struct NFA *nfa, char *state,
const u8 *buf, char top, size_t start_off,
size_t len, NfaCallback cb, void *ctxt);
void nfaExecMcClellan16_SimpStream(const struct NFA *nfa, char *state,
const u8 *buf, char top, size_t start_off,
size_t len, NfaCallback cb, void *ctxt);
/**
* Simple block mode calls:
* - always uses the anchored start state regardless of initial start
*/
char nfaExecMcClellan8_B(const struct NFA *n, u64a offset, const u8 *buffer,
size_t length, NfaCallback cb, void *context);
char nfaExecMcClellan16_B(const struct NFA *n, u64a offset, const u8 *buffer,
size_t length, NfaCallback cb, void *context);
#endif

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2015, 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.
*/
#if defined(__INTEL_COMPILER) || defined(__clang__) || defined(_WIN32) || defined(__GNUC__) && (__GNUC__ < 4)
#define really_flatten
#else
#define really_flatten __attribute__ ((flatten))
#endif
#define CASE_MASK 0xdf
enum MatchMode {
CALLBACK_OUTPUT,
STOP_AT_MATCH,
NO_MATCHES
};
static really_inline
const struct mstate_aux *get_aux(const struct mcclellan *m, u16 s) {
const char *nfa = (const char *)m - sizeof(struct NFA);
const struct mstate_aux *aux
= s + (const struct mstate_aux *)(nfa + m->aux_offset);
assert(ISALIGNED(aux));
return aux;
}
static really_inline
u16 mcclellanEnableStarts(const struct mcclellan *m, u16 s) {
const struct mstate_aux *aux = get_aux(m, s);
DEBUG_PRINTF("enabling starts %hu->%hu\n", s, aux->top);
return aux->top;
}
static really_inline
u16 doSherman16(const char *sherman_state, u8 cprime, const u16 *succ_table,
u32 as) {
assert(ISALIGNED_N(sherman_state, 16));
u8 len = *(const u8 *)(sherman_state + SHERMAN_LEN_OFFSET);
if (len) {
m128 ss_char = load128(sherman_state);
m128 cur_char = set16x8(cprime);
u32 z = movemask128(eq128(ss_char, cur_char));
/* remove header cruft: type 1, len 1, daddy 2*/
z &= ~0xf;
z &= (1U << (len + 4)) - 1;
if (z) {
u32 i = ctz32(z & ~0xf) - 4;
u16 s_out = unaligned_load_u16((const u8 *)sherman_state
+ SHERMAN_STATES_OFFSET(len)
+ sizeof(u16) * i);
DEBUG_PRINTF("found sherman match at %u/%u for c'=%hhu "
"s=%hu\n", i, len, cprime, s_out);
return s_out;
}
}
u16 daddy = *(const u16 *)(sherman_state + SHERMAN_DADDY_OFFSET);
return succ_table[((u32)daddy << as) + cprime];
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MCCLELLAN_INTERNAL_H
#define MCCLELLAN_INTERNAL_H
#include "nfa_internal.h"
#ifdef __cplusplus
extern "C"
{
#endif
#define ACCEPT_FLAG 0x8000
#define ACCEL_FLAG 0x4000
#define STATE_MASK 0x3fff
#define SHERMAN_STATE 1
#define SHERMAN_TYPE_OFFSET 0
#define SHERMAN_FIXED_SIZE 32
#define SHERMAN_LEN_OFFSET 1
#define SHERMAN_DADDY_OFFSET 2
#define SHERMAN_CHARS_OFFSET 4
#define SHERMAN_STATES_OFFSET(sso_len) (4 + (sso_len))
struct report_list {
u32 count;
ReportID report[];
};
struct mstate_aux {
u32 accept;
u32 accept_eod;
u16 top;
u32 accel_offset; /* relative to start of struct mcclellan; 0 if no accel */
};
#define MCCLELLAN_FLAG_SINGLE 1 /**< we raise only single accept id */
struct mcclellan {
u16 state_count; /**< total number of states */
u32 length; /**< length of dfa in bytes */
u16 start_anchored; /**< anchored start state */
u16 start_floating; /**< floating start state */
u32 aux_offset; /**< offset of the aux structures relative to the start of
* the nfa structure */
u32 sherman_offset; /**< offset of to array of sherman state offsets
* the state_info structures relative to the start of the
* nfa structure */
u32 sherman_end; /**< offset of the end of the state_info structures relative
* to the start of the nfa structure */
u16 accel_limit_8; /**< 8 bit, lowest accelerable state */
u16 accept_limit_8; /**< 8 bit, lowest accept state */
u16 sherman_limit; /**< lowest sherman state */
u8 alphaShift;
u8 flags;
u8 has_accel; /**< 1 iff there are any accel planes */
u8 remap[256]; /**< remaps characters to a smaller alphabet */
ReportID arb_report; /**< one of the accepts that this dfa may raise */
u32 accel_offset; /**< offset of the accel structures from start of NFA */
u32 haig_offset; /**< reserved for use by Haig, relative to start of NFA */
};
static really_inline
const char *findShermanState(UNUSED const struct mcclellan *m,
const char *sherman_base_offset, u16 sherman_base,
u16 s) {
const char *rv
= sherman_base_offset + SHERMAN_FIXED_SIZE * (s - sherman_base);
assert(rv < (const char *)m + m->length - sizeof(struct NFA));
UNUSED u8 type = *(const u8 *)(rv + SHERMAN_TYPE_OFFSET);
assert(type == SHERMAN_STATE);
return rv;
}
static really_inline
char *findMutableShermanState(char *sherman_base_offset, u16 sherman_base,
u16 s) {
return sherman_base_offset + SHERMAN_FIXED_SIZE * (s - sherman_base);
}
#ifdef __cplusplus
}
#endif
#endif

1252
src/nfa/mcclellancompile.cpp Normal file

File diff suppressed because it is too large Load Diff

120
src/nfa/mcclellancompile.h Normal file
View File

@@ -0,0 +1,120 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MCCLELLANCOMPILE_H
#define MCCLELLANCOMPILE_H
#include "rdfa.h"
#include "ue2common.h"
#include "util/alloc.h"
#include "util/charreach.h"
#include "util/ue2_containers.h"
#include <memory>
#include <vector>
#include <set>
struct NFA;
namespace ue2 {
struct CompileContext;
struct raw_report_info {
raw_report_info();
virtual ~raw_report_info();
virtual u32 getReportListSize() const = 0; /* in bytes */
virtual size_t size() const = 0; /* number of lists */
virtual void fillReportLists(NFA *n, size_t base_offset,
std::vector<u32> &ro /* out */) const = 0;
};
struct escape_info {
CharReach outs;
CharReach outs2_single;
flat_set<std::pair<u8, u8>> outs2;
bool outs2_broken = false;
};
class dfa_build_strat {
public:
virtual ~dfa_build_strat();
virtual raw_dfa &get_raw() const = 0;
virtual std::unique_ptr<raw_report_info> gatherReports(
std::vector<u32> &reports /* out */,
std::vector<u32> &reports_eod /* out */,
u8 *isSingleReport /* out */,
ReportID *arbReport /* out */) const = 0;
virtual void find_escape_strings(dstate_id_t this_idx,
escape_info *out) const = 0;
virtual size_t accelSize(void) const = 0;
virtual void buildAccel(dstate_id_t this_idx, void *accel_out) = 0;
};
class mcclellan_build_strat : public dfa_build_strat {
public:
explicit mcclellan_build_strat(raw_dfa &r) : rdfa(r) {}
raw_dfa &get_raw() const override { return rdfa; }
std::unique_ptr<raw_report_info> gatherReports(
std::vector<u32> &reports /* out */,
std::vector<u32> &reports_eod /* out */,
u8 *isSingleReport /* out */,
ReportID *arbReport /* out */) const override;
void find_escape_strings(dstate_id_t this_idx,
escape_info *out) const override;
size_t accelSize(void) const override;
void buildAccel(dstate_id_t this_idx, void *accel_out) override;
private:
raw_dfa &rdfa;
};
/* accel_states: (optional) on success, is filled with the set of accelerable
* states */
ue2::aligned_unique_ptr<NFA>
mcclellanCompile(raw_dfa &raw, const CompileContext &cc,
std::set<dstate_id_t> *accel_states = nullptr);
/* used internally by mcclellan/haig/gough compile process */
ue2::aligned_unique_ptr<NFA>
mcclellanCompile_i(raw_dfa &raw, dfa_build_strat &strat,
const CompileContext &cc,
std::set<dstate_id_t> *accel_states = nullptr);
/**
* \brief Returns the width of the character reach at start.
*/
u32 mcclellanStartReachSize(const raw_dfa *raw);
std::set<ReportID> all_reports(const raw_dfa &rdfa);
bool has_accel_dfa(const NFA *nfa);
} // namespace ue2
#endif

View File

@@ -0,0 +1,337 @@
/*
* Copyright (c) 2015, 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.
*/
#include "mcclellancompile_util.h"
#include "rdfa.h"
#include "util/container.h"
#include "util/ue2_containers.h"
#include "ue2common.h"
#include <deque>
#include <boost/functional/hash/hash.hpp>
using namespace std;
namespace ue2 {
#define INIT_STATE 1
static
u32 count_dots(const raw_dfa &raw) {
assert(raw.start_anchored == INIT_STATE);
u32 i = INIT_STATE;
for (; i < raw.states.size() && i != raw.start_floating; i++) {
DEBUG_PRINTF("checking %u\n", i);
assert(raw.states[i].reports.empty());
assert(raw.states[i].reports_eod.empty());
for (symbol_t s = 0; s < raw.getImplAlphaSize(); s++) {
DEBUG_PRINTF("%hu -> %hu\n", s, raw.states[i].next[s]);
if (raw.states[i].next[s] != i + 1) {
goto validate;
}
}
if (!raw.states[raw.states[i].next[0]].reports.empty()
|| !raw.states[raw.states[i].next[0]].reports_eod.empty()) {
goto validate;
}
DEBUG_PRINTF("got dot\n");
}
validate:
u32 dot_count = i - INIT_STATE;
/* we need to check that no later state has a transition into these leading
* dots */
for (; i < raw.states.size(); i++) {
for (symbol_t s = 0; s < raw.getImplAlphaSize(); s++) {
DEBUG_PRINTF("%hu -> %hu\n", s, raw.states[i].next[s]);
dstate_id_t n = raw.states[i].next[s];
if (n != DEAD_STATE && n <= dot_count) {
return 0;
}
}
}
return dot_count;
}
static
void prune_leading_states(raw_dfa &raw, u32 count) {
if (!count) {
return;
}
for (u32 i = INIT_STATE + count; i < raw.states.size(); i++) {
dstate &curr = raw.states[i - count];
curr = raw.states[i];
if (curr.daddy > count) {
curr.daddy -= count;
} else {
curr.daddy = DEAD_STATE;
}
for (u32 j = 0; j < raw.alpha_size; j++) {
assert(curr.next[j] == DEAD_STATE || curr.next[j] > count);
if (curr.next[j]) {
curr.next[j] -= count;
}
}
}
raw.states.erase(raw.states.end() - count, raw.states.end());
}
u32 remove_leading_dots(raw_dfa &raw) {
u32 count = count_dots(raw);
prune_leading_states(raw, count);
DEBUG_PRINTF("removed %u leading dots\n", count);
return count;
}
static never_inline
u32 calc_min_dist_from_bob(raw_dfa &raw, vector<u32> *dist_in) {
vector<u32> &dist = *dist_in;
dist.clear();
dist.resize(raw.states.size(), ~0U);
assert(raw.start_anchored != DEAD_STATE);
deque<dstate_id_t> to_visit;
to_visit.push_back(raw.start_anchored);
dist[raw.start_anchored] = 0;
u32 last_d = 0;
while (!to_visit.empty()) {
dstate_id_t s = to_visit.front();
DEBUG_PRINTF("inspecting %u\n", s);
to_visit.pop_front();
assert(s != DEAD_STATE);
u32 d = dist[s];
assert(d >= last_d);
assert(d != ~0U);
for (u32 j = 0; j < raw.alpha_size; j++) {
dstate_id_t t = raw.states[s].next[j];
if (t == DEAD_STATE) {
continue;
}
if (dist[t] == ~0U) {
to_visit.push_back(t);
dist[t] = d + 1;
} else {
assert(dist[t] <= d + 1);
}
}
last_d = d;
}
return last_d;
}
static
void find_in_edges(const raw_dfa &raw, vector<vector<dstate_id_t> > *in_edges) {
in_edges->clear();
in_edges->resize(raw.states.size());
ue2::unordered_set<dstate_id_t> seen;
for (u32 s = 1; s < raw.states.size(); s++) {
seen.clear();
for (u32 j = 0; j < raw.alpha_size; j++) {
dstate_id_t t = raw.states[s].next[j];
if (contains(seen, t)) {
continue;
}
seen.insert(t);
(*in_edges)[t].push_back(s);
}
}
}
static
void calc_min_dist_to_accept(const raw_dfa &raw,
const vector<vector<dstate_id_t> > &in_edges,
vector<u32> *accept_dist) {
vector<u32> &dist = *accept_dist;
dist.clear();
dist.resize(raw.states.size(), ~0U);
/* for reporting states to start from */
deque<dstate_id_t> to_visit;
for (u32 s = 0; s < raw.states.size(); s++) {
if (!raw.states[s].reports.empty()
|| !raw.states[s].reports_eod.empty()) {
to_visit.push_back(s);
dist[s] = 0;
}
}
/* bfs */
UNUSED u32 last_d = 0;
while (!to_visit.empty()) {
dstate_id_t s = to_visit.front();
to_visit.pop_front();
assert(s != DEAD_STATE);
u32 d = dist[s];
assert(d >= last_d);
assert(d != ~0U);
for (vector<dstate_id_t>::const_iterator it = in_edges[s].begin();
it != in_edges[s].end(); ++it) {
dstate_id_t t = *it;
if (t == DEAD_STATE) {
continue;
}
if (dist[t] == ~0U) {
to_visit.push_back(t);
dist[t] = d + 1;
} else {
assert(dist[t] <= d + 1);
}
}
last_d = d;
}
}
void prune_overlong(raw_dfa &raw, u32 max_offset) {
DEBUG_PRINTF("pruning to at most %u\n", max_offset);
vector<u32> bob_dist;
u32 max_min_dist_bob = calc_min_dist_from_bob(raw, &bob_dist);
if (max_min_dist_bob <= max_offset) {
return;
}
vector<vector<dstate_id_t> > in_edges;
find_in_edges(raw, &in_edges);
vector<u32> accept_dist;
calc_min_dist_to_accept(raw, in_edges, &accept_dist);
in_edges.clear();
/* look over the states and filter out any which cannot reach a report
* states before max_offset */
vector<dstate_id_t> new_ids(raw.states.size());
vector<dstate> new_states;
u32 count = 1;
new_states.push_back(raw.states[DEAD_STATE]);
for (u32 s = DEAD_STATE + 1; s < raw.states.size(); s++) {
if (bob_dist[s] + accept_dist[s] > max_offset) {
DEBUG_PRINTF("pruned %u: bob %u, report %u\n", s, bob_dist[s],
accept_dist[s]);
new_ids[s] = DEAD_STATE;
} else {
new_ids[s] = count++;
new_states.push_back(raw.states[s]);
assert(new_states.size() == count);
assert(new_ids[s] <= s);
}
}
/* swap states */
DEBUG_PRINTF("pruned %zu -> %u\n", raw.states.size(), count);
raw.states.swap(new_states);
new_states.clear();
/* update edges and daddys to refer to the new ids */
for (u32 s = DEAD_STATE + 1; s < raw.states.size(); s++) {
for (u32 j = 0; j < raw.alpha_size; j++) {
dstate_id_t old_t = raw.states[s].next[j];
raw.states[s].next[j] = new_ids[old_t];
}
raw.states[s].daddy = new_ids[raw.states[s].daddy];
}
/* update specials */
raw.start_floating = new_ids[raw.start_floating];
raw.start_anchored = new_ids[raw.start_anchored];
}
set<ReportID> all_reports(const raw_dfa &rdfa) {
set<ReportID> all;
for (const auto &ds : rdfa.states) {
insert(&all, ds.reports);
insert(&all, ds.reports_eod);
}
return all;
}
bool has_eod_accepts(const raw_dfa &rdfa) {
for (const auto &ds : rdfa.states) {
if (!ds.reports_eod.empty()) {
return true;
}
}
return false;
}
bool has_non_eod_accepts(const raw_dfa &rdfa) {
for (const auto &ds : rdfa.states) {
if (!ds.reports.empty()) {
return true;
}
}
return false;
}
size_t hash_dfa_no_reports(const raw_dfa &rdfa) {
using boost::hash_combine;
using boost::hash_range;
size_t v = 0;
hash_combine(v, rdfa.alpha_size);
hash_combine(v, hash_range(begin(rdfa.alpha_remap), end(rdfa.alpha_remap)));
for (const auto &ds : rdfa.states) {
hash_combine(v, hash_range(begin(ds.next), end(ds.next)));
}
return v;
}
size_t hash_dfa(const raw_dfa &rdfa) {
using boost::hash_combine;
size_t v = 0;
hash_combine(v, hash_dfa_no_reports(rdfa));
hash_combine(v, all_reports(rdfa));
return v;
}
} // namespace ue2

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MCCLELLAN_COMPILE_UTIL_H
#define MCCLELLAN_COMPILE_UTIL_H
#include "ue2common.h"
#include <set>
namespace ue2 {
struct raw_dfa;
u32 remove_leading_dots(raw_dfa &raw);
void prune_overlong(raw_dfa &raw, u32 max_offset);
std::set<ReportID> all_reports(const raw_dfa &rdfa);
bool has_eod_accepts(const raw_dfa &rdfa);
bool has_non_eod_accepts(const raw_dfa &rdfa);
/** \brief Compute a simple hash of this raw_dfa. Does not include report
* information. */
size_t hash_dfa_no_reports(const raw_dfa &rdfa);
/** \brief Compute a simple hash of this raw_dfa, including its reports. */
size_t hash_dfa(const raw_dfa &rdfa);
} // namespace ue2
#endif

438
src/nfa/mcclellandump.cpp Normal file
View File

@@ -0,0 +1,438 @@
/*
* Copyright (c) 2015, 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.
*/
#include "config.h"
#include "mcclellandump.h"
#include "accel.h"
#include "accel_dump.h"
#include "nfa_dump_internal.h"
#include "nfa_internal.h"
#include "mcclellan_internal.h"
#include "rdfa.h"
#include "ue2common.h"
#include "util/charreach.h"
#include "util/dump_charclass.h"
#include "util/unaligned.h"
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
using namespace std;
namespace ue2 {
const mstate_aux *getAux(const NFA *n, dstate_id_t i) {
assert(n && isDfaType(n->type));
const mcclellan *m = (const mcclellan *)getImplNfa(n);
const mstate_aux *aux_base
= (const mstate_aux *)((const char *)n + m->aux_offset);
const mstate_aux *aux = aux_base + i;
assert((const char *)aux < (const char *)n + m->length);
return aux;
}
static
void mcclellanGetTransitions(const NFA *n, u16 s, u16 *t) {
assert(isMcClellanType(n->type));
const mcclellan *m = (const mcclellan *)getImplNfa(n);
const mstate_aux *aux = getAux(n, s);
const u32 as = m->alphaShift;
if (n->type == MCCLELLAN_NFA_8) {
const u8 *succ_table = (const u8 *)((const char *)m
+ sizeof(mcclellan));
for (u16 c = 0; c < N_CHARS; c++) {
t[c] = succ_table[((u32)s << as) + m->remap[c]];
}
} else {
u16 base_s = s;
const char *winfo_base = (const char *)n + m->sherman_offset;
const char *state_base
= winfo_base + SHERMAN_FIXED_SIZE * (s - m->sherman_limit);
if (s >= m->sherman_limit) {
base_s = unaligned_load_u16(state_base + SHERMAN_DADDY_OFFSET);
}
const u16 *succ_table = (const u16 *)((const char *)m
+ sizeof(mcclellan));
for (u16 c = 0; c < N_CHARS; c++) {
const u8 *addr = (const u8*)(succ_table + (((u32)base_s << as)
+ m->remap[c]));
t[c] = unaligned_load_u16(addr);
t[c] &= STATE_MASK;
}
if (s >= m->sherman_limit) {
UNUSED char type = *(state_base + SHERMAN_TYPE_OFFSET);
assert(type == SHERMAN_STATE);
u8 len = *(const u8 *)(SHERMAN_LEN_OFFSET + state_base);
const char *chars = state_base + SHERMAN_CHARS_OFFSET;
const u16 *states = (const u16 *)(state_base
+ SHERMAN_STATES_OFFSET(len));
for (u8 i = 0; i < len; i++) {
for (u16 c = 0; c < N_CHARS; c++) {
if (m->remap[c] == chars[i]) {
t[c] = unaligned_load_u16((const u8*)&states[i]) & STATE_MASK;
}
}
}
}
}
t[TOP] = aux->top & STATE_MASK;
}
void describeEdge(FILE *f, const u16 *t, u16 i) {
for (u16 s = 0; s < N_CHARS; s++) {
if (!t[s]) {
continue;
}
u16 ss;
for (ss = 0; ss < s; ss++) {
if (t[s] == t[ss]) {
break;
}
}
if (ss != s) {
continue;
}
CharReach reach;
for (ss = s; ss < 256; ss++) {
if (t[s] == t[ss]) {
reach.set(ss);
}
}
fprintf(f, "%u -> %u [ label = \"", i, t[s]);
describeClass(f, reach, 5, CC_OUT_DOT);
fprintf(f, "\" ];\n");
}
}
void dumpAccelText(FILE *f, const union AccelAux *accel) {
switch(accel->accel_type) {
case ACCEL_NONE:
break;
case ACCEL_VERM:
fprintf(f, ":V");
break;
case ACCEL_DVERM:
fprintf(f, ":VV");
break;
case ACCEL_VERM_NOCASE:
fprintf(f, ":VN");
break;
case ACCEL_DVERM_NOCASE:
fprintf(f, ":VVN");
break;
case ACCEL_SHUFTI:
fprintf(f, ":S");
break;
case ACCEL_DSHUFTI:
fprintf(f, ":SS");
break;
case ACCEL_TRUFFLE:
fprintf(f, ":M");
break;
default:
fprintf(f, ":??");
break;
}
}
void dumpAccelDot(FILE *f, u16 i, const union AccelAux *accel) {
switch(accel->accel_type) {
case ACCEL_NONE:
break;
case ACCEL_VERM:
case ACCEL_VERM_NOCASE:
case ACCEL_DVERM:
case ACCEL_DVERM_NOCASE:
fprintf(f, "%u [ color = forestgreen style=diagonals];\n", i);
break;
case ACCEL_SHUFTI:
case ACCEL_DSHUFTI:
case ACCEL_TRUFFLE:
fprintf(f, "%u [ color = darkgreen style=diagonals ];\n", i);
break;
default:
fprintf(f, "%u [ color = yellow style=diagonals ];\n", i);
break;
}
}
static
void describeNode(const NFA *n, const mcclellan *m, u16 i, FILE *f) {
const mstate_aux *aux = getAux(n, i);
bool isSherman = m->sherman_limit && i >= m->sherman_limit;
fprintf(f, "%u [ width = 1, fixedsize = true, fontsize = 12, "
"label = \"%u%s\" ]; \n", i, i, isSherman ? "w":"");
if (aux->accel_offset) {
dumpAccelDot(f, i, (const union AccelAux *)
((const char *)m + aux->accel_offset));
}
if (aux->accept_eod) {
fprintf(f, "%u [ color = darkorchid ];\n", i);
}
if (aux->accept) {
fprintf(f, "%u [ shape = doublecircle ];\n", i);
}
if (aux->top && aux->top != i) {
fprintf(f, "%u -> %u [color = darkgoldenrod weight=0.1 ]\n", i,
aux->top);
}
if (i == m->start_anchored) {
fprintf(f, "STARTA -> %u [color = blue ]\n", i);
}
if (i == m->start_floating) {
fprintf(f, "STARTF -> %u [color = red ]\n", i);
}
if (isSherman) {
const char *winfo_base = (const char *)n + m->sherman_offset;
const char *state_base
= winfo_base + SHERMAN_FIXED_SIZE * (i - m->sherman_limit);
assert(state_base < (const char *)m + m->length - sizeof(NFA));
UNUSED u8 type = *(const u8 *)(state_base + SHERMAN_TYPE_OFFSET);
assert(type == SHERMAN_STATE);
fprintf(f, "%u [ fillcolor = lightblue style=filled ];\n", i);
u16 daddy = *(const u16 *)(state_base + SHERMAN_DADDY_OFFSET);
if (daddy) {
fprintf(f, "%u -> %u [ color=royalblue style=dashed weight=0.1]\n",
i, daddy);
}
}
}
void dumpDotPreambleDfa(FILE *f) {
dumpDotPreamble(f);
// DFA specific additions.
fprintf(f, "STARTF [style=invis];\n");
fprintf(f, "STARTA [style=invis];\n");
fprintf(f, "0 [style=invis];\n");
}
void nfaExecMcClellan16_dumpDot(const NFA *nfa, FILE *f) {
assert(nfa->type == MCCLELLAN_NFA_16);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
dumpDotPreambleDfa(f);
for (u16 i = 1; i < m->state_count; i++) {
describeNode(nfa, m, i, f);
u16 t[ALPHABET_SIZE];
mcclellanGetTransitions(nfa, i, t);
describeEdge(f, t, i);
}
fprintf(f, "}\n");
}
void nfaExecMcClellan8_dumpDot(const NFA *nfa, FILE *f) {
assert(nfa->type == MCCLELLAN_NFA_8);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
dumpDotPreambleDfa(f);
for (u16 i = 1; i < m->state_count; i++) {
describeNode(nfa, m, i, f);
u16 t[ALPHABET_SIZE];
mcclellanGetTransitions(nfa, i, t);
describeEdge(f, t, i);
}
fprintf(f, "}\n");
}
static
void dumpAccelMasks(FILE *f, const mcclellan *m, const mstate_aux *aux) {
fprintf(f, "\n");
fprintf(f, "Acceleration\n");
fprintf(f, "------------\n");
for (u16 i = 0; i < m->state_count; i++) {
if (!aux[i].accel_offset) {
continue;
}
const AccelAux *accel = (const AccelAux *)((const char *)m
+ aux[i].accel_offset);
fprintf(f, "%05hu ", i);
dumpAccelInfo(f, *accel);
}
}
void describeAlphabet(FILE *f, const mcclellan *m) {
map<u8, CharReach> rev;
for (u16 i = 0; i < N_CHARS; i++) {
rev[m->remap[i]].clear();
}
for (u16 i = 0; i < N_CHARS; i++) {
rev[m->remap[i]].set(i);
}
map<u8, CharReach>::const_iterator it;
fprintf(f, "\nAlphabet\n");
for (it = rev.begin(); it != rev.end(); ++it) {
fprintf(f, "%3hhu: ", it->first);
describeClass(f, it->second, 10240, CC_OUT_TEXT);
fprintf(f, "\n");
}
fprintf(f, "\n");
}
static
void dumpCommonHeader(FILE *f, const mcclellan *m) {
fprintf(f, "report: %u, states: %u, length: %u\n", m->arb_report,
m->state_count, m->length);
fprintf(f, "astart: %hu, fstart: %hu\n", m->start_anchored,
m->start_floating);
fprintf(f, "single accept: %d, has_accel: %d\n",
!!(int)m->flags & MCCLELLAN_FLAG_SINGLE, m->has_accel);
}
static
void dumpTransitions(FILE *f, const NFA *nfa, const mcclellan *m,
const mstate_aux *aux) {
for (u16 i = 0; i < m->state_count; i++) {
fprintf(f, "%05hu", i);
if (aux[i].accel_offset) {
dumpAccelText(f, (const union AccelAux *)((const char *)m +
aux[i].accel_offset));
}
u16 trans[ALPHABET_SIZE];
mcclellanGetTransitions(nfa, i, trans);
int rstart = 0;
u16 prev = 0xffff;
for (int j = 0; j < N_CHARS; j++) {
u16 curr = trans[j];
if (curr == prev) {
continue;
}
if (prev != 0xffff) {
if (j == rstart + 1) {
fprintf(f, " %02x->%hu", rstart, prev);
} else {
fprintf(f, " [%02x - %02x]->%hu", rstart, j - 1, prev);
}
}
prev = curr;
rstart = j;
}
if (N_CHARS == rstart + 1) {
fprintf(f, " %02x->%hu", rstart, prev);
} else {
fprintf(f, " [%02x - %02x]->%hu", rstart, N_CHARS - 1, prev);
}
fprintf(f, "\n");
}
}
void nfaExecMcClellan16_dumpText(const NFA *nfa, FILE *f) {
assert(nfa->type == MCCLELLAN_NFA_16);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
const mstate_aux *aux =
(const mstate_aux *)((const char *)nfa + m->aux_offset);
fprintf(f, "mcclellan 16\n");
dumpCommonHeader(f, m);
fprintf(f, "sherman_limit: %d, sherman_end: %d\n", (int)m->sherman_limit,
(int)m->sherman_end);
fprintf(f, "\n");
describeAlphabet(f, m);
dumpTransitions(f, nfa, m, aux);
dumpAccelMasks(f, m, aux);
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
void nfaExecMcClellan8_dumpText(const NFA *nfa, FILE *f) {
assert(nfa->type == MCCLELLAN_NFA_8);
const mcclellan *m = (const mcclellan *)getImplNfa(nfa);
const mstate_aux *aux =
(const mstate_aux *)((const char *)nfa + m->aux_offset);
fprintf(f, "mcclellan 8\n");
dumpCommonHeader(f, m);
fprintf(f, "accel_limit: %hu, accept_limit %hu\n", m->accel_limit_8,
m->accept_limit_8);
fprintf(f, "\n");
describeAlphabet(f, m);
dumpTransitions(f, nfa, m, aux);
dumpAccelMasks(f, m, aux);
fprintf(f, "\n");
dumpTextReverse(nfa, f);
}
} // namespace ue2

63
src/nfa/mcclellandump.h Normal file
View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MCCLELLAN_DUMP_H
#define MCCLELLAN_DUMP_H
#ifdef DUMP_SUPPORT
#include "rdfa.h"
#include <cstdio>
struct mcclellan;
struct mstate_aux;
struct NFA;
union AccelAux;
namespace ue2 {
void nfaExecMcClellan8_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecMcClellan16_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecMcClellan8_dumpText(const struct NFA *nfa, FILE *file);
void nfaExecMcClellan16_dumpText(const struct NFA *nfa, FILE *file);
/* These functions are shared with the Haig dump code. */
const mstate_aux *getAux(const NFA *n, dstate_id_t i);
void describeEdge(FILE *f, const u16 *t, u16 i);
void dumpAccelText(FILE *f, const union AccelAux *accel);
void dumpAccelDot(FILE *f, u16 i, const union AccelAux *accel);
void describeAlphabet(FILE *f, const mcclellan *m);
void dumpDotPreambleDfa(FILE *f);
} // namespace ue2
#endif // DUMP_SUPPORT
#endif // MCCLELLAN_DUMP_H

1093
src/nfa/mpv.c Normal file

File diff suppressed because it is too large Load Diff

60
src/nfa/mpv.h Normal file
View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MPV_H
#define MPV_H
#include "ue2common.h"
struct mq;
struct NFA;
char nfaExecMpv0_Q(const struct NFA *n, struct mq *q, s64a end);
char nfaExecMpv0_reportCurrent(const struct NFA *n, struct mq *q);
char nfaExecMpv0_inAccept(const struct NFA *n, ReportID report, struct mq *q);
char nfaExecMpv0_queueInitState(const struct NFA *n, struct mq *q);
char nfaExecMpv0_initCompressedState(const struct NFA *n, u64a offset,
void *state, u8 key);
char nfaExecMpv0_queueCompressState(const struct NFA *nfa, const struct mq *q,
s64a loc);
char nfaExecMpv0_expandState(const struct NFA *nfa, void *dest, const void *src,
u64a offset, u8 key);
#define nfaExecMpv0_testEOD NFA_API_NO_IMPL
#define nfaExecMpv0_inAccept NFA_API_NO_IMPL
#define nfaExecMpv0_QR NFA_API_NO_IMPL
#define nfaExecMpv0_Q2 NFA_API_NO_IMPL /* for non-chained suffixes. */
#define nfaExecMpv0_B_Reverse NFA_API_NO_IMPL
#define nfaExecMpv0_zombie_status NFA_API_NO_IMPL
/**
* return 0 if the mpv dies, otherwise returns the location of the next possible
* match (given the currently known events). */
s64a nfaExecMpv0_QueueExecRaw(const struct NFA *nfa, struct mq *q, s64a end);
#endif

152
src/nfa/mpv_dump.cpp Normal file
View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2015, 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.
*/
#include "config.h"
#include "mpv_dump.h"
#include "mpv_internal.h"
#include "nfa_dump_internal.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include "util/compare.h"
#include "util/dump_mask.h"
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cctype>
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
namespace ue2 {
void nfaExecMpv0_dumpDot(UNUSED const NFA *nfa, UNUSED FILE *file) {
}
static really_inline
u32 largest_puff_repeat(const mpv *m, const mpv_kilopuff *kp) {
return get_puff_array(m, kp)[kp->count - 1].repeats;
}
static
void dumpKilo(FILE *f, const mpv *m, const mpv_kilopuff *k) {
if (k->auto_restart) {
fprintf(f, " AUTO RESTART\n");
}
fprintf(f, " ");
switch (k->type) {
case MPV_DOT:
fprintf(f, "dot\n");
break;
case MPV_VERM:
if (!ourisprint(k->u.verm.c)) {
fprintf(f, "verm 0x%hhu\n", k->u.verm.c);
} else {
fprintf(f, "verm 0x%hhu '%c'\n", k->u.verm.c, k->u.verm.c);
}
break;
case MPV_SHUFTI:
fprintf(f, "shufti\n");
fprintf(f, "lo %s\n",
dumpMask((const u8 *)&k->u.shuf.mask_lo, 128).c_str());
fprintf(f, "hi %s\n",
dumpMask((const u8 *)&k->u.shuf.mask_hi, 128).c_str());
break;
case MPV_TRUFFLE:
fprintf(f, "truffle\n");
break;
case MPV_NVERM:
if (!ourisprint(k->u.verm.c)) {
fprintf(f, "nverm 0x%hhu\n", k->u.verm.c);
} else {
fprintf(f, "nverm 0x%hhu '%c'\n", k->u.verm.c, k->u.verm.c);
}
break;
default:
fprintf(f, "unknown type: %hhu\n", k->type);
}
fprintf(f, " %u puffettes\n", k->count);
fprintf(f, " largest repeat %u\n", largest_puff_repeat(m, k));
fprintf(f, " dead point %llu\n", k->dead_point);
fprintf(f, " counter offset %u\n", k->counter_offset);
fprintf(f, " puffette offset %u\n", k->puffette_offset);
const mpv_puffette *p = get_puff_array(m, k);
for (u32 i = 0; i < k->count; i++) {
fprintf(f, "\n");
fprintf(f, " Puffette %u\n", i);
fprintf(f, " repeats: %u%s\n", p[i].repeats,
p[i].unbounded ? "," : "");
fprintf(f, " report id: %u\n", p[i].report);
}
fprintf(f, "\n");
}
static
void dumpCounter(FILE *f, const mpv_counter_info *c) {
fprintf(f, " max value %llu\n", c->max_counter);
fprintf(f, " state offset %u\n", c->counter_offset);
fprintf(f, " used by kilopuffs %u - %u\n", c->kilo_begin,
c->kilo_end - 1);
fprintf(f, " bytes %u\n", c->counter_size);
fprintf(f, "\n");
}
void nfaExecMpv0_dumpText(const NFA *nfa, FILE *f) {
const mpv *m = (const mpv *)getImplNfa(nfa);
fprintf(f, "Puff the Magic Engines\n");
fprintf(f, "\n");
fprintf(f, "%u puffettes in %u kilopuffs\n", m->puffette_count,
m->kilo_count);
fprintf(f, "initial kilopuffs %u - %u\n", m->top_kilo_begin,
m->top_kilo_end - 1);
const mpv_kilopuff *k = (const mpv_kilopuff *)(m + 1);
for (u32 i = 0; i < m->kilo_count; i++) {
fprintf(f, "\nKILOPUFF %u\n", i);
dumpKilo(f, m, k++);
}
const mpv_counter_info *c = get_counter_info(m);
for (u32 i = 0; i < m->counter_count; i++) {
fprintf(f, "\nCOUNTER %u\n", i);
dumpCounter(f, c++);
}
dumpTextReverse(nfa, f);
}
} // namespace ue2

47
src/nfa/mpv_dump.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MPVDUMP_H
#define MPVDUMP_H
#if defined(DUMP_SUPPORT)
#include <cstdio>
struct NFA;
namespace ue2 {
void nfaExecMpv0_dumpDot(const struct NFA *nfa, FILE *file);
void nfaExecMpv0_dumpText(const struct NFA *nfa, FILE *file);
} // namespace ue2
#endif
#endif // MPVDUMP_H

188
src/nfa/mpv_internal.h Normal file
View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MPV_INTERNAL_H
#define MPV_INTERNAL_H
#include "ue2common.h"
#define MPV_DOT 0
#define MPV_VERM 1
#define MPV_SHUFTI 2
#define MPV_TRUFFLE 3
#define MPV_NVERM 4
struct mpv_puffette {
u32 repeats;
char unbounded;
ReportID report;
};
struct mpv_kilopuff {
u32 counter_offset; /**< offset (in full stream state) to the counter that
* this kilopuff refers to */
u32 count; /**< number of real (non sentinel mpv puffettes) */
u32 puffette_offset; /**< relative to base of mpv, points past the 1st
* sent */
u64a dead_point;
u8 auto_restart;
u8 type; /* MPV_DOT, MPV_VERM, etc */
union {
struct {
char c;
} verm;
struct {
m128 mask_lo;
m128 mask_hi;
} shuf;
struct {
m128 mask1;
m128 mask2;
} truffle;
} u;
};
struct mpv_counter_info {
u64a max_counter; /**< maximum value this counter needs to track */
u32 counter_size; /**< number of bytes to represent the counter in stream
* state */
u32 counter_offset; /**< offset that this counter is stored at in the
* full stream state */
u32 kilo_begin; /**< first kilo to turn on when the counter is started */
u32 kilo_end; /**< 1 + last kilo to turn on when the counter is started */
};
struct ALIGN_AVX_DIRECTIVE mpv {
u32 kilo_count; /**< number of kilopuffs following */
u32 counter_count; /**< number of counters managed by the mpv */
u32 puffette_count; /**< total number of puffettes under all the kilos */
u32 pq_offset; /**< offset to the priority queue in the decompressed
* state */
u32 reporter_offset; /**< offset to the reporter mmbit in the decompressed
* state */
u32 report_list_offset; /**< offset to the report list scratch space in the
* decompressed state */
u32 active_offset; /**< offset to the active kp mmbit in the compressed
* state */
u32 top_kilo_begin; /**< first kilo to switch on when top arrives */
u32 top_kilo_end; /**< one past the last kilo to switch on when top
* arrives */
};
struct mpv_decomp_kilo {
u64a limit;
const struct mpv_puffette *curr;
};
/* note: size varies on different platforms */
struct mpv_decomp_state {
u32 pq_size;
char filled;
u64a counter_adj; /**< progress not yet written to the real counters */
struct mpv_decomp_kilo active[];
};
/* ---
* | | mpv
* ---
* | |
* | | kilo_count * mpv_kilopuffs
* | |
* ...
* | |
* ---
* | |
* | | counter_count * mpv_counter_infos
* | |
* ...
* | |
* ---
* | | sentinel mpv_puffette
* ---
* | | mpv_puffettes for 1st kilopuff
* | | (mpv_puffettes are ordered by minimum number of repeats)
* | |
* ---
* | | sentinel mpv_puffette
* ---
* | | mpv_puffettes for 2nd kilopuff
* ...
* | |
* ---
* | | sentinel mpv_puffette
* ---
*/
/*
* Stream State
* [Compressed Counter 0]
* [Compressed Counter 1]
* ...
* [Compressed Counter N]
* [mmbit of active kilopuffs]
*
* Decompressed State
* [header (limit pq_size)]
* [
* [kilo 1 current reports]
* ...
* [kilo N current reports]
* ]
* [
* [Full Counter 0]
* [Full Counter 1]
* ...
* [Full Counter N]
* ]
* [pq of kilo changes]
* [scratch space for current report lists (total number of puffettes)]
* [mmbit of kilopuffs with active reports]
*/
struct mpv_pq_item {
u64a trigger_loc;
u32 kilo;
};
/* returns pointer to first non sentinel mpv_puff */
static really_inline
const struct mpv_puffette *get_puff_array(const struct mpv *m,
const struct mpv_kilopuff *kp) {
return (const struct mpv_puffette *)((const char *)m + kp->puffette_offset);
}
static really_inline
const struct mpv_counter_info *get_counter_info(const struct mpv *m) {
return (const struct mpv_counter_info *)((const char *)(m + 1)
+ m->kilo_count * sizeof(struct mpv_kilopuff));
}
#define MPV_DEAD_VALUE (~0ULL)
#define INVALID_REPORT (~0U)
#endif

389
src/nfa/mpvcompile.cpp Normal file
View File

@@ -0,0 +1,389 @@
/*
* Copyright (c) 2015, 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.
*/
#include "mpvcompile.h"
#include "mpv_internal.h"
#include "nfa_api_queue.h"
#include "nfa_internal.h"
#include "shufticompile.h"
#include "trufflecompile.h"
#include "util/alloc.h"
#include "util/multibit_internal.h"
#include "util/order_check.h"
#include "util/verify_types.h"
#include <algorithm>
#include <iterator>
#include <map>
#include <boost/range/adaptor/map.hpp>
using namespace std;
using boost::adaptors::map_values;
using boost::adaptors::map_keys;
namespace ue2 {
namespace {
struct pcomp {
bool operator()(const raw_puff &a, const raw_puff &b) const {
ORDER_CHECK(repeats);
ORDER_CHECK(unbounded);
ORDER_CHECK(report);
return false;
}
};
struct ClusterKey {
explicit ClusterKey(const raw_puff &src)
: trigger_event(MQE_INVALID), reach(src.reach),
auto_restart(src.auto_restart) {}
ClusterKey(u32 event, const raw_puff &src)
: trigger_event(event), reach(src.reach),
auto_restart(src.auto_restart) {}
u32 trigger_event;
CharReach reach;
bool auto_restart;
bool operator<(const ClusterKey &b) const {
const ClusterKey &a = *this;
ORDER_CHECK(trigger_event); /* want triggered puffs first */
ORDER_CHECK(auto_restart);
ORDER_CHECK(reach);
return false;
}
};
} // namespace
static
void writePuffette(mpv_puffette *out, const raw_puff &rp) {
DEBUG_PRINTF("outputting %u %d %u to %p\n", rp.repeats, (int)rp.unbounded,
rp.report, out);
out->repeats = rp.repeats;
out->unbounded = rp.unbounded;
out->report = rp.report;
}
static
void writeDeadPoint(mpv_kilopuff *out, const vector<raw_puff> &puffs) {
for (const auto &puff : puffs) {
if (puff.unbounded) { /* mpv can never die */
out->dead_point = MPV_DEAD_VALUE;
return;
}
}
out->dead_point = puffs.back().repeats + 1;
}
static
size_t calcSize(const map<ClusterKey, vector<raw_puff>> &raw,
const vector<mpv_counter_info> &counters) {
size_t len = sizeof(NFA) + sizeof(mpv);
len += sizeof(mpv_kilopuff) * raw.size(); /* need a kilopuff for each
distinct reach */
len += sizeof(mpv_counter_info) * counters.size();
len += sizeof(mpv_puffette); /* initial sent */
for (const vector<raw_puff> &puffs : raw | map_values) {
len += sizeof(mpv_puffette) * puffs.size();
len += sizeof(mpv_puffette); /* terminal sent */
}
return len;
}
static
void populateClusters(const vector<raw_puff> &puffs_in,
const vector<raw_puff> &triggered_puffs,
map<ClusterKey, vector<raw_puff>> *raw) {
map<ClusterKey, vector<raw_puff>> &puff_clusters = *raw;
u32 e = MQE_TOP_FIRST;
for (const auto &puff : triggered_puffs) {
puff_clusters[ClusterKey(e, puff)].push_back(puff);
e++;
}
for (const auto &puff : puffs_in) {
puff_clusters[ClusterKey(puff)].push_back(puff);
}
for (vector<raw_puff> &puffs : puff_clusters | map_values) {
sort(puffs.begin(), puffs.end(), pcomp());
}
}
static
void writeKiloPuff(const map<ClusterKey, vector<raw_puff>>::const_iterator &it,
u32 counter_offset, mpv *m, mpv_kilopuff *kp,
mpv_puffette **pa) {
const CharReach &reach = it->first.reach;
const vector<raw_puff> &puffs = it->second;
kp->auto_restart = it->first.auto_restart;
if (reach.all()) {
kp->type = MPV_DOT;
} else if (reach.count() == 255) {
kp->type = MPV_VERM;
size_t unset = (~reach).find_first();
assert(unset != CharReach::npos);
kp->u.verm.c = (char)unset;
} else if (reach.count() == 1) {
kp->type = MPV_NVERM;
size_t set = reach.find_first();
assert(set != CharReach::npos);
kp->u.verm.c = (char)set;
} else if (shuftiBuildMasks(~reach, &kp->u.shuf.mask_lo,
&kp->u.shuf.mask_hi) != -1) {
kp->type = MPV_SHUFTI;
} else {
kp->type = MPV_TRUFFLE;
truffleBuildMasks(~reach, &kp->u.truffle.mask1, &kp->u.truffle.mask2);
}
kp->count = verify_u32(puffs.size());
kp->counter_offset = counter_offset;
/* start of real puffette array */
kp->puffette_offset = verify_u32((char *)*pa - (char *)m);
for (size_t i = 0; i < puffs.size(); i++) {
assert(!it->first.auto_restart || puffs[i].unbounded);
writePuffette(*pa + i, puffs[i]);
}
*pa += puffs.size();
writePuffette(*pa, raw_puff(0U, false, INVALID_REPORT, CharReach()));
++*pa;
writeDeadPoint(kp, puffs);
}
static
void writeCoreNfa(NFA *nfa, u32 len, u32 min_width, u32 max_counter,
u32 streamStateSize, u32 scratchStateSize) {
assert(nfa);
nfa->length = len;
nfa->nPositions = max_counter - 1;
nfa->type = MPV_NFA_0;
nfa->streamStateSize = streamStateSize;
assert(16 >= sizeof(mpv_decomp_kilo));
nfa->scratchStateSize = scratchStateSize;
nfa->minWidth = min_width;
}
static
void findCounterSize(map<ClusterKey, vector<raw_puff>>::const_iterator kp_it,
map<ClusterKey, vector<raw_puff>>::const_iterator kp_ite,
u64a *max_counter_out, u32 *counter_size) {
u32 max_counter = 0; /* max counter that we may need to know about is one
more than largest repeat */
for (; kp_it != kp_ite; ++kp_it) {
max_counter = MAX(max_counter, kp_it->second.back().repeats + 1);
}
if (max_counter < (1U << 8)) {
*counter_size = 1;
} else if (max_counter < (1U << 16)) {
*counter_size = 2;
} else if (max_counter < (1U << 24)) {
*counter_size = 3;
} else {
*counter_size = 4;
}
*max_counter_out = max_counter;
}
static
void fillCounterInfo(mpv_counter_info *out, u32 *curr_decomp_offset,
u32 *curr_comp_offset,
const map<ClusterKey, vector<raw_puff>> &kilopuffs,
map<ClusterKey, vector<raw_puff>>::const_iterator kp_it,
map<ClusterKey, vector<raw_puff>>::const_iterator kp_ite) {
out->kilo_begin = distance(kilopuffs.begin(), kp_it);
out->kilo_end = distance(kilopuffs.begin(), kp_ite);
findCounterSize(kp_it, kp_ite, &out->max_counter, &out->counter_size);
out->counter_offset = *curr_decomp_offset;
*curr_decomp_offset += sizeof(u64a);
*curr_comp_offset += out->counter_size;
}
static
void fillCounterInfos(vector<mpv_counter_info> *out, u32 *curr_decomp_offset,
u32 *curr_comp_offset,
const map<ClusterKey, vector<raw_puff>> &kilopuffs) {
/* first the triggered puffs */
map<ClusterKey, vector<raw_puff>>::const_iterator it = kilopuffs.begin();
while (it != kilopuffs.end() && it->first.trigger_event != ~0U) {
assert(!it->first.auto_restart);
assert(it->first.trigger_event
== MQE_TOP_FIRST + distance(kilopuffs.begin(), it));
out->push_back(mpv_counter_info());
map<ClusterKey, vector<raw_puff>>::const_iterator it_o = it;
++it;
fillCounterInfo(&out->back(), curr_decomp_offset, curr_comp_offset,
kilopuffs, it_o, it);
}
/* we may have 2 sets of non triggered puffs:
* 1) always started with no auto_restart
* 2) always started with auto_restart
*/
map<ClusterKey, vector<raw_puff>>::const_iterator trig_ite = it;
while (it != kilopuffs.end() && !it->first.auto_restart) {
assert(it->first.trigger_event == ~0U);
++it;
}
if (it != trig_ite) {
out->push_back(mpv_counter_info());
fillCounterInfo(&out->back(), curr_decomp_offset, curr_comp_offset,
kilopuffs, kilopuffs.begin(), it);
}
while (it != kilopuffs.end() && it->first.auto_restart) {
assert(it->first.trigger_event == ~0U);
out->push_back(mpv_counter_info());
map<ClusterKey, vector<raw_puff>>::const_iterator it_o = it;
++it;
fillCounterInfo(&out->back(), curr_decomp_offset, curr_comp_offset,
kilopuffs, it_o, it);
}
}
static
const mpv_counter_info &findCounter(const vector<mpv_counter_info> &counters,
u32 i) {
for (const auto &counter : counters) {
if (i >= counter.kilo_begin && i < counter.kilo_end) {
return counter;
}
}
assert(0);
return counters.front();
}
aligned_unique_ptr<NFA> mpvCompile(const vector<raw_puff> &puffs_in,
const vector<raw_puff> &triggered_puffs) {
assert(!puffs_in.empty() || !triggered_puffs.empty());
u32 puffette_count = puffs_in.size() + triggered_puffs.size();
map<ClusterKey, vector<raw_puff>> puff_clusters;
populateClusters(puffs_in, triggered_puffs, &puff_clusters);
u32 curr_comp_offset = 0;
u32 curr_decomp_offset = sizeof(mpv_decomp_state);
curr_decomp_offset += 16 * puff_clusters.size();
vector<mpv_counter_info> counters;
fillCounterInfos(&counters, &curr_decomp_offset, &curr_comp_offset,
puff_clusters);
u32 pq_offset = curr_decomp_offset;
curr_decomp_offset += sizeof(mpv_pq_item) * puff_clusters.size();
u32 rl_offset = curr_decomp_offset;
curr_decomp_offset += sizeof(ReportID) * puffette_count;
u32 reporter_offset = curr_decomp_offset;
curr_decomp_offset += mmbit_size(puff_clusters.size());
u32 active_offset = curr_comp_offset;
curr_comp_offset += mmbit_size(puff_clusters.size());
u32 len = calcSize(puff_clusters, counters);
DEBUG_PRINTF("%u puffs, len = %u\n", puffette_count, len);
aligned_unique_ptr<NFA> nfa = aligned_zmalloc_unique<NFA>(len);
mpv_puffette *pa_base = (mpv_puffette *)
((char *)nfa.get() + sizeof(NFA) + sizeof(mpv)
+ sizeof(mpv_kilopuff) * puff_clusters.size()
+ sizeof(mpv_counter_info) * counters.size());
mpv_puffette *pa = pa_base;
writePuffette(pa, raw_puff(0U, false, INVALID_REPORT, CharReach()));
++pa; /* skip init sentinel */
u32 min_repeat = ~0U;
u32 max_counter = 0; /* max counter that we may need to know about is one
more than largest repeat */
for (const vector<raw_puff> &puffs : puff_clusters | map_values) {
max_counter = max(max_counter, puffs.back().repeats + 1);
min_repeat = min(min_repeat, puffs.front().repeats);
}
mpv *m = (mpv *)getMutableImplNfa(nfa.get());
m->kilo_count = verify_u32(puff_clusters.size());
m->counter_count = verify_u32(counters.size());
m->puffette_count = puffette_count;
m->pq_offset = pq_offset;
m->reporter_offset = reporter_offset;
m->report_list_offset = rl_offset;
m->active_offset = active_offset;
m->top_kilo_begin = verify_u32(triggered_puffs.size());
m->top_kilo_end = verify_u32(puff_clusters.size());
mpv_kilopuff *kp_begin = (mpv_kilopuff *)(m + 1);
mpv_kilopuff *kp = kp_begin;
for (auto it = puff_clusters.begin(); it != puff_clusters.end(); ++it) {
writeKiloPuff(it, findCounter(counters, kp - kp_begin).counter_offset,
m, kp, &pa);
++kp;
}
assert((char *)pa == (char *)nfa.get() + len);
mpv_counter_info *out_ci = (mpv_counter_info *)kp;
for (const auto &counter : counters) {
*out_ci = counter;
++out_ci;
}
assert((char *)out_ci == (char *)pa_base);
writeCoreNfa(nfa.get(), len, min_repeat, max_counter, curr_comp_offset,
curr_decomp_offset);
return nfa;
}
} // namespace ue2

65
src/nfa/mpvcompile.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef MPV_COMPILE_H
#define MPV_COMPILE_H
#include "ue2common.h"
#include "util/alloc.h"
#include "util/charreach.h"
#include <memory>
#include <vector>
struct NFA;
namespace ue2 {
struct raw_puff {
raw_puff(u32 repeats_in, bool unbounded_in, ReportID report_in,
const CharReach &reach_in, bool auto_restart_in = false)
: repeats(repeats_in), unbounded(unbounded_in),
auto_restart(auto_restart_in), report(report_in), reach(reach_in) {}
u32 repeats; /**< report match after this many matching bytes */
bool unbounded; /**< keep producing matches after repeats are reached */
bool auto_restart; /**< for /[^X]{n}/ type patterns */
ReportID report;
CharReach reach; /**< = ~escapes */
};
/*
* puffs in the triggered_puffs vector are enabled when an TOP_N event is
* delivered corresponding to their index in the vector
*/
aligned_unique_ptr<NFA>
mpvCompile(const std::vector<raw_puff> &puffs,
const std::vector<raw_puff> &triggered_puffs);
} // namespace ue2
#endif

257
src/nfa/nfa_api.h Normal file
View File

@@ -0,0 +1,257 @@
/*
* Copyright (c) 2015, 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 Declarations for the main NFA Engine API.
*
* This file provides the internal API for all runtime engines ("NFAs", even if
* they're not strictly NFA implementations).
*/
#ifndef NFA_API_H
#define NFA_API_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "callback.h"
#include "ue2common.h"
struct hs_scratch;
struct mq;
struct NFA;
/**
* Indicates if an nfa is a zombie. Note: that there were plans for a more
* nuanced view of zombiehood but this never eventuated.
*/
enum nfa_zombie_status {
NFA_ZOMBIE_NO, /**< nfa is not a zombie and will respond to top events */
NFA_ZOMBIE_ALWAYS_YES /**< nfa is a zombie and will always be a zombie */
};
/**
* Compresses an engine's state.
* The expanded state (@ref mq::state, @ref mq::streamState) is reduced purely
* to a corresponding compressed stream state (@ref mq::streamState).
*
* @param nfa engine the state belongs to
* @param q queue for the engine. The final compressed stream stream is placed
* in the location indicated by @ref mq::streamState
* @param loc the location corresponding to the engine's current state
*/
char nfaQueueCompressState(const struct NFA *nfa, const struct mq *q, s64a loc);
/**
* Expands an engine's compressed stream state, into its scratch space
* representation. This is required before an engine starts operating over its
* queue.
*
* @param nfa engine the state belongs to
* @param dest location in scratch for decompressed state
* @param src compressed stream state
* @param offset the current stream offset.
* @param key byte corresponding to the location where the compressed state was
* created.
*/
char nfaExpandState(const struct NFA *nfa, void *dest, const void *src,
u64a offset, u8 key);
/**
* Gives us a properly initialised dead state suitable for later @ref
* nfaQueueExec calls.
*/
char nfaQueueInitState(const struct NFA *nfa, struct mq *q);
/**
* Initialise the state, applying a TOP appropriate for the offset. If the
* NFA becomes inactive, return zero. Otherwise, write out its compressed
* representation to `state' and return non-zero.
*
* @param nfa engine the state belongs to
* @param offset offset in the stream (relative to start of stream)
* @param state pointer indicating where the state is to be written
* @param key byte corresponding to the location where the compressed state is
* to be created.
*/
char nfaInitCompressedState(const struct NFA *nfa, u64a offset, void *state,
u8 key);
/**
* Process the queued commands on the given NFA.
*
* @param nfa the NFA to execute
* @param q the queued commands. It must start with some variant of start and
* end with some variant of end. The location field of the events must
* be monotonically increasing.
* @param end stop processing command queue when we reach this point
*
* @return non-zero if the nfa is still active, if the nfa is not active the
* state data is undefined
*
* Note: this function can not process events from the past: the location field
* of each event must be >= current offset.
*/
char nfaQueueExec(const struct NFA *nfa, struct mq *q, s64a end);
/** Return value indicating that the engine is alive. */
#define MO_ALIVE 1
/** Return value from @ref nfaQueueExecToMatch indicating that engine progress
* stopped as a match state was reached. */
#define MO_MATCHES_PENDING 2
/**
* Process the queued commands on the given nfa up to end or the first match.
* This function will only fire the callback in response to an report_current
* being set and accepts at the starting offset, in all other situations accepts
* will result in the queue pausing with a return value of
* @ref MO_MATCHES_PENDING.
*
* @param nfa the NFA to execute
* @param q the queued commands. It must start with some variant of start and
* end with some variant of end. The location field of the events must
* be monotonically increasing. If not all the data was processed during
* the call, the queue is updated to reflect the remaining work.
* @param end stop processing command queue when we reach this point
*
* @return @ref MO_ALIVE if the nfa is still active with no matches pending,
* and @ref MO_MATCHES_PENDING if there are matches pending, 0 if not
* alive
*
* Note: if it can be determined that the stream can never match, the nfa
* may be reported as dead even if not all the data was scanned
*
* Note: if the nfa is not alive the state data is undefined
*
* Note: this function can not process events from the past: the location field
* of each event must be >= current offset.
*/
char nfaQueueExecToMatch(const struct NFA *nfa, struct mq *q, s64a end);
/**
* Report matches at the current queue location.
*
* @param nfa the NFA to execute
* @param q the queued commands. It must start with some variant of start and
* end with some variant of end. The location field of the events must
* be monotonically increasing.
*
* Note: the queue MUST be located at position where @ref nfaQueueExecToMatch
* returned @ref MO_MATCHES_PENDING.
*
* Note: the return value of this call is undefined, and should be ignored.
*/
char nfaReportCurrentMatches(const struct NFA *nfa, struct mq *q);
/**
* Returns non-zero if the NFA is in an accept state with the given report ID.
*/
char nfaInAcceptState(const struct NFA *nfa, ReportID report, struct mq *q);
/**
* Process the queued commands on the given NFA up to end or the first match.
*
* Note: This version is meant for rose prefix NFAs:
* - never uses a callback
* - loading of state at a point in history is not special cased
*
* @param nfa the NFA to execute
* @param q the queued commands. It must start with some variant of start and
* end with some variant of end. The location field of the events must
* be monotonically increasing. If not all the data was processed during
* the call, the queue is updated to reflect the remaining work.
* @param report we are interested in, if set at the end of the scan returns
* @ref MO_MATCHES_PENDING
* @return @ref MO_ALIVE if the nfa is still active with no matches pending,
* and @ref MO_MATCHES_PENDING if there are matches pending, 0 if not
* alive
*
* Note: if it can be determined that the stream can never match, the nfa
* may be reported as dead even if not all the data was scanned
*
* Note: if the NFA is not active the state data is undefined.
*/
char nfaQueueExecRose(const struct NFA *nfa, struct mq *q, ReportID report);
/**
* Runs an NFA in reverse from (buf + buflen) to buf and then from (hbuf + hlen)
* to hbuf (main buffer and history buffer).
*
* @param nfa engine to run
* @param offset base offset of buf
* @param buf main buffer
* @param buflen length of buf
* @param hbuf history buf
* @param hlen length of hbuf
* @param scratch scratch
* @param callback the callback to call for each match raised
* @param context context pointer passed to each callback
*
* Note: is NOT reentrant
*/
char nfaBlockExecReverse(const struct NFA *nfa, u64a offset, const u8 *buf,
size_t buflen, const u8 *hbuf, size_t hlen,
struct hs_scratch *scratch, NfaCallback callback,
void *context);
/**
* Check whether the given NFA's state indicates that it is in one or more
* final (accept at end of data) state. If so, call the callback for each
* match.
*
* @param nfa the NFA to execute
* @param state current state associated with this NFA
* @param streamState stream version of the state associated with this NFA
* (including br region)
* @param offset the offset to return (via the callback) with each match
* @param callback the callback to call for each match raised
* @param som_cb the callback to call for each match raised (Haig)
* @param context context pointer passed to each callback
*/
char nfaCheckFinalState(const struct NFA *nfa, const char *state,
const char *streamState, u64a offset,
NfaCallback callback, SomNfaCallback som_cb,
void *context);
/**
* Indicates if an engine is a zombie.
*
* @param nfa engine to consider
* @param q queue corresponding to the engine
* @param loc current location in the buffer for an engine
*/
enum nfa_zombie_status nfaGetZombieStatus(const struct NFA *nfa, struct mq *q,
s64a loc);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

376
src/nfa/nfa_api_dispatch.c Normal file
View File

@@ -0,0 +1,376 @@
/*
* Copyright (c) 2015, 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 Dispatches NFA engine API calls to the appropriate engines
*/
#include "nfa_api.h"
#include "nfa_api_queue.h"
#include "nfa_internal.h"
#include "ue2common.h"
// Engine implementations.
#include "castle.h"
#include "gough.h"
#include "lbr.h"
#include "limex.h"
#include "mcclellan.h"
#include "mpv.h"
#define DISPATCH_CASE(dc_ltype, dc_ftype, dc_subtype, dc_func_call) \
case dc_ltype##_NFA_##dc_subtype: \
return nfaExec##dc_ftype##dc_subtype##dc_func_call; \
break
// general framework calls
#define DISPATCH_BY_NFA_TYPE(dbnt_func) \
switch (nfa->type) { \
DISPATCH_CASE(LIMEX, LimEx, 32_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_7, dbnt_func); \
DISPATCH_CASE(MCCLELLAN, McClellan, 8, dbnt_func); \
DISPATCH_CASE(MCCLELLAN, McClellan, 16, dbnt_func); \
DISPATCH_CASE(GOUGH, Gough, 8, dbnt_func); \
DISPATCH_CASE(GOUGH, Gough, 16, dbnt_func); \
DISPATCH_CASE(MPV, Mpv, 0, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Dot, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Verm, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, NVerm, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Shuf, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Truf, dbnt_func); \
DISPATCH_CASE(CASTLE, Castle, 0, dbnt_func); \
default: \
assert(0); \
}
char nfaCheckFinalState(const struct NFA *nfa, const char *state,
const char *streamState, u64a offset,
NfaCallback callback, SomNfaCallback som_cb,
void *context) {
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
// Caller should avoid calling us if we can never produce matches.
assert(nfaAcceptsEod(nfa));
DISPATCH_BY_NFA_TYPE(_testEOD(nfa, state, streamState, offset, callback,
som_cb, context));
return 0;
}
char nfaQueueInitState(const struct NFA *nfa, struct mq *q) {
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
DISPATCH_BY_NFA_TYPE(_queueInitState(nfa, q));
return 0;
}
static really_inline
char nfaQueueExec_i(const struct NFA *nfa, struct mq *q, s64a end) {
DISPATCH_BY_NFA_TYPE(_Q(nfa, q, end));
return 0;
}
static really_inline
char nfaQueueExec2_i(const struct NFA *nfa, struct mq *q, s64a end) {
DISPATCH_BY_NFA_TYPE(_Q2(nfa, q, end));
return 0;
}
static really_inline
char nfaQueueExecRose_i(const struct NFA *nfa, struct mq *q, ReportID report) {
DISPATCH_BY_NFA_TYPE(_QR(nfa, q, report));
return 0;
}
/** Returns 0 if this NFA cannot possibly match (due to width constraints etc)
* and the caller should return 0. May also edit the queue. */
static really_inline
char nfaQueueCanMatch(const struct NFA *nfa, struct mq *q, s64a end,
char *q_trimmed) {
assert(q_trimmed);
assert(q->end - q->cur >= 2);
assert(end >= 0);
DEBUG_PRINTF("q->offset=%llu, end=%lld\n", q->offset, end);
DEBUG_PRINTF("maxBiAnchoredWidth=%u, maxOffset=%u\n",
nfa->maxBiAnchoredWidth, nfa->maxOffset);
if (nfa->maxBiAnchoredWidth &&
(end + q->offset > nfa->maxBiAnchoredWidth)) {
DEBUG_PRINTF("stream too long: o %llu l %zu max: %hhu\n", q->offset,
q->length, nfa->maxBiAnchoredWidth);
return 0;
}
if (nfa->maxOffset) {
if (q->offset >= nfa->maxOffset) {
DEBUG_PRINTF("stream is past maxOffset\n");
return 0;
}
if (q->offset + end > nfa->maxOffset) {
s64a maxEnd = nfa->maxOffset - q->offset;
DEBUG_PRINTF("me %lld off %llu len = %lld\n", maxEnd,
q->offset, end);
while (q->end > q->cur
&& q->items[q->end - 1].location > maxEnd) {
*q_trimmed = 1;
DEBUG_PRINTF("killing item %u %lld %u\n", q->end,
q->items[q->end - 1].location,
q->items[q->end - 1].type);
q->items[q->end - 1].location = maxEnd;
q->items[q->end - 1].type = MQE_END;
if (q->end - q->cur < 2
||q->items[q->end - 2].location <= maxEnd) {
break;
}
q->end--;
}
if (q->end - q->cur < 2) { /* nothing left on q */
DEBUG_PRINTF("queue empty\n");
return 0;
}
}
#ifdef DEBUG
if (*q_trimmed) {
debugQueue(q);
}
#endif
}
return 1;
}
char nfaQueueExec(const struct NFA *nfa, struct mq *q, s64a end) {
DEBUG_PRINTF("nfa=%p end=%lld\n", nfa, end);
#ifdef DEBUG
debugQueue(q);
#endif
assert(q && q->context && q->state);
assert(end >= 0);
assert(q->cur < q->end);
assert(q->end <= MAX_MQE_LEN);
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
assert(end < q->items[q->end - 1].location
|| q->items[q->end - 1].type == MQE_END);
if (q->items[q->cur].location > end) {
return 1;
}
char q_trimmed = 0;
assert(end <= (s64a)q->length || !q->hlength);
/* due to reverse accel in block mode some queues may work on a truncated
* buffer */
if (end > (s64a)q->length) {
end = q->length;
q_trimmed = 1;
}
if (!nfaQueueCanMatch(nfa, q, end, &q_trimmed)) {
if (q->report_current) {
nfaReportCurrentMatches(nfa, q);
q->report_current = 0;
}
return 0;
}
char rv = nfaQueueExec_i(nfa, q, end);
#ifdef DEBUG
debugQueue(q);
#endif
assert(!q->report_current);
DEBUG_PRINTF("returned rv=%d, q_trimmed=%d\n", rv, q_trimmed);
return rv && !q_trimmed;
}
char nfaQueueExecToMatch(const struct NFA *nfa, struct mq *q, s64a end) {
DEBUG_PRINTF("nfa=%p end=%lld\n", nfa, end);
#ifdef DEBUG
debugQueue(q);
#endif
assert(q);
assert(end >= 0);
assert(q->context);
assert(q->state);
assert(q->cur < q->end);
assert(q->end <= MAX_MQE_LEN);
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
assert(end < q->items[q->end - 1].location
|| q->items[q->end - 1].type == MQE_END);
char q_trimmed_ra = 0;
assert(end <= (s64a)q->length || !q->hlength);
/* due to reverse accel in block mode some queues may work on a truncated
* buffer */
if (q->items[q->cur].location > end) {
return 1;
}
if (end > (s64a)q->length) {
end = q->length;
q_trimmed_ra = 1;
}
char q_trimmed = 0;
if (!nfaQueueCanMatch(nfa, q, end, &q_trimmed)) {
if (q->report_current) {
nfaReportCurrentMatches(nfa, q);
q->report_current = 0;
}
return 0;
}
char rv = nfaQueueExec2_i(nfa, q, end);
assert(!q->report_current);
DEBUG_PRINTF("returned rv=%d, q_trimmed=%d\n", rv, q_trimmed);
if (rv == MO_MATCHES_PENDING) {
if (q_trimmed) {
// We need to "fix" the queue so that subsequent operations must
// trim it as well.
assert(q->end > 0);
assert(nfa->maxOffset);
q->items[q->end - 1].location = nfa->maxOffset + 1;
}
return rv;
}
return rv && !q_trimmed && !q_trimmed_ra;
}
char nfaReportCurrentMatches(const struct NFA *nfa, struct mq *q) {
DISPATCH_BY_NFA_TYPE(_reportCurrent(nfa, q));
return 0;
}
char nfaInAcceptState(const struct NFA *nfa, ReportID report, struct mq *q) {
DISPATCH_BY_NFA_TYPE(_inAccept(nfa, report, q));
return 0;
}
char nfaQueueExecRose(const struct NFA *nfa, struct mq *q, ReportID r) {
DEBUG_PRINTF("nfa=%p\n", nfa);
#ifdef DEBUG
debugQueue(q);
#endif
assert(q && !q->context && q->state);
assert(q->cur <= q->end);
assert(q->end <= MAX_MQE_LEN);
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
assert(!q->report_current);
return nfaQueueExecRose_i(nfa, q, r);
}
char nfaBlockExecReverse(const struct NFA *nfa, u64a offset, const u8 *buf,
size_t buflen, const u8 *hbuf, size_t hlen,
struct hs_scratch *scratch, NfaCallback callback,
void *context) {
assert(nfa);
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
DISPATCH_BY_NFA_TYPE(_B_Reverse(nfa, offset, buf, buflen, hbuf, hlen,
scratch, callback, context));
return 0;
}
char nfaQueueCompressState(const struct NFA *nfa, const struct mq *q,
s64a loc) {
assert(nfa && q);
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
DISPATCH_BY_NFA_TYPE(_queueCompressState(nfa, q, loc));
return 0;
}
char nfaExpandState(const struct NFA *nfa, void *dest, const void *src,
u64a offset, u8 key) {
assert(nfa && dest && src);
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
DISPATCH_BY_NFA_TYPE(_expandState(nfa, dest, src, offset, key));
return 0;
}
char nfaInitCompressedState(const struct NFA *nfa, u64a offset, void *state,
u8 key) {
assert(nfa && state);
assert(ISALIGNED_CL(nfa) && ISALIGNED_CL(getImplNfa(nfa)));
DISPATCH_BY_NFA_TYPE(_initCompressedState(nfa, offset, state, key));
return 0;
}
enum nfa_zombie_status nfaGetZombieStatus(const struct NFA *nfa, struct mq *q,
s64a loc) {
DISPATCH_BY_NFA_TYPE(_zombie_status(nfa, q, loc));
return NFA_ZOMBIE_NO;
}

278
src/nfa/nfa_api_queue.h Normal file
View File

@@ -0,0 +1,278 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef NFA_API_QUEUE_H
#define NFA_API_QUEUE_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "ue2common.h"
#include "callback.h"
/** Size of mq::items, max elements on a queue. */
#define MAX_MQE_LEN 10
/** Queue events */
enum mqe_event {
MQE_START = 0, /**< and begin! Note: stateless engines will start from
* this location */
MQE_END = 1, /**< stop scanning */
MQE_TOP = 2, /**< enable start + start dot star */
MQE_TOP_FIRST = 4, /**< first event corresponding to a TOP _N_ */
/*
* Additional tops (in multi-top engines) use the event values from
* MQE_TOP_FIRST to something.
*/
MQE_INVALID = ~0U
};
/** Queue item */
struct mq_item {
u32 type; /**< event; from mqe_event */
s64a location; /**< relative to the start of the current buffer */
u64a som; /**< pattern start-of-match corresponding to a top, only used
* by som engines. */
};
// Forward decl.
struct NFA;
/**
* Queue of events to control engine execution. mq::cur is index of first
* valid event, mq::end is one past the index of last valid event.
*/
struct mq {
const struct NFA *nfa; /**< nfa corresponding to the queue */
u32 cur; /**< index of the first valid item in the queue */
u32 end; /**< index one past the last valid item in the queue */
char *state; /**< uncompressed stream state; lives in scratch */
char *streamState; /**<
* real stream state; used to access structures which
* not duplicated the scratch state (bounded repeats,
* etc) */
u64a offset; /**< base offset of the buffer */
const u8 *buffer; /**< buffer to scan */
size_t length; /**< length of buffer */
const u8 *history; /**<
* history buffer; (logically) immediately before the
* main buffer */
size_t hlength; /**< length of the history buffer */
struct hs_scratch *scratch; /**< global scratch space */
char report_current; /**<
* report_current matches at starting offset through
* callback. If true, the queue must be located at a
* point where MO_MATCHES_PENDING was returned */
NfaCallback cb; /**< callback to trigger on matches */
SomNfaCallback som_cb; /**< callback with som info; used by haig */
void *context; /**< context to pass along with a callback */
struct mq_item items[MAX_MQE_LEN]; /**< queue items */
};
/**
* Pushes an (event, location, som) item onto a queue. If it is identical to the
* previous item on the queue, it is not added to the queue.
* @param q queue
* @param e event
* @param som som marker
* @param loc event location
*/
static really_inline
void pushQueueSom(struct mq * restrict q, u32 e, s64a loc, u64a som) {
DEBUG_PRINTF("pushing %u@%lld -> %u [som = %llu]\n", e, loc, q->end, som);
assert(q->end < MAX_MQE_LEN);
assert(e < MQE_INVALID);
/* stop gcc getting too smart for its own good */
/* assert(!q->end || q->items[q->end - 1].location <= loc); */
assert(q->end || e == MQE_START);
// Avoid duplicate items on the queue.
if (q->end) {
struct mq_item *item = &q->items[q->end - 1];
if (item->type == e && item->location == loc) {
DEBUG_PRINTF("dropping duplicate item\n");
LIMIT_TO_AT_MOST(&item->som, som); /* take lower som */
return;
}
}
u32 end = q->end;
struct mq_item *item = &q->items[end];
item->type = e;
item->location = loc;
item->som = som;
q->end = end + 1;
}
/**
* Pushes an (event, location) item onto a queue. If it is identical to the
* previous item on the queue, it is not added to the queue.
* @param q queue
* @param e event
* @param loc event location
*/
static really_inline
void pushQueue(struct mq * restrict q, u32 e, s64a loc) {
pushQueueSom(q, e, loc, 0);
}
/**
* Pushes an (event, location) item onto a queue.
* This version of @ref pushQueue does not check to ensure that the item being
* added is not already on the queue. Used for events other than tops.
*/
static really_inline
void pushQueueNoMerge(struct mq * restrict q, u32 e, s64a loc) {
DEBUG_PRINTF("pushing %u@%lld -> %u\n", e, loc, q->end);
assert(q->end < MAX_MQE_LEN);
assert(e < MQE_INVALID);
/* stop gcc getting too smart for its own good */
/* assert(!q->end || q->items[q->end - 1].location <= loc); */
assert(q->end || e == MQE_START);
#ifndef NDEBUG
// We assert that the event is different from its predecessor. If it's a
// dupe, you should have used the ordinary pushQueue call.
if (q->end) {
UNUSED struct mq_item *prev = &q->items[q->end - 1];
assert(prev->type != e || prev->location != loc);
}
#endif
u32 end = q->end;
struct mq_item *item = &q->items[end];
item->type = e;
item->location = loc;
item->som = 0;
q->end = end + 1;
}
/** \brief Returns the type of the current queue event. */
static really_inline u32 q_cur_type(const struct mq *q) {
assert(q->cur < q->end);
assert(q->cur < MAX_MQE_LEN);
return q->items[q->cur].type;
}
/** \brief Returns the location (relative to the beginning of the current data
* buffer) of the current queue event. */
static really_inline s64a q_cur_loc(const struct mq *q) {
assert(q->cur < q->end);
assert(q->cur < MAX_MQE_LEN);
return q->items[q->cur].location;
}
/** \brief Returns the location (relative to the beginning of the current data
* buffer) of the last event in the queue. */
static really_inline s64a q_last_loc(const struct mq *q) {
assert(q->cur < q->end);
assert(q->end > 0);
assert(q->end <= MAX_MQE_LEN);
return q->items[q->end - 1].location;
}
/** \brief Returns the absolute stream offset of the current queue event. */
static really_inline u64a q_cur_offset(const struct mq *q) {
assert(q->cur < q->end);
assert(q->cur < MAX_MQE_LEN);
return q->offset + (u64a)q->items[q->cur].location;
}
/**
* \brief Removes all events in the queue before the given location.
*/
static really_inline
void q_skip_forward_to(struct mq *q, s64a min_loc) {
assert(q->cur < q->end);
assert(q->cur < MAX_MQE_LEN);
assert(q->items[q->cur].type == MQE_START);
if (q_cur_loc(q) >= min_loc) {
DEBUG_PRINTF("all events >= loc %lld\n", min_loc);
return;
}
const u32 start_loc = q->cur;
do {
DEBUG_PRINTF("remove item with loc=%lld\n", q_cur_loc(q));
q->cur++;
} while (q->cur < q->end && q_cur_loc(q) < min_loc);
if (q->cur > start_loc) {
// Move original MQE_START item forward.
q->cur--;
q->items[q->cur] = q->items[start_loc];
}
}
#ifdef DEBUG
// Dump the contents of the given queue.
static never_inline UNUSED
void debugQueue(const struct mq *q) {
DEBUG_PRINTF("q=%p, nfa=%p\n", q, q->nfa);
DEBUG_PRINTF("q offset=%llu, buf={%p, len=%zu}, history={%p, len=%zu}\n",
q->offset, q->buffer, q->length, q->history, q->hlength);
DEBUG_PRINTF("q cur=%u, end=%u\n", q->cur, q->end);
for (u32 cur = q->cur; cur < q->end; cur++) {
const char *type = "UNKNOWN";
u32 e = q->items[cur].type;
switch (e) {
case MQE_START:
type = "MQE_START";
break;
case MQE_END:
type = "MQE_END";
break;
case MQE_TOP:
type = "MQE_TOP";
break;
case MQE_INVALID:
type = "MQE_INVALID";
break;
default:
assert(e >= MQE_TOP_FIRST && e < MQE_INVALID);
type = "MQE_TOP_N";
break;
}
DEBUG_PRINTF("\tq[%u] %lld %d:%s\n", cur, q->items[cur].location,
q->items[cur].type, type);
}
}
#endif // DEBUG
#ifdef __cplusplus
}
#endif
#endif

82
src/nfa/nfa_api_util.h Normal file
View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef NFA_API_UTIL_H
#define NFA_API_UTIL_H
#include "nfa_api_queue.h"
#include "ue2common.h"
/* returns the byte prior to the given location, NUL if not available */
static really_inline
u8 queue_prev_byte(const struct mq *q, s64a loc) {
if (loc <= 0) {
if (1LL - loc > (s64a)q->hlength) {
return 0; /* assume NUL for start of stream write */
}
// In the history buffer.
assert(q->history);
assert(q->hlength >= (u64a)(loc * -1));
return q->history[q->hlength - 1 + loc];
} else {
// In the stream write buffer.
assert(q->buffer);
assert(q->length >= (u64a)loc);
return q->buffer[loc - 1];
}
}
/* this is a modified version of pushQueue where we statically know the state of
* the queue. Does not attempt to merge and inserts at the given queue
* position. */
static really_inline
void pushQueueAt(struct mq * restrict q, u32 pos, u32 e, s64a loc) {
assert(pos == q->end);
DEBUG_PRINTF("pushing %u@%lld -> %u\n", e, loc, q->end);
assert(q->end < MAX_MQE_LEN);
assert(e < MQE_INVALID);
/* stop gcc getting too smart for its own good */
/* assert(!q->end || q->items[q->end - 1].location <= loc); */
assert(q->end || e == MQE_START);
#ifndef NDEBUG
// We assert that the event is different from its predecessor. If it's a
// dupe, you should have used the ordinary pushQueue call.
if (q->end) {
UNUSED struct mq_item *prev = &q->items[q->end - 1];
assert(prev->type != e || prev->location != loc);
}
#endif
struct mq_item *item = &q->items[pos];
item->type = e;
item->location = loc;
item->som = 0;
q->end = pos + 1;
}
#endif

434
src/nfa/nfa_build_util.cpp Normal file
View File

@@ -0,0 +1,434 @@
/*
* Copyright (c) 2015, 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.
*/
#include "nfa_build_util.h"
#include "limex_internal.h"
#include "mcclellancompile.h"
#include "nfa_internal.h"
#include "repeat_internal.h"
#include "ue2common.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
using namespace std;
namespace ue2 {
namespace {
template<NFAEngineType t> struct NFATraits { };
template<template<NFAEngineType t> class sfunc, typename rv_t, typename arg_t,
NFAEngineType lb>
struct DISPATCH_BY_NFA_TYPE_INT {
static rv_t doOp(NFAEngineType i, const arg_t &arg) {
if (i == lb) {
return sfunc<lb>::call(arg);
} else {
return DISPATCH_BY_NFA_TYPE_INT<sfunc, rv_t, arg_t,
(NFAEngineType)(lb + 1)>
::doOp(i, arg);
}
}
};
template<template<NFAEngineType t> class sfunc, typename rv_t, typename arg_t>
struct DISPATCH_BY_NFA_TYPE_INT<sfunc, rv_t, arg_t, INVALID_NFA> {
// dummy
static rv_t doOp(NFAEngineType, const arg_t &) {
assert(0);
throw std::logic_error("Unreachable");
}
};
#define DISPATCH_BY_NFA_TYPE(i, op, arg) \
DISPATCH_BY_NFA_TYPE_INT<op, decltype(op<(NFAEngineType)0>::call(arg)), \
decltype(arg), (NFAEngineType)0>::doOp(i, arg)
}
typedef bool (*has_accel_fn)(const NFA *nfa);
template<typename T>
static
bool has_accel_limex(const NFA *nfa) {
const T *limex = (const T *)getImplNfa(nfa);
return limex->accelCount;
}
static
bool has_accel_generic(const NFA *) {
return false;
}
#ifdef DUMP_SUPPORT
namespace {
template<NFAEngineType t>
struct getName {
static const char *call(void *) {
return NFATraits<t>::name;
}
};
// descr helper for LimEx NFAs
template<NFAEngineType t>
static
string getDescriptionLimEx(const NFA *nfa) {
const typename NFATraits<t>::implNFA_t *limex =
(const typename NFATraits<t>::implNFA_t *)getImplNfa(nfa);
ostringstream oss;
oss << NFATraits<t>::name << "/" << limex->exceptionCount;
if (limex->repeatCount) {
oss << " +" << limex->repeatCount << "r";
}
return oss.str();
}
}
// generic description: just return the name
namespace {
template<NFAEngineType t>
struct getDescription {
static string call(const void *) {
return string(NFATraits<t>::name);
}
};
}
#endif
/* build-utility Traits */
namespace {
enum NFACategory {NFA_LIMEX, NFA_OTHER};
// Some of our traits we want around in DUMP_SUPPORT mode only.
#if defined(DUMP_SUPPORT)
#define DO_IF_DUMP_SUPPORT(a) a
#else
#define DO_IF_DUMP_SUPPORT(a)
#endif
#define MAKE_LIMEX_TRAITS(mlt_size, mlt_shift) \
template<> struct NFATraits<LIMEX_NFA_##mlt_size##_##mlt_shift> { \
static UNUSED const char *name; \
static const NFACategory category = NFA_LIMEX; \
typedef LimExNFA##mlt_size implNFA_t; \
typedef u_##mlt_size tableRow_t; \
static const has_accel_fn has_accel; \
static const u32 stateAlign = \
MAX(alignof(tableRow_t), alignof(RepeatControl)); \
static const bool fast = mlt_size <= 64; \
}; \
const has_accel_fn NFATraits<LIMEX_NFA_##mlt_size##_##mlt_shift>::has_accel \
= has_accel_limex<LimExNFA##mlt_size>; \
DO_IF_DUMP_SUPPORT( \
const char *NFATraits<LIMEX_NFA_##mlt_size##_##mlt_shift>::name \
= "LimEx (0-"#mlt_shift") "#mlt_size; \
template<> struct getDescription<LIMEX_NFA_##mlt_size##_##mlt_shift> { \
static string call(const void *ptr) { \
return getDescriptionLimEx<LIMEX_NFA_##mlt_size##_##mlt_shift>((const NFA *)ptr); \
} \
};)
MAKE_LIMEX_TRAITS(32, 1)
MAKE_LIMEX_TRAITS(32, 2)
MAKE_LIMEX_TRAITS(32, 3)
MAKE_LIMEX_TRAITS(32, 4)
MAKE_LIMEX_TRAITS(32, 5)
MAKE_LIMEX_TRAITS(32, 6)
MAKE_LIMEX_TRAITS(32, 7)
MAKE_LIMEX_TRAITS(128, 1)
MAKE_LIMEX_TRAITS(128, 2)
MAKE_LIMEX_TRAITS(128, 3)
MAKE_LIMEX_TRAITS(128, 4)
MAKE_LIMEX_TRAITS(128, 5)
MAKE_LIMEX_TRAITS(128, 6)
MAKE_LIMEX_TRAITS(128, 7)
MAKE_LIMEX_TRAITS(256, 1)
MAKE_LIMEX_TRAITS(256, 2)
MAKE_LIMEX_TRAITS(256, 3)
MAKE_LIMEX_TRAITS(256, 4)
MAKE_LIMEX_TRAITS(256, 5)
MAKE_LIMEX_TRAITS(256, 6)
MAKE_LIMEX_TRAITS(256, 7)
MAKE_LIMEX_TRAITS(384, 1)
MAKE_LIMEX_TRAITS(384, 2)
MAKE_LIMEX_TRAITS(384, 3)
MAKE_LIMEX_TRAITS(384, 4)
MAKE_LIMEX_TRAITS(384, 5)
MAKE_LIMEX_TRAITS(384, 6)
MAKE_LIMEX_TRAITS(384, 7)
MAKE_LIMEX_TRAITS(512, 1)
MAKE_LIMEX_TRAITS(512, 2)
MAKE_LIMEX_TRAITS(512, 3)
MAKE_LIMEX_TRAITS(512, 4)
MAKE_LIMEX_TRAITS(512, 5)
MAKE_LIMEX_TRAITS(512, 6)
MAKE_LIMEX_TRAITS(512, 7)
template<> struct NFATraits<MCCLELLAN_NFA_8> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 1;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<MCCLELLAN_NFA_8>::has_accel = has_accel_dfa;
#if defined(DUMP_SUPPORT)
const char *NFATraits<MCCLELLAN_NFA_8>::name = "McClellan 8";
#endif
template<> struct NFATraits<MCCLELLAN_NFA_16> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 2;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<MCCLELLAN_NFA_16>::has_accel = has_accel_dfa;
#if defined(DUMP_SUPPORT)
const char *NFATraits<MCCLELLAN_NFA_16>::name = "McClellan 16";
#endif
template<> struct NFATraits<GOUGH_NFA_8> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<GOUGH_NFA_8>::has_accel = has_accel_dfa;
#if defined(DUMP_SUPPORT)
const char *NFATraits<GOUGH_NFA_8>::name = "Goughfish 8";
#endif
template<> struct NFATraits<GOUGH_NFA_16> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<GOUGH_NFA_16>::has_accel = has_accel_dfa;
#if defined(DUMP_SUPPORT)
const char *NFATraits<GOUGH_NFA_16>::name = "Goughfish 16";
#endif
template<> struct NFATraits<MPV_NFA_0> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<MPV_NFA_0>::has_accel = has_accel_generic;
#if defined(DUMP_SUPPORT)
const char *NFATraits<MPV_NFA_0>::name = "Mega-Puff-Vac";
#endif
template<> struct NFATraits<CASTLE_NFA_0> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<CASTLE_NFA_0>::has_accel = has_accel_generic;
#if defined(DUMP_SUPPORT)
const char *NFATraits<CASTLE_NFA_0>::name = "Castle";
#endif
template<> struct NFATraits<LBR_NFA_Dot> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<LBR_NFA_Dot>::has_accel = has_accel_generic;
#if defined(DUMP_SUPPORT)
const char *NFATraits<LBR_NFA_Dot>::name = "Lim Bounded Repeat (D)";
#endif
template<> struct NFATraits<LBR_NFA_Verm> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<LBR_NFA_Verm>::has_accel = has_accel_generic;
#if defined(DUMP_SUPPORT)
const char *NFATraits<LBR_NFA_Verm>::name = "Lim Bounded Repeat (V)";
#endif
template<> struct NFATraits<LBR_NFA_NVerm> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<LBR_NFA_NVerm>::has_accel = has_accel_generic;
#if defined(DUMP_SUPPORT)
const char *NFATraits<LBR_NFA_NVerm>::name = "Lim Bounded Repeat (NV)";
#endif
template<> struct NFATraits<LBR_NFA_Shuf> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<LBR_NFA_Shuf>::has_accel = has_accel_generic;
#if defined(DUMP_SUPPORT)
const char *NFATraits<LBR_NFA_Shuf>::name = "Lim Bounded Repeat (S)";
#endif
template<> struct NFATraits<LBR_NFA_Truf> {
UNUSED static const char *name;
static const NFACategory category = NFA_OTHER;
static const u32 stateAlign = 8;
static const bool fast = true;
static const has_accel_fn has_accel;
};
const has_accel_fn NFATraits<LBR_NFA_Truf>::has_accel = has_accel_generic;
#if defined(DUMP_SUPPORT)
const char *NFATraits<LBR_NFA_Truf>::name = "Lim Bounded Repeat (M)";
#endif
} // namespace
#if defined(DUMP_SUPPORT)
const char *nfa_type_name(NFAEngineType type) {
return DISPATCH_BY_NFA_TYPE(type, getName, nullptr);
}
string describe(const NFA &nfa) {
return DISPATCH_BY_NFA_TYPE((NFAEngineType)nfa.type, getDescription, &nfa);
}
#endif /* DUMP_SUPPORT */
namespace {
template<NFAEngineType t>
struct getStateAlign {
static u32 call(void *) {
return NFATraits<t>::stateAlign;
}
};
}
u32 state_alignment(const NFA &nfa) {
return DISPATCH_BY_NFA_TYPE((NFAEngineType)nfa.type, getStateAlign, nullptr);
}
namespace {
template<NFAEngineType t>
struct getFastness {
static u32 call(void *) {
return NFATraits<t>::fast;
}
};
}
bool is_fast(const NFA &nfa) {
NFAEngineType t = (NFAEngineType)nfa.type;
return DISPATCH_BY_NFA_TYPE(t, getFastness, nullptr);
}
namespace {
template<NFAEngineType t>
struct is_limex {
static bool call(const void *) {
return NFATraits<t>::category == NFA_LIMEX;
}
};
}
bool has_bounded_repeats_other_than_firsts(const NFA &nfa) {
if (!DISPATCH_BY_NFA_TYPE((NFAEngineType)nfa.type, is_limex, &nfa)) {
return false;
}
const LimExNFABase *limex = (const LimExNFABase *)getImplNfa(&nfa);
const char *ptr = (const char *)limex;
const u32 *repeatOffset = (const u32 *)(ptr + limex->repeatOffset);
for (u32 i = 0; i < limex->repeatCount; i++) {
u32 offset = repeatOffset[i];
const NFARepeatInfo *info = (const NFARepeatInfo *)(ptr + offset);
const RepeatInfo *repeat =
(const RepeatInfo *)((const char *)info + sizeof(*info));
if (repeat->type != REPEAT_FIRST) {
return true;
}
}
return false;
}
bool has_bounded_repeats(const NFA &nfa) {
if (!DISPATCH_BY_NFA_TYPE((NFAEngineType)nfa.type, is_limex, &nfa)) {
return false;
}
const LimExNFABase *limex = (const LimExNFABase *)getImplNfa(&nfa);
return limex->repeatCount;
}
namespace {
template<NFAEngineType t>
struct has_accel_dispatch {
static has_accel_fn call(const void *) {
return NFATraits<t>::has_accel;
}
};
}
bool has_accel(const NFA &nfa) {
return DISPATCH_BY_NFA_TYPE((NFAEngineType)nfa.type, has_accel_dispatch,
&nfa)
(&nfa);
}
bool requires_decompress_key(const NFA &nfa) {
return DISPATCH_BY_NFA_TYPE((NFAEngineType)nfa.type, is_limex, &nfa);
}
} // namespace ue2

64
src/nfa/nfa_build_util.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef NFA_BUILD_UTIL_H
#define NFA_BUILD_UTIL_H
#include "ue2common.h"
#include "nfa_internal.h"
#include <string>
struct NFA;
namespace ue2 {
#ifdef DUMP_SUPPORT
/* provided for debugging functions */
const char *nfa_type_name(NFAEngineType type);
std::string describe(const NFA &nfa);
#endif
// For a given NFA, retrieve the alignment required by its uncompressed state.
u32 state_alignment(const NFA &nfa);
/* returns true if the nfa is considered 'fast'. TODO: work out what we mean by
* fast. */
bool is_fast(const NFA &n);
bool has_bounded_repeats_other_than_firsts(const NFA &n);
bool has_bounded_repeats(const NFA &n);
bool has_accel(const NFA &n);
bool requires_decompress_key(const NFA &n);
} // namespace ue2
#endif

56
src/nfa/nfa_dump_api.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2015, 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 Declarations for the NFA Engine dump API.
*/
#ifndef NFA_DUMP_API_H
#define NFA_DUMP_API_H
#if defined(DUMP_SUPPORT)
#include <cstdio>
struct NFA;
namespace ue2 {
/**
* \brief Dump (in Graphviz 'dot' format) a representation of the NFA into the
* file pointed to by dotFile.
*/
void nfaDumpDot(const struct NFA *nfa, FILE *dotFile);
/** \brief Dump a textual representation of the NFA. */
void nfaDumpText(const struct NFA *fact, FILE *textFile);
} // namespace ue2
#endif // DUMP_SUPPORT
#endif // NFA_DUMP_API_H

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2015, 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.
*/
#include "config.h"
#include "nfa_dump_api.h"
#include "nfa_internal.h"
#include "ue2common.h"
// Engine implementations.
#include "goughdump.h"
#include "castle_dump.h"
#include "lbr_dump.h"
#include "limex.h"
#include "mcclellandump.h"
#include "mpv_dump.h"
#ifndef DUMP_SUPPORT
#error "no dump support"
#endif
namespace ue2 {
#define DISPATCH_CASE(dc_ltype, dc_ftype, dc_subtype, dc_func_call) \
case dc_ltype##_NFA_##dc_subtype: \
nfaExec##dc_ftype##dc_subtype##dc_func_call; \
break
// general framework calls
#define DISPATCH_BY_NFA_TYPE(dbnt_func) \
DEBUG_PRINTF("dispatch for NFA type %u\n", nfa->type); \
switch (nfa->type) { \
DISPATCH_CASE(LIMEX, LimEx, 32_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 32_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 128_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 256_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 384_7, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_1, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_2, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_3, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_4, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_5, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_6, dbnt_func); \
DISPATCH_CASE(LIMEX, LimEx, 512_7, dbnt_func); \
DISPATCH_CASE(MCCLELLAN, McClellan, 8, dbnt_func); \
DISPATCH_CASE(MCCLELLAN, McClellan, 16, dbnt_func); \
DISPATCH_CASE(GOUGH, Gough, 8, dbnt_func); \
DISPATCH_CASE(GOUGH, Gough, 16, dbnt_func); \
DISPATCH_CASE(MPV, Mpv, 0, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Dot, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Verm, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, NVerm, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Shuf, dbnt_func); \
DISPATCH_CASE(LBR, Lbr, Truf, dbnt_func); \
DISPATCH_CASE(CASTLE, Castle, 0, dbnt_func); \
default: \
assert(0); \
}
void nfaDumpDot(const struct NFA *nfa, FILE *dotFile) {
DISPATCH_BY_NFA_TYPE(_dumpDot(nfa, dotFile));
}
void nfaDumpText(const struct NFA *nfa, FILE *txtFile) {
DISPATCH_BY_NFA_TYPE(_dumpText(nfa, txtFile));
}
} // namespace ue2

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 2015, 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.
*/
#include "config.h"
#include "nfa_dump_internal.h"
#include "accel.h"
#include "nfa_internal.h"
#include "ue2common.h"
#include <cctype> // for isprint
#include <sstream>
#ifndef DUMP_SUPPORT
#error No dump support!
#endif
namespace ue2 {
void dumpDotPreamble(FILE *dotFile) {
fprintf(dotFile, "digraph NFA {\n");
fprintf(dotFile, "rankdir=LR;\n");
fprintf(dotFile, "size=\"11.5,8\"\n");
fprintf(dotFile, "node [ shape = circle ];\n");
fprintf(dotFile, "START [style=invis];\n");
}
void dumpDotTrailer(FILE *dotFile) {
fprintf(dotFile, "}\n");
}
static
void dumpFlags(const struct NFA *nfa, FILE *f) {
fprintf(f, "Flags : ");
if (nfa->flags & NFA_ACCEPTS_EOD) {
fprintf(f, "ACCEPTS_EOD ");
}
if (nfa->flags & NFA_ZOMBIE) {
fprintf(f, "ZOMBIE ");
}
fprintf(f, "\n");
}
// Render an unsigned integer as a string, returning "inf/unknown" if it's
// zero.
static
std::string value_or_inf(const u64a v) {
std::ostringstream oss;
if (v) {
oss << v;
} else {
oss << "inf/unknown";
}
return oss.str();
}
void dumpTextReverse(const struct NFA *nfa, FILE *f) {
bool twofer = false;
fprintf(f, "Queue : %u\n", nfa->queueIndex);
fprintf(f, "Length : %u bytes\n", nfa->length);
fprintf(f, "Num Positions : %u\n", nfa->nPositions);
fprintf(f, "Scratch State : %u bytes\n", nfa->scratchStateSize);
fprintf(f, "Stream State : %u bytes\n", nfa->streamStateSize);
dumpFlags(nfa, f);
fprintf(f, "Max Width : %s\n", value_or_inf(nfa->maxWidth).c_str());
fprintf(f, "Min Width : %u\n", nfa->minWidth);
fprintf(f, "BiAnchored Width : %s\n",
value_or_inf(nfa->maxBiAnchoredWidth).c_str());
fprintf(f, "Max Offset : %s\n", value_or_inf(nfa->maxOffset).c_str());
fprintf(f, "Reverse Acceleration : ");
switch (nfa->rAccelType) {
case ACCEL_NONE:
fprintf(f, "NONE\n");
return;
case ACCEL_RVERM:
fprintf(f, "R VERM");
break;
case ACCEL_RVERM_NOCASE:
fprintf(f, "R VERM NOCASE");
break;
case ACCEL_RDVERM:
fprintf(f, "R VERM x2");
twofer = true;
break;
case ACCEL_RDVERM_NOCASE:
fprintf(f, "R VERM NOCASE x2");
twofer = true;
break;
case ACCEL_REOD:
fprintf(f, "R EOD");
break;
case ACCEL_REOD_NOCASE:
fprintf(f, "R EOD NOCASE");
break;
case ACCEL_RDEOD:
fprintf(f, "R EOD x2");
twofer = true;
break;
case ACCEL_RDEOD_NOCASE:
fprintf(f, "R EOD x2 NOCASE");
twofer = true;
break;
default:
fprintf(f, "UNKNOWN\n");
return;
}
char c1 = nfa->rAccelData.array[0];
char c2 = nfa->rAccelData.array[1];
if (!twofer) {
fprintf(f, " \\x%02hhx (%c) ", c1, isprint(c1) ? c1 : '?');
} else {
fprintf(f, " \\x%02hhx\\x%02hhx (%c%c) ", c1, c2,
isprint(c1) ? c1 : '?', isprint(c2) ? c2 : '?');
}
fprintf(f, "offset %hhd\n", nfa->rAccelOffset);
}
} // namespace ue2

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2015, 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 Common dump code for nfa engines.
*/
#ifndef NFA_DUMP_INTERNAL_H
#define NFA_DUMP_INTERNAL_H
#if defined(DUMP_SUPPORT)
#include <cstdio>
struct NFA;
namespace ue2 {
/** \brief Writes a Graphviz DOT graph preamble. */
void dumpDotPreamble(FILE *f);
/** \brief Finishes a Graphviz DOT graph. */
void dumpDotTrailer(FILE *f);
/** \brief Dumps text information about \a nfa. */
void dumpTextReverse(const struct NFA *nfa, FILE *f);
}
#endif // DUMP_SUPPORT
#endif

256
src/nfa/nfa_internal.h Normal file
View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2015, 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 Declarations for the main NFA engine types and structures.
*/
#ifndef NFA_INTERNAL_H
#define NFA_INTERNAL_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "ue2common.h"
// Constants
#define MO_INVALID_IDX 0xffffffff /**< index meaning value is invalid */
// Flags (used in NFA::flags)
#define NFA_ACCEPTS_EOD 1U /**< can produce matches on EOD. */
#define NFA_ZOMBIE 2U /**< supports zombies */
// Common data structures for NFAs
enum NFAEngineType {
LIMEX_NFA_32_1,
LIMEX_NFA_32_2,
LIMEX_NFA_32_3,
LIMEX_NFA_32_4,
LIMEX_NFA_32_5,
LIMEX_NFA_32_6,
LIMEX_NFA_32_7,
LIMEX_NFA_128_1,
LIMEX_NFA_128_2,
LIMEX_NFA_128_3,
LIMEX_NFA_128_4,
LIMEX_NFA_128_5,
LIMEX_NFA_128_6,
LIMEX_NFA_128_7,
LIMEX_NFA_256_1,
LIMEX_NFA_256_2,
LIMEX_NFA_256_3,
LIMEX_NFA_256_4,
LIMEX_NFA_256_5,
LIMEX_NFA_256_6,
LIMEX_NFA_256_7,
LIMEX_NFA_384_1,
LIMEX_NFA_384_2,
LIMEX_NFA_384_3,
LIMEX_NFA_384_4,
LIMEX_NFA_384_5,
LIMEX_NFA_384_6,
LIMEX_NFA_384_7,
LIMEX_NFA_512_1,
LIMEX_NFA_512_2,
LIMEX_NFA_512_3,
LIMEX_NFA_512_4,
LIMEX_NFA_512_5,
LIMEX_NFA_512_6,
LIMEX_NFA_512_7,
MCCLELLAN_NFA_8, /**< magic pseudo nfa */
MCCLELLAN_NFA_16, /**< magic pseudo nfa */
GOUGH_NFA_8, /**< magic pseudo nfa */
GOUGH_NFA_16, /**< magic pseudo nfa */
MPV_NFA_0, /**< magic pseudo nfa */
LBR_NFA_Dot, /**< magic pseudo nfa */
LBR_NFA_Verm, /**< magic pseudo nfa */
LBR_NFA_NVerm, /**< magic pseudo nfa */
LBR_NFA_Shuf, /**< magic pseudo nfa */
LBR_NFA_Truf, /**< magic pseudo nfa */
CASTLE_NFA_0, /**< magic pseudo nfa */
/** \brief bogus NFA - not used */
INVALID_NFA
};
/** \brief header for the NFA implementation. */
struct ALIGN_CL_DIRECTIVE NFA {
u32 flags;
/** \brief The size in bytes of the NFA engine. The engine is
* serialized to the extent that copying length bytes back into a
* 16-byte aligned memory location yields a structure that has the same
* behaviour as the original engine. */
u32 length;
/** \brief Active implementation used by this NFAEngineType */
u8 type;
u8 rAccelType;
u8 rAccelOffset;
u8 maxBiAnchoredWidth; /**< if non zero, max width of the block */
union {
u8 c;
u16 dc;
u8 array[2];
} rAccelData;
u32 queueIndex; /**< index of the associated queue in scratch */
/** \brief The number of valid positions/states for this NFA. Debug only */
u32 nPositions;
/** \brief Size of the state required in scratch space.
*
* This state has less strict size requirements (as it doesn't go in stream
* state) and does not persist between stream writes.
*/
u32 scratchStateSize;
/** \brief Size of the state required in stream state.
*
* This encompasses all state stored by the engine that must persist between
* stream writes. */
u32 streamStateSize;
u32 maxWidth; /**< longest possible match in this NFA, 0 if unbounded */
u32 minWidth; /**< minimum bytes required to match this NFA */
u32 maxOffset; /**< non zero: maximum offset this pattern can match at */
/* Note: implementation (e.g. a LimEx) directly follows struct in memory */
} ;
// Accessor macro for the implementation NFA: we do things this way to avoid
// type-punning warnings.
#define getImplNfa(nfa) \
((const void *)((const char *)(nfa) + sizeof(struct NFA)))
// Non-const version of the above, used at compile time.
#define getMutableImplNfa(nfa) ((char *)(nfa) + sizeof(struct NFA))
static really_inline u32 nfaAcceptsEod(const struct NFA *nfa) {
return nfa->flags & NFA_ACCEPTS_EOD;
}
static really_inline u32 nfaSupportsZombie(const struct NFA *nfa) {
return nfa->flags & NFA_ZOMBIE;
}
/** \brief True if the given type (from NFA::type) is a McClellan DFA. */
static really_inline int isMcClellanType(u8 t) {
return t == MCCLELLAN_NFA_8 || t == MCCLELLAN_NFA_16;
}
/** \brief True if the given type (from NFA::type) is a Gough DFA. */
static really_inline int isGoughType(u8 t) {
return t == GOUGH_NFA_8 || t == GOUGH_NFA_16;
}
/** \brief True if the given type (from NFA::type) is a McClellan or Gough DFA.
* */
static really_inline int isDfaType(u8 t) {
return isMcClellanType(t) || isGoughType(t);
}
/** \brief True if the given type (from NFA::type) is an NFA. */
static really_inline int isNfaType(u8 t) {
switch (t) {
case LIMEX_NFA_32_1:
case LIMEX_NFA_32_2:
case LIMEX_NFA_32_3:
case LIMEX_NFA_32_4:
case LIMEX_NFA_32_5:
case LIMEX_NFA_32_6:
case LIMEX_NFA_32_7:
case LIMEX_NFA_128_1:
case LIMEX_NFA_128_2:
case LIMEX_NFA_128_3:
case LIMEX_NFA_128_4:
case LIMEX_NFA_128_5:
case LIMEX_NFA_128_6:
case LIMEX_NFA_128_7:
case LIMEX_NFA_256_1:
case LIMEX_NFA_256_2:
case LIMEX_NFA_256_3:
case LIMEX_NFA_256_4:
case LIMEX_NFA_256_5:
case LIMEX_NFA_256_6:
case LIMEX_NFA_256_7:
case LIMEX_NFA_384_1:
case LIMEX_NFA_384_2:
case LIMEX_NFA_384_3:
case LIMEX_NFA_384_4:
case LIMEX_NFA_384_5:
case LIMEX_NFA_384_6:
case LIMEX_NFA_384_7:
case LIMEX_NFA_512_1:
case LIMEX_NFA_512_2:
case LIMEX_NFA_512_3:
case LIMEX_NFA_512_4:
case LIMEX_NFA_512_5:
case LIMEX_NFA_512_6:
case LIMEX_NFA_512_7:
return 1;
default:
break;
}
return 0;
}
/** \brief True if the given type (from NFA::type) is an LBR. */
static really_inline
int isLbrType(u8 t) {
return t == LBR_NFA_Dot || t == LBR_NFA_Verm || t == LBR_NFA_NVerm ||
t == LBR_NFA_Shuf || t == LBR_NFA_Truf;
}
static really_inline
int isMultiTopType(u8 t) {
return !isDfaType(t) && !isLbrType(t);
}
/** Macro used in place of unimplemented NFA API functions for a given
* engine. */
#if !defined(_WIN32)
#define NFA_API_NO_IMPL(...) \
({ \
assert("not implemented for this engine!"); \
0; /* return value, for places that need it */ \
})
#else
#define NFA_API_NO_IMPL(...) 0
#endif
#ifdef __cplusplus
}
#endif
#endif

57
src/nfa/nfa_kind.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef NFA_KIND_H
#define NFA_KIND_H
#include "ue2common.h"
namespace ue2 {
/** \brief Specify the use-case for an nfa engine. */
enum nfa_kind {
NFA_PREFIX, //!< rose prefix
NFA_INFIX, //!< rose infix
NFA_SUFFIX, //!< rose suffix
NFA_OUTFIX, //!< "outfix" nfa not triggered by external events
NFA_REV_PREFIX, //! reverse running prefixes (for som)
};
static UNUSED
bool is_triggered(enum nfa_kind k) {
return k == NFA_INFIX || k == NFA_SUFFIX || k == NFA_REV_PREFIX;
}
static UNUSED
bool generates_callbacks(enum nfa_kind k) {
return k == NFA_SUFFIX || k == NFA_OUTFIX || k == NFA_REV_PREFIX;
}
} // namespace ue2
#endif

157
src/nfa/nfa_rev_api.h Normal file
View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2015, 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 Reverse-acceleration optimizations for the NFA API block mode scans.
*/
#ifndef NFA_REV_API_H
#define NFA_REV_API_H
#include "accel.h"
#include "nfa_internal.h"
#include "vermicelli.h"
#include "util/unaligned.h"
static really_inline
size_t nfaRevAccel_i(const struct NFA *nfa, const u8 *buffer, size_t length) {
DEBUG_PRINTF("checking rev accel mw %u\n", nfa->minWidth);
assert(nfa->rAccelOffset >= 1);
assert(nfa->rAccelOffset <= nfa->minWidth);
const u8 *rv; // result for accel engine
switch (nfa->rAccelType) {
case ACCEL_RVERM:
DEBUG_PRINTF("ACCEL_RVERM\n");
if (length + 1 - nfa->rAccelOffset < 16) {
break;
}
rv = rvermicelliExec(nfa->rAccelData.c, 0, buffer,
buffer + length + 1 - nfa->rAccelOffset);
length = (size_t)(rv - buffer + nfa->rAccelOffset);
break;
case ACCEL_RVERM_NOCASE:
DEBUG_PRINTF("ACCEL_RVERM_NOCASE\n");
if (length + 1 - nfa->rAccelOffset < 16) {
break;
}
rv = rvermicelliExec(nfa->rAccelData.c, 1, buffer,
buffer + length + 1 - nfa->rAccelOffset);
length = (size_t)(rv - buffer + nfa->rAccelOffset);
break;
case ACCEL_RDVERM:
DEBUG_PRINTF("ACCEL_RDVERM\n");
if (length + 1 - nfa->rAccelOffset < 17) {
break;
}
rv = rvermicelliDoubleExec(nfa->rAccelData.array[0],
nfa->rAccelData.array[1], 0, buffer,
buffer + length + 1 - nfa->rAccelOffset);
length = (size_t)(rv - buffer + nfa->rAccelOffset);
break;
case ACCEL_RDVERM_NOCASE:
DEBUG_PRINTF("ACCEL_RVERM_NOCASE\n");
if (length + 1 - nfa->rAccelOffset < 17) {
break;
}
rv = rvermicelliDoubleExec(nfa->rAccelData.array[0],
nfa->rAccelData.array[1], 1, buffer,
buffer + length + 1 - nfa->rAccelOffset);
length = (size_t)(rv - buffer + nfa->rAccelOffset);
break;
case ACCEL_REOD:
DEBUG_PRINTF("ACCEL_REOD\n");
if (buffer[length - nfa->rAccelOffset] != nfa->rAccelData.c) {
return 0;
}
break;
case ACCEL_REOD_NOCASE:
DEBUG_PRINTF("ACCEL_REOD_NOCASE\n");
if ((buffer[length - nfa->rAccelOffset] & CASE_CLEAR) !=
nfa->rAccelData.c) {
return 0;
}
break;
case ACCEL_RDEOD:
DEBUG_PRINTF("ACCEL_RDEOD\n");
if (unaligned_load_u16(buffer + length - nfa->rAccelOffset) !=
nfa->rAccelData.dc) {
return 0;
}
break;
case ACCEL_RDEOD_NOCASE:
DEBUG_PRINTF("ACCEL_RDEOD_NOCASE\n");
if ((unaligned_load_u16(buffer + length - nfa->rAccelOffset) &
DOUBLE_CASE_CLEAR) != nfa->rAccelData.dc) {
return 0;
}
break;
default:
assert(!"not here");
}
if (nfa->minWidth > length) {
DEBUG_PRINTF("post-accel, scan skipped: %zu < min %u bytes\n", length,
nfa->minWidth);
return 0;
}
return length;
}
/** \brief Reverse acceleration check. Returns a new length for the block,
* guaranteeing that a match cannot occur beyond that point. */
static really_inline
size_t nfaRevAccelCheck(const struct NFA *nfa, const u8 *buffer,
size_t length) {
assert(nfa);
// If this block is not long enough to satisfy the minimum width
// constraint on this NFA, we can avoid the scan altogether.
if (nfa->minWidth > length) {
DEBUG_PRINTF("scan skipped: %zu < min %u bytes\n", length,
nfa->minWidth);
return 0;
}
if (nfa->rAccelType == ACCEL_NONE) {
DEBUG_PRINTF("no rev accel available\n");
return length;
}
size_t rv_length = nfaRevAccel_i(nfa, buffer, length);
assert(rv_length <= length);
return rv_length;
}
#endif

91
src/nfa/rdfa.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef RDFA_H
#define RDFA_H
#include "nfa_kind.h"
#include "ue2common.h"
#include "util/ue2_containers.h"
#include <array>
#include <vector>
namespace ue2 {
typedef u16 dstate_id_t;
typedef u16 symbol_t;
static constexpr symbol_t TOP = 256;
static constexpr symbol_t ALPHABET_SIZE = 257;
static constexpr symbol_t N_SPECIAL_SYMBOL = 1;
static constexpr dstate_id_t DEAD_STATE = 0;
/** Structure representing a dfa state during construction. */
struct dstate {
/** Next state; indexed by remapped sym */
std::vector<dstate_id_t> next;
/** Set by ng_mcclellan, refined by mcclellancompile */
dstate_id_t daddy = 0;
/** Set by mcclellancompile, implementation state id, excludes edge
* decorations */
dstate_id_t impl_id = 0;
/** Reports to fire (at any location). */
flat_set<ReportID> reports;
/** Reports to fire (at EOD). */
flat_set<ReportID> reports_eod;
explicit dstate(size_t alphabet_size) : next(alphabet_size, 0) {}
};
struct raw_dfa {
nfa_kind kind;
std::vector<dstate> states;
dstate_id_t start_anchored = DEAD_STATE;
dstate_id_t start_floating = DEAD_STATE;
u16 alpha_size = 0; /* including special symbols */
/* mapping from input symbol --> equiv class id */
std::array<u16, ALPHABET_SIZE> alpha_remap;
explicit raw_dfa(nfa_kind k) : kind(k) {}
virtual ~raw_dfa();
u16 getImplAlphaSize() const;
virtual void stripExtraEodReports(void);
bool hasEodReports(void) const;
};
}
#endif

393
src/nfa/rdfa_merge.cpp Normal file
View File

@@ -0,0 +1,393 @@
/*
* Copyright (c) 2015, 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.
*/
#include "rdfa_merge.h"
#include "grey.h"
#include "dfa_min.h"
#include "mcclellancompile_util.h"
#include "rdfa.h"
#include "ue2common.h"
#include "nfagraph/ng_mcclellan_internal.h"
#include "util/container.h"
#include "util/determinise.h"
#include "util/make_unique.h"
#include "util/report_manager.h"
#include "util/ue2_containers.h"
#include <queue>
using namespace std;
namespace ue2 {
#define MAX_DFA_STATES 16383
namespace {
class Automaton_Merge {
public:
typedef vector<u16> StateSet;
typedef ue2::unordered_map<StateSet, dstate_id_t> StateMap;
Automaton_Merge(const raw_dfa *rdfa1, const raw_dfa *rdfa2,
const ReportManager *rm_in, const Grey &grey_in)
: rm(rm_in), grey(grey_in), nfas{rdfa1, rdfa2}, dead(2) {
calculateAlphabet();
populateAsFs();
prunable = isPrunable();
}
Automaton_Merge(const vector<const raw_dfa *> &dfas,
const ReportManager *rm_in, const Grey &grey_in)
: rm(rm_in), grey(grey_in), nfas(dfas), dead(nfas.size()) {
calculateAlphabet();
populateAsFs();
prunable = isPrunable();
}
void populateAsFs(void) {
bool fs_same = true;
bool fs_dead = true;
as.resize(nfas.size());
fs.resize(nfas.size());
for (size_t i = 0, end = nfas.size(); i < end; i++) {
as[i] = nfas[i]->start_anchored;
fs[i] = nfas[i]->start_floating;
if (fs[i]) {
fs_dead = false;
}
if (as[i] != fs[i]) {
fs_same = false;
}
}
start_anchored = DEAD_STATE + 1;
if (fs_same) {
start_floating = start_anchored;
} else if (fs_dead) {
start_floating = DEAD_STATE;
} else {
start_floating = start_anchored + 1;
}
}
void calculateAlphabet(void) {
DEBUG_PRINTF("calculating alphabet\n");
vector<CharReach> esets = {CharReach::dot()};
for (const auto &rdfa : nfas) {
DEBUG_PRINTF("...next dfa alphabet\n");
assert(rdfa);
const auto &alpha_remap = rdfa->alpha_remap;
for (size_t i = 0; i < esets.size(); i++) {
assert(esets[i].count());
if (esets[i].count() == 1) {
DEBUG_PRINTF("skipping singleton eq set\n");
continue;
}
CharReach t;
u8 leader_s = alpha_remap[esets[i].find_first()];
DEBUG_PRINTF("checking eq set, leader %02hhx \n", leader_s);
for (size_t s = esets[i].find_first(); s != CharReach::npos;
s = esets[i].find_next(s)) {
if (alpha_remap[s] != leader_s) {
t.set(s);
}
}
if (t.any() && t != esets[i]) {
esets[i] &= ~t;
esets.push_back(t);
}
}
}
alphasize = buildAlphabetFromEquivSets(esets, alpha, unalpha);
}
bool isPrunable() const {
if (!grey.highlanderPruneDFA || !rm) {
DEBUG_PRINTF("disabled, or not managed reports\n");
return false;
}
assert(!nfas.empty());
if (!generates_callbacks(nfas.front()->kind)) {
DEBUG_PRINTF("doesn't generate callbacks\n");
return false;
}
// Collect all reports from all merge candidates.
flat_set<ReportID> merge_reports;
for (const auto &rdfa : nfas) {
insert(&merge_reports, all_reports(*rdfa));
}
DEBUG_PRINTF("all reports: %s\n", as_string_list(merge_reports).c_str());
// Return true if they're all exhaustible with the same exhaustion key.
u32 ekey = INVALID_EKEY;
for (const auto &report_id : merge_reports) {
const Report &r = rm->getReport(report_id);
if (!isSimpleExhaustible(r)) {
DEBUG_PRINTF("report %u not simple exhaustible\n", report_id);
return false;
}
assert(r.ekey != INVALID_EKEY);
if (ekey == INVALID_EKEY) {
ekey = r.ekey;
} else if (ekey != r.ekey) {
DEBUG_PRINTF("two different ekeys, %u and %u\n", ekey, r.ekey);
return false;
}
}
DEBUG_PRINTF("is prunable\n");
return true;
}
void transition(const StateSet &in, StateSet *next) {
u16 t[ALPHABET_SIZE];
for (u32 i = 0; i < alphasize; i++) {
next[i].resize(nfas.size());
}
for (size_t j = 0, j_end = nfas.size(); j < j_end; j++) {
getFullTransitionFromState(*nfas[j], in[j], t);
for (u32 i = 0; i < alphasize; i++) {
next[i][j] = t[unalpha[i]];
}
}
}
const vector<StateSet> initial() {
vector<StateSet> rv = {as};
if (start_floating != DEAD_STATE && start_floating != start_anchored) {
rv.push_back(fs);
}
return rv;
}
private:
void reports_i(const StateSet &in, flat_set<ReportID> dstate::*r_set,
flat_set<ReportID> &r) const {
for (size_t i = 0, end = nfas.size(); i < end; i++) {
const auto &rs = nfas[i]->states[in[i]].*r_set;
insert(&r, rs);
}
}
public:
void reports(const StateSet &in, flat_set<ReportID> &rv) const {
reports_i(in, &dstate::reports, rv);
}
void reportsEod(const StateSet &in, flat_set<ReportID> &rv) const {
reports_i(in, &dstate::reports_eod, rv);
}
bool canPrune(const flat_set<ReportID> &test_reports) const {
if (!grey.highlanderPruneDFA || !prunable) {
return false;
}
// Must all be external reports.
assert(rm);
for (const auto &report_id : test_reports) {
if (!isExternalReport(rm->getReport(report_id))) {
return false;
}
}
return true;
}
/** True if the minimization algorithm should be run after merging. */
bool shouldMinimize() const {
// We only need to run minimization if our merged DFAs shared a report.
flat_set<ReportID> seen_reports;
for (const auto &rdfa : nfas) {
for (const auto &report_id : all_reports(*rdfa)) {
if (!seen_reports.insert(report_id).second) {
DEBUG_PRINTF("report %u in several dfas\n", report_id);
return true;
}
}
}
return false;
}
private:
const ReportManager *rm;
const Grey &grey;
vector<const raw_dfa *> nfas;
vector<dstate_id_t> as;
vector<dstate_id_t> fs;
bool prunable = false;
public:
std::array<u16, ALPHABET_SIZE> alpha;
std::array<u16, ALPHABET_SIZE> unalpha;
u16 alphasize;
StateSet dead;
u16 start_anchored;
u16 start_floating;
};
} // namespace
unique_ptr<raw_dfa> mergeTwoDfas(const raw_dfa *d1, const raw_dfa *d2,
size_t max_states, const ReportManager *rm,
const Grey &grey) {
assert(d1 && d2);
assert(d1->kind == d2->kind);
assert(max_states <= MAX_DFA_STATES);
auto rdfa = ue2::make_unique<raw_dfa>(d1->kind);
Automaton_Merge autom(d1, d2, rm, grey);
if (!determinise(autom, rdfa->states, max_states)) {
rdfa->start_anchored = autom.start_anchored;
rdfa->start_floating = autom.start_floating;
rdfa->alpha_size = autom.alphasize;
rdfa->alpha_remap = autom.alpha;
DEBUG_PRINTF("merge succeeded, %zu states\n", rdfa->states.size());
if (autom.shouldMinimize()) {
minimize_hopcroft(*rdfa, grey);
DEBUG_PRINTF("minimized, %zu states\n", rdfa->states.size());
}
return rdfa;
}
return nullptr;
}
void mergeDfas(vector<unique_ptr<raw_dfa>> &dfas, size_t max_states,
const ReportManager *rm, const Grey &grey) {
assert(max_states <= MAX_DFA_STATES);
if (dfas.size() <= 1) {
return;
}
DEBUG_PRINTF("before merging, we have %zu dfas\n", dfas.size());
queue<unique_ptr<raw_dfa>> q;
for (auto &dfa : dfas) {
q.push(move(dfa));
}
// All DFAs are now on the queue, so we'll clear the vector and use it for
// output from here.
dfas.clear();
while (q.size() > 1) {
// Attempt to merge the two front elements of the queue.
unique_ptr<raw_dfa> d1 = move(q.front());
q.pop();
unique_ptr<raw_dfa> d2 = move(q.front());
q.pop();
auto rdfa = mergeTwoDfas(d1.get(), d2.get(), max_states, rm, grey);
if (rdfa) {
q.push(move(rdfa));
} else {
DEBUG_PRINTF("failed to merge\n");
// Put the larger of the two DFAs on the output list, retain the
// smaller one on the queue for further merge attempts.
if (d2->states.size() > d1->states.size()) {
dfas.push_back(move(d2));
q.push(move(d1));
} else {
dfas.push_back(move(d1));
q.push(move(d2));
}
}
}
while (!q.empty()) {
dfas.push_back(move(q.front()));
q.pop();
}
DEBUG_PRINTF("after merging, we have %zu dfas\n", dfas.size());
}
unique_ptr<raw_dfa> mergeAllDfas(const vector<const raw_dfa *> &dfas,
size_t max_states, const ReportManager *rm,
const Grey &grey) {
assert(max_states <= MAX_DFA_STATES);
assert(!dfas.empty());
// All the DFAs should be of the same kind.
const auto kind = dfas.front()->kind;
assert(all_of(begin(dfas), end(dfas),
[&kind](const raw_dfa *rdfa) { return rdfa->kind == kind; }));
auto rdfa = ue2::make_unique<raw_dfa>(kind);
Automaton_Merge n(dfas, rm, grey);
DEBUG_PRINTF("merging dfa\n");
if (determinise(n, rdfa->states, max_states)) {
DEBUG_PRINTF("state limit (%zu) exceeded\n", max_states);
return nullptr; /* over state limit */
}
rdfa->start_anchored = n.start_anchored;
rdfa->start_floating = n.start_floating;
rdfa->alpha_size = n.alphasize;
rdfa->alpha_remap = n.alpha;
DEBUG_PRINTF("merged, building impl dfa (a,f) = (%hu,%hu)\n",
rdfa->start_anchored, rdfa->start_floating);
if (n.shouldMinimize()) {
minimize_hopcroft(*rdfa, grey);
DEBUG_PRINTF("minimized, %zu states\n", rdfa->states.size());
}
return rdfa;
}
} // namespace ue2

62
src/nfa/rdfa_merge.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2015, 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 Merge code for McClellan DFA.
*/
#ifndef RDFA_MERGE_H
#define RDFA_MERGE_H
#include <memory>
#include <vector>
namespace ue2 {
class ReportManager;
struct raw_dfa;
struct Grey;
/** \brief Attempts to merge two raw_dfas into one. */
std::unique_ptr<raw_dfa> mergeTwoDfas(const raw_dfa *d1, const raw_dfa *d2,
size_t max_states, const ReportManager *rm,
const Grey &grey);
/** \brief Attempts to merge all the given raw_dfas into one. */
std::unique_ptr<raw_dfa> mergeAllDfas(const std::vector<const raw_dfa *> &dfas,
size_t max_states,
const ReportManager *rm,
const Grey &grey);
/** \brief Merges the given list of raw_dfas as much as possible in-place. */
void mergeDfas(std::vector<std::unique_ptr<raw_dfa>> &dfas, size_t max_states,
const ReportManager *rm, const Grey &grey);
} // namespace ue2
#endif // RDFA_MERGE_H

1553
src/nfa/repeat.c Normal file

File diff suppressed because it is too large Load Diff

361
src/nfa/repeat.h Normal file
View File

@@ -0,0 +1,361 @@
/*
* Copyright (c) 2015, 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 API for handling bounded repeats.
*
* This file provides an internal API for handling bounded repeats of character
* classes. It is used by the Large Bounded Repeat (LBR) engine and by the
* bounded repeat handling in the LimEx NFA engine as well.
*
* The state required by these functions is split into two regions:
*
* 1. Control block. This is a small structure (size varies with repeat mode)
* that may be copied around or compressed into stream state.
* 2. Repeat state. This is a larger structure that can be quite big for large
* repeats, often containing a multibit ring or large vector of indices.
* This generally lives in stream state and is not copied.
*/
#ifndef REPEAT_H
#define REPEAT_H
#include "ue2common.h"
#include "repeat_internal.h"
#include "util/bitutils.h"
#ifdef __cplusplus
extern "C"
{
#endif
/** Returns the offset of the most recent 'top' offset set in the repeat. */
static really_inline
u64a repeatLastTop(const struct RepeatInfo *info,
const union RepeatControl *ctrl, const void *state);
/** Returns the offset of the next match after 'offset', or zero if no further
* matches are possible. */
static really_inline
u64a repeatNextMatch(const struct RepeatInfo *info,
const union RepeatControl *ctrl, const void *state,
u64a offset);
/** Stores a new top in the repeat. If is_alive is false, the repeat will be
* initialised first and this top will become the first (and only) one. */
static really_inline
void repeatStore(const struct RepeatInfo *info, union RepeatControl *ctrl,
void *state, u64a offset, char is_alive);
/** Return type for repeatHasMatch. */
enum RepeatMatch {
REPEAT_NOMATCH, /**< This offset is not a valid match. */
REPEAT_MATCH, /**< This offset is a valid match. */
REPEAT_STALE /**< This offset is not a valid match and no greater
offset will be (unless another top is stored). */
};
/** Query whether the repeat has a match at the given offset. Returns
* ::REPEAT_STALE if it does not have a match at that offset _and_
* no further matches are possible. */
static really_inline
enum RepeatMatch repeatHasMatch(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset);
/** \brief Serialize a packed version of the repeat control block into stream
* state. */
void repeatPack(char *dest, const struct RepeatInfo *info,
const union RepeatControl *ctrl, u64a offset);
/** \brief Deserialize a packed version of the repeat control block. */
void repeatUnpack(const char *src, const struct RepeatInfo *info, u64a offset,
union RepeatControl *ctrl);
////
//// IMPLEMENTATION.
////
u64a repeatLastTopRing(const struct RepeatInfo *info,
const union RepeatControl *ctrl);
u64a repeatLastTopRange(const union RepeatControl *ctrl,
const void *state);
u64a repeatLastTopBitmap(const union RepeatControl *ctrl);
u64a repeatLastTopTrailer(const struct RepeatInfo *info,
const union RepeatControl *ctrl);
u64a repeatLastTopSparseOptimalP(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state);
static really_inline
u64a repeatLastTop(const struct RepeatInfo *info,
const union RepeatControl *ctrl, const void *state) {
assert(info && ctrl && state);
switch ((enum RepeatType)info->type) {
case REPEAT_RING:
return repeatLastTopRing(info, ctrl);
case REPEAT_FIRST:
case REPEAT_LAST:
return ctrl->offset.offset;
case REPEAT_RANGE:
return repeatLastTopRange(ctrl, state);
case REPEAT_BITMAP:
return repeatLastTopBitmap(ctrl);
case REPEAT_SPARSE_OPTIMAL_P:
return repeatLastTopSparseOptimalP(info, ctrl, state);
case REPEAT_TRAILER:
return repeatLastTopTrailer(info, ctrl);
}
DEBUG_PRINTF("bad repeat type %u\n", info->type);
assert(0);
return 0;
}
// Used for both FIRST and LAST models.
static really_inline
u64a repeatNextMatchOffset(const struct RepeatInfo *info,
const union RepeatControl *ctrl, u64a offset) {
u64a first = ctrl->offset.offset + info->repeatMin;
if (offset < first) {
return first;
}
if (info->repeatMax == REPEAT_INF ||
offset < ctrl->offset.offset + info->repeatMax) {
return offset + 1;
}
return 0; // No more matches.
}
u64a repeatNextMatchRing(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset);
u64a repeatNextMatchRange(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset);
u64a repeatNextMatchBitmap(const struct RepeatInfo *info,
const union RepeatControl *ctrl, u64a offset);
u64a repeatNextMatchSparseOptimalP(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset);
u64a repeatNextMatchTrailer(const struct RepeatInfo *info,
const union RepeatControl *ctrl, u64a offset);
static really_inline
u64a repeatNextMatch(const struct RepeatInfo *info,
const union RepeatControl *ctrl, const void *state,
u64a offset) {
assert(info && ctrl && state);
assert(ISALIGNED(info));
assert(ISALIGNED(ctrl));
switch ((enum RepeatType)info->type) {
case REPEAT_RING:
return repeatNextMatchRing(info, ctrl, state, offset);
case REPEAT_FIRST:
// fall through
case REPEAT_LAST:
return repeatNextMatchOffset(info, ctrl, offset);
case REPEAT_RANGE:
return repeatNextMatchRange(info, ctrl, state, offset);
case REPEAT_BITMAP:
return repeatNextMatchBitmap(info, ctrl, offset);
case REPEAT_SPARSE_OPTIMAL_P:
return repeatNextMatchSparseOptimalP(info, ctrl, state, offset);
case REPEAT_TRAILER:
return repeatNextMatchTrailer(info, ctrl, offset);
}
DEBUG_PRINTF("bad repeat type %u\n", info->type);
assert(0);
return 0;
}
static really_inline
void repeatStoreFirst(union RepeatControl *ctrl, u64a offset,
char is_alive) {
if (is_alive) {
return;
}
ctrl->offset.offset = offset;
}
static really_inline
void repeatStoreLast(union RepeatControl *ctrl, u64a offset,
UNUSED char is_alive) {
assert(!is_alive || offset >= ctrl->offset.offset);
ctrl->offset.offset = offset;
}
void repeatStoreRing(const struct RepeatInfo *info,
union RepeatControl *ctrl, void *state, u64a offset,
char is_alive);
void repeatStoreRange(const struct RepeatInfo *info,
union RepeatControl *ctrl, void *state, u64a offset,
char is_alive);
void repeatStoreBitmap(const struct RepeatInfo *info,
union RepeatControl *ctrl, u64a offset,
char is_alive);
void repeatStoreSparseOptimalP(const struct RepeatInfo *info,
union RepeatControl *ctrl, void *state,
u64a offset, char is_alive);
void repeatStoreTrailer(const struct RepeatInfo *info,
union RepeatControl *ctrl, u64a offset,
char is_alive);
static really_inline
void repeatStore(const struct RepeatInfo *info, union RepeatControl *ctrl,
void *state, u64a offset, char is_alive) {
assert(info && ctrl && state);
assert(ISALIGNED(info));
assert(ISALIGNED(ctrl));
assert(info->repeatMin <= info->repeatMax);
assert(info->repeatMax <= REPEAT_INF);
switch ((enum RepeatType)info->type) {
case REPEAT_RING:
repeatStoreRing(info, ctrl, state, offset, is_alive);
break;
case REPEAT_FIRST:
repeatStoreFirst(ctrl, offset, is_alive);
break;
case REPEAT_LAST:
repeatStoreLast(ctrl, offset, is_alive);
break;
case REPEAT_RANGE:
repeatStoreRange(info, ctrl, state, offset, is_alive);
break;
case REPEAT_BITMAP:
repeatStoreBitmap(info, ctrl, offset, is_alive);
break;
case REPEAT_SPARSE_OPTIMAL_P:
repeatStoreSparseOptimalP(info, ctrl, state, offset, is_alive);
break;
case REPEAT_TRAILER:
repeatStoreTrailer(info, ctrl, offset, is_alive);
break;
}
}
static really_inline
enum RepeatMatch repeatHasMatchFirst(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
u64a offset) {
if (offset < ctrl->offset.offset + info->repeatMin) {
return REPEAT_NOMATCH;
}
// FIRST models are {N,} repeats, i.e. they always have inf max depth.
assert(info->repeatMax == REPEAT_INF);
return REPEAT_MATCH;
}
static really_inline
enum RepeatMatch repeatHasMatchLast(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
u64a offset) {
if (offset < ctrl->offset.offset + info->repeatMin) {
return REPEAT_NOMATCH;
}
assert(info->repeatMax < REPEAT_INF);
if (offset <= ctrl->offset.offset + info->repeatMax) {
return REPEAT_MATCH;
}
return REPEAT_STALE;
}
enum RepeatMatch repeatHasMatchRing(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset);
enum RepeatMatch repeatHasMatchRange(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset);
enum RepeatMatch repeatHasMatchSparseOptimalP(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset);
enum RepeatMatch repeatHasMatchBitmap(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
u64a offset);
enum RepeatMatch repeatHasMatchTrailer(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
u64a offset);
static really_inline
enum RepeatMatch repeatHasMatch(const struct RepeatInfo *info,
const union RepeatControl *ctrl,
const void *state, u64a offset) {
assert(info && ctrl && state);
assert(ISALIGNED(info));
assert(ISALIGNED(ctrl));
switch ((enum RepeatType)info->type) {
case REPEAT_RING:
return repeatHasMatchRing(info, ctrl, state, offset);
case REPEAT_FIRST:
return repeatHasMatchFirst(info, ctrl, offset);
case REPEAT_LAST:
return repeatHasMatchLast(info, ctrl, offset);
case REPEAT_RANGE:
return repeatHasMatchRange(info, ctrl, state, offset);
case REPEAT_BITMAP:
return repeatHasMatchBitmap(info, ctrl, offset);
case REPEAT_SPARSE_OPTIMAL_P:
return repeatHasMatchSparseOptimalP(info, ctrl, state, offset);
case REPEAT_TRAILER:
return repeatHasMatchTrailer(info, ctrl, offset);
}
assert(0);
return REPEAT_NOMATCH;
}
#ifdef __cplusplus
}
#endif
#endif // REPEAT_H

212
src/nfa/repeat_internal.h Normal file
View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef REPEAT_INTERNAL_H
#define REPEAT_INTERNAL_H
#include "ue2common.h"
/** \file
* \brief Bounded Repeat models.
*
* Used by the NFA, to represent bounded repeats managed via special POS and
* TUG exceptions, and by the LBR (limited bounded repeat) and Castle
* specialist engines.
*
* We currently have a number of different kinds of bounded repeat model, for
* different kinds of {N,M} repeats, described by ::RepeatType.
*/
/** Different types of bounded repeats. */
enum RepeatType {
/** General mechanism for tracking {N,M} repeats. Stores the first top as
* an absolute offset, then subsequent tops in the {N,M} range as a ring of
* relative top indices stored in a multibit. */
REPEAT_RING = 0,
/** Used to track {N,} repeats. Uses the \ref RepeatOffsetControl structure,
* since only the first top encountered needs to be stored. */
REPEAT_FIRST = 1,
/** Used to track {0,N} repeats. Much like ::REPEAT_FIRST, except that we
* store the most recent top encountered. */
REPEAT_LAST = 2,
/** Like ::REPEAT_RING, this is also used for {N,M} repeats, but for cases
* where there is a large difference between N and M, and developed to
* reduce the state requirements of this case (relative to the RING model).
* Uses a small ordered array of top indices relative to \ref
* RepeatRangeControl::offset. */
REPEAT_RANGE = 3,
/** Used for {N,M} repeats where 0 < M <= 64. Uses the \ref
* RepeatBitmapControl structure at runtime. */
REPEAT_BITMAP = 4,
/** Optimal mechanism for tracking {N,M} repeats when there is a bound on
* how frequently they can be retriggered.
* Assume f(repeat, min) representing the number of possible bit patterns
* we can have for repeat size = repeat, minimum period = min
* We will have the following recurrence relation:
* f(repeat, min) = f(repeat - 1, min) + f(repeat - min, min);
* We use this recurrence to encode bit patterns with 64-bit values by
* referencing a table that stores values from f(0, min) to f(repeat, min)
* eg: repeat = 5, min = 2. 10001 => f(4,2) + f(0,2) = 9.
* We search the optimal patch size between min and repeat in advance and
* use the scheme above to do encoding and decoding to reduce stream state size
* */
REPEAT_SPARSE_OPTIMAL_P = 5,
/** Used for {N,M} repeats where 0 < N < 64. Uses the \ref RepeatTrailerControl
* structure at runtime. */
REPEAT_TRAILER = 6,
};
/**
* \brief Value used to represent an unbounded max repeat.
*
* Note that we do not support \ref RepeatInfo::repeatMax values larger than
* this.
*/
#define REPEAT_INF 65535
/** Max slots used by ::REPEAT_RANGE repeat model. */
#define REPEAT_RANGE_MAX_SLOTS 16
/** Structure describing a bounded repeat in the bytecode */
struct RepeatInfo {
u8 type; //!< from enum RepeatType.
u32 repeatMin; //!< minimum number of repeats.
u32 repeatMax; //!< maximum number of repeats, or REPEAT_INF if unbounded.
/** Maximum value that is required to be stored in the control block
* counters. Any value greater than this will be capped at the horizon.
*/
u32 horizon;
/** Size of the compressed control block in bytes. This is what is written
* out to stream state at stream boundaries. */
u32 packedCtrlSize;
/** Size of the repeat state block in bytes. This is where the REPEAT_RANGE
* vector and REPEAT_RING multibit are stored, in stream state, and they
* are manipulated directly (i.e. not copied at stream boundaries). */
u32 stateSize;
/** How soon after one trigger we can see the next trigger.
* Used by REPEAT_SPARSE_OPTIMAL_P. */
u32 minPeriod;
/** Packed control block field sizes (in bits), used by REPEAT_TRAILER. */
u32 packedFieldSizes[2];
/* Number of patches, used by REPEAT_SPARSE_OPTIMAL_P. */
u32 patchCount;
/* Optimal patch length, used by REPEAT_SPARSE_OPTIMAL_P. */
u32 patchSize;
/* Encoding patch length in bytes, used by REPEAT_SPARSE_OPTIMAL_P. */
u32 encodingSize;
/* RepeatInfo struct length including table size. */
u32 length;
/** Offset of patches relative to the start of repeat stream state,
* used by REPEAT_SPARSE_OPTIMAL_P. */
u32 patchesOffset;
};
/** Runtime control block structure for ::REPEAT_RING and
* ::REPEAT_SPARSE_OPTIMAL_P bounded repeats. Note that this struct is packed
* (may not be aligned). */
struct RepeatRingControl {
u64a offset; //!< index of first top.
u16 first; //!< start index in ring.
u16 last; //!< end index in ring.
};
/** Runtime control block structure for ::REPEAT_RANGE bounded repeats. Note
* that this struct is packed (may not be aligned). */
struct RepeatRangeControl {
u64a offset; //!< index of first top.
u8 num; //!< number of elements in array.
};
/** Runtime control block structure for cases where only a single offset is
* needed to track the repeat, both ::REPEAT_FIRST and ::REPEAT_LAST. Note that
* this struct is packed (may not be aligned). */
struct RepeatOffsetControl {
u64a offset; //!< index of a top.
};
/** Runtime control block structure for ::REPEAT_BITMAP bounded repeats. */
struct RepeatBitmapControl {
u64a offset; //!< index of first top.
u64a bitmap; //!< forward bitmap of tops relative to base offset.
};
/** Runtime control block structure for ::REPEAT_TRAILER bounded repeats. */
struct RepeatTrailerControl {
u64a offset; //!< min extent of most recent match window.
u64a bitmap; //!< trailing bitmap of earlier matches, relative to offset.
};
/** \brief Union of control block types, used at runtime. */
union RepeatControl {
struct RepeatRingControl ring;
struct RepeatRangeControl range;
struct RepeatOffsetControl offset;
struct RepeatBitmapControl bitmap;
struct RepeatTrailerControl trailer;
};
/** For debugging, returns the name of a repeat model. */
static really_inline UNUSED
const char *repeatTypeName(u8 type) {
switch ((enum RepeatType)type) {
case REPEAT_RING:
return "RING";
case REPEAT_FIRST:
return "FIRST";
case REPEAT_LAST:
return "LAST";
case REPEAT_RANGE:
return "RANGE";
case REPEAT_BITMAP:
return "BITMAP";
case REPEAT_SPARSE_OPTIMAL_P:
return "SPARSE_OPTIMAL_P";
case REPEAT_TRAILER:
return "TRAILER";
}
assert(0);
return "UNKNOWN";
}
#endif // REPEAT_INTERNAL_H

374
src/nfa/repeatcompile.cpp Normal file
View File

@@ -0,0 +1,374 @@
/*
* Copyright (c) 2015, 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 Bounded repeat compile-time code.
*/
#include "repeatcompile.h"
#include "util/bitutils.h"
#include "util/charreach.h"
#include "util/depth.h"
#include "util/dump_charclass.h"
#include "util/multibit_internal.h"
#include "util/verify_types.h"
#include <algorithm>
#include <cstring> // memset
#include <utility>
using namespace std;
namespace ue2 {
/** \brief Calculate the number of slots required to store the given repeat in
* a RANGE model. */
static
u32 numRangeSlots(u32 repeatMin, u32 repeatMax) {
assert(repeatMax > repeatMin);
u32 d = repeatMax - repeatMin;
u32 slots = 2 * ((repeatMax / d) + 1);
return slots;
}
static
u32 calcPackedBits(u64a val) {
assert(val);
if (val <= 1) {
return 1;
}
u32 bits = lg2_64(val - 1) + 1U; /* lg2 rounds down */
DEBUG_PRINTF("packing %llu into %u bits\n", val, bits);
return bits;
}
/* returns the min number of bytes required to represent val options */
u32 calcPackedBytes(u64a val) {
u32 rv = (calcPackedBits(val) + 7U) / 8U;
DEBUG_PRINTF("packing %llu into %u bytes\n", val, rv);
return rv;
}
static
u64a repeatRecurTable(struct RepeatStateInfo *info, const depth &repeatMax,
const u32 minPeriod) {
u32 repeatTmp = info->patchCount > 2 ? 64 : (u32)repeatMax;
u32 repeat_index = repeatTmp < minPeriod ? repeatTmp : minPeriod;
for (u32 i = 0; i <= repeat_index; i++) {
info->table.push_back(i + 1);
}
for (u32 i = minPeriod + 1; i <= repeatTmp; i++) {
info->table.push_back(info->table[i - 1] + info->table[i - minPeriod]);
if (info->table[i] < info->table[i - 1]) {
return i - 1;
}
}
return 0;
}
static
u32 findOptimalPatchSize(struct RepeatStateInfo *info, const depth &repeatMax,
const u32 minPeriod, u64a rv) {
u32 cnt = 0;
u32 patch_bits = 0;
u32 total_size = 0;
u32 min = ~0U;
u32 patch_len = 0;
if (!rv) {
rv = repeatMax;
}
for (u32 i = minPeriod; i <= rv; i++) {
cnt = ((u32)repeatMax + (i - 1)) / i + 1;
// no bit packing version
patch_bits = calcPackedBits(info->table[i]);
total_size = (patch_bits + 7U) / 8U * cnt;
if (total_size < min) {
patch_len = i;
min = total_size;
info->patchCount = cnt;
}
}
return patch_len;
}
RepeatStateInfo::RepeatStateInfo(enum RepeatType type, const depth &repeatMin,
const depth &repeatMax, u32 minPeriod)
: stateSize(0), packedCtrlSize(0), horizon(0), patchCount(0),
patchSize(0), encodingSize(0), patchesOffset(0) {
assert(repeatMin <= repeatMax);
assert(repeatMax.is_reachable());
assert(minPeriod || type != REPEAT_SPARSE_OPTIMAL_P);
switch (type) {
case REPEAT_FIRST:
assert(repeatMin.is_finite());
stateSize = 0; // everything is in the control block.
horizon = repeatMin;
packedCtrlSize = calcPackedBytes(horizon + 1);
break;
case REPEAT_LAST:
assert(repeatMax.is_finite());
stateSize = 0; // everything is in the control block.
horizon = repeatMax + 1;
packedCtrlSize = calcPackedBytes(horizon + 1);
break;
case REPEAT_RING:
assert(repeatMax.is_finite());
stateSize = mmbit_size(repeatMax + 1);
horizon = repeatMax * 2 + 1; /* TODO: investigate tightening */
// Packed offset member, plus two bytes for each ring index, reduced to
// one byte each if they'll fit in eight bits.
{
u32 offset_len = calcPackedBytes(horizon + 1);
u32 ring_indices_len = repeatMax < depth(254) ? 2 : 4;
packedCtrlSize = offset_len + ring_indices_len;
}
break;
case REPEAT_RANGE:
assert(repeatMax.is_finite());
assert(repeatMin < repeatMax);
stateSize = numRangeSlots(repeatMin, repeatMax) * sizeof(u16);
horizon = repeatMax * 2 + 1;
// Packed offset member, plus one byte for the number of range
// elements.
packedCtrlSize = calcPackedBytes(horizon + 1) + 1;
break;
case REPEAT_BITMAP:
stateSize = 0; // everything is in the control block.
horizon = 0; // unused
packedCtrlSize = ROUNDUP_N(repeatMax + 1, 8) / 8;
break;
case REPEAT_SPARSE_OPTIMAL_P:
assert(minPeriod);
assert(repeatMax.is_finite());
{
u64a rv = repeatRecurTable(this, repeatMax, minPeriod);
u32 repeatTmp = 0;
if ((u32)repeatMax < minPeriod) {
repeatTmp = repeatMax;
patchCount = 1;
} else {
// find optimal patch size
repeatTmp =
findOptimalPatchSize(this, repeatMax, minPeriod, rv);
assert(patchCount < 65536);
}
DEBUG_PRINTF("repeat[%u %u], period=%u\n", (u32)repeatMin,
(u32)repeatMax, minPeriod);
u64a maxVal = table[repeatTmp];
encodingSize = calcPackedBytes(maxVal);
patchSize = repeatTmp;
assert(encodingSize <= 64);
patchesOffset = mmbit_size(patchCount);
stateSize = patchesOffset + encodingSize * patchCount;
horizon = (repeatTmp * patchCount) * 2 + 1;
u32 ring_indices_len = patchCount < depth(254) ? 2 : 4;
packedCtrlSize = calcPackedBytes(horizon + 1) + ring_indices_len;
}
break;
case REPEAT_TRAILER:
assert(repeatMax.is_finite());
assert(repeatMin <= depth(64));
stateSize = 0; // everything is in the control block.
horizon = repeatMax + 1;
packedFieldSizes.resize(2);
packedFieldSizes[0] = calcPackedBits(horizon + 1);
packedFieldSizes[1] = repeatMin;
packedCtrlSize = (packedFieldSizes[0] + packedFieldSizes[1] + 7U) / 8U;
break;
}
DEBUG_PRINTF("stateSize=%u, packedCtrlSize=%u, horizon=%u\n", stateSize,
packedCtrlSize, horizon);
assert(packedCtrlSize <= sizeof(RepeatControl));
}
/** \brief Returns the packed control block size in bytes for a given bounded
* repeat. */
static
u32 packedSize(enum RepeatType type, const depth &repeatMin,
const depth &repeatMax, u32 minPeriod) {
RepeatStateInfo rsi(type, repeatMin, repeatMax, minPeriod);
return rsi.packedCtrlSize;
}
/** \brief Returns the stream state size in bytes for a given bounded
* repeat. */
static
u32 streamStateSize(enum RepeatType type, const depth &repeatMin,
const depth &repeatMax, u32 minPeriod) {
RepeatStateInfo rsi(type, repeatMin, repeatMax, minPeriod);
return rsi.stateSize;
}
enum RepeatType chooseRepeatType(const depth &repeatMin, const depth &repeatMax,
u32 minPeriod, bool is_reset) {
if (repeatMax.is_infinite()) {
return REPEAT_FIRST;
}
if (repeatMin == depth(0) || is_reset) {
return REPEAT_LAST;
}
// Cases with max < 64 can be handled with either bitmap or trailer. We use
// whichever has smaller packed state.
if (repeatMax < depth(64)) {
u32 bitmap_len =
packedSize(REPEAT_BITMAP, repeatMin, repeatMax, minPeriod);
u32 trailer_len =
packedSize(REPEAT_TRAILER, repeatMin, repeatMax, minPeriod);
return bitmap_len <= trailer_len ? REPEAT_BITMAP : REPEAT_TRAILER;
}
if (repeatMin <= depth(64)) {
return REPEAT_TRAILER;
}
u32 range_len = ~0U;
if (repeatMax > repeatMin &&
numRangeSlots(repeatMin, repeatMax) <= REPEAT_RANGE_MAX_SLOTS) {
assert(numRangeSlots(repeatMin, repeatMax) < 256); // stored in u8
range_len =
streamStateSize(REPEAT_RANGE, repeatMin, repeatMax, minPeriod);
}
assert(repeatMax.is_finite());
u32 sparse_len = ~0U;
if (minPeriod > 6) {
sparse_len =
streamStateSize(REPEAT_SPARSE_OPTIMAL_P, repeatMin, repeatMax, minPeriod);
}
if (range_len != ~0U || sparse_len != ~0U) {
return range_len < sparse_len ? REPEAT_RANGE : REPEAT_SPARSE_OPTIMAL_P;
}
return REPEAT_RING;
}
bool matches(vector<CharReach>::const_iterator a_it,
vector<CharReach>::const_iterator a_ite,
vector<CharReach>::const_iterator b_it,
UNUSED vector<CharReach>::const_iterator b_ite) {
for (; a_it != a_ite; ++a_it, ++b_it) {
assert(b_it != b_ite);
if ((*a_it & *b_it).none()) {
return false;
}
}
assert(b_it == b_ite);
return true;
}
static
u32 minDistAfterA(const vector<CharReach> &a, const vector<CharReach> &b) {
/* we do not count the case where b can end at the same position as a */
for (u32 i = 1; i < b.size(); i++) {
u32 overlap_len = b.size() - i;
if (overlap_len <= a.size()) {
if (matches(a.end() - overlap_len, a.end(),
b.begin(), b.end() - i)) {
return i;
}
} else {
assert(overlap_len > a.size());
if (matches(a.begin(), a.end(),
b.end() - i - a.size(), b.end() - i)) {
return i;
}
}
}
return b.size();
}
vector<size_t> minResetDistToEnd(const vector<vector<CharReach>> &triggers,
const CharReach &cr) {
/* if a trigger does not reset the repeat, it gets a distance of trigger
length */
vector<size_t> out;
for (const auto &trig : triggers) {
size_t size = trig.size();
size_t i = 0;
for (; i < size; i++) {
if ((trig[size - i - 1] & cr).none()) {
break;
}
}
out.push_back(i);
}
return out;
}
#if defined(DEBUG) || defined(DUMP_SUPPORT)
static UNUSED
string dumpTrigger(const vector<CharReach> &trigger) {
string s;
for (const auto &cr : trigger) {
s += describeClass(cr);
}
return s;
}
#endif
u32 minPeriod(const vector<vector<CharReach>> &triggers, const CharReach &cr,
bool *can_reset) {
assert(!triggers.empty());
u32 rv = ~0U;
*can_reset = true;
vector<size_t> min_reset_dist = minResetDistToEnd(triggers, cr);
for (const auto &trigger : triggers) {
DEBUG_PRINTF("trigger: %s\n", dumpTrigger(trigger).c_str());
for (size_t j = 0; j < triggers.size(); j++) {
u32 min_ext = minDistAfterA(trigger, triggers[j]);
rv = min(rv, min_ext);
if (min_ext <= min_reset_dist[j]) {
*can_reset = false;
}
}
}
DEBUG_PRINTF("min period %u\n", rv);
return rv;
}
} // namespace ue2

89
src/nfa/repeatcompile.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2015, 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 Bounded repeat compile-time code.
*/
#ifndef REPEATCOMPILE_H
#define REPEATCOMPILE_H
#include "repeat_internal.h"
#include <cstdint>
#include <utility>
#include <vector>
namespace ue2 {
class CharReach;
class depth;
/**
* \brief Structure representing the various state requirements for a given
* bounded repeat.
*/
struct RepeatStateInfo {
RepeatStateInfo(enum RepeatType type, const depth &repeatMin,
const depth &repeatMax, u32 minPeriod);
u32 stateSize;
u32 packedCtrlSize;
u32 horizon;
u32 patchCount;
u32 patchSize;
u32 encodingSize;
u32 patchesOffset;
std::vector<u32> packedFieldSizes;
std::vector<uint64_t> table; // not u64a, for boost/gcc-4.9
};
/**
* \brief Given the parameters of a repeat, choose a repeat implementation
* type.
*/
enum RepeatType chooseRepeatType(const depth &repeatMin, const depth &repeatMax,
u32 minPeriod, bool is_reset);
u32 calcPackedBytes(u64a val);
bool matches(std::vector<CharReach>::const_iterator a_it,
std::vector<CharReach>::const_iterator a_ite,
std::vector<CharReach>::const_iterator b_it,
std::vector<CharReach>::const_iterator b_ite);
std::vector<size_t>
minResetDistToEnd(const std::vector<std::vector<CharReach>> &triggers,
const CharReach &cr);
u32 minPeriod(const std::vector<std::vector<CharReach>> &triggers,
const CharReach &cr, bool *can_reset);
} // namespace ue2
#endif // REPEATCOMPILE_H

623
src/nfa/shufti.c Normal file
View File

@@ -0,0 +1,623 @@
/*
* Copyright (c) 2015, 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 Shufti: character class acceleration.
*
* Utilises the SSSE3 pshufb shuffle instruction
*/
#include "shufti.h"
#include "ue2common.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
#include "util/unaligned.h"
/** \brief Naive byte-by-byte implementation. */
static really_inline
const u8 *shuftiFwdSlow(const u8 *lo, const u8 *hi, const u8 *buf,
const u8 *buf_end) {
assert(buf < buf_end);
for (; buf < buf_end; ++buf) {
u8 c = *buf;
if (lo[c & 0xf] & hi[c >> 4]) {
break;
}
}
return buf;
}
/** \brief Naive byte-by-byte implementation. */
static really_inline
const u8 *shuftiRevSlow(const u8 *lo, const u8 *hi, const u8 *buf,
const u8 *buf_end) {
assert(buf < buf_end);
for (buf_end--; buf_end >= buf; buf_end--) {
u8 c = *buf_end;
if (lo[c & 0xf] & hi[c >> 4]) {
break;
}
}
return buf_end;
}
#ifdef DEBUG
#include <ctype.h>
#define DUMP_MSK(_t) \
static UNUSED \
void dumpMsk##_t(m##_t msk) { \
u8 * mskAsU8 = (u8 *)&msk; \
for (unsigned i = 0; i < sizeof(msk); i++) { \
u8 c = mskAsU8[i]; \
for (int j = 0; j < 8; j++) { \
if ((c >> (7-j)) & 0x1) \
printf("1"); \
else \
printf("0"); \
} \
printf(" "); \
} \
} \
static UNUSED \
void dumpMsk##_t##AsChars(m##_t msk) { \
u8 * mskAsU8 = (u8 *)&msk; \
for (unsigned i = 0; i < sizeof(msk); i++) { \
u8 c = mskAsU8[i]; \
if (isprint(c)) \
printf("%c",c); \
else \
printf("."); \
} \
}
DUMP_MSK(128)
#endif
#include "util/simd_utils_ssse3.h"
#if !defined(__AVX2__)
/* Normal SSSE3 shufti */
#define GET_LO_4(chars) and128(chars, low4bits)
#define GET_HI_4(chars) rshift2x64(andnot128(low4bits, chars), 4)
static really_inline
const u8 *firstMatch(const u8 *buf, m128 t, m128 compare) {
#ifdef DEBUG
DEBUG_PRINTF("confirming match in:"); dumpMsk128(t); printf("\n");
#endif
u32 z = movemask128(eq128(t, compare));
if (unlikely(z != 0xffff)) {
u32 pos = ctz32(~z & 0xffff);
assert(pos < 16);
return buf + pos;
} else {
return NULL; // no match
}
}
static really_inline
const u8 *fwdBlock(m128 mask_lo, m128 mask_hi, m128 chars, const u8 *buf,
const m128 low4bits, const m128 zeroes) {
m128 c_lo = pshufb(mask_lo, GET_LO_4(chars));
m128 c_hi = pshufb(mask_hi, GET_HI_4(chars));
m128 t = and128(c_lo, c_hi);
#ifdef DEBUG
DEBUG_PRINTF(" chars: "); dumpMsk128AsChars(chars); printf("\n");
DEBUG_PRINTF(" char: "); dumpMsk128(chars); printf("\n");
DEBUG_PRINTF(" c_lo: "); dumpMsk128(c_lo); printf("\n");
DEBUG_PRINTF(" c_hi: "); dumpMsk128(c_hi); printf("\n");
DEBUG_PRINTF(" t: "); dumpMsk128(t); printf("\n");
#endif
return firstMatch(buf, t, zeroes);
}
const u8 *shuftiExec(m128 mask_lo, m128 mask_hi, const u8 *buf,
const u8 *buf_end) {
assert(buf && buf_end);
assert(buf < buf_end);
// Slow path for small cases.
if (buf_end - buf < 16) {
return shuftiFwdSlow((const u8 *)&mask_lo, (const u8 *)&mask_hi,
buf, buf_end);
}
const m128 zeroes = zeroes128();
const m128 low4bits = _mm_set1_epi8(0xf);
const u8 *rv;
size_t min = (size_t)buf % 16;
assert(buf_end - buf >= 16);
// Preconditioning: most of the time our buffer won't be aligned.
m128 chars = loadu128(buf);
rv = fwdBlock(mask_lo, mask_hi, chars, buf, low4bits, zeroes);
if (rv) {
return rv;
}
buf += (16 - min);
// Unrolling was here, but it wasn't doing anything but taking up space.
// Reroll FTW.
const u8 *last_block = buf_end - 16;
while (buf < last_block) {
m128 lchars = load128(buf);
rv = fwdBlock(mask_lo, mask_hi, lchars, buf, low4bits, zeroes);
if (rv) {
return rv;
}
buf += 16;
}
// Use an unaligned load to mop up the last 16 bytes and get an accurate
// picture to buf_end.
assert(buf <= buf_end && buf >= buf_end - 16);
chars = loadu128(buf_end - 16);
rv = fwdBlock(mask_lo, mask_hi, chars, buf_end - 16, low4bits, zeroes);
if (rv) {
return rv;
}
return buf_end;
}
static really_inline
const u8 *lastMatch(const u8 *buf, m128 t, m128 compare) {
#ifdef DEBUG
DEBUG_PRINTF("confirming match in:"); dumpMsk128(t); printf("\n");
#endif
u32 z = movemask128(eq128(t, compare));
if (unlikely(z != 0xffff)) {
u32 pos = clz32(~z & 0xffff);
DEBUG_PRINTF("buf=%p, pos=%u\n", buf, pos);
assert(pos >= 16 && pos < 32);
return buf + (31 - pos);
} else {
return NULL; // no match
}
}
static really_inline
const u8 *revBlock(m128 mask_lo, m128 mask_hi, m128 chars, const u8 *buf,
const m128 low4bits, const m128 zeroes) {
m128 c_lo = pshufb(mask_lo, GET_LO_4(chars));
m128 c_hi = pshufb(mask_hi, GET_HI_4(chars));
m128 t = and128(c_lo, c_hi);
#ifdef DEBUG
DEBUG_PRINTF(" chars: "); dumpMsk128AsChars(chars); printf("\n");
DEBUG_PRINTF(" char: "); dumpMsk128(chars); printf("\n");
DEBUG_PRINTF(" c_lo: "); dumpMsk128(c_lo); printf("\n");
DEBUG_PRINTF(" c_hi: "); dumpMsk128(c_hi); printf("\n");
DEBUG_PRINTF(" t: "); dumpMsk128(t); printf("\n");
#endif
return lastMatch(buf, t, zeroes);
}
const u8 *rshuftiExec(m128 mask_lo, m128 mask_hi, const u8 *buf,
const u8 *buf_end) {
assert(buf && buf_end);
assert(buf < buf_end);
// Slow path for small cases.
if (buf_end - buf < 16) {
return shuftiRevSlow((const u8 *)&mask_lo, (const u8 *)&mask_hi,
buf, buf_end);
}
const m128 zeroes = zeroes128();
const m128 low4bits = _mm_set1_epi8(0xf);
const u8 *rv;
assert(buf_end - buf >= 16);
// Preconditioning: most of the time our buffer won't be aligned.
m128 chars = loadu128(buf_end - 16);
rv = revBlock(mask_lo, mask_hi, chars, buf_end - 16, low4bits, zeroes);
if (rv) {
return rv;
}
buf_end = (const u8 *)((size_t)buf_end & ~((size_t)0xf));
// Unrolling was here, but it wasn't doing anything but taking up space.
// Reroll FTW.
const u8 *last_block = buf + 16;
while (buf_end > last_block) {
buf_end -= 16;
m128 lchars = load128(buf_end);
rv = revBlock(mask_lo, mask_hi, lchars, buf_end, low4bits, zeroes);
if (rv) {
return rv;
}
}
// Use an unaligned load to mop up the last 16 bytes and get an accurate
// picture to buf.
chars = loadu128(buf);
rv = revBlock(mask_lo, mask_hi, chars, buf, low4bits, zeroes);
if (rv) {
return rv;
}
return buf - 1;
}
static really_inline
const u8 *fwdBlock2(m128 mask1_lo, m128 mask1_hi, m128 mask2_lo, m128 mask2_hi,
m128 chars, const u8 *buf, const m128 low4bits,
const m128 ones) {
m128 chars_lo = GET_LO_4(chars);
m128 chars_hi = GET_HI_4(chars);
m128 c_lo = pshufb(mask1_lo, chars_lo);
m128 c_hi = pshufb(mask1_hi, chars_hi);
m128 t = or128(c_lo, c_hi);
#ifdef DEBUG
DEBUG_PRINTF(" chars: "); dumpMsk128AsChars(chars); printf("\n");
DEBUG_PRINTF(" char: "); dumpMsk128(chars); printf("\n");
DEBUG_PRINTF(" c_lo: "); dumpMsk128(c_lo); printf("\n");
DEBUG_PRINTF(" c_hi: "); dumpMsk128(c_hi); printf("\n");
DEBUG_PRINTF(" t: "); dumpMsk128(t); printf("\n");
#endif
m128 c2_lo = pshufb(mask2_lo, chars_lo);
m128 c2_hi = pshufb(mask2_hi, chars_hi);
m128 t2 = or128(t, shiftRight8Bits(or128(c2_lo, c2_hi)));
#ifdef DEBUG
DEBUG_PRINTF(" c2_lo: "); dumpMsk128(c2_lo); printf("\n");
DEBUG_PRINTF(" c2_hi: "); dumpMsk128(c2_hi); printf("\n");
DEBUG_PRINTF(" t2: "); dumpMsk128(t2); printf("\n");
#endif
return firstMatch(buf, t2, ones);
}
const u8 *shuftiDoubleExec(m128 mask1_lo, m128 mask1_hi,
m128 mask2_lo, m128 mask2_hi,
const u8 *buf, const u8 *buf_end) {
const m128 ones = ones128();
const m128 low4bits = _mm_set1_epi8(0xf);
const u8 *rv;
size_t min = (size_t)buf % 16;
// Preconditioning: most of the time our buffer won't be aligned.
m128 chars = loadu128(buf);
rv = fwdBlock2(mask1_lo, mask1_hi, mask2_lo, mask2_hi,
chars, buf, low4bits, ones);
if (rv) {
return rv;
}
buf += (16 - min);
// Unrolling was here, but it wasn't doing anything but taking up space.
// Reroll FTW.
const u8 *last_block = buf_end - 16;
while (buf < last_block) {
m128 lchars = load128(buf);
rv = fwdBlock2(mask1_lo, mask1_hi, mask2_lo, mask2_hi,
lchars, buf, low4bits, ones);
if (rv) {
return rv;
}
buf += 16;
}
// Use an unaligned load to mop up the last 16 bytes and get an accurate
// picture to buf_end.
chars = loadu128(buf_end - 16);
rv = fwdBlock2(mask1_lo, mask1_hi, mask2_lo, mask2_hi,
chars, buf_end - 16, low4bits, ones);
if (rv) {
return rv;
}
return buf_end;
}
#else // AVX2 - 256 wide shuftis
#ifdef DEBUG
DUMP_MSK(256)
#endif
#define GET_LO_4(chars) and256(chars, low4bits)
#define GET_HI_4(chars) rshift4x64(andnot256(low4bits, chars), 4)
static really_inline
const u8 *firstMatch(const u8 *buf, m256 t, m256 compare) {
#ifdef DEBUG
DEBUG_PRINTF("confirming match in:"); dumpMsk256(t); printf("\n");
#endif
u32 z = movemask256(eq256(t, compare));
if (unlikely(z != 0xffffffff)) {
u32 pos = ctz32(~z);
assert(pos < 32);
return buf + pos;
} else {
return NULL; // no match
}
}
static really_inline
const u8 *fwdBlock(m256 mask_lo, m256 mask_hi, m256 chars, const u8 *buf,
const m256 low4bits, const m256 zeroes) {
m256 c_lo = vpshufb(mask_lo, GET_LO_4(chars));
m256 c_hi = vpshufb(mask_hi, GET_HI_4(chars));
m256 t = and256(c_lo, c_hi);
#ifdef DEBUG
DEBUG_PRINTF(" chars: "); dumpMsk256AsChars(chars); printf("\n");
DEBUG_PRINTF(" char: "); dumpMsk256(chars); printf("\n");
DEBUG_PRINTF(" c_lo: "); dumpMsk256(c_lo); printf("\n");
DEBUG_PRINTF(" c_hi: "); dumpMsk256(c_hi); printf("\n");
DEBUG_PRINTF(" t: "); dumpMsk256(t); printf("\n");
#endif
return firstMatch(buf, t, zeroes);
}
/* takes 128 bit masks, but operates on 256 bits of data */
const u8 *shuftiExec(m128 mask_lo, m128 mask_hi, const u8 *buf,
const u8 *buf_end) {
assert(buf && buf_end);
assert(buf < buf_end);
// Slow path for small cases.
if (buf_end - buf < 32) {
return shuftiFwdSlow((const u8 *)&mask_lo, (const u8 *)&mask_hi,
buf, buf_end);
}
const m256 zeroes = zeroes256();
const m256 low4bits = set32x8(0xf);
const m256 wide_mask_lo = set2x128(mask_lo);
const m256 wide_mask_hi = set2x128(mask_hi);
const u8 *rv;
size_t min = (size_t)buf % 32;
assert(buf_end - buf >= 32);
// Preconditioning: most of the time our buffer won't be aligned.
m256 chars = loadu256(buf);
rv = fwdBlock(wide_mask_lo, wide_mask_hi, chars, buf, low4bits, zeroes);
if (rv) {
return rv;
}
buf += (32 - min);
// Unrolling was here, but it wasn't doing anything but taking up space.
// Reroll FTW.
const u8 *last_block = buf_end - 32;
while (buf < last_block) {
m256 lchars = load256(buf);
rv = fwdBlock(wide_mask_lo, wide_mask_hi, lchars, buf, low4bits, zeroes);
if (rv) {
return rv;
}
buf += 32;
}
// Use an unaligned load to mop up the last 32 bytes and get an accurate
// picture to buf_end.
assert(buf <= buf_end && buf >= buf_end - 32);
chars = loadu256(buf_end - 32);
rv = fwdBlock(wide_mask_lo, wide_mask_hi, chars, buf_end - 32, low4bits, zeroes);
if (rv) {
return rv;
}
return buf_end;
}
static really_inline
const u8 *lastMatch(const u8 *buf, m256 t, m256 compare) {
#ifdef DEBUG
DEBUG_PRINTF("confirming match in:"); dumpMsk256(t); printf("\n");
#endif
u32 z = movemask256(eq256(t, compare));
if (unlikely(z != 0xffffffff)) {
u32 pos = clz32(~z);
DEBUG_PRINTF("buf=%p, pos=%u\n", buf, pos);
return buf + (31 - pos);
} else {
return NULL; // no match
}
}
static really_inline
const u8 *revBlock(m256 mask_lo, m256 mask_hi, m256 chars, const u8 *buf,
const m256 low4bits, const m256 zeroes) {
m256 c_lo = vpshufb(mask_lo, GET_LO_4(chars));
m256 c_hi = vpshufb(mask_hi, GET_HI_4(chars));
m256 t = and256(c_lo, c_hi);
#ifdef DEBUG
DEBUG_PRINTF(" chars: "); dumpMsk256AsChars(chars); printf("\n");
DEBUG_PRINTF(" char: "); dumpMsk256(chars); printf("\n");
DEBUG_PRINTF(" c_lo: "); dumpMsk256(c_lo); printf("\n");
DEBUG_PRINTF(" c_hi: "); dumpMsk256(c_hi); printf("\n");
DEBUG_PRINTF(" t: "); dumpMsk256(t); printf("\n");
#endif
return lastMatch(buf, t, zeroes);
}
/* takes 128 bit masks, but operates on 256 bits of data */
const u8 *rshuftiExec(m128 mask_lo, m128 mask_hi, const u8 *buf,
const u8 *buf_end) {
assert(buf && buf_end);
assert(buf < buf_end);
// Slow path for small cases.
if (buf_end - buf < 64) {
return shuftiRevSlow((const u8 *)&mask_lo, (const u8 *)&mask_hi,
buf, buf_end);
}
const m256 zeroes = zeroes256();
const m256 low4bits = set32x8(0xf);
const m256 wide_mask_lo = set2x128(mask_lo);
const m256 wide_mask_hi = set2x128(mask_hi);
const u8 *rv;
assert(buf_end - buf >= 32);
// Preconditioning: most of the time our buffer won't be aligned.
m256 chars = loadu256(buf_end - 32);
rv = revBlock(wide_mask_lo, wide_mask_hi, chars, buf_end - 32, low4bits, zeroes);
if (rv) {
return rv;
}
buf_end = (const u8 *)((size_t)buf_end & ~((size_t)0x1f));
// Unrolling was here, but it wasn't doing anything but taking up space.
// Reroll FTW.
const u8 *last_block = buf + 32;
while (buf_end > last_block) {
buf_end -= 32;
m256 lchars = load256(buf_end);
rv = revBlock(wide_mask_lo, wide_mask_hi, lchars, buf_end, low4bits, zeroes);
if (rv) {
return rv;
}
}
// Use an unaligned load to mop up the last 32 bytes and get an accurate
// picture to buf.
chars = loadu256(buf);
rv = revBlock(wide_mask_lo, wide_mask_hi, chars, buf, low4bits, zeroes);
if (rv) {
return rv;
}
return buf - 1;
}
static really_inline
const u8 *fwdBlock2(m256 mask1_lo, m256 mask1_hi, m256 mask2_lo, m256 mask2_hi,
m256 chars, const u8 *buf, const m256 low4bits,
const m256 ones) {
DEBUG_PRINTF("buf %p\n", buf);
m256 chars_lo = GET_LO_4(chars);
m256 chars_hi = GET_HI_4(chars);
m256 c_lo = vpshufb(mask1_lo, chars_lo);
m256 c_hi = vpshufb(mask1_hi, chars_hi);
m256 t = or256(c_lo, c_hi);
#ifdef DEBUG
DEBUG_PRINTF(" chars: "); dumpMsk256AsChars(chars); printf("\n");
DEBUG_PRINTF(" char: "); dumpMsk256(chars); printf("\n");
DEBUG_PRINTF(" c_lo: "); dumpMsk256(c_lo); printf("\n");
DEBUG_PRINTF(" c_hi: "); dumpMsk256(c_hi); printf("\n");
DEBUG_PRINTF(" t: "); dumpMsk256(t); printf("\n");
#endif
m256 c2_lo = vpshufb(mask2_lo, chars_lo);
m256 c2_hi = vpshufb(mask2_hi, chars_hi);
m256 t2 = or256(t, shift256Right8Bits(or256(c2_lo, c2_hi)));
#ifdef DEBUG
DEBUG_PRINTF(" c2_lo: "); dumpMsk256(c2_lo); printf("\n");
DEBUG_PRINTF(" c2_hi: "); dumpMsk256(c2_hi); printf("\n");
DEBUG_PRINTF(" t2: "); dumpMsk256(t2); printf("\n");
#endif
return firstMatch(buf, t2, ones);
}
/* takes 128 bit masks, but operates on 256 bits of data */
const u8 *shuftiDoubleExec(m128 mask1_lo, m128 mask1_hi,
m128 mask2_lo, m128 mask2_hi,
const u8 *buf, const u8 *buf_end) {
if (buf_end - buf < 32) {
// not worth it
return buf;
}
const m256 ones = ones256();
const m256 low4bits = set32x8(0xf);
const m256 wide_mask1_lo = set2x128(mask1_lo);
const m256 wide_mask1_hi = set2x128(mask1_hi);
const m256 wide_mask2_lo = set2x128(mask2_lo);
const m256 wide_mask2_hi = set2x128(mask2_hi);
const u8 *rv;
size_t min = (size_t)buf % 32;
// Preconditioning: most of the time our buffer won't be aligned.
m256 chars = loadu256(buf);
rv = fwdBlock2(wide_mask1_lo, wide_mask1_hi, wide_mask2_lo, wide_mask2_hi,
chars, buf, low4bits, ones);
if (rv) {
return rv;
}
buf += (32 - min);
// Unrolling was here, but it wasn't doing anything but taking up space.
// Reroll FTW.
const u8 *last_block = buf_end - 32;
while (buf < last_block) {
m256 lchars = load256(buf);
rv = fwdBlock2(wide_mask1_lo, wide_mask1_hi, wide_mask2_lo, wide_mask2_hi,
lchars, buf, low4bits, ones);
if (rv) {
return rv;
}
buf += 32;
}
// Use an unaligned load to mop up the last 32 bytes and get an accurate
// picture to buf_end.
chars = loadu256(buf_end - 32);
rv = fwdBlock2(wide_mask1_lo, wide_mask1_hi, wide_mask2_lo, wide_mask2_hi,
chars, buf_end - 32, low4bits, ones);
if (rv) {
return rv;
}
return buf_end;
}
#endif //AVX2

61
src/nfa/shufti.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2015, 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 Shufti: character class acceleration.
*
* Utilises the SSSE3 pshufb shuffle instruction
*/
#ifndef SHUFTI_H
#define SHUFTI_H
#include "ue2common.h"
#include "util/simd_utils.h"
#ifdef __cplusplus
extern "C"
{
#endif
const u8 *shuftiExec(m128 mask_lo, m128 mask_hi, const u8 *buf,
const u8 *buf_end);
// Returns (buf - 1) if not found.
const u8 *rshuftiExec(m128 mask_lo, m128 mask_hi, const u8 *buf,
const u8 *buf_end);
const u8 *shuftiDoubleExec(m128 mask1_lo, m128 mask1_hi,
m128 mask2_lo, m128 mask2_hi,
const u8 *buf, const u8 *buf_end);
#ifdef __cplusplus
}
#endif
#endif

191
src/nfa/shufticompile.cpp Normal file
View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2015, 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 Shufti acceleration: compile code.
*/
#include "shufticompile.h"
#include "ue2common.h"
#include "util/charreach.h"
#include "util/ue2_containers.h"
#include <array>
#include <cassert>
#include <cstring>
#include <map>
using namespace std;
namespace ue2 {
/** \brief Single-byte variant.
*
* Returns -1 if unable to construct masks, otherwise returns number of bits
* used in the mask.
*
* Note: always able to construct masks for 8 or fewer characters.
*/
int shuftiBuildMasks(const CharReach &c, m128 *lo, m128 *hi) {
/* Things could be packed much more optimally, but this should be able to
* handle any set of characters entirely in the lower half. */
assert(c.count() < 256);
assert(!c.none());
map<u8, CharReach> by_hi; /* hi nibble -> set of matching lo nibbles */
/* group matching characters by high nibble */
for (size_t i = c.find_first(); i != CharReach::npos; i = c.find_next(i)) {
u8 it_hi = i >> 4;
u8 it_lo = i & 0xf;
by_hi[it_hi].set(it_lo);
}
map<CharReach, CharReach> by_lo_set;
/* group all hi nibbles with a common set of lo nibbles together */
for (map<u8, CharReach>::const_iterator it = by_hi.begin();
it != by_hi.end(); ++it) {
by_lo_set[it->second].set(it->first);
}
if (by_lo_set.size() > 8) {
/* too many char classes on the dance floor */
assert(c.size() > 8);
return -1;
}
u8 bit_index = 0;
array<u8, 16> lo_a; lo_a.fill(0);
array<u8, 16> hi_a; hi_a.fill(0);
for (map<CharReach, CharReach>::const_iterator it = by_lo_set.begin();
it != by_lo_set.end(); ++it) {
const CharReach &lo_nibbles = it->first;
const CharReach &hi_nibbles = it->second;
/* set bits in low mask */
for (size_t j = lo_nibbles.find_first(); j != CharReach::npos;
j = lo_nibbles.find_next(j)) {
lo_a[j] |= (1 << bit_index);
}
/* set bits in high mask */
for (size_t j = hi_nibbles.find_first(); j != CharReach::npos;
j = hi_nibbles.find_next(j)) {
hi_a[j] |= (1 << bit_index);
}
bit_index++;
}
memcpy(lo, lo_a.data(), sizeof(m128));
memcpy(hi, hi_a.data(), sizeof(m128));
return bit_index;
}
void shuftiBuildDoubleMasks(const CharReach &onechar,
const flat_set<pair<u8, u8>> &twochar,
m128 *lo1, m128 *hi1, m128 *lo2, m128 *hi2) {
DEBUG_PRINTF("unibytes %zu dibytes %zu\n", onechar.size(),
twochar.size());
assert(onechar.count() + twochar.size() <= 8);
array<u8, 16> lo1_a;
array<u8, 16> lo2_a;
array<u8, 16> hi1_a;
array<u8, 16> hi2_a;
lo1_a.fill(0xff);
lo2_a.fill(0xff);
hi1_a.fill(0xff);
hi2_a.fill(0xff);
u32 i = 0;
// two-byte literals
for (flat_set<pair<u8, u8>>::const_iterator it = twochar.begin();
it != twochar.end(); ++it, i++) {
DEBUG_PRINTF("%u: %02hhx %02hhx\n", i, it->first, it->second);
u8 b1 = it->first & 0xf;
u8 t1 = it->first >> 4;
u8 b2 = it->second & 0xf;
u8 t2 = it->second >> 4;
lo1_a[b1] &= ~(1 << i);
hi1_a[t1] &= ~(1 << i);
lo2_a[b2] &= ~(1 << i);
hi2_a[t2] &= ~(1 << i);
}
// one-byte literals (second byte is a wildcard)
for (size_t it = onechar.find_first(); it != CharReach::npos;
it = onechar.find_next(it), i++) {
DEBUG_PRINTF("%u: %02hhx\n", i, (u8)it);
u8 b1 = it & 0xf;
u8 t1 = it >> 4;
lo1_a[b1] &= ~(1 << i);
hi1_a[t1] &= ~(1 << i);
for (int j = 0; j < 16; j++) {
lo2_a[j] &= ~(1 << i);
hi2_a[j] &= ~(1 << i);
}
}
memcpy(lo1, lo1_a.data(), sizeof(m128));
memcpy(lo2, lo2_a.data(), sizeof(m128));
memcpy(hi1, hi1_a.data(), sizeof(m128));
memcpy(hi2, hi2_a.data(), sizeof(m128));
}
void mergeShuftiMask(m128 *lo, const m128 lo_in, u32 lo_bits) {
assert(lo_bits <= 8);
const u8 *lo_in_p = (const u8 *)&lo_in;
u8 *lo_p = (u8 *)lo;
for (u32 i = 0; i < 16; i++) {
lo_p[i] |= lo_in_p[i] << lo_bits;
}
}
#ifdef DUMP_SUPPORT
CharReach shufti2cr(const m128 lo_in, const m128 hi_in) {
const u8 *lo = (const u8 *)&lo_in;
const u8 *hi = (const u8 *)&hi_in;
CharReach cr;
for (u32 i = 0; i < 256; i++) {
if (lo[(u8)i & 0xf] & hi[(u8)i >> 4]) {
cr.set(i);
}
}
return cr;
}
#endif // DUMP_SUPPORT
} // namespace ue2

71
src/nfa/shufticompile.h Normal file
View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2015, 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 Shufti acceleration: compile code.
*/
#ifndef SHUFTI_COMPILE_H
#define SHUFTI_COMPILE_H
#include "ue2common.h"
#include "util/charreach.h"
#include "util/ue2_containers.h"
#include <utility>
namespace ue2 {
/** \brief Single-byte variant.
*
* Returns -1 if unable to construct masks, otherwise returns number of bits
* used in the mask.
*
* Note: always able to construct masks for 8 or fewer characters.
*/
int shuftiBuildMasks(const CharReach &chars, m128 *lo, m128 *hi);
void shuftiBuildDoubleMasks(const CharReach &onechar,
const flat_set<std::pair<u8, u8>> &twochar,
m128 *lo1, m128 *hi1, m128 *lo2, m128 *hi2);
void mergeShuftiMask(m128 *lo, const m128 lo_in, u32 lo_bits);
#ifdef DUMP_SUPPORT
/**
* \brief Dump code: returns a CharReach with the reach that would match this
* shufti.
*/
CharReach shufti2cr(const m128 lo, const m128 hi);
#endif // DUMP_SUPPORT
} // namespace ue2
#endif // SHUFTI_COMPILE_H

236
src/nfa/truffle.c Normal file
View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2015, 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.
*/
/*
* Matches a byte in a charclass using three shuffles
*/
#include "ue2common.h"
#include "truffle.h"
#include "util/bitutils.h"
#include "util/simd_utils.h"
#include "util/simd_utils_ssse3.h"
#define shift128r(a, b) _mm_srli_epi64((a), (b))
static really_inline
const u8 *firstMatch(const u8 *buf, u32 z) {
if (unlikely(z != 0xffff)) {
u32 pos = ctz32(~z & 0xffff);
assert(pos < 16);
return buf + pos;
}
return NULL; // no match
}
static really_inline
const u8 *lastMatch(const u8 *buf, u32 z) {
if (unlikely(z != 0xffff)) {
u32 pos = clz32(~z & 0xffff);
assert(pos >= 16 && pos < 32);
return buf + (31 - pos);
}
return NULL; // no match
}
static really_inline
u32 block(m128 shuf_mask_lo_highclear, m128 shuf_mask_lo_highset, m128 v) {
m128 highconst = _mm_set1_epi8(0x80);
m128 shuf_mask_hi = _mm_set1_epi64x(0x8040201008040201);
// and now do the real work
m128 shuf1 = pshufb(shuf_mask_lo_highclear, v);
m128 t1 = xor128(v, highconst);
m128 shuf2 = pshufb(shuf_mask_lo_highset, t1);
m128 t2 = andnot128(highconst, shift128r(v, 4));
m128 shuf3 = pshufb(shuf_mask_hi, t2);
m128 tmp = and128(or128(shuf1, shuf2), shuf3);
m128 tmp2 = eq128(tmp, zeroes128());
u32 z = movemask128(tmp2);
return z;
}
static really_inline
const u8 *fwdBlock(m128 shuf_mask_lo_highclear, m128 shuf_mask_lo_highset,
m128 v, const u8 *buf) {
u32 z = block(shuf_mask_lo_highclear, shuf_mask_lo_highset, v);
return firstMatch(buf, z);
}
static really_inline
const u8 *revBlock(m128 shuf_mask_lo_highclear, m128 shuf_mask_lo_highset,
m128 v, const u8 *buf) {
u32 z = block(shuf_mask_lo_highclear, shuf_mask_lo_highset, v);
return lastMatch(buf, z);
}
static
const u8 *truffleMini(m128 shuf_mask_lo_highclear, m128 shuf_mask_lo_highset,
const u8 *buf, const u8 *buf_end) {
uintptr_t len = buf_end - buf;
assert(len < 16);
m128 chars = zeroes128();
memcpy(&chars, buf, len);
u32 z = block(shuf_mask_lo_highclear, shuf_mask_lo_highset, chars);
// can't be these bytes in z
u32 mask = (0xFFFF >> (16 - len)) ^ 0xFFFF;
const u8 *rv = firstMatch(buf, z| mask);
if (rv) {
return rv;
} else {
return buf_end;
}
}
const u8 *truffleExec(m128 shuf_mask_lo_highclear,
m128 shuf_mask_lo_highset,
const u8 *buf, const u8 *buf_end) {
DEBUG_PRINTF("len %zu\n", buf_end - buf);
assert(buf && buf_end);
assert(buf < buf_end);
const u8 *rv;
if (buf_end - buf < 16) {
return truffleMini(shuf_mask_lo_highclear, shuf_mask_lo_highset, buf,
buf_end);
}
size_t min = (size_t)buf % 16;
assert(buf_end - buf >= 16);
// Preconditioning: most of the time our buffer won't be aligned.
m128 chars = loadu128(buf);
rv = fwdBlock(shuf_mask_lo_highclear, shuf_mask_lo_highset, chars, buf);
if (rv) {
return rv;
}
buf += (16 - min);
const u8 *last_block = buf_end - 16;
while (buf < last_block) {
m128 lchars = load128(buf);
rv = fwdBlock(shuf_mask_lo_highclear, shuf_mask_lo_highset, lchars,
buf);
if (rv) {
return rv;
}
buf += 16;
}
// Use an unaligned load to mop up the last 16 bytes and get an accurate
// picture to buf_end.
assert(buf <= buf_end && buf >= buf_end - 16);
chars = loadu128(buf_end - 16);
rv = fwdBlock(shuf_mask_lo_highclear, shuf_mask_lo_highset, chars,
buf_end - 16);
if (rv) {
return rv;
}
return buf_end;
}
static
const u8 *truffleRevMini(m128 shuf_mask_lo_highclear,
m128 shuf_mask_lo_highset, const u8 *buf,
const u8 *buf_end) {
uintptr_t len = buf_end - buf;
assert(len < 16);
m128 chars = zeroes128();
memcpy(&chars, buf, len);
u32 mask = (0xFFFF >> (16 - len)) ^ 0xFFFF;
u32 z = block(shuf_mask_lo_highclear, shuf_mask_lo_highset, chars);
const u8 *rv = lastMatch(buf, z | mask);
if (rv) {
return rv;
}
return buf - 1;
}
const u8 *rtruffleExec(m128 shuf_mask_lo_highclear,
m128 shuf_mask_lo_highset,
const u8 *buf, const u8 *buf_end) {
assert(buf && buf_end);
assert(buf < buf_end);
const u8 *rv;
DEBUG_PRINTF("len %zu\n", buf_end - buf);
if (buf_end - buf < 16) {
return truffleRevMini(shuf_mask_lo_highclear, shuf_mask_lo_highset, buf,
buf_end);
}
assert(buf_end - buf >= 16);
// Preconditioning: most of the time our buffer won't be aligned.
m128 chars = loadu128(buf_end - 16);
rv = revBlock(shuf_mask_lo_highclear, shuf_mask_lo_highset, chars,
buf_end - 16);
if (rv) {
return rv;
}
buf_end = (const u8 *)((size_t)buf_end & ~((size_t)0xf));
const u8 *last_block = buf + 16;
while (buf_end > last_block) {
buf_end -= 16;
m128 lchars = load128(buf_end);
rv = revBlock(shuf_mask_lo_highclear, shuf_mask_lo_highset, lchars,
buf_end);
if (rv) {
return rv;
}
}
// Use an unaligned load to mop up the last 16 bytes and get an accurate
// picture to buf_end.
chars = loadu128(buf);
rv = revBlock(shuf_mask_lo_highclear, shuf_mask_lo_highset, chars, buf);
if (rv) {
return rv;
}
return buf - 1;
}

49
src/nfa/truffle.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 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.
*/
#ifndef TRUFFLE_H
#define TRUFFLE_H
#include "util/simd_types.h"
#ifdef __cplusplus
extern "C"
{
#endif
const u8 *truffleExec(m128 shuf_mask_lo_highclear, m128 shuf_mask_lo_highset,
const u8 *buf, const u8 *buf_end);
const u8 *rtruffleExec(m128 shuf_mask_lo_highclear, m128 shuf_mask_lo_highset,
const u8 *buf, const u8 *buf_end);
#ifdef __cplusplus
}
#endif
#endif /* TRUFFLE_H */

Some files were not shown because too many files have changed in this diff Show More