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

121
unit/CMakeLists.txt Normal file
View File

@@ -0,0 +1,121 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXX_FLAGS}")
set(gtest_SOURCES gtest/gtest-all.cc gtest/gtest.h)
if(NOT XCODE)
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR})
else()
set(CMAKE_CXX_FLAGS "-isystem ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CXX_FLAGS}")
endif()
include_directories(${CMAKE_SOURCE_DIR}/util)
# remove some warnings
# cmake's scope means these only apply here
if (CXX_MISSING_DECLARATIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-declarations")
endif()
if(CXX_WEAK_VTABLES)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-weak-vtables")
endif()
if(CXX_WUNUSED_VARIABLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable")
endif()
add_library(gtest ${gtest_SOURCES})
add_definitions(-DGTEST_HAS_PTHREAD=0 -DSRCDIR=${CMAKE_SOURCE_DIR})
if (NOT RELEASE_BUILD)
set(unit_internal_SOURCES
internal/bitfield.cpp
internal/bitutils.cpp
internal/charreach.cpp
internal/compare.cpp
internal/database.cpp
internal/depth.cpp
internal/fdr.cpp
internal/fdr_flood.cpp
internal/fdr_loadval.cpp
internal/flat_set.cpp
internal/flat_map.cpp
internal/graph.cpp
internal/lbr.cpp
internal/limex_nfa.cpp
internal/masked_move.cpp
internal/multi_bit.cpp
internal/nfagraph_common.h
internal/nfagraph_comp.cpp
internal/nfagraph_equivalence.cpp
internal/nfagraph_find_matches.cpp
internal/nfagraph_literal_analysis.cpp
internal/nfagraph_redundancy.cpp
internal/nfagraph_repeat.cpp
internal/nfagraph_util.cpp
internal/nfagraph_width.cpp
internal/noodle.cpp
internal/pack_bits.cpp
internal/parser.cpp
internal/partial.cpp
internal/pqueue.cpp
internal/repeat.cpp
internal/rose_build_merge.cpp
internal/rvermicelli.cpp
internal/sidecar.cpp
internal/simd_utils.cpp
internal/shuffle.cpp
internal/shufti.cpp
internal/state_compress.cpp
internal/truffle.cpp
internal/unaligned.cpp
internal/unicode_set.cpp
internal/uniform_ops.cpp
internal/utf8_validate.cpp
internal/util_string.cpp
internal/vermicelli.cpp
internal/main.cpp
)
add_executable(unit-internal ${unit_internal_SOURCES})
target_link_libraries(unit-internal hs gtest corpusomatic)
endif(NOT RELEASE_BUILD)
set(unit_hyperscan_SOURCES
hyperscan/allocators.cpp
hyperscan/arg_checks.cpp
hyperscan/bad_patterns.cpp
hyperscan/bad_patterns.txt
hyperscan/behaviour.cpp
hyperscan/expr_info.cpp
hyperscan/extparam.cpp
hyperscan/identical.cpp
hyperscan/main.cpp
hyperscan/multi.cpp
hyperscan/order.cpp
hyperscan/scratch_op.cpp
hyperscan/serialize.cpp
hyperscan/single.cpp
hyperscan/som.cpp
hyperscan/stream_op.cpp
hyperscan/test_util.cpp
hyperscan/test_util.h
)
add_executable(unit-hyperscan ${unit_hyperscan_SOURCES})
if (BUILD_STATIC_AND_SHARED OR BUILD_SHARED_LIBS)
target_link_libraries(unit-hyperscan hs_shared gtest expressionutil)
else()
target_link_libraries(unit-hyperscan hs gtest expressionutil)
endif()
#
# build target to run unit tests
#
add_custom_target(
unit
COMMAND bin/unit-internal
COMMAND bin/unit-hyperscan
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS unit-internal unit-hyperscan
)

9592
unit/gtest/gtest-all.cc Normal file

File diff suppressed because it is too large Load Diff

20061
unit/gtest/gtest.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,151 @@
/*
* 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 "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
#include <cstdlib>
static void *null_malloc(size_t) { return nullptr; }
TEST(CustomAllocator, DatabaseInfoBadAlloc) {
hs_database_t *db = buildDB("foobar", 0, 0, HS_MODE_BLOCK);
ASSERT_TRUE(db != nullptr);
hs_set_allocator(null_malloc, nullptr);
char *info = nullptr;
hs_error_t err = hs_database_info(db, &info);
ASSERT_EQ(HS_NOMEM, err);
hs_set_allocator(nullptr, nullptr);
hs_free_database(db);
}
static
void * two_aligned_malloc(size_t len) {
void *mem = malloc(len + 2);
if (!mem) {
return nullptr;
}
return (char *)mem + 2;
}
static
void two_aligned_free(void *mem) {
if (!mem) {
return;
}
// Allocated with two_aligned_malloc above.
free((char *)mem - 2);
}
TEST(CustomAllocator, TwoAlignedCompile) {
hs_set_database_allocator(two_aligned_malloc, two_aligned_free);
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
const hs_platform_info_t *platform = nullptr;
hs_error_t err =
hs_compile("foobar", 0, HS_MODE_BLOCK, platform, &db, &compile_err);
ASSERT_EQ(HS_COMPILER_ERROR, err);
ASSERT_EQ(nullptr, db);
ASSERT_NE(nullptr, compile_err);
hs_free_compile_error(compile_err);
hs_set_database_allocator(nullptr, nullptr);
}
TEST(CustomAllocator, TwoAlignedDatabaseInfo) {
hs_database_t *db = buildDB("foobar", 0, 0, HS_MODE_BLOCK);
ASSERT_TRUE(db != nullptr);
hs_set_misc_allocator(two_aligned_malloc, two_aligned_free);
char *info = nullptr;
hs_error_t err = hs_database_info(db, &info);
ASSERT_EQ(HS_BAD_ALLOC, err);
hs_set_misc_allocator(nullptr, nullptr);
hs_free_database(db);
}
TEST(CustomAllocator, TwoAlignedSerialize) {
hs_database_t *db = buildDB("foobar", 0, 0, HS_MODE_BLOCK);
ASSERT_TRUE(db != nullptr);
hs_set_misc_allocator(two_aligned_malloc, two_aligned_free);
char *bytes = nullptr;
size_t serialized_len = 0;
hs_error_t err = hs_serialize_database(db, &bytes, &serialized_len);
ASSERT_EQ(HS_BAD_ALLOC, err);
hs_set_misc_allocator(nullptr, nullptr);
hs_free_database(db);
}
TEST(CustomAllocator, TwoAlignedDeserialize) {
hs_database_t *db = buildDB("foobar", 0, 0, HS_MODE_BLOCK);
ASSERT_TRUE(db != nullptr);
char *bytes = nullptr;
size_t serialized_len = 0;
hs_error_t err = hs_serialize_database(db, &bytes, &serialized_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, bytes);
hs_free_database(db);
db = nullptr;
hs_set_database_allocator(two_aligned_malloc, two_aligned_free);
err = hs_deserialize_database(bytes, serialized_len, &db);
ASSERT_EQ(HS_BAD_ALLOC, err);
ASSERT_EQ(nullptr, db);
hs_set_database_allocator(nullptr, nullptr);
free(bytes);
}
TEST(CustomAllocator, TwoAlignedAllocScratch) {
hs_database_t *db = buildDB("foobar", 0, 0, HS_MODE_BLOCK);
ASSERT_TRUE(db != nullptr);
hs_set_scratch_allocator(two_aligned_malloc, two_aligned_free);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_BAD_ALLOC, err);
hs_set_scratch_allocator(nullptr, nullptr);
hs_free_database(db);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,402 @@
/*
* 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 "gtest/gtest.h"
#include "hs.h"
#include <fstream>
#include <string>
#include <boost/algorithm/string/trim.hpp>
#include "util/expressions.h"
#include "util/ExpressionParser.h"
using namespace std;
using namespace testing;
class HyperscanFailBadPattern
: public TestWithParam<tuple<bool, const char*>> {
protected:
virtual void SetUp() {
// compiles a database for this test instantiation
bool isStreaming;
tie(isStreaming, pattern) = GetParam();
err = hs_compile(pattern, 0,
isStreaming ? HS_MODE_STREAM : HS_MODE_BLOCK, nullptr,
&db, &compile_err);
}
virtual void TearDown() {
hs_free_database(db);
hs_free_compile_error(compile_err);
}
hs_error_t err;
const char *pattern;
hs_compile_error_t *compile_err;
hs_database_t *db;
};
static const char *badPatterns[] = {
// vacuous patterns
"",
"a?",
"a*",
"(foo)?",
"(foo)*(bar)*",
"^arg|(foo)*(bar)*",
"foo|bar|",
"a*(b?)*c*",
"a{0,3}",
"[\\s\\S]*",
"[^\\s\\S]*",
// too big for our largest NFA
"(ewh|m?uit|f|snmv.g.gx[yofl]|.[^g][hbd])((.h|((y|vypfw|dfg{4}|x+|o.|y{8,}))+|k{9}t|cgp...gsk+)){17,}",
// illegal bounds
"fooa{0}",
"a{4,3}",
"a{2,1}",
// nothing to repeat
"a++",
"a+?+",
"a??",
"a?+",
"?qa",
"*abc",
"+abc",
// repeating boundaries is not allowed (UE-1007)
"^?0",
"^*0",
"^+0",
"^{1,3}0",
"0$?",
"0$*",
"0$+",
"0${1,3}",
// zero width asserts ("lookarounds")
"[a-z]+(?=;)", // positive lookahead
".((?!\\x00\\x00)..)*?foo", // negative lookahead
"(?<=bullock|donkey)", // positive lookbehind
"(?<!foo)bar", // negative lookbehind
// embedded anchors
"foo^bar",
"foo$bar",
"(foo^bar|other)",
"(foo$bar|other)",
"$test",
"test^",
"foo(a|^)bar",
"a$^b",
"(^foo)+bar",
"foo(bar$)+",
"a^{3}=",
// atomic groups
"foobar(?>.{3,})bar",
// possessive quantifiers
"\\d++foo",
"(abc|xyz){2,3}+",
// back-reference inside a repeat (also too big, actually)
"^..\x02.{10,522}([^\00])\1{16}",
// char classes
"[]",
"[]foobar",
"[`-\\80",
"[A-\\K]",
// bad named classes
"[[:foo:]]",
"[[:1234:]]",
"[[:f\\oo:]]",
"[[: :]]",
"[[:...:]]",
"[[:l\\ower:]]",
"[[:abc\\:]]",
"[abc[:x\\]pqr:]]",
"[[:a\\dz:]]",
// unhandled subroutines and backrefs
"foo\\g''bar",
"foo\\g'45'bar",
"foo\\g'hatstand'bar",
"foo\\g<>bar",
"foo\\g<45>bar",
"foo\\g<teakettle>bar",
"((?i)rah)\\s+\\1",
"(?<p1>(?i)rah)\\s+\\k<p1>",
"(?'p1'(?i)rah)\\s+\\k{p1}",
"(?P<p1>(?i)rah)\\s+(?P=p1)",
"(?<p1>(?i)rah)\\s+\\g{p1}",
// truly enormous and with complicated assert resolution (UE-1107)
"((c(p|p)h{2,}bh.|p|((((cq|j|c|(\\b)|.[^nbgn]|(\\B)[qfh]a)){10,12}|ih|a|mnde[pa].|.g)){5,8})){21,29}",
// conditional subpatterns
"(?(?=[^a-z]*[a-z])\\d{2}-[a-z]{3}-\\d{2}|\\d{2}-\\d{2}-\\d{2)}",
// unmatched parens
"(foo",
"foo)",
"((foo)",
"(foo))",
// unterminated comment
"/foo(?#comment/",
// bogus \g backrefs
"A\\g",
"A(.*)\\ga",
// malformed \g backrefs (see UE-950)
"^(a)\\g",
"^(a)\\g{3",
"\\g{A",
"[\\g6666666666]",
"(^(a|b\\g<-1'c))",
// oniguruma subroutine calls (UE-950 as well)
"^(?<name>a|b\\g'name'c)",
"^(a|b\\g'1'c)",
"^(a|b\\g'-1'c)",
// backtracking control verbs
"A((?:A|B(*ACCEPT)|C)D)",
"(*FAIL)",
"(*F)",
"a+(*COMMIT)b",
"(*PRUNE)",
"a+(*SKIP)b",
// other unsupported PCRE features
"\\R",
"foo\\Kbar",
"\\Gfoo",
"(?|(Sat)ur|(Sun))day", // duplicate subpatterns, see UE-958
"foobar\\", // trailing unescaped backslash
"(?x)abc(#i)def" // unterminated extended-mode comment
};
// Did we correctly fail the compile?
TEST_P(HyperscanFailBadPattern, Compile) {
ASSERT_NE(HS_SUCCESS, err) << "Compile should have failed for expr: " << pattern;
EXPECT_EQ(HS_COMPILER_ERROR, err);
EXPECT_TRUE(db == nullptr);
EXPECT_TRUE(compile_err != nullptr);
// We shouldn't fail with the following messagess
EXPECT_STRNE("An invalid flag was specified.", compile_err->message);
EXPECT_STRNE("Unable to allocate memory.", compile_err->message);
EXPECT_STRNE("Internal error.", compile_err->message);
EXPECT_STRNE("Match can be raised on EOD", compile_err->message);
}
INSTANTIATE_TEST_CASE_P(CompileBadPatterns,
HyperscanFailBadPattern,
Combine(Bool(), ValuesIn(badPatterns)));
struct BadPatternParam {
BadPatternParam(const string &expr_in, unsigned int flags_in,
const hs_expr_ext &ext_in,
const string &expected_error_in)
: expr(expr_in), flags(flags_in), ext(ext_in),
expected_error(expected_error_in) {}
string expr;
unsigned int flags;
hs_expr_ext ext;
string expected_error;
// Wrap hs_compile_ext_multi for single patterns.
hs_error_t compile(unsigned int mode, hs_database_t **db,
hs_compile_error_t **compile_err) const {
const char *regex = expr.c_str();
const hs_expr_ext *extp = &ext;
return hs_compile_ext_multi(&regex, &flags, nullptr, &extp, 1,
mode, nullptr, db, compile_err);
}
};
void PrintTo(const BadPatternParam &p, ::std::ostream *os) {
*os << "expr: \"" << p.expr << "\", expected error: " << p.expected_error;
}
class BadPattern : public TestWithParam<BadPatternParam> {
};
TEST_P(BadPattern, Block) {
const BadPatternParam &p = GetParam();
SCOPED_TRACE(p.expr);
hs_compile_error_t *compile_err;
hs_database_t *db;
hs_error_t err = p.compile(HS_MODE_NOSTREAM, &db, &compile_err);
EXPECT_EQ(HS_COMPILER_ERROR, err);
EXPECT_TRUE(db == nullptr);
EXPECT_TRUE(compile_err != nullptr);
if (compile_err) {
EXPECT_STREQ(p.expected_error.c_str(), compile_err->message);
}
hs_free_database(db);
hs_free_compile_error(compile_err);
}
TEST_P(BadPattern, Stream) {
const BadPatternParam &p = GetParam();
SCOPED_TRACE(p.expr);
hs_compile_error_t *compile_err;
hs_database_t *db;
hs_error_t err = p.compile(HS_MODE_STREAM | HS_MODE_SOM_HORIZON_LARGE, &db,
&compile_err);
EXPECT_EQ(HS_COMPILER_ERROR, err);
EXPECT_TRUE(db == nullptr);
EXPECT_TRUE(compile_err != nullptr);
if (compile_err) {
EXPECT_STREQ(p.expected_error.c_str(), compile_err->message);
}
hs_free_database(db);
hs_free_compile_error(compile_err);
}
// happy fun preprocessor hoop jumping
#define xstr(s) str(s)
#define str(s) #s
#define SRCDIR_PREFIX xstr(SRCDIR)
static
vector<BadPatternParam> getBadPatterns() {
string filename = "unit/hyperscan/bad_patterns.txt";
ifstream f;
f.open(filename.c_str(), ifstream::in);
if (!f.good()) {
// try it with the src prefix
f.open((string(SRCDIR_PREFIX) + "/" + filename).c_str(), ifstream::in);
}
vector<BadPatternParam> rv;
if (!f.good()) {
string expr("couldn't find input file:" + filename);
cerr << expr << endl;
abort();
return rv;
}
string line;
while (f.good()) {
getline(f, line);
if (line.empty()) {
continue;
}
size_t hashIdx = line.find_first_of('#');
size_t colonIdx = line.find_first_of(':');
assert(hashIdx != string::npos);
assert(colonIdx != string::npos);
if (!hashIdx) {
continue;
}
string error = line.substr(hashIdx + 1);
string expr = line.substr(colonIdx + 1, hashIdx - colonIdx - 1);
boost::trim(expr);
unsigned int flags;
string regex;
hs_expr_ext ext;
if (!readExpression(expr, regex, &flags, &ext)) {
cerr << expr << " failed in readExpression" << endl;
abort();
}
rv.push_back(BadPatternParam(regex, flags, ext, error));
}
f.close();
return rv;
}
INSTANTIATE_TEST_CASE_P(Hyperscan, BadPattern, ValuesIn(getBadPatterns()));
TEST(ResourceLimits, longPattern) {
#define LONG_PATTERN_LEN 16384
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
char pattern[LONG_PATTERN_LEN];
memset(pattern, 'a', LONG_PATTERN_LEN);
pattern[LONG_PATTERN_LEN - 1] = 0;
hs_error_t err = hs_compile(pattern, HS_FLAG_DOTALL, HS_MODE_BLOCK, nullptr,
&db, &compile_err);
EXPECT_EQ(HS_COMPILER_ERROR, err);
EXPECT_TRUE(db == nullptr);
EXPECT_TRUE(compile_err != nullptr);
if (compile_err) {
EXPECT_STREQ("Pattern length exceeds limit.", compile_err->message);
}
hs_free_database(db);
hs_free_compile_error(compile_err);
}
TEST(ResourceLimits, longPatternInfo) {
#define LONG_PATTERN_LEN 16384
hs_expr_info_t *info = nullptr;
hs_compile_error_t *compile_err = nullptr;
char pattern[LONG_PATTERN_LEN];
memset(pattern, 'a', LONG_PATTERN_LEN);
pattern[LONG_PATTERN_LEN - 1] = 0;
hs_error_t err =
hs_expression_info(pattern, HS_FLAG_DOTALL, &info, &compile_err);
EXPECT_EQ(HS_COMPILER_ERROR, err);
EXPECT_TRUE(compile_err != nullptr);
if (compile_err) {
EXPECT_STREQ("Pattern length exceeds limit.", compile_err->message);
}
free(info);
hs_free_compile_error(compile_err);
}
TEST(ResourceLimits, longLiteral) {
#define LONG_PATTERN_LEN 16384
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
const char *pattern = "(abcd){4096}";
hs_error_t err = hs_compile(pattern, HS_FLAG_DOTALL, HS_MODE_BLOCK, nullptr,
&db, &compile_err);
EXPECT_EQ(HS_COMPILER_ERROR, err);
EXPECT_TRUE(db == nullptr);
EXPECT_TRUE(compile_err != nullptr);
if (compile_err) {
EXPECT_STREQ("Resource limit exceeded.", compile_err->message);
}
hs_free_database(db);
hs_free_compile_error(compile_err);
}

View File

@@ -0,0 +1,129 @@
1:/\c空/ #\c must be followed by an ASCII character at index 0.
2:/\c/ #\c must be followed by an ASCII character at index 0.
3:/[\c空]/ #\c must be followed by an ASCII character at index 1.
4:/[\c]/ #Unterminated character class starting at index 0.
5:/\c空/8 #\c must be followed by an ASCII character at index 0.
6:/<([^>+i)>.*?</\1>/sP #Unterminated character class starting at index 2.
6:/[foo/ #Unterminated character class starting at index 0.
7:/[\p{X}]/8 #Unknown property at index 4.
8:/[\p{^X}]/8 #Unknown property at index 5.
9:/[\p{L]/8 #Malformed property at index 0.
10:/[\p{^L]/8 #Malformed property at index 0.
11:/[\P{L]/8 #Malformed property at index 0.
12:/[\P{^L]/8 #Malformed property at index 0.
13:/\p/8 #Malformed property at index 0.
14:/\P/8 #Malformed property at index 0.
15:/\p{/8 #Malformed property at index 0.
16:/\P{/8 #Malformed property at index 0.
17:/\p{^/8 #Malformed property at index 0.
18:/\P{^/8 #Malformed property at index 0.
19:/[\p/8 #Malformed property at index 1.
20:/[\P/8 #Malformed property at index 1.
21:/[\p{/8 #Malformed property at index 0.
22:/[\P{/8 #Malformed property at index 0.
23:/[\p{^/8 #Malformed property at index 0.
24:/[\P{^/8 #Malformed property at index 0.
25:/\pl/8 #Unknown property at index 2.
26:/\p{any}/8 #Unknown property at index 3.
27:/\p{greek}/8 #Unknown property at index 3.
28:/\b/8W #\b unsupported in UCP mode at index 0.
29:/(*UCP)\b/8 #\b unsupported in UCP mode at index 6.
30:/\B/8W #\B unsupported in UCP mode at index 0.
31:/\B/W #\B unsupported in UCP mode at index 0.
32:/foo(?{print "Hello world\n";})bar/ #Embedded code is not supported at index 3.
33:/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i #Embedded code is not supported at index 9.
34:/foobar\E/s #Unmatched \E at index 6.
35:/\X/8 #\X unsupported at index 0.
36:/\B+/ #Invalid repeat at index 2.
37:/\B?/ #Invalid repeat at index 2.
38:/\B*/ #Invalid repeat at index 2.
39:/\B{0,6}/ #Invalid repeat at index 2.
40:/\b+/ #Invalid repeat at index 2.
41:/\b?/ #Invalid repeat at index 2.
42:/\b*/ #Invalid repeat at index 2.
43:/\b{0,6}/ #Invalid repeat at index 2.
44:/[.ch.]/ #Unsupported POSIX collating element at index 0.
45:/[=ch=]/ #Unsupported POSIX collating element at index 0.
46:/[:digit:]/ #POSIX named classes are only supported inside a class at index 0.
47:/[[.ch.]]/ #Unsupported POSIX collating element at index 1.
48:/[[=ch=]]/ #Unsupported POSIX collating element at index 1.
49:/foo(?m)?bar/ #Invalid repeat at index 7.
50:/.(?)+/ #Invalid repeat at index 4.
51:/(abc)\2/P #Invalid back reference to expression 2.
52:/\x{100000000}/ #Value in \x{...} sequence is too large at index 0.
53:/^foo/{min_offset=5} #Expression is anchored and cannot satisfy min_offset=5 as it can only produce matches of length 3 bytes at most.
54:/foobar/{min_length=20} #Expression has min_length=20 but can only produce matches of length 6 bytes at most.
55:/foobar/{max_offset=3} #Expression has max_offset=3 but requires 6 bytes to match.
56:/mkdzo(x|u)(\b)kd/{max_offset=29} #Pattern can never match.
57:/[^\x00-\xff]/ #Pattern can never match.
58:/[^\x00-\xff]foo/ #Pattern can never match.
59:/^\Bfoo/ #Pattern can never match.
60:/^\B\Bfoo/ #Pattern can never match.
61:/can't_match\b\B/ #Pattern can never match.
62:/\b\Bcan't_match/ #Pattern can never match.
63:/^\b$/m #Pattern can never match.
64:/^\b\Z/m #Pattern can never match.
65:/^\b\z/m #Pattern can never match.
66:/\A\b$/m #Pattern can never match.
67:/\A\b\Z/m #Pattern can never match.
68:/\A\b\z/m #Pattern can never match.
69:/^[^\x00-\xff]foo/ #Pattern can never match.
70:/foo[^\x00-\xff]/ #Pattern can never match.
71:/foo[^\x00-\xff]$/ #Pattern can never match.
72:/\Bd\B/i{min_length=2,min_offset=4,max_offset=54} #Expression has min_length=2 but can only produce matches of length 1 bytes at most.
73:/(((.|aaa)aaaaaa.aaa){14,19}a((a|a{5,6}|aa){3,11}|aa.|a){2}){4}\Z/sm #Pattern is too large.
74:/(((.|aaa)aaaaaa.aaa){14,19}a((a|a{5,6}|aa){3,11}|aa.|a){2}){4}\Z/smL #Pattern is too large.
75:/\B/s8{min_length=1} #Expression has min_length=1 but can only produce matches of length 0 bytes at most.
76:/(f|d|(\b)|i|a\Z)/mHV8{min_length=2,min_offset=9,max_offset=14} #Expression has min_length=2 but can only produce matches of length 1 bytes at most.
77:/(f|e|d{19,}|h\Z|^j|\Aa)/smi{min_length=7,min_offset=8,max_offset=18} #Extended parameter constraints can not be satisfied for any match from this expression.
78:/(i{13,}|i\Z)/s{min_length=3,max_offset=5} #Extended parameter constraints can not be satisfied for any match from this expression.
79:/(?P<dupename>foo).*(?P<dupename>bar)/ #Two named subpatterns use the name 'dupename' at index 19.
80:/_W{0,3}bazr_W{0,3}(ac[_a-z]{22}a)?e_W{0,3}bazr[_a-z](ac[a-z]{4}c{14}[a-z]{5})?e_W{0,3}bazr[_a-z](e|ac[_a-z]{4}c{16}([_a-z]|[a-p]W|[o-z]WW){3}([_a-z]|WWW))_W{0,3}bazr([_a-z]|[a-p]WW?|[o-z]WWW)a(foobar|c([a-z]W{0,3})bc([a-z]W{0,3})c{14}([_a-z]W{0,3}){6})((fooaa|[_a-z]W{0,3})bazr[_a-z]W{0,5}a(foobar|c([_a-z]|[a-z]W{1,3})bc([_a-z]|[o-z]W{1,5})c{14}([_a-f]|[A-Z0]W|~WW|;WWW){6})){40}(fooaa|_)bazr[_a-z]/sL #Pattern is too large.
81:/[..]/ #Unsupported POSIX collating element at index 0.
82:/[==]/ #Unsupported POSIX collating element at index 0.
83:/[.\].]/ #Unsupported POSIX collating element at index 0.
84:/[=\]=]/ #Unsupported POSIX collating element at index 0.
85:/A(?!)+Z/ #Invalid repeat at index 5.
86:/\X/ #\X unsupported at index 0.
87:/[a\Qz\E]/ #\Q..\E sequences in character classes not supported at index 2.
88:/[A-\d]/ #Invalid range in character class at index 3.
89:/[A-[:digit:]]/ #Invalid range in character class at index 3.
90:/B[--[:digit:]--]+/ #Invalid range in character class at index 4.
91:/a\owibble/ #Value in \o{...} sequence is non-octal or missing braces at index 1.
92:/a\o{wibble/ #Value in \o{...} sequence is non-octal or missing braces at index 1.
93:/a\o{777}/ #Value in \o{...} sequence is too large at index 1.
94:/(*UTF16)foo/ #(*UTF16) not supported at index 2.
95:/(*BSR_UNICODE)abc/ #Unknown control verb at index 2.
96:/a+(*SKIP)b/ #Unknown control verb at index 4.
97:/foo(*/ #Invalid repeat at index 4.
98:/[:\]:]/ #POSIX named classes are only supported inside a class at index 0.
99:/[[:[:]/ #Invalid POSIX named class at index 1.
100:/abc(?(1)def|ghi)/P #Invalid conditional reference to expression 1.
101:/abc(?(<blah>)def|ghi)/P #Invalid conditional reference to label 'blah'.
102:/(?(DEFINE)foo|bar)/P #DEFINE conditional group with more than one branch at index 17.
103:/(?<1name>group)/ #Group name cannot begin with a digit at index 0.
104:/abc((def)?(?(R)bar))+/P #Pattern recursion not supported at index 10.
105:/abc((def)?(?(R2)bar))+/P #Pattern recursion not supported at index 10.
106:/abc((def)(?(R&label)bar))+/P #Pattern recursion not supported at index 9.
107:/\o{4200000}/8 #Value in \o{...} sequence is too large at index 0.
108:/\o{19}/ #Value in \o{...} sequence is non-octal or missing braces at index 0.
109:/\o{/ #Value in \o{...} sequence is non-octal or missing braces at index 0.
110:/\o{1/ #Value in \o{...} sequence is non-octal or missing braces at index 0.
111:/\x{0x110000}/8 #Value in \x{...} sequence is non-hex or missing } at index 0.
112:/\cÀ/ #\c must be followed by an ASCII character at index 0.
113:/[\cÀ]/ #\c must be followed by an ASCII character at index 1.
114:/[\o{4200000}]/8 #Value in \o{...} sequence is too large at index 1.
115:/[\x{0x110000}]/8 #Value in \x{...} sequence is non-hex or missing } at index 1.
116:/[\o{70]/ #Value in \o{...} sequence is non-octal or missing braces at index 1.
117:/[\x{ff]/ #Value in \x{...} sequence is non-hex or missing } at index 1.
118:/foo/{min_offset=10,max_offset=9} #In hs_expr_ext, min_offset must be less than or equal to max_offset.
120:/foo/{min_length=10,max_offset=9} #In hs_expr_ext, min_length must be less than or equal to max_offset.
121:/.e(?:(((eEbd..(d[^Be]{1,7}|A)){8,22}aD.){7}|EecA?(?:\b)c|bB[Dd])){29,37}[adb](?:.|A|c|[BEA]|D)..((?:c|[Cba]))?([Ee]|D)B+(.|[dbB]|E|E).[EcCe]ce(?:C|D)dD[EA]Ac.[aE]d/smiHWP #Pattern too large.
122:/<2F><>/8 #Expression is not valid UTF-8.
123:/hello \6 world/P #Invalid back reference to expression 6.
124:/hello \6 world|dog/P #Invalid back reference to expression 6.
125:/[~-\V]/8 #Invalid range in character class at index 3.
126:/(*UTF8)<29><>/ #Expression is not valid UTF-8.
127:/^fo?ob{ro|nax_off\Qt=10omnax+8Wnah/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>0}l.{1,60}Car*k|npanomnax+8Wnah/8 #Expression is not valid UTF-8.
128:/(*UTF8)^fo?ob{ro|nax_off\Qt=10omnax+8Wnah/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>0}l.{1,60}Car*k|npanomnax+8Wnah/ #Expression is not valid UTF-8.
129:/bignum \1111111111111111111/ #Number is too big at index 7.

1513
unit/hyperscan/behaviour.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
/*
* 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 <limits.h>
#include <vector>
#include "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
using namespace std;
using namespace testing;
namespace /* anonymous */ {
struct expected_info {
const char *pattern;
unsigned min;
unsigned max;
char unordered_matches;
char matches_at_eod;
char matches_only_at_eod;
};
class ExprInfop : public TestWithParam<expected_info> {
};
TEST_P(ExprInfop, width) {
const expected_info &ei = GetParam();
SCOPED_TRACE(ei.pattern);
hs_expr_info_t *info = nullptr;
hs_compile_error_t *c_err = nullptr;
hs_error_t err = hs_expression_info(ei.pattern, 0, &info, &c_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(info != nullptr);
ASSERT_TRUE(c_err == nullptr);
EXPECT_EQ(ei.min, info->min_width);
EXPECT_EQ(ei.max, info->max_width);
EXPECT_EQ(ei.unordered_matches, info->unordered_matches);
EXPECT_EQ(ei.matches_at_eod, info->matches_at_eod);
EXPECT_EQ(ei.matches_only_at_eod, info->matches_only_at_eod);
free(info);
}
static const expected_info ei_test[] = {
{"abc", 3, 3, 0, 0, 0},
{"abc.*def", 6, UINT_MAX, 0, 0, 0},
{"abc|defghi", 3, 6, 0, 0, 0},
{"abc(def)?", 3, 6, 0, 0, 0},
{"abc(def){0,3}", 3, 12, 0, 0, 0},
{"abc(def){1,4}", 6, 15, 0, 0, 0},
{"", 0, 0, 0, 0, 0},
{"^", 0, 0, 0, 0, 0},
{"^\\b", 0, 0, 1, 0, 0},
{"\\b$", 0, 0, 1, 1, 1},
{"(?m)\\b$", 0, 0, 1, 1, 0},
{"\\A", 0, 0, 0, 0, 0},
{"\\z", 0, 0, 0, 1, 1},
{"\\Z", 0, 0, 1, 1, 1},
{"$", 0, 0, 1, 1, 1},
{"(?m)$", 0, 0, 1, 1, 0},
{"^foo", 3, 3, 0, 0, 0},
{"^foo.*bar", 6, UINT_MAX, 0, 0, 0},
{"^foo.*bar?", 5, UINT_MAX, 0, 0, 0},
{"^foo.*bar$", 6, UINT_MAX, 1, 1, 1},
{"^foobar$", 6, 6, 1, 1, 1},
{"foobar$", 6, 6, 1, 1, 1},
{"^.*foo", 3, UINT_MAX, 0, 0, 0},
{"foo\\b", 3, 3, 1, 1, 0},
{"foo.{1,13}bar", 7, 19, 0, 0, 0},
{"foo.{10,}bar", 16, UINT_MAX, 0, 0, 0},
{"foo.{0,10}bar", 6, 16, 0, 0, 0},
{"foo.{,10}bar", 12, 12, 0, 0, 0},
{"foo.{10}bar", 16, 16, 0, 0, 0},
{"(^|\n)foo", 3, 4, 0, 0, 0},
{"(^\n|)foo", 3, 4, 0, 0, 0},
{"(?m)^foo", 3, 3, 0, 0, 0},
{"\\bfoo", 3, 3, 0, 0, 0},
{"^\\bfoo", 3, 3, 0, 0, 0},
{"(?m)^\\bfoo", 3, 3, 0, 0, 0},
{"\\Bfoo", 3, 3, 0, 0, 0},
{"(foo|bar\\z)", 3, 3, 0, 1, 0},
{"(foo|bar)\\z", 3, 3, 0, 1, 1},
};
INSTANTIATE_TEST_CASE_P(ExprInfo, ExprInfop, ValuesIn(ei_test));
}

158
unit/hyperscan/extparam.cpp Normal file
View File

@@ -0,0 +1,158 @@
/*
* 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 <cstring>
#include "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
using namespace std;
using namespace testing;
TEST(ExtParam, LargeMinOffset) {
hs_expr_ext ext;
memset(&ext, 0, sizeof(ext));
ext.min_offset = 100000;
ext.flags = HS_EXT_FLAG_MIN_OFFSET;
pattern p("hatstand.*teakettle", 0, 0, ext);
hs_database_t *db = buildDB(p, HS_MODE_NOSTREAM);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
CallBackContext c;
// First, scan a matching corpus that's shorter than our min_offset and
// ensure it doesn't match.
string corpus = "hatstand" + string(80000, '_') + "teakettle";
err = hs_scan(db, corpus.c_str(), corpus.length(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
// Try exactly at the min_offset.
corpus = "hatstand" + string(99983, '_') + "teakettle";
err = hs_scan(db, corpus.c_str(), corpus.length(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(100000, 0), c.matches[0]);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(ExtParam, LargeExactOffset) {
hs_expr_ext ext;
memset(&ext, 0, sizeof(ext));
ext.min_offset = 200000;
ext.max_offset = 200000;
ext.flags = HS_EXT_FLAG_MIN_OFFSET | HS_EXT_FLAG_MAX_OFFSET;
pattern p("hatstand.*teakettle", 0, 0, ext);
hs_database_t *db = buildDB(p, HS_MODE_NOSTREAM);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
CallBackContext c;
// First, scan a matching corpus that's shorter than our min_offset and
// ensure it doesn't match.
string corpus = "hatstand" + string(199982, '_') + "teakettle";
err = hs_scan(db, corpus.c_str(), corpus.length(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
// Try the exact match.
corpus = "hatstand" + string(199983, '_') + "teakettle";
err = hs_scan(db, corpus.c_str(), corpus.length(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(200000, 0), c.matches[0]);
// Try one byte too far.
c.clear();
corpus = "hatstand" + string(199984, '_') + "teakettle";
err = hs_scan(db, corpus.c_str(), corpus.length(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(ExtParam, LargeMinLength) {
hs_expr_ext ext;
memset(&ext, 0, sizeof(ext));
ext.min_length = 100000;
ext.flags = HS_EXT_FLAG_MIN_LENGTH;
pattern p("hatstand.*teakettle", 0, 0, ext);
hs_database_t *db = buildDB(p, HS_MODE_NOSTREAM);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
CallBackContext c;
// First, scan a matching corpus that contains a match that's a bit too
// short.
string corpus = string(10000, '_') + "hatstand" + string(80000, '_') + "teakettle";
err = hs_scan(db, corpus.c_str(), corpus.length(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
// Now, a match of the right length.
corpus = string(10000, '_') + "hatstand" + string(99983, '_') + "teakettle";
err = hs_scan(db, corpus.c_str(), corpus.length(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(110000, 0), c.matches[0]);
hs_free_scratch(scratch);
hs_free_database(db);
}

View File

@@ -0,0 +1,194 @@
/*
* 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 "gtest/gtest.h"
#include "test_util.h"
#include "hs.h"
namespace {
struct PatternInfo {
std::string expr;
unsigned flags;
std::string corpus;
unsigned long long match;
};
class IdenticalTest : public testing::TestWithParam<PatternInfo> {};
TEST_P(IdenticalTest, Block) {
const PatternInfo &info = GetParam();
std::vector<pattern> patterns;
for (unsigned i = 0; i < 100; i++) {
patterns.push_back(pattern(info.expr, info.flags, i));
}
hs_database_t *db = buildDB(patterns, HS_MODE_BLOCK);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, scratch);
CallBackContext cb;
err = hs_scan(db, info.corpus.c_str(), info.corpus.size(), 0, scratch,
record_cb, &cb);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_scratch(scratch);
hs_free_database(db);
ASSERT_EQ(patterns.size(), cb.matches.size());
std::set<unsigned> ids;
for (size_t i = 0; i < cb.matches.size(); i++) {
ASSERT_EQ(info.match, cb.matches[i].to);
ids.insert(cb.matches[i].id);
}
ASSERT_EQ(patterns.size(), ids.size());
ASSERT_EQ(0, *ids.begin());
ASSERT_EQ(patterns.size() - 1, *ids.rbegin());
}
TEST_P(IdenticalTest, Stream) {
const PatternInfo &info = GetParam();
std::vector<pattern> patterns;
for (unsigned i = 0; i < 100; i++) {
patterns.push_back(pattern(info.expr, info.flags, i));
}
hs_database_t *db =
buildDB(patterns, HS_MODE_STREAM | HS_MODE_SOM_HORIZON_LARGE);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, scratch);
CallBackContext cb;
hs_stream_t *stream = nullptr;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, stream);
err = hs_scan_stream(stream, info.corpus.c_str(), info.corpus.size(), 0,
scratch, record_cb, &cb);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_close_stream(stream, scratch, record_cb, &cb);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_scratch(scratch);
hs_free_database(db);
ASSERT_EQ(patterns.size(), cb.matches.size());
std::set<unsigned> ids;
for (size_t i = 0; i < cb.matches.size(); i++) {
ASSERT_EQ(info.match, cb.matches[i].to);
ids.insert(cb.matches[i].id);
}
ASSERT_EQ(patterns.size(), ids.size());
ASSERT_EQ(0, *ids.begin());
ASSERT_EQ(patterns.size() - 1, *ids.rbegin());
}
TEST_P(IdenticalTest, Vectored) {
const PatternInfo &info = GetParam();
std::vector<pattern> patterns;
for (unsigned i = 0; i < 100; i++) {
patterns.push_back(pattern(info.expr, info.flags, i));
}
hs_database_t *db = buildDB(patterns, HS_MODE_VECTORED);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, scratch);
CallBackContext cb;
const char * const data[] = { info.corpus.c_str() };
const unsigned datalen[] = { (unsigned)info.corpus.size() };
err = hs_scan_vector(db, data, datalen, 1, 0, scratch, record_cb, &cb);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_scratch(scratch);
hs_free_database(db);
ASSERT_EQ(patterns.size(), cb.matches.size());
std::set<unsigned> ids;
for (size_t i = 0; i < cb.matches.size(); i++) {
ASSERT_EQ(info.match, cb.matches[i].to);
ids.insert(cb.matches[i].id);
}
ASSERT_EQ(patterns.size(), ids.size());
ASSERT_EQ(0, *ids.begin());
ASSERT_EQ(patterns.size() - 1, *ids.rbegin());
}
static const PatternInfo patterns[] = {
{ "a", 0, "a", 1 },
{ "a", HS_FLAG_SINGLEMATCH, "a", 1 },
{ "handbasket", 0, "__handbasket__", 12 },
{ "handbasket", HS_FLAG_SINGLEMATCH, "__handbasket__", 12 },
{ "handbasket", HS_FLAG_SOM_LEFTMOST, "__handbasket__", 12 },
{ "foo.*bar", 0, "a foolish embarrassment", 15 },
{ "foo.*bar", HS_FLAG_SINGLEMATCH, "a foolish embarrassment", 15 },
{ "foo.*bar", HS_FLAG_SOM_LEFTMOST, "a foolish embarrassment", 15 },
{ "\\bword\\b(..)+\\d{3,7}", 0, " word 012", 15 },
{ "\\bword\\b(..)+\\d{3,7}", HS_FLAG_SINGLEMATCH, " word 012", 15 },
{ "\\bword\\b(..)+\\d{3,7}", HS_FLAG_SOM_LEFTMOST, " word 012", 15 },
{ "eod\\z", 0, "eod", 3 },
{ "eod\\z", HS_FLAG_SINGLEMATCH, "eod", 3 },
{ "eod\\z", HS_FLAG_SOM_LEFTMOST, "eod", 3 },
};
INSTANTIATE_TEST_CASE_P(Identical, IdenticalTest, testing::ValuesIn(patterns));
// teach google-test how to print a param
void PrintTo(const PatternInfo &p, ::std::ostream *os) {
*os << p.expr << ":" << p.flags << ", " << p.corpus;
}
} // namespace

36
unit/hyperscan/main.cpp 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.
*/
#include "gtest/gtest.h"
// Driver: run all the tests (defined in other source files in this directory)
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

326
unit/hyperscan/multi.cpp Normal file
View File

@@ -0,0 +1,326 @@
/*
* 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 <algorithm>
#include <array>
#include <iostream>
#include <vector>
#include "gtest/gtest.h"
#include "hs.h"
#include "config.h"
#include "test_util.h"
using namespace std;
TEST(MMAdaptor, norm_cont1) { // UE-901
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZ";
const char *expr[] = {"aoo[A-K]", "bar[L-Z]"};
unsigned flags[] = {0, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 0;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(3U, c.matches.size());
ASSERT_EQ(MatchRecord(4, 30), c.matches[0]);
ASSERT_EQ(MatchRecord(8, 30), c.matches[1]);
ASSERT_EQ(MatchRecord(12, 31), c.matches[2]);
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MMAdaptor, norm_cont2) {
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZ ";
const char *expr[] = {"aoo[A-K][^\n]{16}", "bar[L-Z][^\n]{16}"};
unsigned flags[] = {0, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 0;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(3U, c.matches.size());
ASSERT_TRUE(c.matches.end() != find(c.matches.begin(), c.matches.end(), MatchRecord(20, 30)));
ASSERT_TRUE(c.matches.end() != find(c.matches.begin(), c.matches.end(), MatchRecord(24, 30)));
ASSERT_TRUE(c.matches.end() != find(c.matches.begin(), c.matches.end(), MatchRecord(28, 31)));
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MMAdaptor, norm_halt1) {
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZ";
const char *expr[] = {"aoo[A-K]", "bar[L-Z]"};
unsigned flags[] = {0, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 1;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(4, 30), c.matches[0]);
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MMAdaptor, norm_halt2) { // UE-901
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZ ";
const char *expr[] = {"aoo[A-K][^\n]{16}", "bar[L-Z][^\n]{16}"};
unsigned flags[] = {0, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 1;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(20, 30), c.matches[0]);
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MMAdaptor, high_cont1) { // UE-901
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZ";
const char *expr[] = {"aoo[A-K]", "bar[L-Z]"};
unsigned flags[] = {HS_FLAG_SINGLEMATCH, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 0;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_TRUE(c.matches.end() != find(c.matches.begin(), c.matches.end(), MatchRecord(4, 30)));
ASSERT_TRUE(c.matches.end() != find(c.matches.begin(), c.matches.end(), MatchRecord(12, 31)));
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MMAdaptor, high_cont2) {
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZ ";
const char *expr[] = {"aoo[A-K][^\n]{16}", "bar[L-Z][^\n]{16}"};
unsigned flags[] = {HS_FLAG_SINGLEMATCH, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 0;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_TRUE(c.matches.end() != find(c.matches.begin(), c.matches.end(), MatchRecord(20, 30)));
ASSERT_TRUE(c.matches.end() != find(c.matches.begin(), c.matches.end(), MatchRecord(28, 31)));
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MMAdaptor, high_halt1) {
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZ";
const char *expr[] = {"aoo[A-K]", "bar[L-Z]"};
unsigned flags[] = {HS_FLAG_SINGLEMATCH, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 1;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(4, 30), c.matches[0]);
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MMAdaptor, high_halt2) {
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
CallBackContext c;
string data = "aooAaooAbarZbarZaooA ";
const char *expr[] = {"aoo[A-K][^\n]{16}", "bar[L-Z][^\n]{16}"};
unsigned flags[] = {HS_FLAG_SINGLEMATCH, 0};
unsigned ids[] = {30, 31};
hs_error_t err = hs_compile_multi(expr, flags, ids, 2, HS_MODE_NOSTREAM,
nullptr, &db, &compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(scratch != nullptr);
c.halt = 1;
err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_TRUE(MatchRecord(20, 30) == c.matches[0]
|| MatchRecord(28, 31) == c.matches[0]);
hs_free_database(db);
hs_free_scratch(scratch);
}
TEST(MPV, UE_2395) {
vector<pattern> patterns;
patterns.push_back(pattern("^.{200}", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern(".{40,}", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aaa", HS_FLAG_DOTALL, 3));
hs_database_t *db = buildDB(patterns, HS_MODE_BLOCK);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
array<char, 300> data;
data.fill('a');
CallBackContext c;
err = hs_scan(db, data.data(), data.size(), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
unsigned seen = 39;
for (vector<MatchRecord>::const_iterator it = c.matches.begin();
it != c.matches.end(); ++it) {
if (it->id != 2) {
if (it->id == 1) {
ASSERT_EQ(200, it->to);
}
continue;
}
ASSERT_EQ(seen + 1, it->to);
seen = it->to;
}
ASSERT_EQ(300, seen);
hs_free_database(db);
hs_free_scratch(scratch);
}

356
unit/hyperscan/order.cpp Normal file
View File

@@ -0,0 +1,356 @@
/*
* 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 <algorithm>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
using namespace std;
using namespace testing;
namespace /* anonymous */ {
bool matchesOrdered(const vector<MatchRecord> &matches) {
unsigned long lastMatch = 0;
for (vector<MatchRecord>::const_iterator it = matches.begin();
it != matches.end(); ++it) {
if (lastMatch > it->to) {
return false;
}
lastMatch = it->to;
}
return true;
}
unsigned countMatchesById(const vector<MatchRecord> &matches, int id) {
unsigned count = 0;
for (vector<MatchRecord>::const_iterator it = matches.begin();
it != matches.end(); ++it) {
if (id == it->id) {
count++;
}
}
return count;
}
TEST(order, ordering1) {
vector<pattern> patterns;
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
CallBackContext c;
err = hs_scan(db, data, strlen(data), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_EQ(31U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(5U, countMatchesById(c.matches, 4));
EXPECT_EQ(5U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(order, ordering2) {
vector<pattern> patterns;
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
CallBackContext c;
err = hs_scan(db, data, strlen(data), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_EQ(0U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(5U, countMatchesById(c.matches, 4));
EXPECT_EQ(5U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(order, ordering3) {
vector<pattern> patterns;
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
CallBackContext c;
err = hs_scan(db, data, strlen(data), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_EQ(0U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(5U, countMatchesById(c.matches, 4));
EXPECT_EQ(0U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(order, ordering4) {
vector<pattern> patterns;
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
CallBackContext c;
err = hs_scan(db, data, strlen(data), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_EQ(31U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(0U, countMatchesById(c.matches, 4));
EXPECT_EQ(0U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(order, ordering5) {
vector<pattern> patterns;
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_STREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
CallBackContext c;
for (size_t jump = 1; jump <= 8; jump++) {
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
for (unsigned i = 0; i < strlen(data); i += jump) {
err = hs_scan_stream(stream, data + i, min(jump, strlen(data) - i),
0, scratch, record_cb, (void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
}
EXPECT_EQ(31U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(5U, countMatchesById(c.matches, 4));
EXPECT_EQ(5U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
c.matches.clear();
hs_close_stream(stream, scratch, record_cb, (void *)&c);
}
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(order, ordering6) {
vector<pattern> patterns;
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_STREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
CallBackContext c;
for (size_t jump = 1; jump <= 8; jump++) {
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
for (unsigned i = 0; i < strlen(data); i += jump) {
err = hs_scan_stream(stream, data + i, min(jump, strlen(data) - i),
0, scratch, record_cb, (void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
}
EXPECT_EQ(0U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(5U, countMatchesById(c.matches, 4));
EXPECT_EQ(5U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
c.matches.clear();
hs_close_stream(stream, scratch, record_cb, (void *)&c);
}
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(order, ordering7) {
vector<pattern> patterns;
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_STREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
CallBackContext c;
for (size_t jump = 1; jump <= 8; jump++) {
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
for (unsigned i = 0; i < strlen(data); i += jump) {
err = hs_scan_stream(stream, data + i, min(jump, strlen(data) - i),
0, scratch, record_cb, (void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
}
EXPECT_EQ(0U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(5U, countMatchesById(c.matches, 4));
EXPECT_EQ(0U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
c.matches.clear();
hs_close_stream(stream, scratch, record_cb, (void *)&c);
}
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(order, ordering8) {
vector<pattern> patterns;
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
const char *data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
hs_database_t *db = buildDB(patterns, HS_MODE_STREAM);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
CallBackContext c;
for (size_t jump = 1; jump <= 8; jump++) {
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
for (unsigned i = 0; i < strlen(data); i += jump) {
err = hs_scan_stream(stream, data + i, min(jump, strlen(data) - i),
0, scratch, record_cb, (void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
}
EXPECT_EQ(31U, countMatchesById(c.matches, 1));
EXPECT_EQ(30U, countMatchesById(c.matches, 2));
EXPECT_EQ(29U, countMatchesById(c.matches, 3));
EXPECT_EQ(0U, countMatchesById(c.matches, 4));
EXPECT_EQ(0U, countMatchesById(c.matches, 5));
ASSERT_TRUE(matchesOrdered(c.matches));
c.matches.clear();
hs_close_stream(stream, scratch, record_cb, (void *)&c);
}
hs_free_scratch(scratch);
hs_free_database(db);
}
}

View File

@@ -0,0 +1,289 @@
/*
* 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 <stdlib.h>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
using namespace std;
using namespace testing;
namespace /* anonymous */ {
static size_t last_alloc_size;
static void *log_malloc(size_t n) {
last_alloc_size = n;
return malloc(n);
}
static void *bad_alloc(size_t) { return nullptr; }
TEST(scratch, testAlloc) {
vector<pattern> patterns;
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
hs_error_t err;
err = hs_set_allocator(log_malloc, free);
ASSERT_EQ(HS_SUCCESS, err);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
size_t curr_size;
err = hs_scratch_size(scratch, &curr_size);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(last_alloc_size, curr_size);
hs_free_scratch(scratch);
hs_free_database(db);
hs_set_allocator(nullptr, nullptr);
}
TEST(scratch, testScratchAlloc) {
vector<pattern> patterns;
allocated_count = 0;
allocated_count_b = 0;
hs_error_t err = hs_set_allocator(count_malloc_b, count_free_b);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_set_scratch_allocator(count_malloc, count_free);
ASSERT_EQ(HS_SUCCESS, err);
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
ASSERT_EQ(0, allocated_count);
ASSERT_NE(0, allocated_count_b);
size_t old_b = allocated_count_b;
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
size_t curr_size;
err = hs_scratch_size(scratch, &curr_size);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(allocated_count, curr_size);
ASSERT_EQ(allocated_count_b, old_b);
hs_free_scratch(scratch);
hs_free_database(db);
hs_set_allocator(nullptr, nullptr);
ASSERT_EQ(0, allocated_count);
ASSERT_EQ(0, allocated_count_b);
}
TEST(scratch, badAlloc) {
vector<pattern> patterns;
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
hs_error_t err;
err = hs_set_scratch_allocator(bad_alloc, free);
ASSERT_EQ(HS_SUCCESS, err);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_NE(HS_SUCCESS, err);
ASSERT_TRUE(scratch == nullptr);
hs_free_database(db);
hs_set_scratch_allocator(nullptr, nullptr);
}
TEST(scratch, testScratchRealloc) {
vector<pattern> patterns;
patterns.push_back(pattern("aa", HS_FLAG_DOTALL, 1));
patterns.push_back(pattern("aa.", HS_FLAG_DOTALL, 2));
patterns.push_back(pattern("aa..", HS_FLAG_DOTALL, 3));
patterns.push_back(pattern("^.{0,4}aa..", HS_FLAG_DOTALL, 4));
patterns.push_back(pattern("^.{0,4}aa", HS_FLAG_DOTALL, 5));
hs_database_t *db = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db);
patterns.push_back(pattern("^.{0,4}aa{0,4}", HS_FLAG_DOTALL, 6));
patterns.push_back(pattern("^.{0,4}aa{0,4}a..", HS_FLAG_DOTALL, 7));
hs_database_t *db2 = buildDB(patterns, HS_MODE_NOSTREAM);
ASSERT_NE(nullptr, db2);
hs_error_t err;
err = hs_set_scratch_allocator(log_malloc, free);
ASSERT_EQ(HS_SUCCESS, err);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
size_t curr_size;
err = hs_scratch_size(scratch, &curr_size);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(last_alloc_size, curr_size);
err = hs_alloc_scratch(db2, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scratch_size(scratch, &curr_size);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(last_alloc_size, curr_size);
hs_free_scratch(scratch);
hs_free_database(db);
hs_free_database(db2);
hs_set_scratch_allocator(nullptr, nullptr);
}
TEST(scratch, tooSmallForDatabase) {
hs_database_t *db1 = buildDB("foobar", 0, 0, HS_MODE_BLOCK, nullptr);
ASSERT_NE(nullptr, db1);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db1, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_database(db1);
hs_database_t *db2 =
buildDB("(a.?b.?c.?d.?e.?f.?g)|(hatstand(..)+teakettle)", 0, 0,
HS_MODE_BLOCK, nullptr);
ASSERT_NE(nullptr, db2);
// Try and scan some data using db2 and a scratch that has only been
// allocated for db1.
err = hs_scan(db2, "somedata", 8, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_INVALID, err);
// Alloc scratch correctly and try again.
err = hs_alloc_scratch(db2, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan(db2, "somedata", 8, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_scratch(scratch);
hs_free_database(db2);
}
TEST(scratch, tooSmallForDatabase2) {
hs_database_t *db1 = buildDB("foobar", 0, 0, HS_MODE_STREAM, nullptr);
ASSERT_NE(nullptr, db1);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db1, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_database(db1);
hs_database_t *db2 =
buildDB("(a.?b.?c.?d.?e.?f.?g)|(hatstand(..)+teakettle)", 0, 0,
HS_MODE_STREAM, nullptr);
ASSERT_NE(nullptr, db2);
// Try and scan some data using db2 and a scratch that has only been
// allocated for db1.
hs_stream_t *stream = nullptr;
err = hs_open_stream(db2, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, "somedata", 8, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_INVALID, err);
// Alloc scratch correctly and try again.
err = hs_alloc_scratch(db2, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, "somedata", 8, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_close_stream(stream, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_scratch(scratch);
hs_free_database(db2);
}
TEST(scratch, damagedScratch) {
hs_database_t *db = buildDB("foobar", 0, 0, HS_MODE_BLOCK, nullptr);
ASSERT_NE(nullptr, db);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
size_t scratch_size = 0;
err = hs_scratch_size(scratch, &scratch_size);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_LT(4, scratch_size);
// Take a temp copy and then scribble over the first four bytes.
char tmp[4];
memcpy(tmp, scratch, 4);
memset(scratch, 0xff, 4);
err = hs_scan(db, "somedata", 8, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_INVALID, err);
// Restore first four bytes so we can free.
memcpy(scratch, tmp, 4);
err = hs_free_scratch(scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_free_database(db);
}
} // namespace

View File

@@ -0,0 +1,486 @@
/*
* 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.
*/
/**
* Unit tests for serialization functions.
*/
#include "config.h"
#include <cstring>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "hs.h"
#include "hs_internal.h"
#include "test_util.h"
namespace {
using namespace std;
using namespace testing;
static const unsigned validModes[] = {
HS_MODE_STREAM,
HS_MODE_NOSTREAM
};
class Serializep : public TestWithParam<unsigned> {
};
// Check that we can deserialize from a char array at any alignment and the info
// is consistent
TEST_P(Serializep, DeserializeFromAnyAlignment) {
const unsigned mode = GetParam();
SCOPED_TRACE(mode);
hs_error_t err;
hs_database_t *db = buildDB("hatstand.*teakettle.*badgerbrush",
HS_FLAG_CASELESS, 1000, mode);
ASSERT_TRUE(db != nullptr) << "database build failed.";
char *original_info = nullptr;
err = hs_database_info(db, &original_info);
ASSERT_EQ(HS_SUCCESS, err);
const char *mode_string = nullptr;
switch (mode) {
case HS_MODE_STREAM:
mode_string = "STREAM";
break;
case HS_MODE_NOSTREAM:
mode_string = "BLOCK";
}
ASSERT_NE(nullptr, original_info) << "hs_serialized_database_info returned null.";
ASSERT_STREQ("Version:", string(original_info).substr(0, 8).c_str());
ASSERT_TRUE(strstr(original_info, mode_string) != nullptr);
char *bytes = nullptr;
size_t length = 0;
err = hs_serialize_database(db, &bytes, &length);
ASSERT_EQ(HS_SUCCESS, err) << "serialize failed.";
ASSERT_NE(nullptr, bytes);
ASSERT_LT(0U, length);
hs_free_database(db);
db = nullptr;
const size_t maxalign = 16;
char *copy = new char[length + maxalign];
// Deserialize from char arrays at a range of alignments.
for (size_t i = 0; i < maxalign; i++) {
SCOPED_TRACE(i);
memset(copy, 0, length + maxalign);
char *mycopy = copy + i;
memcpy(mycopy, bytes, length);
// We should be able to call hs_serialized_database_info and get back a
// reasonable string.
char *info;
err = hs_serialized_database_info(mycopy, length, &info);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, original_info);
ASSERT_STREQ(original_info, info);
free(info);
// We should be able to deserialize as well.
err = hs_deserialize_database(mycopy, length, &db);
ASSERT_EQ(HS_SUCCESS, err) << "deserialize failed.";
ASSERT_TRUE(db != nullptr);
// And the info there should match.
err = hs_database_info(db, &info);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_STREQ(original_info, info);
free(info);
hs_free_database(db);
db = nullptr;
}
free(original_info);
free(bytes);
delete[] copy;
}
// Check that we can deserialize_at from a char array at any alignment and the
// info is consistent
TEST_P(Serializep, DeserializeAtFromAnyAlignment) {
const unsigned mode = GetParam();
SCOPED_TRACE(mode);
hs_error_t err;
hs_database_t *db = buildDB("hatstand.*teakettle.*badgerbrush",
HS_FLAG_CASELESS, 1000, mode);
ASSERT_TRUE(db != nullptr) << "database build failed.";
char *original_info;
err = hs_database_info(db, &original_info);
ASSERT_EQ(HS_SUCCESS, err);
const char *mode_string = nullptr;
switch (mode) {
case HS_MODE_STREAM:
mode_string = "STREAM";
break;
case HS_MODE_NOSTREAM:
mode_string = "BLOCK";
}
ASSERT_NE(nullptr, original_info) << "hs_serialized_database_info returned null.";
ASSERT_STREQ("Version:", string(original_info).substr(0, 8).c_str());
ASSERT_TRUE(strstr(original_info, mode_string) != nullptr);
char *bytes = nullptr;
size_t length = 0;
err = hs_serialize_database(db, &bytes, &length);
ASSERT_EQ(HS_SUCCESS, err) << "serialize failed.";
ASSERT_NE(nullptr, bytes);
ASSERT_LT(0U, length);
hs_free_database(db);
db = nullptr;
size_t slength;
err = hs_serialized_database_size(bytes, length, &slength);
ASSERT_EQ(HS_SUCCESS, err);
const size_t maxalign = 16;
char *copy = new char[length + maxalign];
char *mem = new char[slength];
db = (hs_database_t *)mem;
// Deserialize from char arrays at a range of alignments.
for (size_t i = 0; i < maxalign; i++) {
SCOPED_TRACE(i);
memset(copy, 0, length + maxalign);
char *mycopy = copy + i;
memcpy(mycopy, bytes, length);
// We should be able to call hs_serialized_database_info and get back a
// reasonable string.
char *info;
err = hs_serialized_database_info(mycopy, length, &info);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, original_info);
ASSERT_STREQ(original_info, info);
free(info);
// Scrub target memory.
memset(mem, 0xff, length);
// We should be able to deserialize as well.
err = hs_deserialize_database_at(mycopy, length, db);
ASSERT_EQ(HS_SUCCESS, err) << "deserialize failed.";
ASSERT_TRUE(db != nullptr);
// And the info there should match.
err = hs_database_info(db, &info);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(info != nullptr);
ASSERT_STREQ(original_info, info);
free(info);
}
free(original_info);
free(bytes);
delete[] copy;
delete[] mem;
}
INSTANTIATE_TEST_CASE_P(Serialize, Serializep,
ValuesIn(validModes));
// Attempt to reproduce the scenario in UE-1946.
TEST(Serialize, CrossCompileSom) {
hs_platform_info plat;
plat.cpu_features = 0;
plat.tune = HS_TUNE_FAMILY_GENERIC;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
const unsigned mode = HS_MODE_STREAM
| HS_MODE_SOM_HORIZON_LARGE;
hs_database_t *db = buildDB(pattern, HS_FLAG_SOM_LEFTMOST, 1000, mode,
&plat);
ASSERT_TRUE(db != nullptr) << "database build failed.";
size_t db_len;
hs_error_t err = hs_database_size(db, &db_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, db_len);
char *bytes = nullptr;
size_t bytes_len = 0;
err = hs_serialize_database(db, &bytes, &bytes_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, bytes_len);
hs_free_database(db);
// Relocate to misaligned block.
char *copy = (char *)malloc(bytes_len + 1);
ASSERT_TRUE(copy != nullptr);
memcpy(copy + 1, bytes, bytes_len);
free(bytes);
size_t ser_len;
err = hs_serialized_database_size(copy + 1, bytes_len, &ser_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, ser_len);
ASSERT_EQ(db_len, ser_len);
free(copy);
}
static void *null_malloc(size_t) {
return nullptr;
}
static void *misaligned_malloc(size_t s) {
char *c = (char *)malloc(s + 1);
return (void *)(c + 1);
}
static void misaligned_free(void *p) {
char *c = (char *)p;
free(c - 1);
}
// make sure that serializing/deserializing to null or an unaligned address fails
TEST(Serialize, CompileNullMalloc) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
// mallocing null should fail compile
hs_set_allocator(null_malloc, nullptr);
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_NE(HS_SUCCESS, err);
ASSERT_TRUE(db == nullptr);
ASSERT_TRUE(c_err != nullptr);
hs_free_compile_error(c_err);
hs_set_allocator(nullptr, nullptr);
}
TEST(Serialize, CompileErrorAllocator) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatsta^nd.*(badgerbrush|teakettle)";
// failing to compile should use the misc allocator
allocated_count = 0;
allocated_count_b = 0;
hs_set_allocator(count_malloc_b, count_free_b);
hs_set_misc_allocator(count_malloc, count_free);
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_NE(HS_SUCCESS, err);
ASSERT_TRUE(db == nullptr);
ASSERT_TRUE(c_err != nullptr);
ASSERT_EQ(0, allocated_count_b);
ASSERT_NE(0, allocated_count);
hs_free_compile_error(c_err);
hs_set_allocator(nullptr, nullptr);
ASSERT_EQ(0, allocated_count);
}
TEST(Serialize, AllocatorsUsed) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
allocated_count = 0;
allocated_count_b = 0;
hs_set_allocator(count_malloc_b, count_free_b);
hs_set_database_allocator(count_malloc, count_free);
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
ASSERT_TRUE(c_err == nullptr);
ASSERT_EQ(0, allocated_count_b);
ASSERT_NE(0, allocated_count);
/* serialize should use the misc allocator */
char *bytes = nullptr;
size_t bytes_len = 0;
err = hs_serialize_database(db, &bytes, &bytes_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, bytes_len);
ASSERT_EQ(bytes_len, allocated_count_b);
count_free_b(bytes);
hs_free_database(db);
hs_set_allocator(nullptr, nullptr);
ASSERT_EQ(0, allocated_count);
ASSERT_EQ(0, allocated_count_b);
}
TEST(Serialize, CompileUnalignedMalloc) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
// unaligned malloc should fail compile
hs_set_allocator(misaligned_malloc, misaligned_free);
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_NE(HS_SUCCESS, err);
ASSERT_TRUE(db == nullptr);
ASSERT_TRUE(c_err != nullptr);
hs_free_compile_error(c_err);
hs_set_allocator(nullptr, nullptr);
}
TEST(Serialize, SerializeNullMalloc) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
size_t db_len;
err = hs_database_size(db, &db_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, db_len);
char *bytes = nullptr;
size_t bytes_len = 0;
// fail when serialize gets a null malloc
hs_set_allocator(null_malloc, nullptr);
err = hs_serialize_database(db, &bytes, &bytes_len);
ASSERT_NE(HS_SUCCESS, err);
hs_set_allocator(nullptr, nullptr);
hs_free_database(db);
}
// make sure that serializing/deserializing to null or an unaligned address fails
TEST(Serialize, SerializeUnalignedMalloc) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
size_t db_len;
err = hs_database_size(db, &db_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, db_len);
char *bytes = nullptr;
size_t bytes_len = 0;
// fail when serialize gets a misaligned malloc
hs_set_allocator(misaligned_malloc, misaligned_free);
err = hs_serialize_database(db, &bytes, &bytes_len);
ASSERT_NE(HS_SUCCESS, err);
hs_set_allocator(nullptr, nullptr);
hs_free_database(db);
}
TEST(Serialize, DeserializeNullMalloc) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
size_t db_len;
err = hs_database_size(db, &db_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, db_len);
char *bytes = nullptr;
size_t bytes_len = 0;
err = hs_serialize_database(db, &bytes, &bytes_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, bytes_len);
hs_free_database(db);
size_t ser_len;
err = hs_serialized_database_size(bytes, bytes_len, &ser_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, ser_len);
err = hs_deserialize_database_at(bytes, ser_len, nullptr);
ASSERT_NE(HS_SUCCESS, err);
free(bytes);
}
TEST(Serialize, DeserializeUnalignedMalloc) {
hs_database_t *db;
hs_compile_error_t *c_err;
static const char *pattern = "hatstand.*(badgerbrush|teakettle)";
hs_error_t err = hs_compile(pattern, 0, HS_MODE_BLOCK, nullptr, &db, &c_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(db != nullptr);
size_t db_len;
err = hs_database_size(db, &db_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, db_len);
char *bytes = nullptr;
size_t bytes_len = 0;
err = hs_serialize_database(db, &bytes, &bytes_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, bytes_len);
hs_free_database(db);
size_t ser_len;
err = hs_serialized_database_size(bytes, bytes_len, &ser_len);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0, ser_len);
// and now fail when deserialize addr is unaligned
char *new_db = (char *)malloc(ser_len + 8);
for (int i = 1; i < 8; i++) {
err = hs_deserialize_database_at(bytes, ser_len,
(hs_database_t *)(new_db + i));
ASSERT_NE(HS_SUCCESS, err);
}
free(new_db);
free(bytes);
}
}

622
unit/hyperscan/single.cpp Normal file
View File

@@ -0,0 +1,622 @@
/*
* 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 "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
#include <string>
#include <tuple>
using namespace std;
using namespace testing;
namespace /* anonymous */ {
class HyperscanSingleBase {
public:
virtual ~HyperscanSingleBase() {}
// Simplest case: did we successfully produce a non-zero length database
void compile() {
SCOPED_TRACE("Compile");
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(db != nullptr);
EXPECT_TRUE(compile_err == nullptr);
size_t db_size;
err = hs_database_size(db, &db_size);
EXPECT_EQ(HS_SUCCESS, err);
EXPECT_NE(0U, db_size);
}
// Can we query the database for various information?
void queryDatabase() {
SCOPED_TRACE("QueryDatabase");
// info about the database, should begin with "Version:"
char *info;
err = hs_database_info(db, &info);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(nullptr, info);
ASSERT_STREQ("Version:", string(info).substr(0, 8).c_str());
free(info);
// stream size, which is non-zero if streaming
size_t stream_size;
err = hs_stream_size(db, &stream_size);
if (mode & HS_MODE_STREAM) {
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_LT(0U, stream_size);
ASSERT_GT(100000, stream_size); /* more than 100k of stream size
* and we are probably returning
* rubbish. */
} else {
ASSERT_EQ(HS_DB_MODE_ERROR, err);
}
}
protected:
unsigned int mode; // HS_MODE_* mode flags
hs_error_t err;
hs_compile_error_t *compile_err;
hs_database_t *db;
};
class HyperscanTestRuntime
: public HyperscanSingleBase,
public TestWithParam<
tuple<const char *, unsigned int, unsigned int, unsigned long long>> {
protected:
virtual void SetUp() {
// compiles a database for this test instantiation
const char *pattern;
unsigned int flags;
unsigned long long feature_mask;
tie(pattern, flags, mode, feature_mask) = GetParam();
hs_platform_info_t plat;
err = hs_populate_platform(&plat);
ASSERT_EQ(HS_SUCCESS, err);
plat.cpu_features &= feature_mask;
err = hs_compile(pattern, flags, mode, &plat, &db, &compile_err);
}
virtual void TearDown() {
hs_free_database(db);
hs_free_compile_error(compile_err);
}
// does a simple scan with a dummy handler (ignores matches)
virtual void simpleScan() {
const size_t datalen = 2048;
char data[datalen];
memset(data, 'X', datalen);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(scratch != nullptr);
if (mode & HS_MODE_STREAM) {
// streaming mode scan
hs_stream_t *stream = nullptr;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
// just for fun, scan a zero-byte block.
err = hs_scan_stream(stream, data, 0, 0, scratch, dummy_cb,
nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data, datalen, 0, scratch,
dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_close_stream(stream, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
} else if (mode & HS_MODE_VECTORED) {
const char * const vec_bufs[] = { data };
const unsigned int vec_lens[] = { datalen };
err = hs_scan_vector(db, vec_bufs, vec_lens, 1, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
} else {
// block mode scan
ASSERT_TRUE(mode & HS_MODE_BLOCK);
err = hs_scan(db, data, datalen, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
}
// teardown
hs_free_scratch(scratch);
}
virtual void zeroLengthScan() {
const size_t datalen = 20;
char data[datalen];
memset(data, 'X', datalen);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(scratch != nullptr);
if (mode & HS_MODE_STREAM) {
// streaming mode scan
hs_stream_t *stream = nullptr;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_scan_stream(stream, data, 0, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_close_stream(stream, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
} else if (mode & HS_MODE_VECTORED) {
const char * const vec_bufs[] = { data };
const unsigned int vec_lens[] = { 0 };
err = hs_scan_vector(db, vec_bufs, vec_lens, 1, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
} else {
// block mode scan
ASSERT_TRUE(mode & HS_MODE_BLOCK);
err = hs_scan(db, data, 0, 0, scratch, dummy_cb, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
}
// teardown
hs_free_scratch(scratch);
}
// Can we allocate and clone scratch
void allocateScratch() {
SCOPED_TRACE("AllocateScratch");
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(scratch != nullptr);
// Try cloning the scratch
hs_scratch_t *cloned = nullptr;
err = hs_clone_scratch(scratch, &cloned);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(cloned != nullptr);
hs_free_scratch(scratch);
hs_free_scratch(cloned);
}
// Can we scan with the database (ignoring the matches, and using
// streaming/block mode appropriately)
void scanIgnoreMatches() {
SCOPED_TRACE("ScanIgnoreMatches");
simpleScan();
}
// Can we serialize and deserialize the database
void serialiseAndDeserializeCombo() {
SCOPED_TRACE("SerialiseAndDeserializeCombo");
char *bytes = nullptr;
size_t len = 0;
// serialise
err = hs_serialize_database(db, &bytes, &len);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(bytes != nullptr);
EXPECT_NE(0U, len);
// destroy original database
size_t origSize;
err = hs_database_size(db, &origSize);
ASSERT_EQ(HS_SUCCESS, err);
memset(db, 0xff, origSize);
free(db); /* hs_free_database not used as it is no longer a valid db */
// relocate to 16 different alignments, ensuring that we can
// deserialize from any string
char *buffer = new char[len + 16];
for (size_t i = 0; i < 16; i++) {
size_t newSize;
memset(buffer, 0, len + 16);
// relocate
char *copy = buffer + i;
memcpy(copy, bytes, len);
// deserialise
hs_database_t *newdb;
err = hs_deserialize_database(copy, len, &newdb);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(newdb != nullptr);
err = hs_database_size(newdb, &newSize);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_EQ(origSize, newSize);
// deserialiseAt
// alloc a new place for this DB
size_t buflen;
err = hs_serialized_database_size(copy, len, &buflen);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_NE(0U, buflen);
char *buf = new char[buflen];
memset(buf, 0, buflen);
// deserialise to our buffer
hs_database_t *newdbAt = (hs_database_t *)buf;
err = hs_deserialize_database_at(copy, len, newdbAt);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_database_size(newdb, &newSize);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_EQ(origSize, newSize);
{
SCOPED_TRACE("DeserializeAt");
// Replace test harness DB and run a simple scan as a test
db = newdbAt;
simpleScan();
delete[] buf;
db = nullptr;
}
{
SCOPED_TRACE("Deserialize");
// Replace test harness DB and run a simple scan as a test
db = newdb;
simpleScan();
hs_free_database(newdb);
db = nullptr;
}
}
delete[] buffer;
free(bytes);
}
};
TEST_P(HyperscanTestRuntime, Combo) {
compile();
queryDatabase();
allocateScratch();
zeroLengthScan();
scanIgnoreMatches();
serialiseAndDeserializeCombo();
}
static const char *validPatterns[] = {
"foobar",
"abd.*def",
"abc[123]def",
"[pqr]",
".",
"\\s",
"hatstand.*(teakettle|badgerbrush)",
"abc{1,3}",
"abc",
"^.{1,10}flibble",
"(foobar)+",
"(foo){2,5}",
"((foo){2}){3}",
"^.*test[a-f]{3}pattern.*$",
"(([^u]|.){16}|x){1,2}", // from bug UE-500
// UE-1553: these should compile without eating all your RAM.
"fooa?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?",
"fooa?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?",
// UE-1816 (makes libpcre hit its match limit)
"((aaa|[aaa]aa|aa[^aaaa]a|a.aaa.|a)){27}",
// UE-2370 (makes libpcre hit its match limit)
"^.{0,40}((e{0,9}|b){16}a.A|[da]..ecbcbcc[^e]de{10,20}[Bb]{3}[dEe]*){2}",
};
static const unsigned validFlags[] = {
0,
HS_FLAG_CASELESS,
HS_FLAG_DOTALL,
HS_FLAG_MULTILINE,
HS_FLAG_CASELESS | HS_FLAG_DOTALL | HS_FLAG_MULTILINE,
HS_FLAG_SINGLEMATCH,
};
static const unsigned validModes[] = {
HS_MODE_BLOCK,
HS_MODE_STREAM,
HS_MODE_VECTORED,
};
// Mode bits for switching off various architecture features
static const unsigned long long featureMask[] = {
~0ULL, /* native */
~HS_CPU_FEATURES_AVX2, /* no avx2 */
};
INSTANTIATE_TEST_CASE_P(Single,
HyperscanTestRuntime,
Combine(ValuesIn(validPatterns),
ValuesIn(validFlags),
ValuesIn(validModes),
ValuesIn(featureMask)));
struct TestPlatform {
TestPlatform() : features(0ULL) {}
TestPlatform(unsigned long long f) : features(f) {}
unsigned long long features;
};
class HyperscanTestCrossCompile
: public HyperscanSingleBase,
public TestWithParam<
tuple<const char *, unsigned, unsigned, TestPlatform>> {
protected:
virtual void SetUp() {
// compiles a database for this test instantiation
const char *pattern;
unsigned flags;
TestPlatform tp;
tie(pattern, flags, mode, tp) = GetParam();
hs_platform_info_t plat;
plat.tune = HS_TUNE_FAMILY_GENERIC;
plat.cpu_features = tp.features;
err = hs_compile(pattern, flags, mode, &plat, &db, &compile_err);
}
virtual void TearDown() {
hs_free_database(db);
hs_free_compile_error(compile_err);
}
// Attempt to allocate a scratch region. This should succeed for the local
// platform and return HS_DB_PLATFORM_ERROR for all the others.
void attemptScratchAlloc() {
SCOPED_TRACE("AttemptScratchAlloc");
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
// XXX: it'd be nice to be a bit less cavalier about this and actually
// find out what the local platform is, then check explicitly that only
// that platform produces HS_SUCCESS.
if (err == HS_SUCCESS) {
// host platform.
hs_free_scratch(scratch);
} else {
ASSERT_EQ(HS_DB_PLATFORM_ERROR, err);
ASSERT_TRUE(!scratch);
}
}
};
TEST_P(HyperscanTestCrossCompile, Build) {
compile();
queryDatabase();
attemptScratchAlloc();
}
static const TestPlatform validPlatforms[] = {
TestPlatform(0),
TestPlatform(HS_CPU_FEATURES_AVX2),
};
INSTANTIATE_TEST_CASE_P(Single,
HyperscanTestCrossCompile,
Combine(ValuesIn(validPatterns),
ValuesIn(validFlags),
ValuesIn(validModes),
ValuesIn(validPlatforms)));
// Also instantiate some normal and cross-compile tests for SOM patterns.
static const char *validSomPatterns[] = {
"foobar",
"abd.*def",
"abc[123]def",
"[pqr]",
".",
"\\s",
"hatstand.*(teakettle|badgerbrush)",
"abc{1,3}",
"abc",
"^.{1,10}flibble",
"(foobar)+",
"(foo){2,5}",
"((foo){2}){3}",
"^.*test[a-f]{3}pattern.*$",
};
static const unsigned validSomFlags[] = {
HS_FLAG_SOM_LEFTMOST,
};
static const unsigned validSomModes[] = {
HS_MODE_BLOCK,
HS_MODE_STREAM | HS_MODE_SOM_HORIZON_LARGE,
HS_MODE_VECTORED,
};
INSTANTIATE_TEST_CASE_P(SingleSom, HyperscanTestRuntime,
Combine(ValuesIn(validSomPatterns),
ValuesIn(validSomFlags),
ValuesIn(validSomModes),
ValuesIn(featureMask)));
INSTANTIATE_TEST_CASE_P(SingleSom, HyperscanTestCrossCompile,
Combine(ValuesIn(validSomPatterns),
ValuesIn(validSomFlags),
ValuesIn(validSomModes),
ValuesIn(validPlatforms)));
struct TerminateMatchData {
TerminateMatchData(const char *pattern_, unsigned int flags_,
const char *corpus_) : pattern(pattern_), flags(flags_),
corpus(corpus_) {}
const char *pattern;
unsigned int flags;
const char *corpus;
};
class HyperscanTestMatchTerminate : public TestWithParam<TerminateMatchData> {
// empty
};
// Simple non-terminating callback: increments the int pointed to by the context
// and tells the matcher to keep going.
int countHandler(unsigned, unsigned long long, unsigned long long,
unsigned, void *ctx) {
int *count = (int *)ctx;
(*count)++;
return 0; // keep going
}
// Simple terminating callback: increments the int pointed to by the context
// and tells the matcher to stop.
int terminateHandler(unsigned, unsigned long long, unsigned long long,
unsigned, void *ctx) {
int *count = (int *)ctx;
(*count)++;
return 1; // stop matching
}
TEST_P(HyperscanTestMatchTerminate, MoreThanOne) {
const TerminateMatchData &data = GetParam();
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch(data.pattern, data.flags, 0,
HS_MODE_BLOCK, &scratch);
int count = 0;
err = hs_scan(db, data.corpus, strlen(data.corpus), 0, scratch,
countHandler, &count);
ASSERT_EQ(HS_SUCCESS, err) << "hs_scan didn't return HS_SCAN_TERMINATED";
ASSERT_LT(1, count) << "Number of matches returned was not greater than 1.";
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST_P(HyperscanTestMatchTerminate, Block) {
const TerminateMatchData &data = GetParam();
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch(data.pattern, data.flags, 0,
HS_MODE_BLOCK, &scratch);
int count = 0;
err = hs_scan(db, data.corpus, strlen(data.corpus), 0, scratch,
terminateHandler, &count);
ASSERT_EQ(HS_SCAN_TERMINATED, err)
<< "hs_scan didn't return HS_SCAN_TERMINATED";
ASSERT_EQ(1, count) << "Number of matches returned was not 1.";
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST_P(HyperscanTestMatchTerminate, StreamWhole) {
const TerminateMatchData& data = GetParam();
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch(data.pattern, data.flags, 0,
HS_MODE_STREAM, &scratch);
int count = 0;
hs_stream_t *stream = nullptr;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err) << "Stream open failed";
ASSERT_TRUE(stream != nullptr);
err = hs_scan_stream(stream, data.corpus, strlen(data.corpus), 0, scratch,
terminateHandler, &count);
ASSERT_TRUE(err == HS_SUCCESS || err == HS_SCAN_TERMINATED);
err = hs_close_stream(stream, scratch, terminateHandler, &count);
ASSERT_EQ(1, count) << "Number of matches returned was not 1.";
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST_P(HyperscanTestMatchTerminate, StreamByteByByte) {
const TerminateMatchData& data = GetParam();
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch(data.pattern, data.flags, 0,
HS_MODE_STREAM, &scratch);
int count = 0;
hs_stream_t *stream = nullptr;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err) << "Stream open failed";
ASSERT_TRUE(stream != nullptr);
size_t len = strlen(data.corpus);
for (size_t i = 0; i < len; i++) {
err = hs_scan_stream(stream, data.corpus + i, 1, 0, scratch,
terminateHandler, &count);
ASSERT_TRUE(err == HS_SUCCESS || err == HS_SCAN_TERMINATED);
}
err = hs_close_stream(stream, scratch, terminateHandler, &count);
ASSERT_EQ(1, count) << "Number of matches returned was not 1.";
hs_free_scratch(scratch);
hs_free_database(db);
}
// Each of these cases must match multiple times
const TerminateMatchData terminateCases[] = {
TerminateMatchData("foobar", 0, "foobarfoobarfoobar"),
TerminateMatchData("a", 0, "a a a a a a a a a a a"),
TerminateMatchData(".", 0, "zzzzzzzzzzzzaaaaaaaaaaaaa"),
TerminateMatchData("...", 0, "zzzzzzzzzzzzaaaaaaaaaaaaa"),
TerminateMatchData("[a-z]{3,7}", 0, "zzzzzzzzzzzzaaaaaaaaaaaaa"),
TerminateMatchData("a", HS_FLAG_CASELESS, " xAaAa"),
TerminateMatchData("xyzzy", HS_FLAG_CASELESS, "abcdef XYZZy xyzzy XyZzY"),
TerminateMatchData("abc.*def", 0, "abc abc abc def def"),
TerminateMatchData("abc.*def", HS_FLAG_DOTALL, "abc abc abc def def"),
TerminateMatchData("(01234|abcde).*(foo|bar)", 0, "abcde xxxx bar foo abcde foo"),
TerminateMatchData("(01234|abcde).*(foo|bar)", HS_FLAG_DOTALL, "abcde xxxx bar foo abcde foo"),
TerminateMatchData("[0-9a-f]{4,10}.*(foobar|bazbaz)", 0, "0123456789abcdef bazbaz foobar"),
TerminateMatchData("[0-9a-f]{4,10}.*(foobar|bazbaz)", HS_FLAG_DOTALL, "0123456789abcdef bazbaz foobar"),
TerminateMatchData("^foobar[^z]{20,}", 0, "foobarxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
TerminateMatchData("hatstand|teakettle|badgerbrush|mnemosyne", 0, "hatstand teakettle badgerbrush mnemosyne"),
TerminateMatchData("a|b|c|d", 0, "a b c d a b c d a b c d"),
TerminateMatchData("bat|cat|mat|rat|fat|sat|pat|hat|vat", HS_FLAG_CASELESS, "VAt hat pat sat fat rat mat caT BAT"),
};
INSTANTIATE_TEST_CASE_P(Single, HyperscanTestMatchTerminate, ValuesIn(terminateCases));
} // namespace

200
unit/hyperscan/som.cpp Normal file
View File

@@ -0,0 +1,200 @@
/*
* 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 <cstring>
#include <vector>
#include "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
using namespace std;
using namespace testing;
namespace {
struct Match {
Match(unsigned i, unsigned long long f, unsigned long long t) :
id(i), from(f), to(t) {}
unsigned int id;
unsigned long long from;
unsigned long long to;
};
}
static
int vectorCallback(unsigned id, unsigned long long from,
unsigned long long to, unsigned, void *ctx) {
//printf("match id %u at (%llu,%llu)\n", id, from, to);
vector<Match> *matches = (vector<Match> *)ctx;
matches->push_back(Match(id, from, to));
return 0;
}
class SomTest : public TestWithParam<unsigned int> {
protected:
virtual void SetUp() {
som_mode = GetParam();
}
unsigned long long getSomHorizon() const {
switch (som_mode) {
case HS_MODE_SOM_HORIZON_SMALL:
return 1ULL << 16;
case HS_MODE_SOM_HORIZON_MEDIUM:
return 1ULL << 32;
default:
return ~(0ULL);
}
}
unsigned int som_mode;
};
TEST_P(SomTest, PastHorizon) {
hs_database_t *db = buildDB("foo.*bar", HS_FLAG_SOM_LEFTMOST, 1000,
HS_MODE_STREAM | som_mode);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(scratch != nullptr);
vector<Match> matches;
hs_stream_t *stream = nullptr;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
const string prefix(" foo");
const string suffix("bar");
const string filler(4096, 'X');
unsigned long long scanned_len = 0;
err = hs_scan_stream(stream, prefix.c_str(), prefix.length(), 0, scratch,
vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0, matches.size());
scanned_len += prefix.length();
const unsigned long long blocks = getSomHorizon() / filler.length();
for (unsigned long long i = 0; i < blocks; i += 1) {
err = hs_scan_stream(stream, filler.c_str(), filler.length(), 0,
scratch, vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0, matches.size());
scanned_len += filler.length();
}
err = hs_scan_stream(stream, suffix.c_str(), suffix.length(), 0, scratch,
vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
scanned_len += suffix.length();
// We receive one match with the correct 'to' offset, but a sentinel value
// for 'from'.
ASSERT_EQ(1, matches.size());
ASSERT_EQ(1000, matches[0].id);
ASSERT_EQ(HS_OFFSET_PAST_HORIZON, matches[0].from);
ASSERT_EQ(scanned_len, matches[0].to);
err = hs_close_stream(stream, scratch, vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
// teardown
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST_P(SomTest, NearHorizon) {
hs_database_t *db = buildDB("foo.*bar", HS_FLAG_SOM_LEFTMOST, 1000,
HS_MODE_STREAM | som_mode);
ASSERT_TRUE(db != nullptr);
hs_scratch_t *scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(scratch != nullptr);
vector<Match> matches;
hs_stream_t *stream = nullptr;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
const string prefix(" foo");
const string suffix("bar");
const string filler(4096, 'X');
unsigned long long scanned_len = 0;
err = hs_scan_stream(stream, prefix.c_str(), prefix.length(), 0, scratch,
vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0, matches.size());
scanned_len += prefix.length();
const unsigned long long blocks = getSomHorizon() / filler.length() - 1;
for (unsigned long long i = 0; i < blocks; i += 1) {
err = hs_scan_stream(stream, filler.c_str(), filler.length(), 0,
scratch, vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0, matches.size());
scanned_len += filler.length();
}
err = hs_scan_stream(stream, suffix.c_str(), suffix.length(), 0, scratch,
vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
scanned_len += suffix.length();
// Both 'to' and 'from' should be accurate.
ASSERT_EQ(1, matches.size());
ASSERT_EQ(1000, matches[0].id);
ASSERT_EQ(1, matches[0].from);
ASSERT_EQ(scanned_len, matches[0].to);
err = hs_close_stream(stream, scratch, vectorCallback, &matches);
ASSERT_EQ(HS_SUCCESS, err);
// teardown
hs_free_scratch(scratch);
hs_free_database(db);
}
INSTANTIATE_TEST_CASE_P(Som, SomTest,
Values(HS_MODE_SOM_HORIZON_SMALL,
HS_MODE_SOM_HORIZON_MEDIUM));

View File

@@ -0,0 +1,767 @@
/*
* 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 <vector>
#include "gtest/gtest.h"
#include "hs.h"
#include "test_util.h"
using namespace std;
namespace /* anonymous */ {
int record_cb2(unsigned id, unsigned long long, unsigned long long to,
unsigned, void *ctxt) {
CallBackContext *c = (CallBackContext *)ctxt;
c->matches.push_back(MatchRecord(to + 1000, id));
return (int)c->halt;
}
static const char data1[] = "barfoobar";
TEST(StreamUtil, reset1) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
CallBackContext c, c2;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_reset_stream(stream, 0, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb2,
(void *)&c2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
ASSERT_EQ(1U, c2.matches.size());
ASSERT_EQ(MatchRecord(1009, 0), c2.matches[0]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, reset2) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
CallBackContext c, c2;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
c.halt = 1;
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_reset_stream(stream, 0, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
ASSERT_EQ(1U, c2.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c2.matches[0]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, reset_matches) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar$", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_scan_stream(stream, data1, strlen(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
err = hs_reset_stream(stream, 0, scratch, record_cb, (void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy1) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_copy_stream(&stream2, stream);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_EQ(MatchRecord(13, 0), c.matches[0]);
ASSERT_EQ(MatchRecord(19, 0), c.matches[1]);
c.matches.clear();
err = hs_scan_stream(stream2, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_EQ(MatchRecord(13, 0), c.matches[0]);
ASSERT_EQ(MatchRecord(19, 0), c.matches[1]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy2) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
c.halt = 1;
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_copy_stream(&stream2, stream);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(0U, c.matches.size());
err = hs_scan_stream(stream2, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(0U, c.matches.size());
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy_reset1) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c, c2;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_open_stream(db, 0, &stream2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream2 != nullptr);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_reset_and_copy_stream(stream, stream2, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb2,
(void *)&c2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
ASSERT_EQ(1U, c2.matches.size());
ASSERT_EQ(MatchRecord(1009, 0), c2.matches[0]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy_reset2) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c, c2;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_open_stream(db, 0, &stream2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream2 != nullptr);
c.halt = 1;
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_reset_and_copy_stream(stream, stream2, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
ASSERT_EQ(1U, c2.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c2.matches[0]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy_reset3) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_open_stream(db, 0, &stream2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_reset_and_copy_stream(stream2, stream, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_EQ(MatchRecord(13, 0), c.matches[0]);
ASSERT_EQ(MatchRecord(19, 0), c.matches[1]);
c.matches.clear();
err = hs_scan_stream(stream2, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_EQ(MatchRecord(13, 0), c.matches[0]);
ASSERT_EQ(MatchRecord(19, 0), c.matches[1]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy_reset4) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_open_stream(db, 0, &stream2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream2 != nullptr);
c.halt = 1;
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_reset_and_copy_stream(stream2, stream, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(0U, c.matches.size());
err = hs_scan_stream(stream2, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SCAN_TERMINATED, err);
ASSERT_EQ(0U, c.matches.size());
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy_reset5) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_open_stream(db, 0, &stream2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
c.matches.clear();
err = hs_scan_stream(stream2, "foo", 3, 0, scratch, record_cb, (void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
err = hs_reset_and_copy_stream(stream2, stream, scratch, nullptr, nullptr);
ASSERT_EQ(HS_SUCCESS, err);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_EQ(MatchRecord(13, 0), c.matches[0]);
ASSERT_EQ(MatchRecord(19, 0), c.matches[1]);
c.matches.clear();
err = hs_scan_stream(stream2, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(2U, c.matches.size());
ASSERT_EQ(MatchRecord(13, 0), c.matches[0]);
ASSERT_EQ(MatchRecord(19, 0), c.matches[1]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
TEST(StreamUtil, copy_reset_matches) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar$", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
err = hs_open_stream(db, 0, &stream2);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream2 != nullptr);
err = hs_scan_stream(stream, data1, strlen(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(0U, c.matches.size());
err = hs_reset_and_copy_stream(stream, stream2, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
static size_t last_alloc;
static
void *wrap_m(size_t s) {
last_alloc = s;
return malloc(s);
}
TEST(StreamUtil, size) {
hs_error_t err;
hs_scratch_t *scratch = nullptr;
hs_database_t *db = buildDBAndScratch("foo.*bar", 0, 0, HS_MODE_STREAM,
&scratch);
hs_stream_t *stream = nullptr;
CallBackContext c, c2;
hs_set_allocator(wrap_m, nullptr);
last_alloc = 0;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
size_t stream_size;
err = hs_stream_size(db, &stream_size);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(last_alloc, stream_size);
hs_set_allocator(nullptr, nullptr);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
}
int alloc_called = 0;
int alloc2_called = 0;
int alloc3_called = 0;
void *bad_alloc(size_t) {
return nullptr;
}
void *my_alloc(size_t s) {
++alloc_called;
return malloc(s);
}
void my_free(void *p) {
free(p);
--alloc_called;
}
void *my_alloc2(size_t s) {
++alloc2_called;
return malloc(s);
}
void my_free2(void *p) {
free(p);
--alloc2_called;
}
void *my_alloc3(size_t s) {
++alloc3_called;
return malloc(s);
}
void my_free3(void *p) {
free(p);
--alloc3_called;
}
TEST(StreamUtil, Alloc) {
hs_compile_error_t *compile_err = nullptr;
hs_database_t *db;
hs_set_stream_allocator(my_alloc, my_free);
alloc_called = 0;
hs_error_t err = hs_compile("foo.*bar", 0, HS_MODE_STREAM, nullptr, &db,
&compile_err);
ASSERT_EQ(HS_SUCCESS, err);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
ASSERT_NE(alloc_called, 0);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
hs_close_stream(stream, scratch, record_cb, (void *)&c);
ASSERT_EQ(alloc_called, 0);
hs_free_scratch(scratch);
hs_free_database(db);
hs_free_compile_error(compile_err);
hs_set_allocator(nullptr, nullptr);
}
TEST(StreamUtil, MoreAlloc) {
hs_compile_error_t *compile_err = nullptr;
hs_database_t *db;
hs_set_allocator(my_alloc, my_free);
alloc_called = 0;
hs_error_t err = hs_compile("foo.*bar", 0, HS_MODE_STREAM, nullptr, &db,
&compile_err);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1, alloc_called);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(2, alloc_called);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
CallBackContext c;
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_TRUE(stream != nullptr);
ASSERT_EQ(alloc_called, 3);
err = hs_scan_stream(stream, data1, sizeof(data1), 0, scratch, record_cb,
(void *)&c);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1U, c.matches.size());
ASSERT_EQ(MatchRecord(9, 0), c.matches[0]);
hs_close_stream(stream, scratch, nullptr, nullptr);
ASSERT_EQ(alloc_called, 2);
hs_free_scratch(scratch);
ASSERT_EQ(alloc_called, 1);
hs_free_database(db);
ASSERT_EQ(alloc_called, 0);
hs_free_compile_error(compile_err);
hs_set_allocator(nullptr, nullptr);
}
TEST(StreamUtil, BadStreamAlloc) {
hs_compile_error_t *compile_err = nullptr;
hs_database_t *db;
hs_set_stream_allocator(bad_alloc, free);
alloc_called = 0;
hs_error_t err = hs_compile("foo.*bar", 0, HS_MODE_STREAM, nullptr, &db,
&compile_err);
ASSERT_EQ(HS_SUCCESS, err);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
CallBackContext c;
// should go boom
err = hs_open_stream(db, 0, &stream);
ASSERT_NE(HS_SUCCESS, err);
hs_free_scratch(scratch);
hs_free_database(db);
hs_free_compile_error(compile_err);
hs_set_stream_allocator(nullptr, nullptr);
}
TEST(StreamUtil, StreamAllocUsage) {
hs_compile_error_t *compile_err = nullptr;
hs_database_t *db;
hs_set_allocator(my_alloc, my_free);
hs_set_stream_allocator(my_alloc2, my_free2);
hs_set_scratch_allocator(my_alloc3, my_free3);
alloc_called = 0;
alloc2_called = 0;
alloc3_called = 0;
hs_error_t err = hs_compile("foo.*bar", 0, HS_MODE_STREAM, nullptr, &db,
&compile_err);
ASSERT_EQ(HS_SUCCESS, err);
hs_scratch_t *scratch = nullptr;
err = hs_alloc_scratch(db, &scratch);
ASSERT_EQ(HS_SUCCESS, err);
hs_stream_t *stream = nullptr;
hs_stream_t *stream2 = nullptr;
hs_stream_t *stream3 = nullptr;
CallBackContext c;
ASSERT_EQ(1, alloc_called);
ASSERT_EQ(0, alloc2_called);
ASSERT_EQ(1, alloc3_called);
err = hs_open_stream(db, 0, &stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1, alloc_called);
ASSERT_EQ(1, alloc2_called);
ASSERT_EQ(1, alloc3_called);
err = hs_copy_stream(&stream2, stream);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1, alloc_called);
ASSERT_EQ(2, alloc2_called);
ASSERT_EQ(1, alloc3_called);
err = hs_open_stream(db, 0, &stream3);
ASSERT_EQ(HS_SUCCESS, err);
ASSERT_EQ(1, alloc_called);
ASSERT_EQ(3, alloc2_called);
ASSERT_EQ(1, alloc3_called);
hs_close_stream(stream, scratch, nullptr, nullptr);
hs_close_stream(stream2, scratch, nullptr, nullptr);
hs_close_stream(stream3, scratch, nullptr, nullptr);
hs_free_scratch(scratch);
hs_free_database(db);
hs_free_compile_error(compile_err);
hs_set_allocator(nullptr, nullptr);
ASSERT_EQ(0, alloc_called);
ASSERT_EQ(0, alloc2_called);
ASSERT_EQ(0, alloc3_called);
}
}

View File

@@ -0,0 +1,234 @@
/*
* 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 <cstring>
#include <iostream>
#include <string>
#include <vector>
#include "hs.h"
#include "test_util.h"
#include "gtest/gtest.h"
#include "util/expressions.h"
#include "util/ExpressionParser.h"
using namespace std;
int record_cb(unsigned id, unsigned long long, unsigned long long to,
unsigned, void *ctxt) {
CallBackContext *c = (CallBackContext *)ctxt;
c->matches.push_back(MatchRecord(to, id));
return (int)c->halt;
}
std::ostream &operator<< (std::ostream &o, const MatchRecord &m) {
return o << "[" << m.to << ", " << m.id << "]";
}
hs_database_t *buildDB(const vector<pattern> &patterns, unsigned int mode,
hs_platform_info *plat) {
vector<const char *> expressions;
vector<unsigned int> flags;
vector<unsigned int> ids;
vector<const hs_expr_ext *> ext;
for (vector<pattern>::const_iterator it = patterns.begin();
it != patterns.end(); ++it) {
expressions.push_back(it->expression.c_str());
flags.push_back(it->flags);
ids.push_back(it->id);
ext.push_back(&it->ext);
}
hs_database_t *db = nullptr;
hs_compile_error_t *compile_err = nullptr;
hs_error_t err;
err = hs_compile_ext_multi(&expressions[0], &flags[0], &ids[0], &ext[0],
patterns.size(), mode, plat, &db, &compile_err);
if (err != HS_SUCCESS) {
return nullptr;
}
return db;
}
hs_database_t *buildDB(const pattern &expr, unsigned int mode) {
return buildDB(vector<pattern>(1, expr), mode);
}
hs_database_t *buildDB(const char *expression, unsigned int flags,
unsigned int id, unsigned int mode,
hs_platform_info_t *plat) {
vector<pattern> patterns;
patterns.push_back(pattern(expression, flags, id));
return buildDB(patterns, mode, plat);
}
hs_database_t *buildDB(const char *filename, unsigned int mode,
unsigned int extra_flags) {
vector<pattern> patterns;
ExpressionMap expressions;
loadExpressionsFromFile(filename, expressions);
for (ExpressionMap::iterator it = expressions.begin();
it != expressions.end(); ++it) {
unsigned int flags = 0;
string regex;
hs_expr_ext ext;
if (!readExpression(it->second, regex, &flags, &ext)) {
return nullptr;
}
patterns.push_back(pattern(regex, flags | extra_flags, it->first,
ext));
}
return buildDB(patterns, mode);
}
static
bool isOrdered(const string &expr, unsigned int flags) {
// SOM doesn't produce ordered matches?
if (flags & HS_FLAG_SOM_LEFTMOST) {
return false;
}
hs_expr_info_t *info = nullptr;
hs_compile_error_t *error = nullptr;
hs_error_t err = hs_expression_info(expr.c_str(), flags, &info, &error);
if (err != HS_SUCCESS) {
// Expression will fail compilation and report error elsewhere.
free(info);
hs_free_compile_error(error);
return false;
}
assert(info);
// Any pattern that does not require offset adjustment should produce
// matches in order.
bool ordered = !info->unordered_matches;
free(info);
return ordered;
}
hs_database_t *buildDB(const char *filename, unsigned int mode,
bool check_ordering) {
vector<pattern> patterns;
ExpressionMap expressions;
loadExpressionsFromFile(filename, expressions);
for (ExpressionMap::iterator it = expressions.begin();
it != expressions.end(); ++it) {
unsigned int flags = 0;
string regex;
hs_expr_ext ext;
bool must_be_ordered;
if (!readExpression(it->second, regex, &flags, &ext, &must_be_ordered)) {
return nullptr;
}
if (check_ordering && must_be_ordered && !isOrdered(regex, flags)) {
return nullptr;
}
patterns.emplace_back(regex, flags, it->first, ext);
}
return buildDB(patterns, mode);
}
hs_database_t *buildDBAndScratch(const char *expression, unsigned int flags,
unsigned int id, unsigned int mode,
hs_scratch_t **scratch) {
hs_database_t *db = buildDB(expression, flags, id, mode);
EXPECT_TRUE(db != nullptr);
*scratch = nullptr;
hs_error_t err = hs_alloc_scratch(db, scratch);
EXPECT_EQ(HS_SUCCESS, err);
EXPECT_TRUE(*scratch != nullptr);
return db;
}
size_t allocated_count;
size_t allocated_count_b;
void *count_malloc(size_t n) {
void *pp = malloc(n + 16);
if (!pp) {
return nullptr;
}
allocated_count += n;
*(size_t *)pp = n;
void *p = (char *)pp + 16;
return p;
}
void count_free(void *p) {
if (!p) {
return;
}
void *pp = (char *)p - 16;
size_t n = *(size_t *)pp;
allocated_count -= n;
free(pp);
}
void *count_malloc_b(size_t n) {
void *pp = malloc(n + 32);
if (!pp) {
return nullptr;
}
allocated_count_b += n;
*(size_t *)pp = n;
void *p = (char *)pp + 32;
return p;
}
void count_free_b(void *p) {
if (!p) {
return;
}
void *pp = (char *)p - 32;
size_t n = *(size_t *)pp;
allocated_count_b -= n;
free(pp);
}

120
unit/hyperscan/test_util.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 TEST_UTIL_H
#define TEST_UTIL_H
#include <iosfwd>
#include <vector>
#include "hs.h"
#ifndef UNUSED
#if defined(_WIN32) || defined(_WIN64)
#define UNUSED
#else
#define UNUSED __attribute__ ((unused))
#endif
#endif
struct MatchRecord {
MatchRecord(unsigned long long t, int i) : to(t), id(i) {}
bool operator==(const MatchRecord &o) const {
return to == o.to && id == o.id;
}
unsigned long long to;
int id;
};
std::ostream &operator<< (std::ostream &o, const MatchRecord &m);
struct CallBackContext {
CallBackContext() : halt(false) {}
bool halt;
std::vector<MatchRecord> matches;
void clear() {
halt = false;
matches.clear();
}
};
int record_cb(unsigned id, unsigned long long, unsigned long long to,
unsigned, void *ctxt);
// Dummy callback: does nothing, returns 0 (keep matching)
static UNUSED
int dummy_cb(unsigned, unsigned long long, unsigned long long, unsigned,
void *) {
// empty
return 0;
}
struct pattern {
std::string expression;
unsigned int flags;
unsigned int id;
hs_expr_ext ext;
pattern(const std::string &expression_in, unsigned int flags_in = 0,
unsigned int id_in = 0) : expression(expression_in),
flags(flags_in), id(id_in) {
memset(&ext, 0, sizeof(ext));
}
pattern(const std::string &expression_in, unsigned int flags_in,
unsigned int id_in, const hs_expr_ext &ext_in) :
expression(expression_in), flags(flags_in), id(id_in),
ext(ext_in) { }
};
hs_database_t *buildDB(const std::vector<pattern> &patterns, unsigned int mode,
hs_platform_info *plat = nullptr);
hs_database_t *buildDB(const pattern &pat, unsigned int mode);
hs_database_t *buildDB(const char *expression, unsigned int flags,
unsigned int id, unsigned int mode,
hs_platform_info *plat = nullptr);
hs_database_t *buildDB(const char *filename, unsigned int mode,
unsigned int extra_flags = 0);
hs_database_t *buildDB(const char *filename, unsigned int mode,
bool check_ordering);
hs_database_t *buildDBAndScratch(const char *expression, unsigned int flags,
unsigned int id, unsigned int mode,
hs_scratch_t **scratch);
extern size_t allocated_count;
extern size_t allocated_count_b;
void *count_malloc(size_t n);
void *count_malloc_b(size_t n);
void count_free(void *p);
void count_free_b(void *p);
#endif

461
unit/internal/bitfield.cpp Normal file
View File

@@ -0,0 +1,461 @@
/*
* 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 "gtest/gtest.h"
#include "util/bitfield.h"
#include "util/ue2_containers.h"
#include <algorithm>
using namespace ue2;
template<size_t N>
std::ostream& operator<<(std::ostream &os, const bitfield<N> &bf) {
const size_t npos = bitfield<N>::npos;
os << "{";
size_t i = bf.find_first();
while (i != npos) {
os << i;
i = bf.find_next(i);
if (i != npos) {
os << ", ";
}
}
os << "}";
return os;
}
template <typename T> class BitfieldTest : public testing::Test {};
typedef ::testing::Types<
bitfield<1>,
bitfield<8>,
bitfield<32>,
bitfield<64>,
bitfield<100>,
bitfield<127>,
bitfield<129>,
bitfield<300>,
bitfield<512>,
bitfield<2048>,
bitfield<10000>
> BitfieldTypes;
TYPED_TEST_CASE(BitfieldTest, BitfieldTypes);
TYPED_TEST(BitfieldTest, empty) {
TypeParam a;
EXPECT_TRUE(a.none());
EXPECT_EQ(0, a.count());
}
TYPED_TEST(BitfieldTest, set1) {
TypeParam a;
const size_t size = a.size();
const size_t npos = TypeParam::npos;
for (size_t i = 0; i < size; i++) {
a.clear();
EXPECT_TRUE(a.none());
a.set(i);
EXPECT_FALSE(a.none());
EXPECT_TRUE(a.test(i));
EXPECT_EQ(i, a.find_first());
EXPECT_EQ(npos, a.find_next(i));
EXPECT_EQ(1, a.count());
a.clear(i);
EXPECT_TRUE(a.none());
EXPECT_FALSE(a.test(i));
EXPECT_EQ(npos, a.find_first());
EXPECT_EQ(0, a.count());
}
}
// Tests set(), find_first(), find_next().
TYPED_TEST(BitfieldTest, setn) {
const size_t size = TypeParam().size();
const size_t strides[] = { 600, 256, 80, 17, 7, 3 };
for (size_t s = 0; s < ARRAY_LENGTH(strides); s++) {
const size_t step = strides[s];
TypeParam a;
size_t num = 0;
for (size_t i = 0; i < size; i += step, num++) {
a.set(i);
}
EXPECT_EQ(num, a.count());
ASSERT_EQ(0, a.find_first());
size_t count = 1;
for (size_t i = a.find_next(0); i != TypeParam::npos;
i = a.find_next(i), count++) {
ASSERT_EQ(count * step, i);
}
ASSERT_EQ(num, count);
}
}
TYPED_TEST(BitfieldTest, allbits) {
TypeParam a;
a.setall();
EXPECT_FALSE(a.none());
EXPECT_TRUE(a.all());
EXPECT_EQ(a.size(), a.count());
a.flip();
EXPECT_TRUE(a.none());
EXPECT_FALSE(a.all());
EXPECT_EQ(0, a.count());
}
TYPED_TEST(BitfieldTest, allbits2) {
TypeParam a;
a.set(); // alias for setall
EXPECT_FALSE(a.none());
EXPECT_TRUE(a.all());
EXPECT_EQ(a.size(), a.count());
a.flip();
EXPECT_TRUE(a.none());
EXPECT_FALSE(a.all());
EXPECT_EQ(0, a.count());
}
TYPED_TEST(BitfieldTest, flipn) {
TypeParam a;
a.setall();
EXPECT_FALSE(a.none());
EXPECT_TRUE(a.all());
EXPECT_EQ(a.size(), a.count());
const size_t size = a.size();
for (size_t i = 0; i < size; i++) {
a.flip(i);
EXPECT_FALSE(a.test(i));
EXPECT_EQ(size - i - 1, a.count());
}
EXPECT_TRUE(a.none());
}
TYPED_TEST(BitfieldTest, flip_on) {
TypeParam a;
EXPECT_TRUE(a.none());
const size_t size = a.size();
for (size_t i = 0; i < size; i++) {
a.flip(i);
EXPECT_TRUE(a.test(i));
EXPECT_EQ(i + 1, a.count());
}
EXPECT_TRUE(a.all());
}
TYPED_TEST(BitfieldTest, equality_copy) {
TypeParam a, b;
const size_t size = a.size();
size_t step = (size + 6) / 7;
ASSERT_NE(0, step);
for (size_t i = 0; i < size; i += step) {
a.set(i);
b.set(i);
}
EXPECT_EQ(a, b);
EXPECT_EQ(a.hash(), b.hash());
TypeParam c(a); // copy
EXPECT_EQ(a, c);
EXPECT_EQ(b, c);
EXPECT_EQ(c.hash(), a.hash());
EXPECT_EQ(c.hash(), b.hash());
c.clear(c.find_first());
EXPECT_EQ(a.count() - 1, c.count());
EXPECT_NE(a, c);
}
TYPED_TEST(BitfieldTest, trivial_operations) {
const TypeParam a = ~TypeParam(); // all ones
const TypeParam b; // all zeroes
ASSERT_TRUE(a.all());
ASSERT_TRUE(b.none());
EXPECT_EQ(a, ~b);
EXPECT_EQ(b, ~a);
EXPECT_EQ(a, a | b);
EXPECT_EQ(b, a & b);
TypeParam c;
c = a; // assignment
EXPECT_EQ(c, a);
c &= b;
EXPECT_EQ(b, c); // all zeroes
c |= a;
EXPECT_EQ(a, c); // all ones
c = a ^ b;
EXPECT_EQ(a, c);
}
TYPED_TEST(BitfieldTest, even_odd) {
TypeParam even, odd;
const size_t size = even.size();
for (size_t i = 0; i < size; i++) {
if (i % 2) {
odd.set(i);
} else {
even.set(i);
}
}
for (size_t i = 0; i < size; i++) {
if (i % 2) {
EXPECT_TRUE(odd.test(i)) << "odd should contain " << i;
EXPECT_FALSE(even.test(i)) << "even should not contain " << i;
} else {
EXPECT_TRUE(even.test(i)) << "even should contain " << i;
EXPECT_FALSE(odd.test(i)) << "odd should not contain " << i;
}
}
EXPECT_TRUE(even != odd);
EXPECT_FALSE(even == odd);
EXPECT_TRUE((even | odd).all());
EXPECT_TRUE((even ^ odd).all());
EXPECT_TRUE((even & odd).none());
}
TYPED_TEST(BitfieldTest, operator_lt) {
const TypeParam a = ~TypeParam(); // all ones
const TypeParam b; // all zeroes
ASSERT_TRUE(a.all());
ASSERT_TRUE(b.none());
// Just some trivial tests; we have no particular ordering requirements for
// bitfield, so long as an ordering exists.
EXPECT_FALSE(a < a);
EXPECT_FALSE(b < b);
EXPECT_TRUE(b < a);
}
TYPED_TEST(BitfieldTest, find_first) {
TypeParam a;
a.setall();
ASSERT_TRUE(a.all());
const size_t size = a.size();
for (size_t i = 0; i < size; ++i) {
ASSERT_EQ(i, a.find_first());
a.clear(i);
}
ASSERT_TRUE(a.none());
const size_t npos = TypeParam::npos;
ASSERT_EQ(npos, a.find_first());
}
TYPED_TEST(BitfieldTest, find_last) {
TypeParam a;
a.setall();
ASSERT_TRUE(a.all());
const size_t size = a.size();
for (size_t i = size; i > 0; i--) {
ASSERT_EQ(i - 1, a.find_last());
a.clear(i - 1);
}
ASSERT_TRUE(a.none());
const size_t npos = TypeParam::npos;
ASSERT_EQ(npos, a.find_last());
}
TYPED_TEST(BitfieldTest, find_next_all) {
TypeParam a;
a.setall();
ASSERT_TRUE(a.all());
ASSERT_EQ(0, a.find_first());
const size_t size = a.size();
for (size_t i = 1; i < size; ++i) {
ASSERT_EQ(i, a.find_next(i - 1));
}
const size_t npos = TypeParam::npos;
ASSERT_EQ(npos, a.find_next(size - 1));
}
TYPED_TEST(BitfieldTest, find_next_npos) {
TypeParam a;
const size_t size = a.size();
const size_t npos = TypeParam::npos;
for (size_t i = 0; i < size; ++i) {
a.clear();
a.set(i);
ASSERT_EQ(i, a.find_first());
ASSERT_EQ(npos, a.find_next(i));
}
}
TYPED_TEST(BitfieldTest, find_next_last) {
TypeParam a;
const size_t size = a.size();
const size_t last = size - 1;
for (size_t i = 0; i < last; ++i) {
a.clear();
a.set(i);
a.set(last);
ASSERT_EQ(i, a.find_first());
ASSERT_EQ(last, a.find_next(i));
}
}
TYPED_TEST(BitfieldTest, find_nth_one) {
TypeParam a;
const size_t size = a.size();
const size_t npos = TypeParam::npos;
for (size_t i = 0; i < size; ++i) {
a.clear();
a.set(i);
ASSERT_EQ(i, a.find_nth(0));
if (1 < size) {
ASSERT_EQ(npos, a.find_nth(1));
}
}
}
TYPED_TEST(BitfieldTest, find_nth_all) {
TypeParam a;
const size_t size = a.size();
a.setall();
for (size_t i = 0; i < size; ++i) {
ASSERT_EQ(i, a.find_nth(i));
}
}
TYPED_TEST(BitfieldTest, find_nth_sparse) {
TypeParam a;
const size_t size = a.size();
const size_t stride = std::max(size / 31, size_t{3});
std::vector<size_t> bits;
for (size_t i = 0; i < size; i += stride) {
a.set(i);
bits.push_back(i);
}
ASSERT_EQ(bits.size(), a.count());
for (size_t n = 0; n < bits.size(); n++) {
ASSERT_EQ(bits[n], a.find_nth(n));
}
}
TYPED_TEST(BitfieldTest, unordered_set) {
const size_t size = TypeParam::size();
// Exercise the hash_value free function by adding bitfields to an
// unordered_set.
ue2::unordered_set<TypeParam> s;
s.reserve(size);
for (size_t i = 0; i < size; ++i) {
TypeParam a;
a.set(i);
s.insert(a);
}
ASSERT_EQ(size, s.size());
for (size_t i = 0; i < size; ++i) {
TypeParam a;
a.set(i);
ASSERT_TRUE(s.find(a) != s.end());
}
}
TYPED_TEST(BitfieldTest, set_range_one) {
TypeParam a;
const size_t size = a.size();
for (size_t i = 0; i < size; ++i) {
a.clear();
a.set_range(i, i);
TypeParam b;
b.set(i);
ASSERT_EQ(b, a);
}
}
TYPED_TEST(BitfieldTest, set_range_all) {
TypeParam a;
const size_t size = a.size();
a.set_range(0, size - 1);
TypeParam b;
b.setall();
ASSERT_EQ(b, a);
}
TYPED_TEST(BitfieldTest, set_range_part) {
TypeParam a;
const size_t size = a.size();
const size_t part = size / 3;
if (part < 1) {
return;
}
for (size_t i = 0; i < size - part; ++i) {
SCOPED_TRACE(i);
a.clear();
a.set_range(i, i + part);
for (size_t j = i; j <= i + part; j++) {
ASSERT_TRUE(a.test(j)) << "bit " << j << " should be on!";
}
// only the set bits should be on.
ASSERT_EQ(part + 1, a.count());
}
}

414
unit/internal/bitutils.cpp Normal file
View File

@@ -0,0 +1,414 @@
/*
* 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 "gtest/gtest.h"
#include "util/bitutils.h"
#include "util/popcount.h"
// open coded implementations to test against
static
u32 our_clz(u32 x) {
u32 n;
if (x == 0) return(32);
n = 0;
if (x <= 0x0000FFFF) { n = n + 16; x = x << 16; }
if (x <= 0x00FFFFFF) { n = n + 8; x = x << 8; }
if (x <= 0x0FFFFFFF) { n = n + 4; x = x << 4; }
if (x <= 0x3FFFFFFF) { n = n + 2; x = x << 2; }
if (x <= 0x7FFFFFFF) { n = n + 1; }
return n;
}
static
u32 our_clzll(u64a x) {
// Synthesise from 32-bit variant.
u32 high = x >> 32;
if (high) {
return our_clz(high);
}
return 32 + our_clz(x);
}
TEST(BitUtils, findAndClearLSB32_1) {
// test that it can find every single-bit case
for (unsigned int i = 0; i < 32; i++) {
u32 input = 1 << i;
u32 idx = findAndClearLSB_32(&input);
EXPECT_EQ(i, idx);
EXPECT_EQ(0U, input);
}
}
TEST(BitUtils, findAndClearLSB32_2) {
// test that we get all 32 indices from an all-ones input
u32 input = ~0;
unsigned int count = 0;
while (input != 0) {
ASSERT_GT(32U, count);
u32 expected_output = input & (input - 1);
u32 idx = findAndClearLSB_32(&input);
EXPECT_EQ(count, idx);
EXPECT_EQ(expected_output, input);
count++;
}
EXPECT_EQ(32U, count);
}
TEST(BitUtils, findAndClearLSB64_1) {
// test that it can find every single-bit case
for (unsigned int i = 0; i < 64; i++) {
u64a input = 1ULL << i;
u32 idx = findAndClearLSB_64(&input);
EXPECT_EQ(i, idx);
EXPECT_EQ(0U, input);
}
}
TEST(BitUtils, findAndClearLSB64_2) {
// test that we get all 32 indices from an all-ones input
u64a input = ~0;
unsigned int count = 0;
while (input != 0) {
ASSERT_GT(64U, count);
u64a expected_output = input & (input - 1);
u32 idx = findAndClearLSB_64(&input);
EXPECT_EQ(count, idx);
EXPECT_EQ(expected_output, input);
count++;
}
EXPECT_EQ(64U, count);
}
TEST(BitUtils, findAndClearMSB32_1) {
// test that it can find every single-bit case
for (unsigned int i = 0; i < 32; i++) {
u32 input = 1 << i;
u32 idx = findAndClearMSB_32(&input);
EXPECT_EQ(i, idx);
EXPECT_EQ(0U, input);
}
}
TEST(BitUtils, findAndClearMSB32_2) {
// test that we get all 32 indices from an all-ones input
u32 input = ~0;
unsigned int count = 0;
while (input != 0) {
ASSERT_GT(32U, count);
u32 offset = our_clz(input);
u32 expected_output = input ^ (1ULL << (31 - our_clz(input)));
u32 idx = findAndClearMSB_32(&input);
EXPECT_EQ(offset, count);
EXPECT_EQ(offset, 31 - idx);
EXPECT_EQ(expected_output, input);
count++;
}
EXPECT_EQ(32U, count);
}
TEST(BitUtils, findAndClearMSB64_1) {
// test that it can find every single-bit case
for (unsigned int i = 0; i < 64; i++) {
u64a input = 1ULL << i;
u32 idx = findAndClearMSB_64(&input);
EXPECT_EQ(i, idx);
EXPECT_EQ(0U, input);
}
}
TEST(BitUtils, findAndClearMSB64_2) {
// test that we get all 64 indices from an all-ones input
u64a input = ~0;
unsigned int count = 0;
while (input != 0) {
u32 offset = our_clzll(input);
u64a expected_output = input ^ (1ULL << (63 - our_clzll(input)));
u32 idx = findAndClearMSB_64(&input);
EXPECT_EQ(offset, 63 - idx);
EXPECT_EQ(offset, count);
EXPECT_EQ(expected_output, input);
count++;
}
EXPECT_EQ(64U, count);
}
TEST(BitUtils, popcount32) {
// some simple tests
EXPECT_EQ(0U, popcount32(0));
EXPECT_EQ(16U, popcount32(0x0000ffffU));
EXPECT_EQ(16U, popcount32(0xffff0000U));
EXPECT_EQ(16U, popcount32(0xf0f0f0f0U));
EXPECT_EQ(32U, popcount32(0xffffffffU));
EXPECT_EQ(32U, popcount32(0xffffffffU));
// single bits
for (u32 i = 0; i < 32; i++) {
EXPECT_EQ(1U, popcount32(1U << i));
}
// random
EXPECT_EQ(13U, popcount32(668732496U));
EXPECT_EQ(15U, popcount32(872800073U));
EXPECT_EQ(15U, popcount32(2831496658U));
EXPECT_EQ(12U, popcount32(2895271296U));
}
TEST(BitUtils, popcount64) {
// some simple tests
EXPECT_EQ(0U, popcount64(0));
EXPECT_EQ(16U, popcount64(0x000000000000ffffULL));
EXPECT_EQ(16U, popcount64(0xffff000000000000ULL));
EXPECT_EQ(32U, popcount64(0xf0f0f0f0f0f0f0f0ULL));
EXPECT_EQ(32U, popcount64(0xffffffff00000000ULL));
EXPECT_EQ(32U, popcount64(0x00000000ffffffffULL));
EXPECT_EQ(64U, popcount64(0xffffffffffffffffULL));
// single bits
for (u32 i = 0; i < 64; i++) {
EXPECT_EQ(1U, popcount64(1ULL << i));
}
// random
EXPECT_EQ(13U, popcount64(668732496ULL));
EXPECT_EQ(15U, popcount64(872800073ULL));
EXPECT_EQ(15U, popcount64(2831496658ULL));
EXPECT_EQ(12U, popcount64(2895271296ULL));
EXPECT_EQ(25U, popcount64(1869119247322218496ULL));
EXPECT_EQ(31U, popcount64(16235371884097357824ULL));
EXPECT_EQ(17U, popcount64(9354015527977637888ULL));
}
TEST(BitUtils, clz32) {
for (u32 i = 0; i < 32; i++) {
EXPECT_EQ(31 - i, clz32(1 << i));
}
}
TEST(BitUtils, clz64) {
for (u64a i = 0; i < 64; i++) {
EXPECT_EQ(63 - i, clz64(1ULL << i));
}
}
TEST(BitUtils, ctz32) {
EXPECT_EQ(0U, ctz32(0x1U));
EXPECT_EQ(15U, ctz32(0x8000));
}
TEST(BitUtils, ctz64) {
EXPECT_EQ(0U, ctz64(0x1ULL));
EXPECT_EQ(15U, ctz64(0x8000ULL));
}
TEST(BitUtils, compress32) {
const u32 all_ones = 0xffffffffu;
const u32 odd_bits = 0x55555555u;
const u32 even_bits = 0xaaaaaaaau;
EXPECT_EQ(0, compress32(0, 0));
EXPECT_EQ(0, compress32(0, 1u));
EXPECT_EQ(0, compress32(0, all_ones));
EXPECT_EQ(all_ones, compress32(all_ones, all_ones));
EXPECT_EQ(0xffffu, compress32(odd_bits, odd_bits));
EXPECT_EQ(0xffffu, compress32(even_bits, even_bits));
EXPECT_EQ(0, compress32(odd_bits, even_bits));
EXPECT_EQ(0, compress32(even_bits, odd_bits));
// Some single-bit tests.
for (u32 i = 0; i < 32; i++) {
const u32 one_bit = 1u << i;
EXPECT_EQ(0, compress32(0, one_bit));
EXPECT_EQ(1u, compress32(one_bit, one_bit));
EXPECT_EQ(one_bit, compress32(one_bit, all_ones));
if (i % 2) {
EXPECT_EQ(1u << (i / 2), compress32(one_bit, even_bits));
EXPECT_EQ(0, compress32(one_bit, odd_bits));
} else {
EXPECT_EQ(1u << (i / 2), compress32(one_bit, odd_bits));
EXPECT_EQ(0, compress32(one_bit, even_bits));
}
}
}
TEST(BitUtils, compress64) {
const u64a all_ones = 0xffffffffffffffffull;
const u64a odd_bits = 0x5555555555555555ull;
const u64a even_bits = 0xaaaaaaaaaaaaaaaaull;
EXPECT_EQ(0, compress64(0, 0));
EXPECT_EQ(0, compress64(0, 1u));
EXPECT_EQ(0, compress64(0, all_ones));
EXPECT_EQ(all_ones, compress64(all_ones, all_ones));
EXPECT_EQ(0xffffffffull, compress64(odd_bits, odd_bits));
EXPECT_EQ(0xffffffffull, compress64(even_bits, even_bits));
EXPECT_EQ(0, compress64(odd_bits, even_bits));
EXPECT_EQ(0, compress64(even_bits, odd_bits));
// Some single-bit tests.
for (u32 i = 0; i < 64; i++) {
const u64a one_bit = 1ull << i;
EXPECT_EQ(0, compress64(0, one_bit));
EXPECT_EQ(1ull, compress64(one_bit, one_bit));
EXPECT_EQ(one_bit, compress64(one_bit, all_ones));
if (i % 2) {
EXPECT_EQ(1ull << (i / 2), compress64(one_bit, even_bits));
EXPECT_EQ(0, compress64(one_bit, odd_bits));
} else {
EXPECT_EQ(1ull << (i / 2), compress64(one_bit, odd_bits));
EXPECT_EQ(0, compress64(one_bit, even_bits));
}
}
}
TEST(BitUtils, expand32) {
const u32 all_ones = 0xffffffffu;
const u32 odd_bits = 0x55555555u;
const u32 even_bits = 0xaaaaaaaau;
EXPECT_EQ(0, expand32(0, 0));
EXPECT_EQ(0, expand32(0, 1u));
EXPECT_EQ(0, expand32(0, all_ones));
EXPECT_EQ(all_ones, expand32(all_ones, all_ones));
EXPECT_EQ(odd_bits, expand32(0xffffu, odd_bits));
EXPECT_EQ(even_bits, expand32(0xffffu, even_bits));
EXPECT_EQ(0, expand32(0xffff0000u, even_bits));
EXPECT_EQ(0, expand32(0xffff0000u, odd_bits));
EXPECT_EQ(1u, expand32(1u, odd_bits));
EXPECT_EQ(2u, expand32(1u, even_bits));
// Some single-bit tests.
for (u32 i = 0; i < 32; i++) {
const u32 one_bit = 1u << i;
EXPECT_EQ(0, expand32(0, one_bit));
EXPECT_EQ(one_bit, expand32(1u, one_bit));
EXPECT_EQ(one_bit, expand32(one_bit, all_ones));
EXPECT_EQ(one_bit,
expand32(1u << (i / 2), i % 2 ? even_bits : odd_bits));
}
}
TEST(BitUtils, expand64) {
const u64a all_ones = 0xffffffffffffffffull;
const u64a odd_bits = 0x5555555555555555ull;
const u64a even_bits = 0xaaaaaaaaaaaaaaaaull;
EXPECT_EQ(0, expand64(0, 0));
EXPECT_EQ(0, expand64(0, 1ull));
EXPECT_EQ(0, expand64(0, all_ones));
EXPECT_EQ(all_ones, expand64(all_ones, all_ones));
EXPECT_EQ(odd_bits, expand64(0xffffffffull, odd_bits));
EXPECT_EQ(even_bits, expand64(0xffffffffull, even_bits));
EXPECT_EQ(0, expand64(0xffffffff00000000ull, even_bits));
EXPECT_EQ(0, expand64(0xffffffff00000000ull, odd_bits));
EXPECT_EQ(1u, expand64(1u, odd_bits));
EXPECT_EQ(2u, expand64(1u, even_bits));
// Some single-bit tests.
for (u32 i = 0; i < 64; i++) {
const u64a one_bit = 1ull << i;
EXPECT_EQ(0, expand64(0, one_bit));
EXPECT_EQ(one_bit, expand64(1ull, one_bit));
EXPECT_EQ(one_bit, expand64(one_bit, all_ones));
EXPECT_EQ(one_bit,
expand64(1ull << (i / 2), i % 2 ? even_bits : odd_bits));
}
}
TEST(BitUtils, bf_op_1) {
u64a a = 0;
for (u32 i = 0; i < 64; i++) {
char rv = bf64_set(&a, i);
ASSERT_EQ(0, rv);
if (i == 63) {
ASSERT_EQ(~0ULL, a);
} else {
ASSERT_EQ((1ULL << (i + 1)) - 1, a);
}
rv = bf64_set(&a, i);
ASSERT_EQ(1, rv);
if (i == 63) {
ASSERT_EQ(~0ULL, a);
} else {
ASSERT_EQ((1ULL << (i + 1)) - 1, a);
}
u64a b = 0;
rv = bf64_set(&b, i);
ASSERT_EQ(0, rv);
ASSERT_EQ(1ULL << i, b);
rv = bf64_set(&b, i);
ASSERT_EQ(1, rv);
ASSERT_EQ(1ULL << i, b);
bf64_unset(&b, i);
ASSERT_EQ(0, b);
}
ASSERT_EQ(~0ULL, a);
}
TEST(BitUtils, bf_it_1) {
ASSERT_EQ(~0U, bf64_iterate(0ULL, ~0U));
ASSERT_EQ(~0U, bf64_iterate(0ULL, 0));
ASSERT_EQ(~0U, bf64_iterate(0ULL, 1));
ASSERT_EQ(~0U, bf64_iterate(0ULL, 63));
ASSERT_EQ(0, bf64_iterate(1ULL, ~0U));
ASSERT_EQ(~0U, bf64_iterate(1ULL, 0));
ASSERT_EQ(~0U, bf64_iterate(1ULL, 1));
ASSERT_EQ(~0U, bf64_iterate(1ULL, 63));
ASSERT_EQ(1, bf64_iterate(2ULL, ~0U));
ASSERT_EQ(1, bf64_iterate(2ULL, 0));
ASSERT_EQ(~0U, bf64_iterate(2ULL, 1));
ASSERT_EQ(~0U, bf64_iterate(2ULL, 63));
ASSERT_EQ(0, bf64_iterate(3ULL, ~0U));
ASSERT_EQ(1, bf64_iterate(3ULL, 0));
ASSERT_EQ(~0U, bf64_iterate(3ULL, 1));
ASSERT_EQ(~0U, bf64_iterate(3ULL, 63));
ASSERT_EQ(63, bf64_iterate(1ULL << 63, ~0U));
ASSERT_EQ(63, bf64_iterate(1ULL << 63, 0));
ASSERT_EQ(63, bf64_iterate(1ULL << 63, 1));
ASSERT_EQ(~0U, bf64_iterate(1ULL << 63, 63));
}

385
unit/internal/charreach.cpp Normal file
View File

@@ -0,0 +1,385 @@
/*
* 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 "gtest/gtest.h"
#include "util/charreach.h"
using namespace ue2;
TEST(ng_charreach, init) {
CharReach cr;
ASSERT_EQ(0U, cr.count());
ASSERT_TRUE(cr.none());
ASSERT_FALSE(cr.all());
ASSERT_EQ(256U, cr.size());
}
TEST(ng_charreach, set) {
CharReach cr;
ASSERT_EQ(0U, cr.count());
ASSERT_TRUE(cr.none());
ASSERT_FALSE(cr.all());
cr.set('q');
ASSERT_EQ(1U, cr.count());
cr.setall();
ASSERT_EQ(cr.size(), cr.count());
ASSERT_TRUE(cr.all());
}
TEST(ng_charreach, clear) {
CharReach cr;
ASSERT_EQ(0U, cr.count());
ASSERT_TRUE(cr.none());
ASSERT_FALSE(cr.all());
cr.set('q');
cr.set('u');
cr.set('a');
cr.set('r');
cr.set('k');
ASSERT_EQ(5U, cr.count());
cr.clear('r');
ASSERT_EQ(4U, cr.count());
ASSERT_FALSE(cr.test('r'));
cr.setall();
ASSERT_EQ(cr.size(), cr.count());
ASSERT_TRUE(cr.all());
cr.clear(0xff);
ASSERT_FALSE(cr.all());
}
TEST(ng_charreach, copy) {
CharReach cr;
cr.set('a');
cr.set('z');
CharReach cr2(cr);
ASSERT_EQ(cr.count(), cr2.count());
ASSERT_TRUE(cr == cr2);
}
TEST(ng_charreach, assignment) {
CharReach cr;
cr.set('f');
cr.set('l');
cr.set('y');
CharReach cr2;
cr2 = cr;
ASSERT_EQ(cr.count(), cr2.count());
ASSERT_TRUE(cr == cr2);
}
TEST(ng_charreach, flip) {
CharReach cr;
ASSERT_EQ(0U, cr.count());
ASSERT_TRUE(cr.none());
cr.flip();
ASSERT_EQ(cr.size(), cr.count());
ASSERT_TRUE(cr.all());
cr.flip();
ASSERT_EQ(0U, cr.count());
ASSERT_TRUE(cr.none());
cr.flip(25);
ASSERT_FALSE(cr.none());
ASSERT_FALSE(cr.all());
ASSERT_EQ(1U, cr.count());
cr.flip();
ASSERT_EQ(cr.size() - 1, cr.count());
}
TEST(ng_charreach, count) {
CharReach cr;
cr.set(1);
cr.set(2);
cr.set('a');
cr.set('Z');
cr.set('m');
cr.set('~');
cr.set(210);
size_t n = cr.find_first();
ASSERT_FALSE(n == CharReach::npos);
unsigned int i = 0;
while (n != CharReach::npos) {
i++;
n = cr.find_next(n);
}
ASSERT_EQ(i, cr.count());
}
TEST(ng_charreach, string) {
CharReach cr;
cr.set(1);
cr.set(2);
cr.set('a');
cr.set('Z');
cr.set('m');
cr.set('~');
cr.set(210);
ASSERT_FALSE(cr.isAlpha());
cr.flip(1);
cr.flip(2);
cr.flip('~');
cr.flip(210);
ASSERT_TRUE(cr.isAlpha());
ASSERT_EQ("Zam", cr.to_string());
}
TEST(ng_charreach, alpha) {
CharReach cr;
ASSERT_EQ(0U, cr.count());
ASSERT_FALSE(cr.isAlpha());
cr.set('a');
ASSERT_FALSE(0 == cr.count());
ASSERT_TRUE(cr.isAlpha());
cr.set('A');
cr.set('b');
cr.set('z');
ASSERT_TRUE(cr.isAlpha());
cr.set(1);
ASSERT_FALSE(cr.isAlpha());
}
TEST(ng_charreach, alpha2) {
// Test all single chars.
for (size_t i = 0; i < 256; i++) {
CharReach cr((unsigned char)i);
ASSERT_EQ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z'),
cr.isAlpha()) << "Failed for char i=" << i;
}
// Test some more complex cases.
ASSERT_FALSE(CharReach::dot().isAlpha());
ASSERT_FALSE(CharReach("0123456789").isAlpha());
ASSERT_FALSE(CharReach("a0").isAlpha());
ASSERT_FALSE(CharReach("abcdef0").isAlpha());
ASSERT_FALSE(CharReach('A', 'z').isAlpha());
ASSERT_FALSE(CharReach('A', 'Z' + 1).isAlpha());
ASSERT_FALSE(CharReach('A' - 1, 'Z').isAlpha());
ASSERT_FALSE(CharReach('a', 'z' + 1).isAlpha());
ASSERT_FALSE(CharReach('a' - 1, 'z').isAlpha());
ASSERT_TRUE(CharReach('A', 'B').isAlpha());
ASSERT_TRUE(CharReach('A', 'F').isAlpha());
ASSERT_TRUE(CharReach('A', 'Z').isAlpha());
ASSERT_TRUE(CharReach('X', 'Z').isAlpha());
ASSERT_TRUE(CharReach('a', 'b').isAlpha());
ASSERT_TRUE(CharReach('a', 'z').isAlpha());
ASSERT_TRUE(CharReach("ABCDEFabcdef").isAlpha());
}
TEST(ng_charreach, caseless) {
CharReach cr;
cr.set('a');
ASSERT_FALSE(cr.isCaselessChar());
cr.set('A');
ASSERT_TRUE(cr.isCaselessChar());
cr.set('b');
ASSERT_FALSE(cr.isCaselessChar());
cr.set('B');
ASSERT_FALSE(cr.isCaselessChar());
}
TEST(ng_charreach, caseless2) {
// Test every pair of characters.
for (size_t i = 0; i < 256; i++) {
ASSERT_FALSE(CharReach((unsigned char)i).isCaselessChar());
for (size_t j = 0; j < 256; j++) {
CharReach cr;
cr.set(i);
cr.set(j);
bool upper_lower = (i >= 'A' && i <= 'Z') && j == i + 0x20;
bool lower_upper = (i >= 'a' && i <= 'z') && i == j + 0x20;
bool caseless_pair = upper_lower | lower_upper;
ASSERT_EQ(caseless_pair, cr.isCaselessChar())
<< "Failed for i=" << i << ", j=" << j;
}
}
}
TEST(ng_charreach, bitwise) {
CharReach cr;
CharReach cr2;
CharReach cr3;
CharReach cr4;
cr.set('a');
cr2.set('z');
cr3.set('a');
cr3.set('z');
ASSERT_TRUE(cr < cr3);
cr4 |= cr;
cr4 |= cr2;
ASSERT_TRUE(cr3 == cr4);
ASSERT_TRUE(cr3 == (cr | cr2));
ASSERT_TRUE(cr4 == (cr | cr2));
ASSERT_TRUE(cr == (cr & cr3));
ASSERT_TRUE(cr2 == (cr2 & cr3));
cr3 &= cr;
ASSERT_FALSE(cr3.test('z'));
}
TEST(ng_charreach, bit5) {
CharReach cr;
ASSERT_TRUE(cr.isBit5Insensitive());
cr.set('a');
ASSERT_FALSE(cr.isBit5Insensitive());
cr.set('A');
ASSERT_TRUE(cr.isBit5Insensitive());
cr.set('!');
ASSERT_FALSE(cr.isBit5Insensitive());
cr.set(1);
ASSERT_TRUE(cr.isBit5Insensitive());
cr.clear();
cr.set('!');
cr.set('A');
ASSERT_FALSE(cr.isBit5Insensitive());
cr.clear();
cr.set('A');
cr.set('b');
ASSERT_FALSE(cr.isBit5Insensitive());
cr.set('a');
cr.set('B');
ASSERT_TRUE(cr.isBit5Insensitive());
}
TEST(ng_charreach, find_last) {
CharReach cr;
cr.set('a');
ASSERT_EQ(cr.find_last(), (size_t)'a');
cr.set('b');
ASSERT_EQ(cr.find_last(), (size_t)'b');
cr.set(192);
ASSERT_EQ(cr.find_last(), (size_t)192);
cr.set(207);
ASSERT_EQ(cr.find_last(), (size_t)207);
cr.set(223);
ASSERT_EQ(cr.find_last(), (size_t)223);
cr.set(255);
ASSERT_EQ(cr.find_last(), (size_t)255);
cr.clear();
ASSERT_EQ(cr.find_last(), cr.size());
cr.set(0);
ASSERT_EQ(cr.find_last(), (size_t)0);
cr.set(1);
ASSERT_EQ(cr.find_last(), (size_t)1);
}
TEST(ng_charreach, setRange) {
// Exhaustive test: every possible contiguous range.
for (unsigned range = 0; range < 256; range++) {
for (unsigned from = 0; from < 256 - range; from++) {
unsigned to = from + range;
CharReach cr;
cr.setRange(from, to);
ASSERT_EQ(from, cr.find_first());
ASSERT_EQ(to, cr.find_last());
ASSERT_EQ(range + 1, cr.count());
}
}
}
TEST(ng_charreach, find_nth) {
const size_t npos = CharReach::npos;
// One bit cases.
for (size_t i = 0; i < 256; i++) {
CharReach cr((unsigned char)i);
ASSERT_EQ(i, cr.find_nth(0));
ASSERT_EQ(npos, cr.find_nth(1));
}
// All bits set.
CharReach dot = CharReach::dot();
for (size_t i = 0; i < 256; i++) {
ASSERT_EQ(i, dot.find_nth(i));
}
// Trivial two bit cases.
for (size_t i = 0; i < 128; i++) {
CharReach cr;
cr.set(i);
cr.set(256 - i);
ASSERT_EQ(i, cr.find_nth(0));
ASSERT_EQ(256 - i, cr.find_nth(1));
ASSERT_EQ(npos, cr.find_nth(3));
}
// More complex case.
const std::string str("\x01\x02\x03\x05\x06\x20!#$%&./0123568:;ABCDEFMNOPUYZbcdefwxyz");
CharReach cr(str);
for (size_t i = 0; i < str.length(); i++) {
ASSERT_EQ(str[i], cr.find_nth(i));
}
ASSERT_EQ(npos, cr.find_nth(str.length()));
}
TEST(ng_charreach, find_empty) {
const size_t npos = CharReach::npos;
ASSERT_EQ(npos, CharReach().find_first());
ASSERT_EQ(npos, CharReach().find_next(npos));
}
TEST(ng_charreach, dot) {
CharReach dot = CharReach::dot();
ASSERT_EQ(256, dot.count());
ASSERT_TRUE(dot.all());
for (size_t i = 0; i < 256; i++) {
ASSERT_TRUE(dot.test(i));
}
}

107
unit/internal/compare.cpp Normal file
View File

@@ -0,0 +1,107 @@
/*
* 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 "gtest/gtest.h"
#include "util/compare.h"
#include <ctype.h>
using namespace std;
using namespace testing;
class compare : public TestWithParam<int> {
};
TEST_P(compare, my_our_is) {
int c = GetParam();
EXPECT_EQ(!!islower(c), !!myislower(c));
EXPECT_EQ(!!isupper(c), !!myisupper(c));
EXPECT_EQ(!!isalpha(c), !!ourisalpha(c));
EXPECT_EQ(!!isprint(c), !!ourisprint(c));
}
TEST_P(compare, my_to) {
int c = GetParam();
EXPECT_EQ((char)tolower(c), mytolower(c));
EXPECT_EQ((char)toupper(c), mytoupper(c));
}
TEST_P(compare, their32) {
u32 c = (u32)GetParam();
u32 cu = (u8)toupper(c);
u32 cccc = c | (c << 8) | (c << 16) | (c << 24);
u32 uuuu = cu | (cu << 8) | (cu << 16) | (cu << 24);
EXPECT_EQ(uuuu, theirtoupper32(cccc));
EXPECT_EQ(uuuu & 0xffU, theirtoupper32(cccc & 0xffU));
EXPECT_EQ(uuuu & 0xff00U, theirtoupper32(cccc & 0xff00U));
EXPECT_EQ(uuuu & 0xff0000U, theirtoupper32(cccc & 0xff0000U));
EXPECT_EQ(uuuu & 0xff000000U, theirtoupper32(cccc & 0xff000000U));
EXPECT_EQ(uuuu | 0xffffff00U, theirtoupper32(cccc | 0xffffff00U));
EXPECT_EQ(uuuu | 0xffff00ffU, theirtoupper32(cccc | 0xffff00ffU));
EXPECT_EQ(uuuu | 0xff00ffffU, theirtoupper32(cccc | 0xff00ffffU));
EXPECT_EQ(uuuu | 0x00ffffffU, theirtoupper32(cccc | 0x00ffffffU));
}
TEST_P(compare, their64) {
u64a c = (u64a)GetParam();
u64a cu = (u8)toupper(c);
u64a cccc = c | (c << 8) | (c << 16) | (c << 24);
cccc |= cccc << 32;
u64a uuuu = cu | (cu << 8) | (cu << 16) | (cu << 24);
uuuu |= uuuu << 32;
EXPECT_EQ(uuuu, theirtoupper64(cccc));
EXPECT_EQ(uuuu & 0xffULL, theirtoupper64(cccc & 0xffULL));
EXPECT_EQ(uuuu & (0xffULL << 8), theirtoupper64(cccc & (0xffULL << 8)));
EXPECT_EQ(uuuu & (0xffULL << 16), theirtoupper64(cccc & (0xffULL << 16)));
EXPECT_EQ(uuuu & (0xffULL << 24), theirtoupper64(cccc & (0xffULL << 24)));
EXPECT_EQ(uuuu & (0xffULL << 32), theirtoupper64(cccc & (0xffULL << 32)));
EXPECT_EQ(uuuu & (0xffULL << 40), theirtoupper64(cccc & (0xffULL << 40)));
EXPECT_EQ(uuuu & (0xffULL << 48), theirtoupper64(cccc & (0xffULL << 48)));
EXPECT_EQ(uuuu & (0xffULL << 56), theirtoupper64(cccc & (0xffULL << 56)));
EXPECT_EQ(uuuu & ~0xffULL, theirtoupper64(cccc & ~0xffULL));
EXPECT_EQ(uuuu & ~(0xffULL << 8), theirtoupper64(cccc & ~(0xffULL << 8)));
EXPECT_EQ(uuuu & ~(0xffULL << 16), theirtoupper64(cccc & ~(0xffULL << 16)));
EXPECT_EQ(uuuu & ~(0xffULL << 24), theirtoupper64(cccc & ~(0xffULL << 24)));
EXPECT_EQ(uuuu & ~(0xffULL << 32), theirtoupper64(cccc & ~(0xffULL << 32)));
EXPECT_EQ(uuuu & ~(0xffULL << 40), theirtoupper64(cccc & ~(0xffULL << 40)));
EXPECT_EQ(uuuu & ~(0xffULL << 48), theirtoupper64(cccc & ~(0xffULL << 48)));
EXPECT_EQ(uuuu & ~(0xffULL << 56), theirtoupper64(cccc & ~(0xffULL << 56)));
}
INSTANTIATE_TEST_CASE_P(compare, compare, Range((int)0, (int)256));

View File

@@ -0,0 +1,67 @@
/*
* 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 "hs.h"
#include "compiler/compiler.h"
#include "crc32.h"
#include "database.h"
#include "ue2common.h"
#include "util/target_info.h"
#include "gtest/gtest.h"
#include <array>
using namespace ue2;
TEST(DB, flagsToPlatform) {
hs_platform_info p;
memset(&p, 0, sizeof(p));
p.cpu_features = 0;
#if defined(__AVX2__)
p.cpu_features |= HS_CPU_FEATURES_AVX2;
#endif
platform_t pp = target_to_platform(target_t(p));
ASSERT_EQ(pp, hs_current_platform);
}
TEST(CRC, alignments) {
std::array<u8, 4096> a;
a.fill('a');
// test the crc32c function at different alignments
for (u8 i = 0; i < 32; i++) {
u32 crc = Crc32c_ComputeBuf(0, (u8 *)a.data() + i, 4000);
ASSERT_EQ(crc, 0x94f04377U);
}
}

280
unit/internal/depth.cpp Normal file
View File

@@ -0,0 +1,280 @@
/*
* 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 "util/depth.h"
#include "util/ue2_containers.h"
#include "gtest/gtest.h"
using namespace ue2;
static UNUSED
std::ostream& operator<<(std::ostream &os, const depth &d) {
// We reimplement depth::str, as it's only available if dump support is
// compiled in.
if (d.is_unreachable()) {
os << "unr";
return os;
} else if (d.is_infinite()) {
os << "inf";
return os;
}
u32 val = d; // finite val
os << val;
return os;
}
static constexpr u32 finite_values[] = {
0, 1, 2, 3, 4, 5, 6, 32, 1000, 1u << 20, depth::max_value()};
TEST(depth, ctor) {
ASSERT_EQ(depth::unreachable(), depth()); // default ctor
// also tests u32 conversion
for (const auto &val : finite_values) {
ASSERT_EQ(val, depth(val));
}
ASSERT_THROW(depth(depth::max_value() + 1), DepthOverflowError);
}
TEST(depth, convert_throw) {
ASSERT_THROW(u32 v = depth::infinity(), DepthOverflowError);
ASSERT_THROW(u32 v = depth::unreachable(), DepthOverflowError);
}
TEST(depth, is_finite) {
for (const auto &val : finite_values) {
ASSERT_TRUE(depth(val).is_finite());
}
ASSERT_FALSE(depth::infinity().is_finite());
ASSERT_FALSE(depth::unreachable().is_finite());
}
TEST(depth, is_infinite) {
for (const auto &val : finite_values) {
ASSERT_FALSE(depth(val).is_infinite());
}
ASSERT_TRUE(depth::infinity().is_infinite());
ASSERT_FALSE(depth::unreachable().is_infinite());
}
TEST(depth, is_unreachable) {
for (const auto &val : finite_values) {
ASSERT_FALSE(depth(val).is_unreachable());
}
ASSERT_FALSE(depth::infinity().is_unreachable());
ASSERT_TRUE(depth::unreachable().is_unreachable());
}
TEST(depth, is_reachable) {
for (const auto &val : finite_values) {
ASSERT_TRUE(depth(val).is_reachable());
}
ASSERT_TRUE(depth::infinity().is_reachable());
ASSERT_FALSE(depth::unreachable().is_reachable());
}
TEST(depth, add_finite) {
ASSERT_EQ(depth(1), depth(0) + depth(1));
ASSERT_EQ(depth(1), depth(1) + depth(0));
ASSERT_EQ(depth(1), depth(0) + 1);
ASSERT_EQ(depth(1), depth(1) + 0);
ASSERT_EQ(depth(2000), depth(1000) + depth(1000));
ASSERT_EQ(depth(2000), depth(1000) + 1000);
ASSERT_EQ(depth(900), depth(1000) + s32{-100});
// overflow must throw
depth d;
ASSERT_THROW(d = depth::max_value() + depth(1), DepthOverflowError);
ASSERT_THROW(d = depth::max_value() + 1, DepthOverflowError);
// underflow must throw
ASSERT_THROW(d = depth(0) + s32{-1}, DepthOverflowError);
}
TEST(depth, add_inf) {
// adding anything finite to inf should produce inf
for (const auto &val : finite_values) {
ASSERT_EQ(depth::infinity(), depth::infinity() + depth(val));
ASSERT_EQ(depth::infinity(), depth(val) + depth::infinity());
}
ASSERT_EQ(depth::infinity(), depth::infinity() + depth::infinity());
}
TEST(depth, add_unr) {
// adding anything to unr should produce unr
for (const auto &val : finite_values) {
ASSERT_EQ(depth::unreachable(), depth::unreachable() + depth(val));
ASSERT_EQ(depth::unreachable(), depth(val) + depth::unreachable());
}
ASSERT_EQ(depth::unreachable(), depth::unreachable() + depth::infinity());
ASSERT_EQ(depth::unreachable(), depth::unreachable() + depth::unreachable());
}
TEST(depth, sub_finite) {
ASSERT_EQ(depth(1), depth(1) - depth(0));
ASSERT_EQ(depth(0), depth(1) - depth(1));
ASSERT_EQ(depth(0), depth(1000) - depth(1000));
// underflow should throw
ASSERT_THROW(depth(0) - depth(1), DepthOverflowError);
}
TEST(depth, sub_inf) {
// subtracting anything from inf should produce inf
for (const auto &val : finite_values) {
ASSERT_EQ(depth::infinity(), depth::infinity() - depth(val));
}
// subtracting non-finite values must throw
ASSERT_THROW(depth::infinity() - depth::infinity(), DepthOverflowError);
ASSERT_THROW(depth::infinity() - depth::unreachable(), DepthOverflowError);
}
TEST(depth, sub_unr) {
// subtracting anything from unr should produce unr
for (const auto &val : finite_values) {
ASSERT_EQ(depth::unreachable(), depth::unreachable() - depth(val));
}
// subtracting non-finite values must throw
ASSERT_THROW(depth::unreachable() - depth::infinity(), DepthOverflowError);
ASSERT_THROW(depth::unreachable() - depth::unreachable(), DepthOverflowError);
}
TEST(depth, operators) {
for (const auto &val : finite_values) {
depth d(val);
ASSERT_TRUE(d == d);
ASSERT_FALSE(d != d);
ASSERT_FALSE(d < d);
ASSERT_FALSE(d > d);
ASSERT_TRUE(d <= d);
ASSERT_TRUE(d >= d);
if (val < depth::max_value()) {
depth d1(val + 1);
ASSERT_FALSE(d == d1);
ASSERT_TRUE(d != d1);
ASSERT_TRUE(d < d1);
ASSERT_FALSE(d > d1);
ASSERT_TRUE(d <= d1);
ASSERT_FALSE(d >= d1);
}
ASSERT_TRUE(d < depth::infinity());
ASSERT_TRUE(d < depth::unreachable());
ASSERT_TRUE(d <= depth::infinity());
ASSERT_TRUE(d <= depth::unreachable());
ASSERT_TRUE(d <= depth(depth::max_value()));
ASSERT_TRUE(depth::infinity() > d);
ASSERT_TRUE(depth::unreachable() > d);
ASSERT_TRUE(depth::infinity() >= d);
ASSERT_TRUE(depth::unreachable() >= d);
ASSERT_TRUE(depth(depth::max_value()) >= d);
ASSERT_TRUE(d != depth::infinity());
ASSERT_TRUE(d != depth::unreachable());
}
ASSERT_TRUE(depth::infinity() <= depth::infinity());
ASSERT_TRUE(depth::infinity() >= depth::infinity());
ASSERT_FALSE(depth::infinity() < depth::infinity());
ASSERT_FALSE(depth::infinity() > depth::infinity());
ASSERT_TRUE(depth::infinity() == depth::infinity());
ASSERT_FALSE(depth::infinity() != depth::infinity());
ASSERT_TRUE(depth::unreachable() <= depth::unreachable());
ASSERT_TRUE(depth::unreachable() >= depth::unreachable());
ASSERT_FALSE(depth::unreachable() < depth::unreachable());
ASSERT_FALSE(depth::unreachable() > depth::unreachable());
ASSERT_TRUE(depth::unreachable() == depth::unreachable());
ASSERT_FALSE(depth::unreachable() != depth::unreachable());
}
TEST(depth, u64a_operators) {
for (const auto &fval : finite_values) {
u64a val = fval;
depth d(fval);
// trivial tests against own value
ASSERT_TRUE(d == val);
ASSERT_FALSE(d != val);
ASSERT_FALSE(d < val);
ASSERT_FALSE(d > val);
ASSERT_TRUE(d <= val);
ASSERT_TRUE(d >= val);
// tests against val + 1
ASSERT_FALSE(d == val + 1);
ASSERT_TRUE(d != val + 1);
ASSERT_TRUE(d < val + 1);
ASSERT_FALSE(d > val + 1);
ASSERT_TRUE(d <= val + 1);
ASSERT_FALSE(d >= val + 1);
// test inf against val
ASSERT_FALSE(depth::infinity() == val);
ASSERT_TRUE(depth::infinity() != val);
ASSERT_FALSE(depth::infinity() < val);
ASSERT_TRUE(depth::infinity() > val);
ASSERT_FALSE(depth::infinity() <= val);
ASSERT_TRUE(depth::infinity() >= val);
// test unreachable against val
ASSERT_FALSE(depth::unreachable() == val);
ASSERT_TRUE(depth::unreachable() != val);
ASSERT_FALSE(depth::unreachable() < val);
ASSERT_TRUE(depth::unreachable() > val);
ASSERT_FALSE(depth::unreachable() <= val);
ASSERT_TRUE(depth::unreachable() >= val);
}
}
TEST(depth, unordered_set) {
ue2::unordered_set<depth> depths;
for (const auto &val : finite_values) {
depths.insert(val);
}
for (const auto &val : finite_values) {
ASSERT_TRUE(depths.find(val) != depths.end());
}
ASSERT_TRUE(depths.find(depth::infinity()) == depths.end());
ASSERT_TRUE(depths.find(depth::unreachable()) == depths.end());
ASSERT_TRUE(depths.find(depth(1337)) == depths.end());
}

828
unit/internal/fdr.cpp Normal file
View File

@@ -0,0 +1,828 @@
/*
* 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 "ue2common.h"
#include "grey.h"
#include "fdr/fdr.h"
#include "fdr/fdr_compile.h"
#include "fdr/fdr_compile_internal.h"
#include "fdr/fdr_engine_description.h"
#include "fdr/teddy_compile.h"
#include "fdr/teddy_engine_description.h"
#include "util/alloc.h"
#include "database.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <fstream>
#include <boost/random.hpp>
using namespace std;
using namespace testing;
using namespace ue2;
#define NO_TEDDY_FAIL_ALLOWED 0
#if(NO_TEDDY_FAIL_ALLOWED)
#define CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint) ASSERT_TRUE(fdr != nullptr)
#else
#define CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint) \
{ \
auto descr = getTeddyDescription(hint); \
if (descr && fdr != nullptr) { \
return; \
} else { \
ASSERT_TRUE(fdr != nullptr); \
} \
}
#endif
namespace {
struct match {
size_t start;
size_t end;
u32 id;
match(size_t start_in, size_t end_in, u32 id_in)
: start(start_in), end(end_in), id(id_in) {}
bool operator==(const match &b) const {
return start == b.start && end == b.end && id == b.id;
}
bool operator<(const match &b) const {
if (id < b.id) {
return true;
} else if (id == b.id) {
if (start < b.start) {
return true;
} else if (start == b.start) {
return end < b.end;
}
}
return false;
}
match operator+(size_t adj) {
return match(start + adj, end + adj, id);
}
};
extern "C" {
static
hwlmcb_rv_t countCallback(UNUSED size_t start, UNUSED size_t end, u32,
void *ctxt) {
if (ctxt) {
++*(u32 *)ctxt;
}
return HWLM_CONTINUE_MATCHING;
}
static
hwlmcb_rv_t decentCallback(size_t start, size_t end, u32 id, void *ctxt) {
DEBUG_PRINTF("match %zu-%zu : %u\n", start, end, id);
if (!ctxt) {
return HWLM_CONTINUE_MATCHING;
}
vector<match> *out = (vector<match> *)ctxt;
out->push_back(match(start, end, id));
return HWLM_CONTINUE_MATCHING;
}
static
hwlmcb_rv_t decentCallbackT(size_t start, size_t end, u32 id, void *ctxt) {
if (!ctxt) {
return HWLM_TERMINATE_MATCHING;
}
vector<match> *out = (vector<match> *)ctxt;
out->push_back(match(start, end, id));
return HWLM_TERMINATE_MATCHING;
}
} // extern "C"
} // namespace
static vector<u32> getValidFdrEngines() {
vector<u32> ret;
vector<FDREngineDescription> des;
getFdrDescriptions(&des);
for (vector<FDREngineDescription>::const_iterator it = des.begin();
it != des.end(); ++it) {
if (it->isValidOnTarget(get_current_target())) {
ret.push_back(it->getID());
}
}
vector<TeddyEngineDescription> tDes;
getTeddyDescriptions(&tDes);
for (vector<TeddyEngineDescription>::const_iterator it = tDes.begin();
it != tDes.end(); ++it) {
if (it->isValidOnTarget(get_current_target())) {
ret.push_back(it->getID());
}
}
return ret;
}
class FDRp : public TestWithParam<u32> {
};
TEST_P(FDRp, Simple) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const char data[] = "mnopqrabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234567890mnopqr";
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("mnopqr", 0, 0));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> matches;
fdrExec(fdr.get(), (const u8 *)data, sizeof(data), 0, decentCallback,
&matches, HWLM_ALL_GROUPS);
ASSERT_EQ(3U, matches.size());
EXPECT_EQ(match(0, 5, 0), matches[0]);
EXPECT_EQ(match(18, 23, 0), matches[1]);
EXPECT_EQ(match(78, 83, 0), matches[2]);
}
TEST_P(FDRp, SimpleSingle) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const char data[] = "mnopqrabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234567890m0m";
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("m", 0, 0));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> matches;
fdrExec(fdr.get(), (const u8 *)data, sizeof(data) - 1 /* skip nul */, 0,
decentCallback, &matches, HWLM_ALL_GROUPS);
ASSERT_EQ(4U, matches.size());
EXPECT_EQ(match(0, 0, 0), matches[0]);
EXPECT_EQ(match(18, 18, 0), matches[1]);
EXPECT_EQ(match(78, 78, 0), matches[2]);
EXPECT_EQ(match(80, 80, 0), matches[3]);
}
TEST_P(FDRp, MultiLocation) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
u8 * data;
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("abc", 0, 1));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
const u32 testSize = 128;
data = (u8 *)malloc(testSize);
memset(data, 0, testSize);
for (u32 i = 0; i < testSize - 3; i++) {
memcpy(data + i, "abc", 3);
vector<match> matches;
fdrExec(fdr.get(), (const u8 *)data, testSize, 0, decentCallback,
&matches, HWLM_ALL_GROUPS);
ASSERT_EQ(1U, matches.size());
EXPECT_EQ(match(i, i+2, 1), matches[0]);
memset(data + i, 0, 3);
}
free(data);
}
TEST_P(FDRp, Flood) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
u8 * data;
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("aaaa", 0, 1));
lits.push_back(hwlmLiteral("aaaaaaaa", 0, 2));
lits.push_back(hwlmLiteral("baaaaaaaa", 0, 3));
lits.push_back(hwlmLiteral("aaaaaaaab", 0, 4));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
const u32 testSize = 1024;
data = (u8 *)malloc(testSize);
memset(data, 'a', testSize);
vector<match> matches;
fdrExec(fdr.get(), (const u8 *)data, testSize, 0, decentCallback, &matches,
HWLM_ALL_GROUPS);
ASSERT_EQ(testSize - 3 + testSize - 7, matches.size());
EXPECT_EQ(match(0, 3, 1), matches[0]);
EXPECT_EQ(match(1, 4, 1), matches[1]);
EXPECT_EQ(match(2, 5, 1), matches[2]);
EXPECT_EQ(match(3, 6, 1), matches[3]);
u32 currentMatch = 4;
for (u32 i = 7; i < testSize; i++, currentMatch += 2) {
EXPECT_TRUE(
(match(i - 3, i, 1) == matches[currentMatch] &&
match(i - 7, i, 2) == matches[currentMatch+1]) ||
(match(i - 7, i, 2) == matches[currentMatch+1] &&
match(i - 3, i, 1) == matches[currentMatch])
);
}
free(data);
}
TEST_P(FDRp, NoRepeat1) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const char data[] = "mnopqrabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234567890m0m";
vector<hwlmLiteral> lits
= { hwlmLiteral("m", 0, 1, 0, HWLM_ALL_GROUPS, {}, {}) };
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> matches;
fdrExec(fdr.get(), (const u8 *)data, sizeof(data) - 1 /* skip nul */, 0,
decentCallback, &matches, HWLM_ALL_GROUPS);
ASSERT_EQ(1U, matches.size());
EXPECT_EQ(match(0, 0, 0), matches[0]);
}
TEST_P(FDRp, NoRepeat2) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const char data[] = "mnopqrabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234567890m0m";
vector<hwlmLiteral> lits
= { hwlmLiteral("m", 0, 1, 0, HWLM_ALL_GROUPS, {}, {}),
hwlmLiteral("A", 0, 42) };
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> matches;
fdrExec(fdr.get(), (const u8 *)data, sizeof(data) - 1 /* skip nul */, 0,
decentCallback, &matches, HWLM_ALL_GROUPS);
ASSERT_EQ(3U, matches.size());
EXPECT_EQ(match(0, 0, 0), matches[0]);
EXPECT_EQ(match(78, 78, 0), matches[2]);
}
TEST_P(FDRp, NoRepeat3) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const char data[] = "mnopqrabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234567890m0m";
vector<hwlmLiteral> lits
= { hwlmLiteral("90m", 0, 1, 0, HWLM_ALL_GROUPS, {}, {}),
hwlmLiteral("zA", 0, 1, 0, HWLM_ALL_GROUPS, {}, {}) };
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> matches;
fdrExec(fdr.get(), (const u8 *)data, sizeof(data) - 1 /* skip nul */, 0,
decentCallback, &matches, HWLM_ALL_GROUPS);
ASSERT_EQ(1U, matches.size());
EXPECT_EQ(match(31, 32, 0), matches[0]);
}
TEST_P(FDRp, SmallStreaming) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("a", 1, 1));
lits.push_back(hwlmLiteral("aardvark", 0, 10));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> expected, matches;
expected.push_back(match(0, 0, 1));
expected.push_back(match(1, 1, 1));
expected.push_back(match(2, 2, 1));
fdrExecStreaming(fdr.get(), (const u8 *)"", 0, (const u8 *)"aaar", 4, 0,
decentCallback, &matches, HWLM_ALL_GROUPS, nullptr);
for (u32 i = 0; i < MIN(expected.size(), matches.size()); i++) {
EXPECT_EQ(expected[i], matches[i]);
}
ASSERT_TRUE(expected.size() == matches.size());
expected.clear();
matches.clear();
expected.push_back(match(6, 6, 1));
expected.push_back(match(1, 8, 10));
fdrExecStreaming(fdr.get(), (const u8 *)"aaar", 4, (const u8 *)"dvark", 5,
0, decentCallback, &matches, HWLM_ALL_GROUPS, nullptr);
for (u32 i = 0; i < MIN(expected.size(), matches.size()); i++) {
EXPECT_EQ(expected[i], matches[i] + 4);
}
ASSERT_EQ(expected.size(), matches.size());
}
TEST_P(FDRp, SmallStreaming2) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("a",1,1));
lits.push_back(hwlmLiteral("kk",1,2));
lits.push_back(hwlmLiteral("aardvark", 0,10));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> expected, matches;
expected.push_back(match(6,6,1));
expected.push_back(match(7,7,1));
expected.push_back(match(11,11,1));
expected.push_back(match(6,13,10));
expected.push_back(match(13,14,2));
expected.push_back(match(14,15,2));
fdrExecStreaming(fdr.get(), (const u8 *)"foobar", 6,
(const u8 *)"aardvarkkk", 10, 0, decentCallback, &matches,
HWLM_ALL_GROUPS, nullptr);
for (u32 i = 0; i < MIN(expected.size(), matches.size()); i++) {
EXPECT_EQ(expected[i], matches[i] + 6);
}
ASSERT_EQ(expected.size(), matches.size());
}
TEST_P(FDRp, LongLiteral) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
size_t sz;
const u8 *data;
vector<hwlmLiteral> lits;
string alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
string alpha4 = alpha+alpha+alpha+alpha;
lits.push_back(hwlmLiteral(alpha4.c_str(), 0,10));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
u32 count = 0;
data = (const u8 *)alpha4.c_str();
sz = alpha4.size();
fdrExec(fdr.get(), data, sz, 0, countCallback, &count, HWLM_ALL_GROUPS);
EXPECT_EQ(1U, count);
count = 0;
fdrExec(fdr.get(), data, sz - 1, 0, countCallback, &count, HWLM_ALL_GROUPS);
EXPECT_EQ(0U, count);
count = 0;
fdrExec(fdr.get(), data + 1, sz - 1, 0, countCallback, &count,
HWLM_ALL_GROUPS);
EXPECT_EQ(0U, count);
}
TEST_P(FDRp, VeryLongLiteral) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
vector<hwlmLiteral> lits;
string s1000;
for(int i = 0; i < 1000; i++) {
s1000 += char('A' + i % 10);
}
string s66k;
for(int i = 0; i < 66; i++) {
s66k += s1000;
}
string corpus = s66k + s66k;
lits.push_back(hwlmLiteral(s66k.c_str(), 0, 10));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
vector<match> matches;
u32 rv = fdrExec(fdr.get(), (const u8 *)s66k.c_str(), s66k.size(), 0,
decentCallback, &matches, HWLM_ALL_GROUPS);
EXPECT_EQ(0U, rv);
ASSERT_EQ(1U, matches.size());
ASSERT_EQ(match(0, 65999, 10), matches[0]);
matches.clear();
rv = fdrExec(fdr.get(), (const u8 *)corpus.c_str(), corpus.size(), 0,
decentCallback, &matches, HWLM_ALL_GROUPS);
EXPECT_EQ(0U, rv);
for (u32 i = 0; i < matches.size(); i++) {
ASSERT_EQ(match(10 * i, 65999 + 10 * i, 10), matches[i]);
}
EXPECT_EQ(6601U, matches.size());
}
TEST_P(FDRp, moveByteStream) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const char data[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234567890";
size_t data_len = strlen(data);
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("mnopqr", 0, 0));
auto fdrTable0 = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdrTable0, hint);
size_t size = fdrSize(fdrTable0.get());
FDR *fdrTable = (FDR *)aligned_zmalloc(size);
EXPECT_TRUE(fdrTable);
memcpy(fdrTable, fdrTable0.get(), size);
// bugger up original
for (size_t i = 0 ; i < size; i++) {
((char *)fdrTable0.get())[i] = (i % 2) ? 0xCA : 0xFE;
}
// check matches
vector<match> matches;
hwlm_error_t fdrStatus = fdrExec(fdrTable, (const u8 *)data, data_len, 0,
decentCallback, &matches, HWLM_ALL_GROUPS);
ASSERT_EQ(0, fdrStatus);
ASSERT_EQ(1U, matches.size());
EXPECT_EQ(match(12, 17, 0), matches[0]);
aligned_free(fdrTable);
}
TEST_P(FDRp, Stream1) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const char data1[] = "fffffffffffffffff";
const char data2[] = "ffffuuuuuuuuuuuuu";
size_t data_len1 = strlen(data1);
size_t data_len2 = strlen(data2);
hwlm_error_t fdrStatus = 1;
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("f", 0, 0));
lits.push_back(hwlmLiteral("longsigislong", 0, 1));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
// check matches
vector<match> matches;
fdrStatus = fdrExecStreaming(fdr.get(), (const u8 *)data1, data_len1,
(const u8 *)data2, data_len2, 0,
decentCallback, &matches, HWLM_ALL_GROUPS, nullptr);
ASSERT_EQ(0, fdrStatus);
ASSERT_EQ(4U, matches.size());
for (size_t i = 0; i < matches.size(); i++) {
EXPECT_EQ(match(i, i, 0), matches[i]);
}
}
INSTANTIATE_TEST_CASE_P(FDR, FDRp, ValuesIn(getValidFdrEngines()));
typedef struct {
string pattern;
unsigned char alien;
} pattern_alien_t;
// gtest helper
void PrintTo(const pattern_alien_t &t, ::std::ostream *os) {
*os << "(" << t.pattern << ", " << t.alien << ")";
}
class FDRpp : public TestWithParam<tuple<u32, pattern_alien_t>> {};
// This test will check if matcher detects properly literals at the beginning
// and at the end of unaligned buffer. It will check as well that match does
// not happen if literal is partially (from 1 character up to full literal
// length) is out of searched buffer - "too early" and "too late" conditions
TEST_P(FDRpp, AlignAndTooEarly) {
const size_t buf_alignment = 32;
// Buffer should be big enough to hold two instances of matching literals
// (up to 64 bytes each) and room for offset (up to 32 bytes)
const size_t data_len = 5 * buf_alignment;
const u32 hint = get<0>(GetParam());
SCOPED_TRACE(hint);
// pattern which is used to generate literals of variable size - from 1 to 64
const string &pattern = get<1>(GetParam()).pattern;
const size_t patLen = pattern.size();
const unsigned char alien = get<1>(GetParam()).alien;
// allocate aligned buffer
auto dataBufAligned = shared_ptr<char>(
(char *)aligned_malloc_internal(data_len, buf_alignment),
aligned_free_internal);
vector<hwlmLiteral> lits;
for (size_t litLen = 1; litLen <= patLen; litLen++) {
// building literal from pattern substring of variable length 1-64
lits.push_back(hwlmLiteral(string(pattern, 0, litLen), 0, 0));
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(),
Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
// check with buffer offset from aligned start from 0 to 31
for (size_t i = 0; i < buf_alignment; i++) {
// fill the whole buffer with 'alien' character
memset(dataBufAligned.get(), alien, data_len);
// put the matching substring to the beginning of unaligned buffer
memcpy(dataBufAligned.get() + i, pattern.data(), litLen);
// put the matching substring to the end of unaligned buffer
memcpy(dataBufAligned.get() + i + 4 * buf_alignment - litLen,
pattern.data(), litLen);
for (size_t j = 0; j <= litLen; j++) {
vector<match> matches;
hwlm_error_t fdrStatus = fdrExec(fdr.get(),
(const u8 *)dataBufAligned.get() + i + j,
4 * buf_alignment - j * 2, 0, decentCallback,
&matches, HWLM_ALL_GROUPS);
ASSERT_EQ(0, fdrStatus);
// j == 0 means that start and end matches are entirely within
// searched buffer. Otherwise they are out of buffer boundaries
// by j number of bytes - "too early" or "too late" conditions
// j == litLen means that matches are completely put of searched buffer
if (j == 0) {
// we should get two and only two matches - at the beginning and
// at the end of unaligned buffer
ASSERT_EQ(2U, matches.size());
ASSERT_EQ(match(0, litLen - 1, 0), matches[0]);
ASSERT_EQ(match(4 * buf_alignment - litLen, 4 * buf_alignment - 1, 0), matches[1]);
matches.clear();
} else {
// "Too early" / "too late" condition - should not match anything
ASSERT_EQ(0U, matches.size());
}
}
}
lits.clear();
}
}
static const pattern_alien_t test_pattern[] = {
{"abaabaaabaaabbaaaaabaaaaabbaaaaaaabaabbaaaabaaaaaaaabbbbaaaaaaab", 'x'},
{"zzzyyzyzyyyyzyyyyyzzzzyyyyyyyyzyyyyyyyzzzzzyzzzzzzzzzyzzyzzzzzzz", (unsigned char)'\x99'},
{"abcdef lafjk askldfjklf alfqwei9rui 'gldgkjnooiuswfs138746453583", '\0'}
};
INSTANTIATE_TEST_CASE_P(FDR, FDRpp, Combine(ValuesIn(getValidFdrEngines()),
ValuesIn(test_pattern)));
// This test generates an exhaustive set of short input buffers of length from
// 1 to 6 (1092 buffers) and 2750 buffers of length from 7 to >64 constructed
// from arbitrary set of short buffers. All buffers contain 3 characters from
// the alphabet given as a parameter to the test.
// Then it generates an exhaustive set of literals of length from 1 to 8
// containing first two characters from the same alphabet (510 literals)
// Literals are grouped by 32 to run search on each and every buffer.
// All resulting matches are checked.
// Fibonacci sequence is used to generate arbitrary buffers
unsigned long long fib (int n) {
unsigned long long fib0 = 1, fib1 = 1, fib2 = 1;
for (int i = 0; i < n; i++) {
fib2 = fib1 + fib0;
fib0 = fib1;
fib1 = fib2;
}
return fib2;
}
class FDRpa : public TestWithParam<tuple<u32, array<unsigned char, 3>>> {};
TEST_P(FDRpa, ShortWritings) {
const u32 hint = get<0>(GetParam());
SCOPED_TRACE(hint);
vector<string> bufs;
// create exhaustive buffer set for up to 6 literals:
const array<unsigned char, 3> &alphabet = get<1>(GetParam());
for (int len = 1; len <= 6; len++) {
for (int j = 0; j < (int)pow((double)3, len); j++) {
string s;
for (int k = 0; k < len; k++) {
s += alphabet[(j / (int)pow((double)3, k) % 3)];
}
bufs.push_back(s);
}
}
size_t buflen = bufs.size();
// create arbitrary buffers from exhaustive set of previously generated 'short'
for (int len = 7; len < 64; len++) {
for (int i = 0; i < 10; i++) {
string s;
for(int j = 0; (int)s.size() < len; j++) {
s += bufs[fib(i * 5 + j + (len - 6) * 10) % buflen];
}
bufs.push_back(s);
}
}
// generate exhaustive set of literals of length from 1 to 8
vector<string> pats;
for (int len = 1; len <= 8; len++) {
for (int j = 0; j < (int)pow((double)2, len); j++) {
string s;
for (int k = 0; k < len; k++) {
s += alphabet[(j >> k) & 1];
}
pats.push_back(s);
}
}
// run the literal matching through all generated literals
for (size_t patIdx = 0; patIdx < pats.size();) {
// group them in the sets of 32
vector<hwlmLiteral> testSigs;
for(int i = 0; i < 32 && patIdx < pats.size(); i++, patIdx++) {
testSigs.push_back(hwlmLiteral(pats[patIdx], false, patIdx));
}
auto fdr = fdrBuildTableHinted(testSigs, false, hint,
get_current_target(), Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
// run the literal matching for the prepared set of 32 literals
// on each generated buffer
for (size_t bufIdx = 0; bufIdx < bufs.size(); bufIdx++) {
const string &buf = bufs[bufIdx];
size_t bufLen = buf.size();
vector<match> matches;
hwlm_error_t fdrStatus = fdrExec(fdr.get(), (const u8 *)buf.data(),
bufLen, 0, decentCallback, &matches, HWLM_ALL_GROUPS);
ASSERT_EQ(0, fdrStatus);
// build the set of expected matches using standard
// stl::string::compare() function
vector<match> expMatches;
for (size_t pIdx = 0; pIdx < testSigs.size(); pIdx++) {
const string &pat = testSigs[pIdx].s;
size_t patLen = pat.size();
for (int j = 0; j <= (int)bufLen - (int)patLen; j++) {
if (!buf.compare(j, patLen, pat)) {
expMatches.push_back(match(j, j + patLen - 1,
testSigs[pIdx].id));
}
}
}
// compare the set obtained matches against expected ones
sort(expMatches.begin(), expMatches.end());
sort(matches.begin(), matches.end());
ASSERT_EQ(expMatches, matches);
}
}
}
static const array<unsigned char, 3> test_alphabet[] = {
{ { 'a', 'b', 'x' } },
{ { 'x', 'y', 'z' } },
{ { '\0', 'A', '\x20' } },
{ { 'a', '\x20', (unsigned char)'\x99' } }
};
INSTANTIATE_TEST_CASE_P(FDR, FDRpa, Combine(ValuesIn(getValidFdrEngines()),
ValuesIn(test_alphabet)));
TEST(FDR, FDRTermS) {
const char data1[] = "fffffffffffffffff";
const char data2[] = "ffffuuuuuuuuuuuuu";
size_t data_len1 = strlen(data1);
size_t data_len2 = strlen(data2);
hwlm_error_t fdrStatus = 0;
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("f", 0, 0));
lits.push_back(hwlmLiteral("ff", 0, 1));
auto fdr = fdrBuildTable(lits, false, get_current_target(), Grey());
ASSERT_TRUE(fdr != nullptr);
// check matches
vector<match> matches;
fdrStatus = fdrExecStreaming(fdr.get(), (const u8 *)data1, data_len1,
(const u8 *)data2, data_len2, 0,
decentCallbackT, &matches, HWLM_ALL_GROUPS, nullptr);
ASSERT_EQ(HWLM_TERMINATED, fdrStatus);
ASSERT_EQ(1U, matches.size());
}
TEST(FDR, FDRTermB) {
const char data1[] = "fffffffffffffffff";
size_t data_len1 = strlen(data1);
hwlm_error_t fdrStatus = 0;
vector<hwlmLiteral> lits;
lits.push_back(hwlmLiteral("f", 0, 0));
lits.push_back(hwlmLiteral("ff", 0, 1));
auto fdr = fdrBuildTable(lits, false, get_current_target(), Grey());
ASSERT_TRUE(fdr != nullptr);
// check matches
vector<match> matches;
fdrStatus = fdrExec(fdr.get(), (const u8 *)data1, data_len1,
0, decentCallbackT, &matches, HWLM_ALL_GROUPS);
ASSERT_EQ(HWLM_TERMINATED, fdrStatus);
ASSERT_EQ(1U, matches.size());
}
TEST(FDR, ManyLengths) {
// UE-2400: we had a crash due to div by zero in the compiler when given a
// set of literals with precisely 512 different lengths.
const u32 num = 512;
vector<hwlmLiteral> lits;
char c = 0;
string s;
for (u32 i = 0; i < num; i++) {
s.push_back(c++);
lits.push_back(hwlmLiteral(s, false, i + 1));
}
auto fdr = fdrBuildTable(lits, false, get_current_target(), Grey());
ASSERT_TRUE(fdr != nullptr);
// Confirm that we can scan against this FDR table as well.
vector<match> matches;
hwlm_error_t fdrStatus =
fdrExec(fdr.get(), (const u8 *)s.c_str(), s.size(), 0, decentCallback,
&matches, HWLM_ALL_GROUPS);
ASSERT_EQ(HWLM_SUCCESS, fdrStatus);
ASSERT_EQ(768U, matches.size());
}

545
unit/internal/fdr_flood.cpp Normal file
View File

@@ -0,0 +1,545 @@
/*
* 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 "ue2common.h"
#include "grey.h"
#include "fdr/fdr.h"
#include "fdr/fdr_compile.h"
#include "fdr/fdr_compile_internal.h"
#include "fdr/fdr_engine_description.h"
#include "fdr/teddy_compile.h"
#include "fdr/teddy_engine_description.h"
#include "util/alloc.h"
#include "util/bitutils.h"
#include "gtest/gtest.h"
using namespace std;
using namespace testing;
using namespace ue2;
#define NO_TEDDY_FAIL_ALLOWED 0
#if(NO_TEDDY_FAIL_ALLOWED)
#define CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint) ASSERT_TRUE(fdr)
#else
#define CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint) \
{ \
auto descr = getTeddyDescription(hint); \
if (descr && fdr != nullptr) { \
return; \
} else { \
ASSERT_TRUE(fdr != nullptr); \
} \
}
#endif
namespace {
struct match {
size_t start;
size_t end;
u32 id;
match(size_t start_in, size_t end_in, u32 id_in)
: start(start_in), end(end_in), id(id_in) {}
bool operator==(const match &b) const {
return start == b.start && end == b.end && id == b.id;
}
bool operator<(const match &b) const {
if (id < b.id) {
return true;
} else if (id == b.id) {
if (start < b.start) {
return true;
} else if (start == b.start) {
return end < b.end;
}
}
return false;
}
match operator+(size_t adj) {
return match(start + adj, end + adj, id);
}
};
template<typename T>
T &operator<<(T &a, const match &b) {
a << "(" << b.start << ", " << b.end << ", " << b.id << ")";
return a;
}
template<typename T>
T &operator<<(T &a, const vector<match> &b) {
a << "(";
for (size_t i = 0; i < b.size(); i++) {
a << b[i];
}
a << ")";
return a;
}
extern "C" {
static hwlmcb_rv_t countCallback(UNUSED size_t start, UNUSED size_t end, u32 id,
void *cntxt) {
if (cntxt) {
map<u32, int> *matchesCounts = (map<u32, int> *)cntxt;
(*matchesCounts)[id]++;
}
return HWLM_CONTINUE_MATCHING;
}
} // extern "C"
} // namespace
static vector<u32> getValidFdrEngines() {
vector<u32> ret;
vector<FDREngineDescription> des;
getFdrDescriptions(&des);
for (vector<FDREngineDescription>::const_iterator it = des.begin();
it != des.end(); ++it) {
if (it->isValidOnTarget(get_current_target())) {
ret.push_back(it->getID());
}
}
vector<TeddyEngineDescription> tDes;
getTeddyDescriptions(&tDes);
for (vector<TeddyEngineDescription>::const_iterator it = tDes.begin();
it != tDes.end(); ++it) {
if (it->isValidOnTarget(get_current_target())) {
ret.push_back(it->getID());
}
}
return ret;
}
class FDRFloodp : public TestWithParam<u32> {
};
TEST_P(FDRFloodp, NoMask) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const size_t dataSize = 1024;
vector<u8> data(dataSize);
u8 c = 0;
while (1) {
SCOPED_TRACE((unsigned int)c);
u8 bit = 1 << (c & 0x7);
u8 cAlt = c ^ bit;
memset(&data[0], c, dataSize);
vector<hwlmLiteral> lits;
// build literals of type "aaaa", "aaab", "baaa"
// of lengths 1, 2, 4, 8, 16, 32, both case-less and case-sensitive
for (int i = 0; i < 6 ; i++) {
string s(1 << i, c);
lits.push_back(hwlmLiteral(s, false, i * 8 + 0));
s[0] = cAlt;
lits.push_back(hwlmLiteral(s, false, i * 8 + 1));
lits.push_back(hwlmLiteral(s, true, i * 8 + 2));
s[0] = c;
s[s.size() - 1] = cAlt;
lits.push_back(hwlmLiteral(s, false, i * 8 + 3));
lits.push_back(hwlmLiteral(s, true, i * 8 + 4));
string sAlt(1 << i, cAlt);
lits.push_back(hwlmLiteral(sAlt, true, i * 8 + 5));
sAlt[0] = c;
lits.push_back(hwlmLiteral(sAlt, true, i * 8 + 6));
lits.push_back(hwlmLiteral(sAlt, false, i * 8 + 7));
}
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(),
Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
map <u32, int> matchesCounts;
hwlm_error_t fdrStatus = fdrExec(fdr.get(), &data[0], dataSize,
0, countCallback, (void *)&matchesCounts, HWLM_ALL_GROUPS);
ASSERT_EQ(0, fdrStatus);
for (u8 i = 0; i < 6 ; i++) {
u32 cnt = dataSize - (1 << i) + 1;
ASSERT_EQ(cnt, matchesCounts[i * 8 + 0]);
ASSERT_EQ(0, matchesCounts[i * 8 + 1]);
ASSERT_EQ(0, matchesCounts[i * 8 + 3]);
ASSERT_EQ(i == 0 ? cnt : 0, matchesCounts[i * 8 + 7]);
if (isalpha(c) && (bit == CASE_BIT)) {
ASSERT_EQ(cnt, matchesCounts[i * 8 + 2]);
ASSERT_EQ(cnt, matchesCounts[i * 8 + 4]);
ASSERT_EQ(cnt, matchesCounts[i * 8 + 5]);
ASSERT_EQ(cnt, matchesCounts[i * 8 + 6]);
} else {
ASSERT_EQ(0, matchesCounts[i * 8 + 2]);
ASSERT_EQ(0, matchesCounts[i * 8 + 4]);
ASSERT_EQ(0, matchesCounts[i * 8 + 5]);
ASSERT_EQ(i == 0 ? cnt : 0, matchesCounts[i * 8 + 6]);
}
}
matchesCounts.clear();
memset(&data[0], cAlt, dataSize);
fdrStatus = fdrExec(fdr.get(), &data[0], dataSize,
0, countCallback, (void *)&matchesCounts, HWLM_ALL_GROUPS);
ASSERT_EQ(0, fdrStatus);
for (u8 i = 0; i < 6 ; i++) {
u32 cnt = dataSize - (1 << i) + 1;
ASSERT_EQ(0, matchesCounts[i * 8 + 0]);
ASSERT_EQ(i == 0 ? cnt : 0, matchesCounts[i * 8 + 1]);
ASSERT_EQ(i == 0 ? cnt : 0, matchesCounts[i * 8 + 3]);
ASSERT_EQ(cnt, matchesCounts[i * 8 + 5]);
ASSERT_EQ(0, matchesCounts[i * 8 + 7]);
if (isalpha(c) && (bit == CASE_BIT)) {
ASSERT_EQ(cnt, matchesCounts[i * 8 + 2]);
ASSERT_EQ(cnt, matchesCounts[i * 8 + 4]);
ASSERT_EQ(cnt, matchesCounts[i * 8 + 6]);
} else {
ASSERT_EQ(i == 0 ? cnt : 0, matchesCounts[i * 8 + 2]);
ASSERT_EQ(i == 0 ? cnt : 0, matchesCounts[i * 8 + 4]);
ASSERT_EQ(0, matchesCounts[i * 8 + 6]);
}
}
if (++c == 0) {
break;
}
}
}
TEST_P(FDRFloodp, WithMask) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const size_t dataSize = 1024;
vector<u8> data(dataSize);
u8 c = '\0';
while (1) {
u8 bit = 1 << (c & 0x7);
u8 cAlt = c ^ bit;
SCOPED_TRACE((unsigned int)c);
memset(&data[0], c, dataSize);
vector<hwlmLiteral> lits;
// literals of type "aaaa" of length 4, 12, and mask of length
// 1, 2, 4, 8, 0f type "b.......", "b......b" both case-less and case-sensitive
string s4(4, c);
string s4Alt(4, cAlt);
for (int i = 0; i < 4 ; i++) {
u32 mskLen = 1 << i;
vector<u8> msk(mskLen, '\0');
vector<u8> cmp(mskLen, '\0');
cmp[0] = cAlt;
msk[0] = '\xff';
// msk[f0000000] cmp[c0000000] lit[aaaa]
if (mskLen > s4.length()) {
lits.push_back(hwlmLiteral(s4, false, false, i * 12 + 0,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 1,
HWLM_ALL_GROUPS, msk, cmp));
}
// msk[f0000000] cmp[e0000000] lit[EEEE]
if (bit == CASE_BIT && isalpha(c)) {
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 2,
HWLM_ALL_GROUPS, msk, cmp));
}
// msk[E0000000] cmp[E0000000] lit[eeee]
if ((cAlt & bit) == 0) {
msk[0] = ~bit;
lits.push_back(hwlmLiteral(s4, false, false, i * 12 + 3,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 4,
HWLM_ALL_GROUPS, msk, cmp));
}
// msk[f0000000] cmp[a0000000] lit[aaaa]
cmp[0] = c;
msk[0] = '\xff';
lits.push_back(hwlmLiteral(s4, false, false, i * 12 + 5,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 6,
HWLM_ALL_GROUPS, msk, cmp));
// msk[f0000000] cmp[a0000000] lit[cccc]
if (mskLen > s4Alt.length()) {
lits.push_back(hwlmLiteral(s4Alt, false, false, i * 12 + 7,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4Alt, true, false, i * 12 + 8,
HWLM_ALL_GROUPS, msk, cmp));
}
if (bit == CASE_BIT && isalpha(c)) {
// msk[f0000000] cmp[e0000000] lit[EEEE]
lits.push_back(hwlmLiteral(s4Alt, true, false, i * 12 + 9,
HWLM_ALL_GROUPS, msk, cmp));
// msk[f0000000] cmp[e000000E] lit[eeee]
cmp[mskLen - 1] = cAlt;
msk[mskLen - 1] = '\xff';
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 10,
HWLM_ALL_GROUPS, msk, cmp));
// msk[f0000000] cmp[E000000E] lit[eeee]
cmp[0] = cAlt;
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 11,
HWLM_ALL_GROUPS, msk, cmp));
}
}
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(),
Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
map <u32, int> matchesCounts;
hwlm_error_t fdrStatus = fdrExec(fdr.get(), &data[0], dataSize,
0, countCallback, &matchesCounts, HWLM_ALL_GROUPS);
ASSERT_EQ(0, fdrStatus);
const u32 cnt4 = dataSize - 4 + 1;
for (u8 i = 0; i < 4; i++) {
u32 mskLen = 1 << i;
u32 cntMask = MIN(cnt4, dataSize - mskLen + 1);
ASSERT_EQ(0, matchesCounts[i * 12 + 0]);
ASSERT_EQ(0, matchesCounts[i * 12 + 1]);
ASSERT_EQ(0, matchesCounts[i * 12 + 2]);
if ((cAlt & bit) == 0) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 3]);
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 4]);
}
if (mskLen > 4) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 5]);
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 6]);
ASSERT_EQ(0, matchesCounts[i * 12 + 7]);
if (bit == CASE_BIT && isalpha(c)) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 8]);
} else {
ASSERT_EQ(0, matchesCounts[i * 12 + 8]);
}
}
else {
ASSERT_EQ(cnt4, matchesCounts[i * 12 + 5]);
ASSERT_EQ(cnt4, matchesCounts[i * 12 + 6]);
}
if (bit == CASE_BIT && isalpha(c)) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 9]);
ASSERT_EQ(0, matchesCounts[i * 12 + 10]);
ASSERT_EQ(0, matchesCounts[i * 12 + 11]);
}
}
memset(&data[0], cAlt, dataSize);
matchesCounts.clear();
fdrStatus = fdrExec(fdr.get(), &data[0], dataSize,
0, countCallback, &matchesCounts, HWLM_ALL_GROUPS);
ASSERT_EQ(0, fdrStatus);
for (u8 i = 0; i < 4; i++) {
u32 mskLen = 1 << i;
u32 cntMask = MIN(cnt4, dataSize - mskLen + 1);
ASSERT_EQ(0, matchesCounts[i * 12 + 0]);
ASSERT_EQ(0, matchesCounts[i * 12 + 3]);
ASSERT_EQ(0, matchesCounts[i * 12 + 5]);
ASSERT_EQ(0, matchesCounts[i * 12 + 6]);
ASSERT_EQ(0, matchesCounts[i * 12 + 7]);
ASSERT_EQ(0, matchesCounts[i * 12 + 8]);
ASSERT_EQ(0, matchesCounts[i * 12 + 9]);
if (bit == CASE_BIT && isalpha(c)) {
ASSERT_EQ(mskLen > 4 ? cntMask : 0, matchesCounts[i * 12 + 1]);
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 2]);
if(islower(c)) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 4]);
} else {
ASSERT_EQ(0, matchesCounts[i * 12 + 4]);
}
ASSERT_EQ(mskLen == 1 ? cnt4 : 0, matchesCounts[i * 12 + 10]);
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 11]);
} else {
ASSERT_EQ(0, matchesCounts[i * 12 + 1]);
ASSERT_EQ(0, matchesCounts[i * 12 + 2]);
ASSERT_EQ(0, matchesCounts[i * 12 + 4]);
ASSERT_EQ(0, matchesCounts[i * 12 + 10]);
ASSERT_EQ(0, matchesCounts[i * 12 + 11]);
}
}
if (++c == '\0') {
break;
}
}
}
TEST_P(FDRFloodp, StreamingMask) {
const u32 hint = GetParam();
SCOPED_TRACE(hint);
const size_t dataSize = 1024;
vector<u8> data(dataSize);
u8 c = '\0';
while (1) {
u8 bit = 1 << (c & 0x7);
u8 cAlt = c ^ bit;
SCOPED_TRACE((unsigned int)c);
memset(&data[0], c, dataSize);
vector<hwlmLiteral> lits;
// literals of type "aaaa" of length 4, 12, and mask of length
// 1, 2, 4, 8, 0f type "b.......", "b......b" both case-less and case-sensitive
string s4(4, c);
string s4Alt(4, cAlt);
for (int i = 0; i < 4 ; i++) {
u32 mskLen = 1 << i;
vector<u8> msk(mskLen, '\0');
vector<u8> cmp(mskLen, '\0');
cmp[0] = cAlt;
msk[0] = '\xff';
// msk[f0000000] cmp[c0000000] lit[aaaa]
if (mskLen > s4.length()) {
lits.push_back(hwlmLiteral(s4, false, false, i * 12 + 0,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 1,
HWLM_ALL_GROUPS, msk, cmp));
}
// msk[f0000000] cmp[e0000000] lit[EEEE]
if (bit == CASE_BIT && isalpha(c)) {
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 2,
HWLM_ALL_GROUPS, msk, cmp));
}
// msk[E0000000] cmp[E0000000] lit[eeee]
if ((cAlt & bit) == 0) {
msk[0] = ~bit;
lits.push_back(hwlmLiteral(s4, false, false, i * 12 + 3,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 4,
HWLM_ALL_GROUPS, msk, cmp));
}
// msk[f0000000] cmp[a0000000] lit[aaaa]
cmp[0] = c;
msk[0] = '\xff';
lits.push_back(hwlmLiteral(s4, false, false, i * 12 + 5,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 6,
HWLM_ALL_GROUPS, msk, cmp));
// msk[f0000000] cmp[a0000000] lit[cccc]
if (mskLen > s4Alt.length()) {
lits.push_back(hwlmLiteral(s4Alt, false, false, i * 12 + 7,
HWLM_ALL_GROUPS, msk, cmp));
lits.push_back(hwlmLiteral(s4Alt, true, false, i * 12 + 8,
HWLM_ALL_GROUPS, msk, cmp));
}
if (bit == CASE_BIT && isalpha(c)) {
// msk[f0000000] cmp[e0000000] lit[EEEE]
lits.push_back(hwlmLiteral(s4Alt, true, false, i * 12 + 9,
HWLM_ALL_GROUPS, msk, cmp));
// msk[f0000000] cmp[e000000E] lit[eeee]
cmp[mskLen - 1] = cAlt;
msk[mskLen - 1] = '\xff';
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 10,
HWLM_ALL_GROUPS, msk, cmp));
// msk[f0000000] cmp[E000000E] lit[eeee]
cmp[0] = cAlt;
lits.push_back(hwlmLiteral(s4, true, false, i * 12 + 11,
HWLM_ALL_GROUPS, msk, cmp));
}
}
auto fdr = fdrBuildTableHinted(lits, false, hint, get_current_target(),
Grey());
CHECK_WITH_TEDDY_OK_TO_FAIL(fdr, hint);
map <u32, int> matchesCounts;
hwlm_error_t fdrStatus;
const u32 cnt4 = dataSize - 4 + 1;
for (u32 streamChunk = 1; streamChunk <= 16; streamChunk *= 2) {
matchesCounts.clear();
fdrStatus = fdrExecStreaming(fdr.get(), nullptr, 0, &data[0], streamChunk,
0, countCallback, &matchesCounts, HWLM_ALL_GROUPS, nullptr);
ASSERT_EQ(0, fdrStatus);
for (u32 j = streamChunk; j < dataSize; j += streamChunk) {
if (j < 8) {
fdrStatus = fdrExecStreaming(fdr.get(), &data[0], j,
&data[0] + j, streamChunk, 0, countCallback,
&matchesCounts, HWLM_ALL_GROUPS, nullptr);
} else {
fdrStatus = fdrExecStreaming(fdr.get(), &data[0] + j - 8,
8, &data[0] + j, streamChunk, 0, countCallback,
&matchesCounts, HWLM_ALL_GROUPS, nullptr);
}
ASSERT_EQ(0, fdrStatus);
}
for (u8 i = 0; i < 4; i++) {
u32 mskLen = 1 << i;
u32 cntMask = MIN(cnt4, dataSize - mskLen + 1);
ASSERT_EQ(0, matchesCounts[i * 12 + 0]);
ASSERT_EQ(0, matchesCounts[i * 12 + 1]);
ASSERT_EQ(0, matchesCounts[i * 12 + 2]);
if ((cAlt & bit) == 0) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 3]);
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 4]);
}
if (mskLen > 4) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 5]);
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 6]);
ASSERT_EQ(0, matchesCounts[i * 12 + 7]);
if (bit == CASE_BIT && isalpha(c)) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 8]);
} else {
ASSERT_EQ(0, matchesCounts[i * 12 + 8]);
}
}
else {
ASSERT_EQ(cnt4, matchesCounts[i * 12 + 5]);
ASSERT_EQ(cnt4, matchesCounts[i * 12 + 6]);
}
if (bit == CASE_BIT && isalpha(c)) {
ASSERT_EQ(cntMask, matchesCounts[i * 12 + 9]);
ASSERT_EQ(0, matchesCounts[i * 12 + 10]);
ASSERT_EQ(0, matchesCounts[i * 12 + 11]);
}
}
}
if (++c == '\0') {
break;
}
}
}
INSTANTIATE_TEST_CASE_P(FDRFlood, FDRFloodp, ValuesIn(getValidFdrEngines()));

View File

@@ -0,0 +1,211 @@
/*
* 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 "gtest/gtest.h"
#include "fdr/fdr_loadval.h"
#include "util/alloc.h"
using namespace std;
using namespace testing;
using namespace ue2;
// Normal (unaligned) load.
template <typename T> T lv(const u8 *ptr, const u8 *lo, const u8 *hi);
// Aligned load.
template <typename T> T lv_a(const u8 *ptr, const u8 *lo, const u8 *hi);
// Cautious forward load.
template <typename T> T lv_cf(const u8 *ptr, const u8 *lo, const u8 *hi);
// Cautious backward load.
template <typename T> T lv_cb(const u8 *ptr, const u8 *lo, const u8 *hi);
// Cautious everywhere load.
template <typename T> T lv_ce(const u8 *ptr, const u8 *lo, const u8 *hi);
// Special case: there is no specific _a "aligned load" func for u8. We proxy
// that to the normal load.
static u8 lv_u8_a(const u8 *ptr, const u8 *lo, const u8 *hi) {
return lv_u8(ptr, lo, hi);
}
#define BUILD_LOADVALS(vtype) \
template <> vtype lv<vtype>(const u8 *ptr, const u8 *lo, const u8 *hi) { \
return lv_##vtype(ptr, lo, hi); \
} \
template <> vtype lv_a<vtype>(const u8 *ptr, const u8 *lo, const u8 *hi) { \
return lv_##vtype##_a(ptr, lo, hi); \
} \
template <> \
vtype lv_cf<vtype>(const u8 *ptr, const u8 *lo, const u8 *hi) { \
return lv_##vtype##_cf(ptr, lo, hi); \
} \
template <> \
vtype lv_cb<vtype>(const u8 *ptr, const u8 *lo, const u8 *hi) { \
return lv_##vtype##_cb(ptr, lo, hi); \
} \
template <> \
vtype lv_ce<vtype>(const u8 *ptr, const u8 *lo, const u8 *hi) { \
return lv_##vtype##_ce(ptr, lo, hi); \
}
BUILD_LOADVALS(u8)
BUILD_LOADVALS(u16)
BUILD_LOADVALS(u32)
BUILD_LOADVALS(u64a)
BUILD_LOADVALS(m128)
template <typename T> class FDR_Loadval : public testing::Test {
// empty
};
typedef ::testing::Types<u8, u16, u32, u64a, m128> LoadvalTypes;
TYPED_TEST_CASE(FDR_Loadval, LoadvalTypes);
static void fillWithBytes(u8 *ptr, size_t len) {
for (size_t i = 0; i < len; i++) {
ptr[i] = (u8)(i % 254 + 1);
}
}
TYPED_TEST(FDR_Loadval, Normal) {
// We should be able to do a normal load at any alignment.
const size_t len = sizeof(TypeParam);
aligned_unique_ptr<u8> mem_p = aligned_zmalloc_unique<u8>(len + 15);
u8 * mem = mem_p.get();
ASSERT_TRUE(ISALIGNED_16(mem));
fillWithBytes(mem, len + 15);
// Test all alignments.
for (size_t i = 0; i < 16; i++) {
const u8 *src = mem + i;
TypeParam val = lv<TypeParam>(src, src, src + len);
// Should be identical to 'src' in byte order.
ASSERT_EQ(0, memcmp(&val, src, len));
}
}
TYPED_TEST(FDR_Loadval, Aligned) {
const size_t len = sizeof(TypeParam);
aligned_unique_ptr<u8> mem_p = aligned_zmalloc_unique<u8>(len); // 16 aligned
u8 * mem = mem_p.get();
ASSERT_TRUE(ISALIGNED_16(mem));
fillWithBytes(mem, len);
TypeParam val = lv_a<TypeParam>(mem, mem, mem + len);
// Should be identical to 'mem' in byte order.
ASSERT_EQ(0, memcmp(&val, mem, len));
}
TYPED_TEST(FDR_Loadval, CautiousForward) {
// For a cautious forward load, we will get zeroes for all bytes after the
// 'hi' ptr.
const size_t len = sizeof(TypeParam);
aligned_unique_ptr<u8> mem_p = aligned_zmalloc_unique<u8>(len + 1);
u8 *mem = mem_p.get() + 1; // force unaligned
fillWithBytes(mem, len);
for (size_t i = 1; i <= len; i++) {
const u8 *ptr = mem;
const u8 *lo = ptr;
const u8 *hi = ptr + i;
union {
TypeParam val;
u8 bytes[sizeof(TypeParam)];
} x;
x.val = lv_cf<TypeParam>(ptr, lo, hi);
// Low bytes will be correct, bytes >= hi will be zero.
for (size_t j = 0; j < len; j++) {
ASSERT_EQ(j < i ? mem[j] : 0, x.bytes[j]);
}
}
}
TYPED_TEST(FDR_Loadval, CautiousBackward) {
// For a cautious backwards load, we will get zeroes for all bytes before
// the 'lo' ptr.
const size_t len = sizeof(TypeParam);
aligned_unique_ptr<u8> mem_p = aligned_zmalloc_unique<u8>(len + 1);
u8 *mem = mem_p.get() + 1; // force unaligned
fillWithBytes(mem, len);
for (size_t i = 1; i <= len; i++) {
const u8 *ptr = mem;
const u8 *lo = ptr + sizeof(TypeParam) - i;
const u8 *hi = ptr + sizeof(TypeParam);
union {
TypeParam val;
u8 bytes[sizeof(TypeParam)];
} x;
x.val = lv_cb<TypeParam>(ptr, lo, hi);
// Low bytes will be zero, bytes >= lo will be correct.
for (size_t j = 0; j < len; j++) {
ASSERT_EQ(j < sizeof(TypeParam) - i ? 0 : mem[j], x.bytes[j]);
}
}
}
TYPED_TEST(FDR_Loadval, CautiousEverywhere) {
// For a cautious backwards load, we will get zeroes for all bytes before
// the 'lo' ptr or after the 'hi' ptr.
const size_t len = sizeof(TypeParam);
aligned_unique_ptr<u8> mem_p = aligned_zmalloc_unique<u8>(len + 1);
u8 *mem = mem_p.get() + 1; // force unaligned
fillWithBytes(mem, len);
for (size_t i = 0; i <= len; i++) {
for (size_t j = 0; j <= len; j++) {
const u8 *ptr = mem;
const u8 *lo = ptr + i;
const u8 *hi = ptr + j;
union {
TypeParam val;
u8 bytes[sizeof(TypeParam)];
} x;
x.val = lv_ce<TypeParam>(ptr, lo, hi);
// Bytes outside [lo,hi) will be zero.
for (size_t k = 0; k < len; k++) {
ASSERT_EQ((k >= i && k < j) ? mem[k] : 0, x.bytes[k]);
}
}
}
}

403
unit/internal/flat_map.cpp Normal file
View File

@@ -0,0 +1,403 @@
/*
* 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 "util/ue2_containers.h"
#include "ue2common.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
using namespace std;
using namespace ue2;
template <class K, class V>
std::ostream &operator<<(std::ostream &os, const flat_map<K, V> &f) {
os << "{";
for (auto it = begin(f); it != end(f); ++it) {
os << "{" << it->first << ", " << it->second << "}";
if (it != end(f)) {
os << ", ";
}
}
os << "}";
return os;
}
template<class FlatMap, class Compare>
bool flat_map_is_sorted_cmp(const FlatMap &f, const Compare &cmp) {
using value_type = typename FlatMap::value_type;
return is_sorted(f.begin(), f.end(),
[&cmp](const value_type &a, const value_type &b) {
return cmp(a.first, b.first);
});
}
template<class FlatMap>
bool flat_map_is_sorted(const FlatMap &f) {
return flat_map_is_sorted_cmp(f, f.key_comp());
}
TEST(flat_map, empty) {
flat_map<u32, u32> f;
EXPECT_TRUE(f.begin() == f.end());
EXPECT_TRUE(f.cbegin() == f.cend());
EXPECT_TRUE(f.rbegin() == f.rend());
EXPECT_TRUE(f.crbegin() == f.crend());
EXPECT_TRUE(f.empty());
EXPECT_EQ(0, f.size());
EXPECT_EQ(0, f.count(10));
}
TEST(flat_map, insert) {
flat_map<u32, u32> f;
f.insert(make_pair(1, 10));
f.insert(make_pair(2, 20));
EXPECT_EQ(2, f.size());
EXPECT_TRUE(flat_map_is_sorted(f));
EXPECT_EQ(1, f.begin()->first);
EXPECT_EQ(10, f.begin()->second);
EXPECT_EQ(2, f.rbegin()->first);
EXPECT_EQ(20, f.rbegin()->second);
EXPECT_EQ(1, f.count(1));
EXPECT_EQ(1, f.count(2));
EXPECT_EQ(0, f.count(3));
}
TEST(flat_map, clear) {
flat_map<u32, u32> f = {{1, 10}, {3, 30}, {2, 20}};
EXPECT_EQ(3, f.size());
EXPECT_FALSE(f.empty());
f.clear();
EXPECT_EQ(0, f.size());
EXPECT_TRUE(f.empty());
}
TEST(flat_map, sorted) {
vector<pair<u32, u32>> vec = {{7, 700}, {1, 100}, {3, 300}, {4, 400}, {2, 200}};
flat_map<u32, u32> f(vec.begin(), vec.end());
EXPECT_EQ(vec.size(), f.size());
EXPECT_EQ(1, f.begin()->first);
EXPECT_EQ(7, f.rbegin()->first);
EXPECT_TRUE(flat_map_is_sorted(f));
// all our elements are there
for (const auto &p : vec) {
auto it = f.find(p.first);
EXPECT_TRUE(it != f.end());
EXPECT_EQ(p.first, it->first);
EXPECT_EQ(p.second, it->second);
}
// these aren't there
EXPECT_TRUE(f.find(0) == f.end());
EXPECT_TRUE(f.find(5) == f.end());
EXPECT_TRUE(f.find(6) == f.end());
EXPECT_TRUE(f.find(8) == f.end());
// at() checks
for (const auto &p : vec) {
EXPECT_EQ(p.second, f.at(p.first));
}
ASSERT_THROW(f.at(0), std::out_of_range);
ASSERT_THROW(f.at(10), std::out_of_range);
// operator[]
for (const auto &p : vec) {
EXPECT_EQ(p.second, f[p.first]);
}
}
TEST(flat_map, dupe_keys) {
vector<pair<u32, u32>> vec = {{7, 700},
{1, 100},
{3, 300},
{4, 400},
{2, 200},
// dupes
{7, 700},
{2, 200},
{2, 200}};
flat_map<u32, u32> f(vec.begin(), vec.end());
EXPECT_EQ(5, f.size());
ASSERT_TRUE(flat_map_is_sorted(f));
}
TEST(flat_map, subscript) {
flat_map<u32, u32> f;
f[1] = 10;
f[3] = 30;
f[2] = 20;
EXPECT_EQ(3, f.size());
f[1] = 100;
f[2] = 200;
EXPECT_EQ(3, f.size());
ASSERT_TRUE(flat_map_is_sorted(f));
}
TEST(flat_map, init_list) {
flat_map<u32, std::string> f = {{1, "sydney"}, {2, "melbourne"}};
EXPECT_EQ(2, f.size());
ASSERT_TRUE(flat_map_is_sorted(f));
f.insert({{17, "adelaide"}, {14, "perth"}, {4, "brisbane" }});
EXPECT_EQ(5, f.size());
ASSERT_TRUE(flat_map_is_sorted(f));
ASSERT_EQ("sydney", f[1]);
ASSERT_EQ("melbourne", f[2]);
ASSERT_EQ("adelaide", f[17]);
ASSERT_EQ("perth", f[14]);
ASSERT_EQ("brisbane", f[4]);
f[1] = "hobart"; // replace
EXPECT_EQ(5, f.size());
ASSERT_EQ("hobart", f[1]);
f.erase(100); // not present
EXPECT_EQ(5, f.size());
f.erase(17);
EXPECT_EQ(4, f.size());
EXPECT_TRUE(f.find(17) == f.end());
}
TEST(flat_map, custom_compare) {
flat_map<u32, u32, std::greater<u32>> f;
ASSERT_EQ(0, f.size());
f.insert({{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70},
{8, 80}, {9, 90}, {10, 100}});
ASSERT_EQ(10, f.size());
ASSERT_EQ(10, f.begin()->first);
ASSERT_EQ(100, f.begin()->second);
ASSERT_EQ(1, f.rbegin()->first);
ASSERT_EQ(10, f.rbegin()->second);
ASSERT_TRUE(flat_map_is_sorted(f));
ASSERT_TRUE(flat_map_is_sorted_cmp(f, std::greater<u32>()));
}
namespace {
class MovableOnly {
public:
MovableOnly(size_t val_in, const string &name_in)
: val(val_in), name(name_in) {}
bool operator<(const MovableOnly &other) const {
return tie(val, name) < tie(other.val, other.name);
}
// Can't copy-construct or copy-assign.
MovableOnly(const MovableOnly &) = delete;
MovableOnly &operator=(const MovableOnly &) = delete;
// Moves are OK though.
MovableOnly(MovableOnly &&) = default;
MovableOnly &operator=(MovableOnly &&) = default;
size_t val;
string name;
};
} // namespace
TEST(flat_map, emplace) {
flat_map<u32, MovableOnly> f;
ASSERT_TRUE(f.empty());
auto rv = f.emplace(10, MovableOnly(1, string("hatstand")));
ASSERT_NE(end(f), rv.first);
ASSERT_EQ(1, rv.first->second.val);
ASSERT_TRUE(rv.second);
rv = f.emplace(30, MovableOnly(3, string("badgerbrush")));
ASSERT_NE(end(f), rv.first);
ASSERT_EQ(3, rv.first->second.val);
ASSERT_TRUE(rv.second);
rv = f.emplace(20, MovableOnly(2, string("teakettle")));
ASSERT_NE(end(f), rv.first);
ASSERT_EQ(2, rv.first->second.val);
ASSERT_TRUE(rv.second);
ASSERT_EQ(3, f.size());
ASSERT_TRUE(std::is_sorted(begin(f), end(f)));
rv = f.emplace(10, MovableOnly(1, string("hatstand"))); // dupe
ASSERT_FALSE(rv.second);
ASSERT_EQ(3, f.size());
ASSERT_TRUE(std::is_sorted(begin(f), end(f)));
}
TEST(flat_map, swap) {
flat_map<u32, u32> a = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 16}, {5, 32}};
flat_map<u32, u32> b = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}};
swap(a, b);
EXPECT_EQ(6, a.size());
EXPECT_EQ(6, b.size());
EXPECT_EQ(6, a.rbegin()->second);
EXPECT_EQ(32, b.rbegin()->second);
}
TEST(flat_map, iter) {
vector<pair<u32, u32>> vec = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 16}, {5, 32}};
flat_map<u32, u32> f(begin(vec), end(vec));
ASSERT_EQ(vec.size(), f.size());
ASSERT_TRUE(std::equal(f.begin(), f.end(), vec.begin()));
ASSERT_TRUE(std::equal(f.rbegin(), f.rend(), vec.rbegin()));
}
TEST(flat_map, erase_values) {
vector<pair<u32, u32>> vec = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 16}, {5, 32}};
flat_map<u32, u32> f(begin(vec), end(vec));
ASSERT_EQ(vec.size(), f.size());
for (const auto &v : vec) {
const auto &key = v.first;
ASSERT_TRUE(f.find(key) != f.end());
f.erase(key);
ASSERT_TRUE(f.find(key) == f.end());
}
ASSERT_TRUE(f.empty());
}
TEST(flat_map, erase_iter) {
vector<pair<u32, u32>> vec = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 16}, {5, 32}};
flat_map<u32, u32> f(begin(vec), end(vec));
ASSERT_EQ(vec.size(), f.size());
for (const auto &v : vec) {
const auto &key = v.first;
auto it = f.find(key);
ASSERT_TRUE(it != f.end());
f.erase(it);
ASSERT_TRUE(f.find(key) == f.end());
}
ASSERT_TRUE(f.empty());
}
TEST(flat_map, erase_iters) {
flat_map<u32, u32> f = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 16}, {5, 32},
{6, 64}, {7, 128}, {8, 256}, {9, 512}};
ASSERT_EQ(10, f.size());
auto first = f.find(3);
ASSERT_NE(end(f), first);
auto last = f.find(8);
ASSERT_NE(end(f), last);
f.erase(first, last);
flat_map<u32, u32> exp = {{0, 1}, {1, 2}, {2, 4}, {8, 256}, {9, 512}};
ASSERT_EQ(exp, f);
}
TEST(flat_map, erase_empty_range) {
flat_map<u32, u32> f = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 16}, {5, 32},
{6, 64}, {7, 128}, {8, 256}, {9, 512}, {10, 1024}};
const auto f2 = f; // copy
ASSERT_EQ(f, f2);
// Erasing (it, it) should do nothing.
for (const auto &val : f2) {
auto it = f.find(val.first);
f.erase(it, it);
ASSERT_EQ(f2, f);
}
}
TEST(flat_map, get_allocator) {
// Not a very interesting test, but it should pass valgrind leak tests,
// etc. Just testing the default allocator for now.
flat_map<u32, u32> f;
const u32 num = 10;
pair<u32, u32> *data = f.get_allocator().allocate(num);
for (u32 i = 0; i < num; i++) {
data[i] = make_pair(i, num - i);
}
for (u32 i = 0; i < num; i++) {
ASSERT_EQ(make_pair(i, num - i), data[i]);
}
f.get_allocator().deallocate(data, num);
}
TEST(flat_map, compare_ops) {
flat_map<u32, u32> f1 = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}};
flat_map<u32, u32> f1_copy = f1;
flat_map<u32, u32> f2 = {{2, 1}, {4, 2}, {6, 3}, {8, 4}, {10, 5}, {12, 6}};
EXPECT_TRUE(f1 == f1);
EXPECT_TRUE(f1 == f1_copy);
EXPECT_FALSE(f1 == f2);
EXPECT_FALSE(f1 != f1);
EXPECT_FALSE(f1 != f1_copy);
EXPECT_TRUE(f1 != f2);
EXPECT_FALSE(f1 < f1);
EXPECT_FALSE(f1 < f1_copy);
EXPECT_TRUE(f1 < f2);
EXPECT_TRUE(f1 <= f1);
EXPECT_TRUE(f1 <= f1_copy);
EXPECT_TRUE(f1 <= f2);
EXPECT_FALSE(f1 > f1);
EXPECT_FALSE(f1 > f1_copy);
EXPECT_FALSE(f1 > f2);
EXPECT_TRUE(f1 >= f1);
EXPECT_TRUE(f1 >= f1_copy);
EXPECT_FALSE(f1 >= f2);
}
TEST(flat_map, max_size) {
flat_map<string, string> f;
ASSERT_LE(1ULL << 24, f.max_size());
}

394
unit/internal/flat_set.cpp Normal file
View File

@@ -0,0 +1,394 @@
/*
* 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 "util/ue2_containers.h"
#include "ue2common.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
using std::string;
using std::tie;
using std::vector;
using namespace ue2;
template <class T>
std::ostream &operator<<(std::ostream &os, const flat_set<T> &f) {
os << "{";
for (auto it = begin(f); it != end(f); ++it) {
os << *it;
if (it != end(f)) {
os << ", ";
}
}
os << "}";
return os;
}
TEST(flat_set, empty) {
flat_set<u32> f;
EXPECT_TRUE(f.begin() == f.end());
EXPECT_TRUE(f.cbegin() == f.cend());
EXPECT_TRUE(f.rbegin() == f.rend());
EXPECT_TRUE(f.crbegin() == f.crend());
EXPECT_TRUE(f.empty());
EXPECT_EQ(0, f.size());
EXPECT_EQ(0, f.count(10));
}
TEST(flat_set, clear) {
flat_set<u32> f = {1000, 2000, 3000};
EXPECT_EQ(3, f.size());
EXPECT_FALSE(f.empty());
f.clear();
EXPECT_EQ(0, f.size());
EXPECT_TRUE(f.empty());
}
TEST(flat_set, one_element) {
flat_set<u32> f;
EXPECT_TRUE(f.empty());
f.insert(10);
EXPECT_FALSE(f.empty());
EXPECT_EQ(1, f.size());
EXPECT_EQ(1, f.count(10));
EXPECT_EQ(0, f.count(11));
ASSERT_FALSE(f.begin() == f.end());
ASSERT_FALSE(f.cbegin() == f.cend());
ASSERT_FALSE(f.rbegin() == f.rend());
ASSERT_FALSE(f.crbegin() == f.crend());
EXPECT_EQ(10, *f.begin());
EXPECT_EQ(10, *f.cbegin());
EXPECT_EQ(10, *f.rbegin());
EXPECT_EQ(10, *f.crbegin());
EXPECT_TRUE(std::next(f.begin()) == f.end());
EXPECT_TRUE(std::next(f.cbegin()) == f.cend());
EXPECT_TRUE(std::next(f.rbegin()) == f.rend());
EXPECT_TRUE(std::next(f.crbegin()) == f.crend());
}
TEST(flat_set, some_elements) {
vector<u32> input = { 10, 5, 2000, 1, 300 };
flat_set<u32> f;
for (const auto &v : input) {
f.insert(v);
}
ASSERT_FALSE(f.empty());
ASSERT_EQ(5, f.size());
ASSERT_EQ(1, *f.begin());
ASSERT_EQ(2000, *f.rbegin());
ASSERT_TRUE(std::is_sorted(f.begin(), f.end()));
for (const auto &v : input) {
ASSERT_TRUE(f.find(v) != f.end());
ASSERT_EQ(v, *f.find(v));
}
ASSERT_TRUE(f.find(2) == f.end());
ASSERT_TRUE(f.find(300000) == f.end());
}
TEST(flat_set, dupe_elements) {
flat_set<u32> f;
f.insert(10);
f.insert(50);
f.insert(10);
f.insert(50);
ASSERT_FALSE(f.empty());
ASSERT_EQ(2, f.size());
ASSERT_EQ(10, *f.begin());
ASSERT_EQ(50, *f.rbegin());
}
TEST(flat_set, init_ctor) {
flat_set<u32> f = {1000, 900, 800, 800, 700, 100, 500}; // unsorted + dupe
ASSERT_EQ(6, f.size());
ASSERT_TRUE(std::is_sorted(f.begin(), f.end()));
}
TEST(flat_set, insert_ilist) {
flat_set<u32> f;
ASSERT_EQ(0, f.size());
f.insert({10, 30, 20});
ASSERT_EQ(3, f.size());
f.insert({10, 30, 20}); // dupes
ASSERT_EQ(3, f.size());
f.insert({100, 50});
ASSERT_EQ(5, f.size());
ASSERT_TRUE(std::is_sorted(f.begin(), f.end()));
}
TEST(flat_set, custom_compare) {
flat_set<u32, std::greater<u32>> f;
ASSERT_EQ(0, f.size());
f.insert({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
ASSERT_EQ(10, f.size());
ASSERT_EQ(10, *f.begin());
ASSERT_EQ(1, *f.rbegin());
ASSERT_TRUE(std::is_sorted(f.begin(), f.end(), f.key_comp()));
ASSERT_TRUE(std::is_sorted(f.begin(), f.end(), f.value_comp()));
ASSERT_TRUE(std::is_sorted(f.begin(), f.end(), std::greater<u32>()));
}
TEST(flat_set, erase_values) {
vector<u32> input = { 10, 5, 2000, 1, 300 };
flat_set<u32> f(input.begin(), input.end());
ASSERT_EQ(input.size(), f.size());
for (const auto &v : input) {
ASSERT_TRUE(f.find(v) != f.end());
f.erase(v);
ASSERT_TRUE(f.find(v) == f.end());
}
ASSERT_TRUE(f.empty());
}
TEST(flat_set, erase_iter) {
vector<u32> input = { 10, 5, 2000, 1, 300 };
flat_set<u32> f(input.begin(), input.end());
ASSERT_EQ(input.size(), f.size());
for (const auto &v : input) {
auto it = f.find(v);
ASSERT_TRUE(it != f.end());
f.erase(it);
ASSERT_TRUE(f.find(v) == f.end());
}
ASSERT_TRUE(f.empty());
}
TEST(flat_set, erase_iters) {
flat_set<u32> f = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
ASSERT_EQ(10, f.size());
auto first = f.find(3);
ASSERT_NE(end(f), first);
auto last = f.find(8);
ASSERT_NE(end(f), last);
f.erase(first, last);
ASSERT_EQ(5, f.size());
ASSERT_EQ(flat_set<u32>({1, 2, 8, 9, 10}), f);
}
TEST(flat_set, erase_empty_range) {
flat_set<u32> f = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
const flat_set<u32> f2 = f; // copy
ASSERT_EQ(f, f2);
// Erasing (it, it) should do nothing.
for (const auto &val : f2) {
auto it = f.find(val);
f.erase(it, it);
ASSERT_EQ(f2, f);
}
}
namespace {
class MovableOnly {
public:
MovableOnly(size_t val_in, const string &name_in)
: val(val_in), name(name_in) {}
bool operator<(const MovableOnly &other) const {
return tie(val, name) < tie(other.val, other.name);
}
// Can't copy-construct or copy-assign.
MovableOnly(const MovableOnly &) = delete;
MovableOnly &operator=(const MovableOnly &) = delete;
// Moves are OK though.
MovableOnly(MovableOnly &&) = default;
MovableOnly &operator=(MovableOnly &&) = default;
size_t val;
string name;
};
} // namespace
TEST(flat_set, emplace) {
flat_set<MovableOnly> f;
ASSERT_TRUE(f.empty());
auto rv = f.emplace(10, string("hatstand"));
ASSERT_NE(end(f), rv.first);
ASSERT_EQ(10, rv.first->val);
ASSERT_TRUE(rv.second);
rv = f.emplace(30, string("badgerbrush"));
ASSERT_NE(end(f), rv.first);
ASSERT_EQ(30, rv.first->val);
ASSERT_TRUE(rv.second);
rv = f.emplace(20, string("teakettle"));
ASSERT_NE(end(f), rv.first);
ASSERT_EQ(20, rv.first->val);
ASSERT_TRUE(rv.second);
ASSERT_EQ(3, f.size());
ASSERT_TRUE(std::is_sorted(begin(f), end(f)));
rv = f.emplace(10, string("hatstand")); // dupe
ASSERT_FALSE(rv.second);
ASSERT_EQ(3, f.size());
ASSERT_TRUE(std::is_sorted(begin(f), end(f)));
}
TEST(flat_set, swap) {
flat_set<u32> a = {1, 2, 4, 8, 16, 32, 64};
flat_set<u32> b = {1, 2, 3, 4, 5, 6, 7};
swap(a, b);
EXPECT_EQ(7, a.size());
EXPECT_EQ(7, b.size());
EXPECT_EQ(7, *a.rbegin());
EXPECT_EQ(64, *b.rbegin());
}
TEST(flat_set, iter) {
const vector<u32> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // sorted
flat_set<u32> f(begin(vec), end(vec));
ASSERT_EQ(vec.size(), f.size());
ASSERT_TRUE(std::equal(f.begin(), f.end(), vec.begin()));
ASSERT_TRUE(std::equal(f.rbegin(), f.rend(), vec.rbegin()));
}
TEST(flat_set, iter_interop) {
const vector<u32> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // sorted
flat_set<u32> f(begin(vec), end(vec));
ASSERT_EQ(vec.size(), f.size());
const auto &cf = f; // const reference
// Forward
flat_set<u32>::iterator mutable_begin = f.begin();
ASSERT_EQ(f.cbegin(), mutable_begin);
ASSERT_EQ(cf.begin(), mutable_begin);
flat_set<u32>::iterator mutable_end = f.end();
ASSERT_EQ(f.cend(), mutable_end);
ASSERT_EQ(cf.end(), mutable_end);
ASSERT_EQ(f.size(), std::distance(mutable_begin, mutable_end));
ASSERT_EQ(f.size(), mutable_end - mutable_begin);
ASSERT_EQ(f.size(), mutable_end - f.cbegin());
ASSERT_EQ(f.size(), f.cend() - mutable_begin);
// Reverse
flat_set<u32>::reverse_iterator mutable_rbegin = f.rbegin();
ASSERT_EQ(f.crbegin(), mutable_rbegin);
ASSERT_EQ(cf.rbegin(), mutable_rbegin);
flat_set<u32>::reverse_iterator mutable_rend = f.rend();
ASSERT_EQ(f.crend(), mutable_rend);
ASSERT_EQ(cf.rend(), mutable_rend);
ASSERT_EQ(f.size(), std::distance(mutable_rbegin, mutable_rend));
ASSERT_EQ(f.size(), mutable_rend - mutable_rbegin);
ASSERT_EQ(f.size(), mutable_rend - f.crbegin());
ASSERT_EQ(f.size(), f.crend() - mutable_rbegin);
}
TEST(flat_set, compare_ops) {
flat_set<u32> f1 = {1, 2, 3, 4, 5};
flat_set<u32> f1_copy = f1;
flat_set<u32> f2 = {2, 4, 6, 8, 10};
EXPECT_TRUE(f1 == f1);
EXPECT_TRUE(f1 == f1_copy);
EXPECT_FALSE(f1 == f2);
EXPECT_FALSE(f1 != f1);
EXPECT_FALSE(f1 != f1_copy);
EXPECT_TRUE(f1 != f2);
EXPECT_FALSE(f1 < f1);
EXPECT_FALSE(f1 < f1_copy);
EXPECT_TRUE(f1 < f2);
EXPECT_TRUE(f1 <= f1);
EXPECT_TRUE(f1 <= f1_copy);
EXPECT_TRUE(f1 <= f2);
EXPECT_FALSE(f1 > f1);
EXPECT_FALSE(f1 > f1_copy);
EXPECT_FALSE(f1 > f2);
EXPECT_TRUE(f1 >= f1);
EXPECT_TRUE(f1 >= f1_copy);
EXPECT_FALSE(f1 >= f2);
}
TEST(flat_set, get_allocator) {
// Not a very interesting test, but it should pass valgrind leak tests,
// etc. Just testing the default allocator for now.
flat_set<u32> f;
const u32 num = 10;
u32 *data = f.get_allocator().allocate(num);
for (u32 i = 0; i < num; i++) {
data[i] = i;
}
for (u32 i = 0; i < num; i++) {
ASSERT_EQ(i, data[i]);
}
f.get_allocator().deallocate(data, num);
}
TEST(flat_set, max_size) {
flat_set<string> f;
ASSERT_LE(1ULL << 24, f.max_size());
}

273
unit/internal/graph.cpp Normal file
View File

@@ -0,0 +1,273 @@
/*
* 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 "gtest/gtest.h"
#include "util/graph.h"
#include <boost/graph/adjacency_iterator.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_traits.hpp>
using namespace boost;
using namespace std;
using namespace ue2;
typedef adjacency_list<vecS, vecS, bidirectionalS, no_property, no_property,
no_property> unit_graph;
typedef unit_graph::vertex_descriptor unit_vertex;
typedef unit_graph::edge_descriptor unit_edge;
TEST(graph_util, parallel) {
unit_graph g;
ASSERT_FALSE(has_parallel_edge(g));
unit_vertex v1 = add_vertex(g);
unit_vertex v2 = add_vertex(g);
ASSERT_FALSE(has_parallel_edge(g));
add_edge(v1, v2, g);
ASSERT_FALSE(has_parallel_edge(g));
add_edge(v2, v1, g);
ASSERT_FALSE(has_parallel_edge(g));
add_edge(v2, v1, g);
ASSERT_TRUE(has_parallel_edge(g));
}
TEST(graph_util, dag) {
unit_graph g;
ASSERT_TRUE(is_dag(g));
unit_vertex v1 = add_vertex(g);
unit_vertex v2 = add_vertex(g);
ASSERT_TRUE(is_dag(g));
add_edge(v1, v2, g);
unit_vertex v3 = add_vertex(g);
add_edge(v1, v3, g);
add_edge(v2, v3, g);
ASSERT_TRUE(is_dag(g));
add_edge(v2, v2, g);
ASSERT_FALSE(is_dag(g));
ASSERT_TRUE(is_dag(g, true)); /* only cycle is self loop */
unit_vertex v4 = add_vertex(g);
add_edge(v3, v4, g);
add_edge(v4, v1, g);
ASSERT_FALSE(is_dag(g, true)); /* now have a large cycle */
}
TEST(graph_util, degrees) {
unit_graph g;
unit_vertex a = add_vertex(g);
unit_vertex b = add_vertex(g);
unit_vertex c = add_vertex(g);
unit_vertex d = add_vertex(g);
unit_vertex e = add_vertex(g);
unit_vertex f = add_vertex(g);
add_edge(a, b, g);
add_edge(b, c, g);
add_edge(d, e, g);
add_edge(f, a, g);
add_edge(f, b, g);
add_edge(f, c, g);
unit_graph::vertex_iterator vi, ve;
tie(vi, ve) = vertices(g);
ASSERT_FALSE(anySelfLoop(g, vi, ve));
add_edge(b, b, g);
tie(vi, ve) = vertices(g);
ASSERT_TRUE(anySelfLoop(g, vi, ve));
add_edge(e, e, g);
ASSERT_FALSE(isLeafNode(a, g));
ASSERT_FALSE(isLeafNode(b, g));
ASSERT_TRUE( isLeafNode(c, g));
ASSERT_FALSE(isLeafNode(d, g));
ASSERT_FALSE( isLeafNode(e, g));
ASSERT_FALSE(isLeafNode(f, g));
ASSERT_FALSE(hasSelfLoop(a, g));
ASSERT_TRUE( hasSelfLoop(b, g));
ASSERT_FALSE(hasSelfLoop(c, g));
ASSERT_FALSE(hasSelfLoop(d, g));
ASSERT_TRUE( hasSelfLoop(e, g));
ASSERT_FALSE(hasSelfLoop(f, g));
ASSERT_EQ((size_t)1, proper_out_degree(a, g));
ASSERT_EQ((size_t)1, proper_out_degree(b, g));
ASSERT_EQ((size_t)0, proper_out_degree(c, g));
ASSERT_EQ((size_t)1, proper_out_degree(d, g));
ASSERT_EQ((size_t)0, proper_out_degree(e, g));
ASSERT_EQ((size_t)3, proper_out_degree(f, g));
ASSERT_EQ((size_t)1, proper_in_degree(a, g));
ASSERT_EQ((size_t)2, proper_in_degree(b, g));
ASSERT_EQ((size_t)2, proper_in_degree(c, g));
ASSERT_EQ((size_t)0, proper_in_degree(d, g));
ASSERT_EQ((size_t)1, proper_in_degree(e, g));
ASSERT_TRUE( has_successor(a, g));
ASSERT_TRUE( has_successor(b, g));
ASSERT_FALSE(has_successor(c, g));
ASSERT_TRUE( has_successor(d, g));
ASSERT_TRUE( has_successor(e, g));
ASSERT_TRUE( has_successor(f, g));
ASSERT_TRUE( has_proper_successor(a, g));
ASSERT_TRUE( has_proper_successor(b, g));
ASSERT_FALSE(has_proper_successor(c, g));
ASSERT_TRUE( has_proper_successor(d, g));
ASSERT_FALSE(has_proper_successor(e, g));
ASSERT_TRUE( has_proper_successor(f, g));
ASSERT_TRUE( hasGreaterInDegree(0, a, g));
ASSERT_FALSE(hasGreaterInDegree(1, a, g));
ASSERT_TRUE( hasGreaterInDegree(2, b, g));
ASSERT_FALSE(hasGreaterInDegree(3, b, g));
ASSERT_TRUE( hasGreaterInDegree(1, c, g));
ASSERT_FALSE(hasGreaterInDegree(2, c, g));
ASSERT_FALSE(hasGreaterInDegree(0, d, g));
ASSERT_TRUE( hasGreaterInDegree(1, e, g));
ASSERT_FALSE(hasGreaterInDegree(2, e, g));
ASSERT_FALSE(hasGreaterInDegree(0, f, g));
ASSERT_TRUE( hasGreaterOutDegree(0, a, g));
ASSERT_FALSE(hasGreaterOutDegree(1, a, g));
ASSERT_TRUE( hasGreaterOutDegree(1, b, g));
ASSERT_FALSE(hasGreaterOutDegree(2, b, g));
ASSERT_FALSE(hasGreaterOutDegree(0, c, g));
ASSERT_TRUE( hasGreaterOutDegree(0, d, g));
ASSERT_FALSE(hasGreaterOutDegree(1, d, g));
ASSERT_TRUE( hasGreaterOutDegree(0, e, g));
ASSERT_FALSE(hasGreaterOutDegree(1, e, g));
ASSERT_TRUE( hasGreaterOutDegree(2, f, g));
ASSERT_FALSE(hasGreaterOutDegree(3, f, g));
}
TEST(graph_util, in_degree_equal_to_1) {
unit_graph g;
unit_vertex a = add_vertex(g);
unit_vertex b = add_vertex(g);
unit_vertex c = add_vertex(g);
unit_vertex d = add_vertex(g);
ASSERT_TRUE(in_degree_equal_to(a, g, 0));
ASSERT_FALSE(in_degree_equal_to(a, g, 1));
ASSERT_FALSE(in_degree_equal_to(a, g, 2));
add_edge(b, a, g);
ASSERT_FALSE(in_degree_equal_to(a, g, 0));
ASSERT_TRUE(in_degree_equal_to(a, g, 1));
ASSERT_FALSE(in_degree_equal_to(a, g, 2));
add_edge(c, a, g);
ASSERT_FALSE(in_degree_equal_to(a, g, 0));
ASSERT_FALSE(in_degree_equal_to(a, g, 1));
ASSERT_TRUE(in_degree_equal_to(a, g, 2));
add_edge(d, a, g);
ASSERT_FALSE(in_degree_equal_to(a, g, 0));
ASSERT_FALSE(in_degree_equal_to(a, g, 1));
ASSERT_FALSE(in_degree_equal_to(a, g, 2));
}
TEST(graph_util, edge_by_target_1) {
unit_graph g;
unit_vertex a = add_vertex(g);
unit_vertex b = add_vertex(g);
unit_vertex c = add_vertex(g);
ASSERT_FALSE(edge_by_target(a, a, g).second);
ASSERT_FALSE(edge_by_target(a, b, g).second);
ASSERT_FALSE(edge_by_target(a, c, g).second);
ASSERT_FALSE(edge_by_target(b, a, g).second);
ASSERT_FALSE(edge_by_target(c, b, g).second);
unit_edge ab = add_edge(a, b, g).first;
ASSERT_FALSE(edge_by_target(a, a, g).second);
ASSERT_TRUE(edge_by_target(a, b, g).second);
ASSERT_TRUE(ab == edge_by_target(a, b, g).first);
ASSERT_FALSE(edge_by_target(a, c, g).second);
ASSERT_FALSE(edge_by_target(b, a, g).second);
ASSERT_FALSE(edge_by_target(b, b, g).second);
ASSERT_FALSE(edge_by_target(c, b, g).second);
unit_edge cb = add_edge(c, b, g).first;
ASSERT_FALSE(edge_by_target(a, a, g).second);
ASSERT_TRUE(edge_by_target(a, b, g).second);
ASSERT_TRUE(ab == edge_by_target(a, b, g).first);
ASSERT_FALSE(edge_by_target(a, c, g).second);
ASSERT_FALSE(edge_by_target(b, a, g).second);
ASSERT_FALSE(edge_by_target(b, b, g).second);
ASSERT_TRUE(edge_by_target(c, b, g).second);
ASSERT_TRUE(cb == edge_by_target(c, b, g).first);
unit_edge aa = add_edge(a, a, g).first;
unit_edge bb = add_edge(b, b, g).first;
ASSERT_TRUE(edge_by_target(a, a, g).second);
ASSERT_TRUE(aa == edge_by_target(a, a, g).first);
ASSERT_TRUE(edge_by_target(a, b, g).second);
ASSERT_TRUE(ab == edge_by_target(a, b, g).first);
ASSERT_FALSE(edge_by_target(a, c, g).second);
ASSERT_FALSE(edge_by_target(b, a, g).second);
ASSERT_TRUE(edge_by_target(b, b, g).second);
ASSERT_TRUE(bb == edge_by_target(b, b, g).first);
ASSERT_TRUE(edge_by_target(c, b, g).second);
ASSERT_TRUE(cb == edge_by_target(c, b, g).first);
}

258
unit/internal/lbr.cpp Normal file
View File

@@ -0,0 +1,258 @@
/*
* 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 "gtest/gtest.h"
#include "util/target_info.h"
#include "util/charreach.h"
#include "nfa/lbr.h"
#include "nfa/nfa_api.h"
#include "nfa/nfa_internal.h"
#include "nfa/nfa_api_util.h"
#include "nfagraph/ng_lbr.h"
#include "scratch.h"
#include "util/alloc.h"
#include "util/compile_context.h"
#include "grey.h"
#include "nfagraph/ng.h"
#include "compiler/compiler.h"
#include "hs_compile.h" /* for controlling ssse3 usage */
#include <ostream>
using namespace std;
using namespace testing;
using namespace ue2;
struct LbrTestParams {
CharReach reach;
u32 min;
u32 max;
// Ugly but simple.
string make_pattern() {
std::ostringstream oss;
oss << "^[";
for (size_t i = reach.find_first(); i != CharReach::npos;
i = reach.find_next(i)) {
oss << "\\x" << std::hex << std::setw(2) << std::setfill('0')
<< (unsigned)(i & 0xff) << std::dec;
}
oss << "]{" << min << "," << max << "}";
return oss.str();
}
};
static
int onMatch(u64a, ReportID, void *ctx) {
unsigned *matches = (unsigned *)ctx;
(*matches)++;
return MO_CONTINUE_MATCHING;
}
class LbrTest : public TestWithParam<LbrTestParams> {
protected:
virtual void SetUp() {
params = GetParam();
hs_platform_info plat;
hs_error_t err = hs_populate_platform(&plat);
ASSERT_EQ(HS_SUCCESS, err);
target_t target(plat);
string pattern = params.make_pattern();
const unsigned flags = 0;
const Grey grey;
const CompileContext cc(true, false, target, grey);
ReportManager rm(cc.grey);
ParsedExpression parsed(0, pattern.c_str(), flags, 0);
unique_ptr<NGWrapper> g = buildWrapper(rm, cc, parsed);
ASSERT_TRUE(g != nullptr);
ASSERT_TRUE(isLBR(*g, grey));
vector<vector<CharReach> > triggers;
triggers.push_back(vector<CharReach>());
triggers.back().push_back(CharReach::dot()); /* lbr triggered by . */
nfa = constructLBR(*g, triggers, cc);
ASSERT_TRUE(nfa != nullptr);
full_state = aligned_zmalloc_unique<char>(nfa->scratchStateSize);
stream_state = aligned_zmalloc_unique<char>(nfa->streamStateSize);
}
virtual void initQueue() {
q.nfa = nfa.get();
q.cur = 0;
q.end = 0;
q.state = full_state.get();
q.streamState = stream_state.get();
q.offset = 0;
q.buffer = nullptr; // filled in by test
q.length = 0; // filled in by test
q.history = nullptr;
q.hlength = 0;
q.scratch = nullptr; // not needed by LBR
q.report_current = 0;
q.cb = onMatch;
q.som_cb = nullptr; // only used by Haig
q.context = &matches;
}
virtual string matchingCorpus(size_t len) const {
string s;
s.reserve(len);
size_t c = params.reach.find_first();
for (size_t i = 0; i < len; i++) {
s.push_back(c);
c = params.reach.find_next(c);
if (c == CharReach::npos) {
c = params.reach.find_first();
}
}
return s;
}
// Params.
LbrTestParams params;
// Match count
unsigned matches;
// Compiled NFA structure.
aligned_unique_ptr<NFA> nfa;
// Space for full state.
aligned_unique_ptr<char> full_state;
// Space for stream state.
aligned_unique_ptr<char> stream_state;
// Space for NFAContext structure.
aligned_unique_ptr<void> nfa_context;
// Queue structure.
struct mq q;
};
static const LbrTestParams params[] = {
{ CharReach::dot(), 100, 100 },
{ CharReach::dot(), 10, 100 },
{ CharReach::dot(), 99, 100 },
{ CharReach::dot(), 20, 40 },
{ CharReach("A"), 32, 64 },
{ CharReach("Aa"), 32, 64 },
{ CharReach("0123456789ABCDEFabcdef"), 1000, 1000 },
{ CharReach("0123456789ABCDEFabcdef"), 100, 1000 },
{ CharReach("0123456789ABCDEFabcdef"), 950, 1000 },
{ ~CharReach("a"), 128, 128 },
{ ~CharReach('\x00'), 128, 256 },
{ ~CharReach('\xff'), 128, 256 },
// set enough bits to create a truffle
{ CharReach("abcXYZ012") | CharReach('\x80') | CharReach('\x91') |
CharReach('\xAA') | CharReach('\xDE') | CharReach('\xFF'), 32, 64 },
{ CharReach("abcXYZ012") | CharReach('\x80') | CharReach('\x91') |
CharReach('\xAA') | CharReach('\xDE') | CharReach('\xFF'), 100, 1000 },
};
INSTANTIATE_TEST_CASE_P(Lbr, LbrTest, ValuesIn(params));
TEST_P(LbrTest, MatchMin) {
ASSERT_FALSE(params.reach.none());
// string of min length consisting entirely of matching chars.
const string corpus = matchingCorpus(params.min);
initQueue();
q.buffer = (const u8 *)corpus.c_str();
q.length = corpus.length();
u64a end = corpus.length();
nfaQueueInitState(nfa.get(), &q);
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
matches = 0;
nfaQueueExec(nfa.get(), &q, end);
ASSERT_EQ(1, matches);
}
TEST_P(LbrTest, MatchMax) {
ASSERT_FALSE(params.reach.none());
// string of min length consisting entirely of matching chars.
const string corpus = matchingCorpus(params.max);
initQueue();
q.buffer = (const u8 *)corpus.c_str();
q.length = corpus.length();
u64a end = corpus.length();
nfaQueueInitState(nfa.get(), &q);
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
matches = 0;
nfaQueueExec(nfa.get(), &q, end);
ASSERT_EQ(params.max - params.min + 1, matches);
}
TEST_P(LbrTest, InitCompressedState0) {
char rv = nfaInitCompressedState(nfa.get(), 0, stream_state.get(), '\0');
ASSERT_NE(0, rv);
}
TEST_P(LbrTest, QueueExecToMatch) {
ASSERT_FALSE(params.reach.none());
// string of min length consisting entirely of matching chars.
const string corpus = matchingCorpus(params.min);
initQueue();
q.buffer = (const u8 *)corpus.c_str();
q.length = corpus.length();
u64a end = corpus.length();
nfaQueueInitState(nfa.get(), &q);
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
matches = 0;
char rv = nfaQueueExecToMatch(nfa.get(), &q, end);
ASSERT_EQ(MO_MATCHES_PENDING, rv);
ASSERT_EQ(0, matches);
ASSERT_NE(0, nfaInAcceptState(nfa.get(), 0, &q));
nfaReportCurrentMatches(nfa.get(), &q);
ASSERT_EQ(1, matches);
}

482
unit/internal/limex_nfa.cpp Normal file
View File

@@ -0,0 +1,482 @@
/*
* 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 "gtest/gtest.h"
#include "grey.h"
#include "compiler/compiler.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_limex.h"
#include "nfagraph/ng_restructuring.h"
#include "nfa/limex_context.h"
#include "nfa/limex_internal.h"
#include "nfa/nfa_api.h"
#include "nfa/nfa_api_util.h"
#include "nfa/nfa_internal.h"
#include "scratch.h"
#include "util/alloc.h"
#include "util/target_info.h"
using namespace std;
using namespace testing;
using namespace ue2;
static const string SCAN_DATA = "___foo______\n___foofoo_foo_^^^^^^^^^^^^^^^^^^^^^^__bar_bar______0_______z_____bar";
static
int onMatch(u64a, ReportID, void *ctx) {
unsigned *matches = (unsigned *)ctx;
(*matches)++;
return MO_CONTINUE_MATCHING;
}
// Parameterized with LimEx model and target flags.
class LimExModelTest : public TestWithParam<int> {
protected:
virtual void SetUp() {
type = GetParam();
hs_platform_info plat;
hs_error_t err = hs_populate_platform(&plat);
ASSERT_EQ(HS_SUCCESS, err);
target_t target(plat);
matches = 0;
const string expr = "(foo.*bar)|end\\z";
const unsigned flags = 0;
CompileContext cc(false, false, target, Grey());
ReportManager rm(cc.grey);
ParsedExpression parsed(0, expr.c_str(), flags, 0);
unique_ptr<NGWrapper> g = buildWrapper(rm, cc, parsed);
ASSERT_TRUE(g != nullptr);
const map<u32, u32> fixed_depth_tops;
const map<u32, vector<vector<CharReach>>> triggers;
bool compress_state = false;
nfa = constructNFA(*g, &rm, fixed_depth_tops, triggers, compress_state,
type, cc);
ASSERT_TRUE(nfa != nullptr);
full_state = aligned_zmalloc_unique<char>(nfa->scratchStateSize);
stream_state = aligned_zmalloc_unique<char>(nfa->streamStateSize);
nfa_context = aligned_zmalloc_unique<void>(sizeof(NFAContext512));
// Mock up a scratch structure that contains the pieces that we need
// for NFA execution.
scratch = aligned_zmalloc_unique<hs_scratch>(sizeof(struct hs_scratch));
scratch->nfaContext = nfa_context.get();
}
virtual void initQueue() {
q.nfa = nfa.get();
q.cur = 0;
q.end = 0;
q.state = full_state.get();
q.streamState = stream_state.get();
q.offset = 0;
q.buffer = (const u8 *)SCAN_DATA.c_str();
q.length = SCAN_DATA.size();
q.history = nullptr;
q.hlength = 0;
q.scratch = scratch.get();
q.report_current = 0;
q.cb = onMatch;
q.som_cb = nullptr; // only used by Haig
q.context = &matches;
}
// NFA type (enum NFAEngineType)
int type;
// Match count
unsigned matches;
// Compiled NFA structure.
aligned_unique_ptr<NFA> nfa;
// Space for full state.
aligned_unique_ptr<char> full_state;
// Space for stream state.
aligned_unique_ptr<char> stream_state;
// Space for NFAContext structure.
aligned_unique_ptr<void> nfa_context;
// Mock scratch.
aligned_unique_ptr<hs_scratch> scratch;
// Queue structure.
struct mq q;
};
INSTANTIATE_TEST_CASE_P(
LimEx, LimExModelTest,
Range((int)LIMEX_NFA_32_1, (int)LIMEX_NFA_512_7));
TEST_P(LimExModelTest, StateSize) {
ASSERT_TRUE(nfa != nullptr);
hs_platform_info plat;
hs_error_t err = hs_populate_platform(&plat);
ASSERT_EQ(HS_SUCCESS, err);
target_t target(plat);
// About all we can say is that any NFA should require at least one byte of
// state space.
EXPECT_LT(0, nfa->scratchStateSize);
EXPECT_LT(0, nfa->streamStateSize);
}
TEST_P(LimExModelTest, QueueExec) {
ASSERT_TRUE(nfa != nullptr);
initQueue();
nfaQueueInitState(nfa.get(), &q);
u64a end = SCAN_DATA.size();
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
nfaQueueExec(nfa.get(), &q, end);
ASSERT_EQ(3, matches);
}
TEST_P(LimExModelTest, CompressExpand) {
ASSERT_TRUE(nfa != nullptr);
// 64-bit NFAs assume during compression that they have >= 5 bytes of
// compressed NFA state, which isn't true for our 8-state test pattern. We
// skip this test for just these models.
if (nfa->scratchStateSize == 8) {
return;
}
initQueue();
nfaQueueInitState(nfa.get(), &q);
// Do some scanning.
u64a end = SCAN_DATA.size();
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
nfaQueueExec(nfa.get(), &q, end);
// Compress state.
nfaQueueCompressState(nfa.get(), &q, end);
// Expand state into a new copy and check that it matches the original
// uncompressed state.
aligned_unique_ptr<char> state_copy =
aligned_zmalloc_unique<char>(nfa->scratchStateSize);
char *dest = state_copy.get();
memset(dest, 0xff, nfa->scratchStateSize);
nfaExpandState(nfa.get(), dest, q.streamState, q.offset,
queue_prev_byte(&q, end));
ASSERT_TRUE(std::equal(dest, dest + nfa->scratchStateSize,
full_state.get()));
}
TEST_P(LimExModelTest, InitCompressedState0) {
ASSERT_TRUE(nfa != nullptr);
// 64-bit NFAs assume during compression that they have >= 5 bytes of
// compressed NFA state, which isn't true for our 8-state test pattern. We
// skip this test for just these models.
if (nfa->scratchStateSize == 8) {
return;
}
// Trivial case: init at zero, like we do with outfixes.
char rv = nfaInitCompressedState(nfa.get(), 0, stream_state.get(), '\0');
ASSERT_NE(0, rv);
}
TEST_P(LimExModelTest, QueueExecToMatch) {
ASSERT_TRUE(nfa != nullptr);
initQueue();
nfaQueueInitState(nfa.get(), &q);
u64a end = SCAN_DATA.size();
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
// FIRST MATCH (of three).
char rv = nfaQueueExecToMatch(nfa.get(), &q, end);
ASSERT_EQ(MO_MATCHES_PENDING, rv);
ASSERT_EQ(0, matches);
ASSERT_NE(0, nfaInAcceptState(nfa.get(), 0, &q));
nfaReportCurrentMatches(nfa.get(), &q);
ASSERT_EQ(1, matches);
// SECOND MATCH (of three).
rv = nfaQueueExecToMatch(nfa.get(), &q, end);
ASSERT_EQ(MO_MATCHES_PENDING, rv);
ASSERT_EQ(1, matches);
ASSERT_NE(0, nfaInAcceptState(nfa.get(), 0, &q));
nfaReportCurrentMatches(nfa.get(), &q);
ASSERT_EQ(2, matches);
// THIRD MATCH (of three).
rv = nfaQueueExecToMatch(nfa.get(), &q, end);
ASSERT_EQ(MO_MATCHES_PENDING, rv);
ASSERT_EQ(2, matches);
ASSERT_NE(0, nfaInAcceptState(nfa.get(), 0, &q));
nfaReportCurrentMatches(nfa.get(), &q);
ASSERT_EQ(3, matches);
// No more.
rv = nfaQueueExecToMatch(nfa.get(), &q, end);
ASSERT_EQ(MO_ALIVE, rv);
ASSERT_EQ(3, matches);
}
TEST_P(LimExModelTest, QueueExecRose) {
ASSERT_TRUE(nfa != nullptr);
initQueue();
// For rose, there's no callback or context.
q.cb = nullptr;
q.context = nullptr;
nfaQueueInitState(nfa.get(), &q);
u64a end = SCAN_DATA.size();
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
char rv = nfaQueueExecRose(nfa.get(), &q, 0 /* report id */);
ASSERT_EQ(MO_MATCHES_PENDING, rv);
pushQueue(&q, MQE_START, end);
ASSERT_NE(0, nfaInAcceptState(nfa.get(), 0, &q));
}
TEST_P(LimExModelTest, CheckFinalState) {
ASSERT_TRUE(nfa != nullptr);
initQueue();
nfaQueueInitState(nfa.get(), &q);
// Do some scanning.
u64a end = SCAN_DATA.size();
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
nfaQueueExec(nfa.get(), &q, end);
ASSERT_EQ(3, matches);
// Check for EOD matches.
char rv = nfaCheckFinalState(nfa.get(), full_state.get(),
stream_state.get(), end, onMatch, nullptr,
&matches);
ASSERT_EQ(MO_CONTINUE_MATCHING, rv);
}
// For testing the _B_Reverse backwards-scanning block-mode path.
class LimExReverseTest : public TestWithParam<int> {
protected:
virtual void SetUp() {
type = GetParam();
matches = 0;
const string expr = "foo.*bar";
const unsigned flags = 0;
CompileContext cc(false, false, get_current_target(), Grey());
ReportManager rm(cc.grey);
ParsedExpression parsed(0, expr.c_str(), flags, 0);
unique_ptr<NGWrapper> g = buildWrapper(rm, cc, parsed);
ASSERT_TRUE(g != nullptr);
// Reverse the graph and add some reports on the accept vertices.
NGHolder g_rev(NFA_REV_PREFIX);
reverseHolder(*g, g_rev);
NFAGraph::inv_adjacency_iterator ai, ae;
for (tie(ai, ae) = inv_adjacent_vertices(g_rev.accept, g_rev); ai != ae;
++ai) {
g_rev[*ai].reports.insert(0);
}
nfa = constructReversedNFA(g_rev, type, cc);
ASSERT_TRUE(nfa != nullptr);
nfa_context = aligned_zmalloc_unique<void>(sizeof(NFAContext512));
// Mock up a scratch structure that contains the pieces that we need
// for reverse NFA execution.
scratch = aligned_zmalloc_unique<hs_scratch>(sizeof(struct hs_scratch));
scratch->nfaContextSom = nfa_context.get();
}
// NFA type (enum NFAEngineType)
int type;
// Match count
unsigned matches;
// Compiled NFA structure.
aligned_unique_ptr<NFA> nfa;
// Space for NFAContext structure.
aligned_unique_ptr<void> nfa_context;
// Mock scratch.
aligned_unique_ptr<hs_scratch> scratch;
};
INSTANTIATE_TEST_CASE_P(LimExReverse, LimExReverseTest,
Range((int)LIMEX_NFA_32_1, (int)LIMEX_NFA_512_7));
TEST_P(LimExReverseTest, BlockExecReverse) {
ASSERT_TRUE(nfa != nullptr);
u64a offset = 0;
const u8 *buf = (const u8 *)SCAN_DATA.c_str();
const size_t buflen = SCAN_DATA.size();
const u8 *hbuf = nullptr;
const size_t hlen = 0;
nfaBlockExecReverse(nfa.get(), offset, buf, buflen, hbuf, hlen,
scratch.get(), onMatch, &matches);
ASSERT_EQ(3, matches);
}
// Test the ZOMBIE path.
static const string ZOMBIE_SCAN_DATA = "braaaiiiiiins!!!!!";
class LimExZombieTest : public TestWithParam<int> {
protected:
virtual void SetUp() {
type = GetParam();
matches = 0;
const string expr = "bra+i+ns.*";
const unsigned flags = HS_FLAG_DOTALL;
CompileContext cc(true, false, get_current_target(), Grey());
ParsedExpression parsed(0, expr.c_str(), flags, 0);
ReportManager rm(cc.grey);
unique_ptr<NGWrapper> g = buildWrapper(rm, cc, parsed);
ASSERT_TRUE(g != nullptr);
const map<u32, u32> fixed_depth_tops;
const map<u32, vector<vector<CharReach>>> triggers;
bool compress_state = false;
nfa = constructNFA(*g, &rm, fixed_depth_tops, triggers, compress_state,
type, cc);
ASSERT_TRUE(nfa != nullptr);
full_state = aligned_zmalloc_unique<char>(nfa->scratchStateSize);
stream_state = aligned_zmalloc_unique<char>(nfa->streamStateSize);
nfa_context = aligned_zmalloc_unique<void>(sizeof(NFAContext512));
// Mock up a scratch structure that contains the pieces that we need
// for NFA execution.
scratch = aligned_zmalloc_unique<hs_scratch>(sizeof(struct hs_scratch));
scratch->nfaContext = nfa_context.get();
}
virtual void initQueue() {
q.nfa = nfa.get();
q.cur = 0;
q.end = 0;
q.state = full_state.get();
q.streamState = stream_state.get();
q.offset = 0;
q.buffer = (const u8 *)ZOMBIE_SCAN_DATA.c_str();
q.length = ZOMBIE_SCAN_DATA.length();
q.history = nullptr;
q.hlength = 0;
q.scratch = scratch.get();
q.report_current = 0;
q.cb = onMatch;
q.som_cb = nullptr; // only used by Haig
q.context = &matches;
}
// NFA type (enum NFAEngineType)
int type;
// Match count
unsigned matches;
// Compiled NFA structure.
aligned_unique_ptr<NFA> nfa;
// Space for full state.
aligned_unique_ptr<char> full_state;
// Space for stream state.
aligned_unique_ptr<char> stream_state;
// Space for NFAContext structure.
aligned_unique_ptr<void> nfa_context;
// Mock scratch.
aligned_unique_ptr<hs_scratch> scratch;
// Queue structure.
struct mq q;
};
INSTANTIATE_TEST_CASE_P(LimExZombie, LimExZombieTest,
Range((int)LIMEX_NFA_32_1, (int)LIMEX_NFA_512_7));
TEST_P(LimExZombieTest, GetZombieStatus) {
ASSERT_TRUE(nfa != nullptr);
ASSERT_TRUE(nfa->flags & NFA_ZOMBIE);
initQueue();
nfaQueueInitState(nfa.get(), &q);
// Not a zombie yet
ASSERT_EQ(NFA_ZOMBIE_NO, nfaGetZombieStatus(nfa.get(), &q, 0));
u64a end = q.length;
pushQueue(&q, MQE_START, 0);
pushQueue(&q, MQE_TOP, 0);
pushQueue(&q, MQE_END, end);
nfaQueueExec(nfa.get(), &q, end);
ASSERT_EQ(6, matches); // one plus the number of '!' chars
// The .* at the end of the pattern should have turned us into a zombie...
ASSERT_EQ(NFA_ZOMBIE_ALWAYS_YES, nfaGetZombieStatus(nfa.get(), &q, end));
}

36
unit/internal/main.cpp 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.
*/
#include "gtest/gtest.h"
// Driver: run all the tests (defined in other source files in this directory)
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

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.
*/
#include "config.h"
#include <cstring>
#include "gtest/gtest.h"
#include "util/masked_move.h"
namespace {
#if defined(__AVX2__)
bool try_mask_len(const u8 *buf, u8 *target, size_t len) {
memset(target, 0, 32);
memcpy(target, buf, len);
m256 mask = masked_move256_len(buf, len);
return (0 == memcmp((u8 *)&mask, target, 32));
}
static const char *alpha = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcde"
"fghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrst"
"uvwxyz";
TEST(MaskedMove, tymm) {
const u8 *buf = (const u8 *)alpha;
u8 target[32];
for (int len = 4; len <= 32; len++) {
EXPECT_TRUE(try_mask_len(buf, target, len)) << "len: " << len;
}
}
#endif
} // namespace

1286
unit/internal/multi_bit.cpp Normal file

File diff suppressed because it is too large Load Diff

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.
*/
#ifndef NFAGRAPH_COMMON_H
#define NFAGRAPH_COMMON_H
#include "compiler/compiler.h"
#include "grey.h"
#include "nfagraph/ng_builder.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_width.h"
#include "util/target_info.h"
namespace ue2 {
// Helper function: construct a graph from an expression, flags and context.
inline
std::unique_ptr<NGWrapper> constructGraphWithCC(const std::string &expr,
CompileContext &cc,
unsigned flags) {
ReportManager rm(cc.grey);
ParsedExpression parsed(0, expr.c_str(), flags, 0);
return buildWrapper(rm, cc, parsed);
}
// Helper function: construct a graph from an expression and its flags.
inline
std::unique_ptr<NGWrapper> constructGraph(const std::string &expr,
unsigned flags) {
CompileContext cc(false, false, get_current_target(), Grey());
return constructGraphWithCC(expr, cc, flags);
}
} // namespace ue2
#endif // NFAGRAPH_COMMON_H

View File

@@ -0,0 +1,81 @@
/*
* 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.
*/
/**
* Unit tests for checking the calc comp code in nfagraph/ng_calc_components.cpp
*/
#include "config.h"
#include "gtest/gtest.h"
#include "grey.h"
#include "hs.h"
#include "compiler/compiler.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_builder.h"
#include "nfagraph/ng_calc_components.h"
#include "util/target_info.h"
using namespace std;
using namespace ue2;
// Helper: build us an NFA graph from a regex
static
unique_ptr<NGWrapper> constructGraph(const string &expr) {
CompileContext cc(false, false, get_current_target(), Grey());
ParsedExpression parsed(0, expr.c_str(), 0, 0);
ReportManager rm(cc.grey);
return buildWrapper(rm, cc, parsed);
}
TEST(NFAGraph, CalcComp1) {
auto graph = constructGraph("abc|def|ghi");
ASSERT_TRUE(graph != nullptr);
deque<unique_ptr<NGHolder>> comps = calcComponents(*graph);
ASSERT_EQ(3, comps.size());
}
TEST(NFAGraph, CalcComp2) {
auto graph = constructGraph("a|b|c|d|e|f|g|h|i");
ASSERT_TRUE(graph != nullptr);
deque<unique_ptr<NGHolder>> comps = calcComponents(*graph);
// We should be identifying this as a trivial case and not splitting it.
ASSERT_EQ(1, comps.size());
}
TEST(NFAGraph, RecalcComp1) {
deque<unique_ptr<NGHolder>> comps;
comps.push_back(constructGraph("abc|def|ghi"));
ASSERT_TRUE(comps.back() != nullptr);
recalcComponents(comps);
ASSERT_EQ(3, comps.size());
}

View File

@@ -0,0 +1,655 @@
/*
* 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.
*/
/**
* Unit tests for checking the removeGraphEquivalences code in nfagraph/ng_equivalence.cpp.
*/
#include "config.h"
#include "gtest/gtest.h"
#include "nfagraph_common.h"
#include "grey.h"
#include "hs.h"
#include "parser/Component.h"
#include "parser/Parser.h"
#include "compiler/compiler.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_builder.h"
#include "nfagraph/ng_equivalence.h"
#include "nfagraph/ng_misc_opt.h"
#include "util/target_info.h"
using namespace std;
using namespace ue2;
// left equivalence
TEST(NFAGraph, RemoveEquivalence1) {
// Build a small graph with a redundant vertex: (ab|ac)
// The graph should be merged into: a(b|c)
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("(ab|ac)", cc, 0));
ASSERT_TRUE(graph != nullptr);
NGHolder &g = *graph;
g.kind = NFA_SUFFIX;
// Run reduceGraphEquivalences
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
// Our graph should only have three non-special nodes
ASSERT_EQ((size_t)N_SPECIALS + 3, num_vertices(g));
// Start dot start state should have edges to itself and one vertex
ASSERT_EQ(2U, out_degree(g.startDs, g));
// Accept state should have two incoming edges
ASSERT_EQ(2U, in_degree(g.accept, g));
// Find a vertex that goes right after startDs
NFAVertex a = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(g.startDs, g); ai != ae; ++ai) {
a = *ai;
if (a == g.startDs) {
continue;
}
// check if it has the right char reach
const CharReach &tmpcr = g[a].char_reach;
ASSERT_EQ(1U, tmpcr.count());
ASSERT_TRUE(tmpcr.test('a'));
}
// check if we found our vertex
ASSERT_TRUE(a != nullptr);
// There should be two edges from v to nodes with reachability 'b' and 'c'
NFAVertex b = NFAGraph::null_vertex();
NFAVertex c = NFAGraph::null_vertex();
for (NFAVertex tmp : adjacent_vertices_range(a, g)) {
const CharReach &tmpcr = g[tmp].char_reach;
ASSERT_EQ(1U, tmpcr.count());
if (tmpcr.test('b')) {
b = tmp;
} else if (tmpcr.test('c')) {
c = tmp;
} else {
FAIL();
}
}
// check if we found our vertices
ASSERT_TRUE(b != nullptr);
ASSERT_TRUE(c != nullptr);
// both vertices should have an edge to accept
ASSERT_TRUE(edge(b, g.accept, g).second);
ASSERT_TRUE(edge(c, g.accept, g).second);
}
// right equivalence
TEST(NFAGraph, RemoveEquivalence2) {
// Build a small graph with a redundant vertex: (ba|ca)
// The graph should be merged into: (b|c)a
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("(ba|ca)", cc, 0));
ASSERT_TRUE(graph != nullptr);
NGHolder &g = *graph;
g.kind = NFA_SUFFIX;
// Run reduceGraphEquivalences
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
// Our graph should only have two non-special nodes
ASSERT_EQ((size_t)N_SPECIALS + 3, num_vertices(g));
// Start dot start state should have edges to itself and two more vertices
ASSERT_EQ(3U, out_degree(g.startDs, g));
// Accept start state should have edges from one vertex only
ASSERT_EQ(1U, in_degree(g.accept, g));
// Find a vertex leading to accept
NFAVertex a = NFAGraph::null_vertex();
NFAGraph::inv_adjacency_iterator ai, ae;
for (tie(ai, ae) = inv_adjacent_vertices(g.accept, g); ai != ae;
++ai) {
a = *ai;
if (a == g.accept) {
continue;
}
// check if it has the right char reach
const CharReach &tmpcr = g[a].char_reach;
ASSERT_EQ(1U, tmpcr.count());
ASSERT_TRUE(tmpcr.test('a'));
}
// check if we found our vertex
ASSERT_TRUE(a != nullptr);
// There should be two edges from v to nodes with reachability 'b' and 'c'
NFAVertex b = NFAGraph::null_vertex();
NFAVertex c = NFAGraph::null_vertex();
for (NFAVertex tmp : inv_adjacent_vertices_range(a, g)) {
const CharReach &tmpcr = g[tmp].char_reach;
ASSERT_EQ(1U, tmpcr.count());
if (tmpcr.test('b')) {
b = tmp;
} else if (tmpcr.test('c')) {
c = tmp;
} else {
FAIL();
}
}
// check if we found our vertices
ASSERT_TRUE(b != nullptr);
ASSERT_TRUE(c != nullptr);
// both new vertices should have edges from startDs
ASSERT_TRUE(edge(g.startDs, b, g).second);
ASSERT_TRUE(edge(g.startDs, c, g).second);
}
// more complex left equivalence
TEST(NFAGraph, RemoveEquivalence3) {
// Build a small graph with a redundant vertex: a(..)+X|a(..)+Y
// The graph should be merged into: a(..)+(X|Y)
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("a(..)+X|a(..)+Y", cc,
HS_FLAG_DOTALL));
ASSERT_TRUE(graph != nullptr);
NGHolder &g = *graph;
g.kind = NFA_SUFFIX;
// Run reduceGraphEquivalences
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
// Our graph should only have five non-special nodes
ASSERT_EQ((size_t)N_SPECIALS + 5, num_vertices(g));
// Start dot start state should have edges to itself and one vertex
ASSERT_EQ(2U, out_degree(g.startDs, g));
// Accept state should have two incoming edges
ASSERT_EQ(2U, in_degree(g.accept, g));
// Find a vertex 'a' that goes right after startDs
NFAVertex a = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(g.startDs, g); ai != ae; ++ai) {
a = *ai;
if (a == g.startDs) {
continue;
}
// check if it has the right char reach
const CharReach &tmpcr = g[a].char_reach;
ASSERT_EQ(1U, tmpcr.count());
ASSERT_TRUE(tmpcr.test('a'));
}
// check if we found our 'a'
ASSERT_TRUE(a != nullptr);
// There should be an edge from 'a' to '.'
ASSERT_EQ(1U, out_degree(a, g));
NFAVertex dot1 = *(adjacent_vertices(a, g).first);
// check if this is a dot
const CharReach dot1cr = g[dot1].char_reach;
ASSERT_TRUE(dot1cr.all());
// After dot1, there should be another '.'
ASSERT_EQ(1U, out_degree(dot1, g));
NFAVertex dot2 = *(adjacent_vertices(dot1, g).first);
// check its char reach as well
const CharReach dot2cr = g[dot2].char_reach;
ASSERT_TRUE(dot2cr.all());
// the second dot should have three edges - to dot1, and to X and Y
// first, check an edge to dot1
ASSERT_EQ(3U, out_degree(dot2, g));
ASSERT_TRUE(edge(dot2, dot1, g).second);
// now, let's find X and Y nodes
NFAVertex X = NFAGraph::null_vertex();
NFAVertex Y = NFAGraph::null_vertex();
for (tie(ai, ae) = adjacent_vertices(dot2, g); ai != ae; ++ai) {
NFAVertex tmp = *ai;
// we already know about dot1, so skip it
if (tmp == dot1) {
continue;
}
const CharReach tmpcr = g[tmp].char_reach;
ASSERT_EQ(1U, tmpcr.count());
if (tmpcr.test('X')) {
X = tmp;
} else if (tmpcr.test('Y')) {
Y = tmp;
} else {
FAIL();
}
}
// check if we found both vertices
ASSERT_TRUE(X != nullptr);
ASSERT_TRUE(Y != nullptr);
// finally, check if these two vertices only have edges to accept
ASSERT_EQ(1U, out_degree(X, g));
ASSERT_EQ(1U, out_degree(Y, g));
ASSERT_TRUE(edge(X, g.accept, g).second);
ASSERT_TRUE(edge(Y, g.accept, g).second);
}
// more complex right equivalence
TEST(NFAGraph, RemoveEquivalence4) {
// Build a small graph with a redundant vertex: X(..)+a|Y(..)+a
// The graph should be merged into: (X|Y)(..)+a
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("X(..)+a|Y(..)+a", cc,
HS_FLAG_DOTALL));
ASSERT_TRUE(graph != nullptr);
NGHolder &g = *graph;
g.kind = NFA_SUFFIX;
// Run reduceGraphEquivalences
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
// Our graph should only have five non-special nodes
ASSERT_EQ((size_t)N_SPECIALS + 5, num_vertices(g));
// Start dot start state should have edges to itself and two vertices
ASSERT_EQ(3U, out_degree(g.startDs, g));
// Accept state should have one incoming edge
ASSERT_EQ(1U, in_degree(g.accept, g));
// Find X and Y nodes that are connected to startDs
NFAVertex X = NFAGraph::null_vertex();
NFAVertex Y = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(g.startDs, g); ai != ae; ++ai) {
NFAVertex tmp = *ai;
// skip startDs
if (tmp == g.startDs) {
continue;
}
// get char reach
const CharReach tmpcr = g[tmp].char_reach;
ASSERT_EQ(1U, tmpcr.count());
if (tmpcr.test('X')) {
X = tmp;
} else if (tmpcr.test('Y')) {
Y = tmp;
} else {
FAIL();
}
}
// check if we found both vertices
ASSERT_TRUE(X != nullptr);
ASSERT_TRUE(Y != nullptr);
// now, find first dot from X
ASSERT_EQ(1U, out_degree(X, g));
NFAVertex dot1 = *(adjacent_vertices(X, g).first);
// make sure Y also has an edge to that dot
ASSERT_EQ(1U, out_degree(Y, g));
ASSERT_TRUE(edge(Y, dot1, g).second);
// now, verify that it's actually a dot
const CharReach dot1cr = g[dot1].char_reach;
ASSERT_TRUE(dot1cr.all());
// the first dot has one edge to another dot
ASSERT_EQ(1U, out_degree(dot1, g));
NFAVertex dot2 = *(adjacent_vertices(dot1, g).first);
// verify it's a dot
const CharReach dot2cr = g[dot2].char_reach;
ASSERT_TRUE(dot2cr.all());
// second dot should have two edges - to dot1 and to 'a'
ASSERT_EQ(2U, out_degree(dot2, g));
ASSERT_TRUE(edge(dot2, dot1, g).second);
// now find 'a'
NFAVertex a = NFAGraph::null_vertex();
for (tie(ai, ae) = adjacent_vertices(dot2, g); ai != ae; ++ai) {
NFAVertex tmp = *ai;
// skip dot1
if (tmp == dot1) {
continue;
}
// get char reach
const CharReach tmpcr = g[tmp].char_reach;
ASSERT_EQ(1U, tmpcr.count());
if (tmpcr.test('a')) {
a = tmp;
} else {
FAIL();
}
}
// make sure we found our 'a'
ASSERT_TRUE(a != nullptr);
// now, check if 'a' has an edge to accept
ASSERT_EQ(1U, out_degree(a, g));
ASSERT_TRUE(edge(a, g.accept, g).second);
}
// catching UE-2693
TEST(NFAGraph, RemoveEquivalence5) {
// Build a small graph with a redundant vertex: [^\x00][^x00]*[\\x00]
// The graph should be merged into: [^\x00]*[\x00]
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("[^\\x00][^\\x00]*[\\x00]",
cc, 0));
ASSERT_TRUE(graph != nullptr);
NGHolder &g = *graph;
g.kind = NFA_PREFIX;
// Run reduceGraphEquivalences
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
// Our graph should only have two non-special nodes
ASSERT_EQ((size_t)N_SPECIALS + 2, num_vertices(g));
// Start dot start state should have edges to itself and one vertex
ASSERT_EQ(2U, out_degree(g.startDs, g));
// Accept state should have one incoming edge
ASSERT_EQ(1U, in_degree(g.accept, g));
// find first vertex and ensure it has a self loop
NFAVertex v = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(g.startDs, g); ai != ae; ++ai) {
v = *ai;
if (v == g.startDs) {
continue;
}
// check if it has the right char reach
const CharReach &tmpcr = g[v].char_reach;
ASSERT_EQ(255U, tmpcr.count());
ASSERT_TRUE(!tmpcr.test(0));
ASSERT_TRUE(edge(v, v, g).second);
}
// check if we found our vertex
ASSERT_TRUE(v != nullptr);
// now, find the vertex leading to accept
NFAVertex v2 = NFAGraph::null_vertex();
for (tie(ai, ae) = adjacent_vertices(v, g); ai != ae; ++ai) {
NFAVertex tmp = *ai;
// skip self-loop
if (tmp == v) {
continue;
}
v2 = *ai;
// get char reach
const CharReach tmpcr = g[tmp].char_reach;
ASSERT_EQ(1U, tmpcr.count());
ASSERT_TRUE(tmpcr.test(0));
ASSERT_TRUE(edge(tmp, g.accept, g).second);
}
// check if we found our vertex
ASSERT_TRUE(v2 != nullptr);
}
// catching UE-2692
TEST(NFAGraph, RemoveEquivalence6) {
// Build a small graph with two redundant vertices: ^(.*|.*)a
// The graph should be merged into: a
unique_ptr<NGWrapper> graph(constructGraph("^(.*|.*)a", HS_FLAG_DOTALL));
ASSERT_TRUE(graph != nullptr);
NGHolder &g = *graph;
// Run mergeCyclicDotStars
ASSERT_TRUE(mergeCyclicDotStars(g));
// Our graph should only have one non-special node
ASSERT_EQ((size_t)N_SPECIALS + 1, num_vertices(g));
// Start dot start state should have edges to itself and one vertex
ASSERT_EQ(2U, out_degree(g.startDs, g));
// Accept state should have one incoming edge
ASSERT_EQ(1U, in_degree(g.accept, g));
// find that vertex and ensure it has no self loops and an edge to accept
NFAVertex v = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(g.startDs, g); ai != ae; ++ai) {
v = *ai;
if (v == g.startDs) {
continue;
}
// check if it has the right char reach
const CharReach &tmpcr = g[v].char_reach;
ASSERT_EQ(1U, tmpcr.count());
ASSERT_TRUE(tmpcr.test('a'));
ASSERT_TRUE(!edge(v, v, g).second);
ASSERT_TRUE(edge(v, g.accept, g).second);
}
// check if we found our vertex
ASSERT_TRUE(v != nullptr);
}
// catching UE-2692
TEST(NFAGraph, RemoveEquivalence7) {
// Build a small graph with no redundant vertices: ^.+a
// Make sure we don't merge anything
unique_ptr<NGWrapper> graph(constructGraph("^.+a", HS_FLAG_DOTALL));
ASSERT_TRUE(graph != nullptr);
NGHolder &g = *graph;
// Run mergeCyclicDotStars
ASSERT_FALSE(mergeCyclicDotStars(g));
// Our graph should have two non-special nodes
ASSERT_EQ((size_t)N_SPECIALS + 2, num_vertices(g));
// Start dot start state should have an edge to itself only
ASSERT_EQ(1U, out_degree(g.startDs, g));
// Start should have edges to startDs and one other vertex
ASSERT_EQ(2U, out_degree(g.start, g));
// Accept state should have one incoming edge
ASSERT_EQ(1U, in_degree(g.accept, g));
// find that vertex and ensure it's a dot self loop and has one outgoing edge
NFAVertex v = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(g.start, g); ai != ae; ++ai) {
if (*ai == g.startDs) {
continue;
}
v = *ai;
// check if it has the right char reach
const CharReach &tmpcr = g[v].char_reach;
ASSERT_TRUE(tmpcr.all());
ASSERT_TRUE(edge(v, v, g).second);
ASSERT_EQ(1U, proper_out_degree(v, g));
}
// check if we found our vertex
ASSERT_TRUE(v != nullptr);
// find the next vertex and ensure it has an edge to accept
NFAVertex v2 = NFAGraph::null_vertex();
for (tie(ai, ae) = adjacent_vertices(v, g); ai != ae; ++ai) {
// skip self loop
if (*ai == v) {
continue;
}
v2 = *ai;
// check if it has the right char reach
const CharReach &tmpcr = g[v2].char_reach;
ASSERT_EQ(1U, tmpcr.count());
ASSERT_TRUE(tmpcr.test('a'));
ASSERT_TRUE(!edge(v2, v2, g).second);
ASSERT_EQ(1U, proper_out_degree(v2, g));
ASSERT_TRUE(edge(v2, g.accept, g).second);
}
// check if we found our vertex
ASSERT_TRUE(v2 != nullptr);
}
TEST(NFAGraph, RemoveEquivalence_Reports1) {
CompileContext cc(false, false, get_current_target(), Grey());
NGHolder g(NFA_SUFFIX);
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
add_edge(g.start, a, g);
add_edge(a, b, g);
add_edge(a, c, g);
add_edge(b, g.accept, g);
add_edge(c, g.accept, g);
g[b].reports.insert(0);
g[c].reports.insert(1);
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
ASSERT_EQ(num_vertices(g), N_SPECIALS + 2); /* b, c should be merged */
ASSERT_EQ(in_degree(g.accept, g), 1);
}
TEST(NFAGraph, RemoveEquivalence_Reports2) {
CompileContext cc(false, false, get_current_target(), Grey());
NGHolder g(NFA_SUFFIX);
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
add_edge(g.start, a, g);
add_edge(a, b, g);
add_edge(a, c, g);
add_edge(b, g.accept, g);
add_edge(c, g.acceptEod, g);
g[b].reports.insert(0);
g[c].reports.insert(1);
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
ASSERT_EQ(num_vertices(g), N_SPECIALS + 3); /* b, c should not be merged */
ASSERT_EQ(in_degree(g.accept, g), 1);
ASSERT_EQ(in_degree(g.acceptEod, g), 2);
}
TEST(NFAGraph, RemoveEquivalence_Reports3) {
CompileContext cc(false, false, get_current_target(), Grey());
NGHolder g(NFA_SUFFIX);
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
add_edge(g.start, a, g);
add_edge(a, b, g);
add_edge(a, c, g);
add_edge(b, g.accept, g);
add_edge(c, g.acceptEod, g);
g[b].reports.insert(0);
g[c].reports.insert(0);
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
ASSERT_EQ(num_vertices(g), N_SPECIALS + 2); /* b, c should be merged */
ASSERT_EQ(in_degree(g.accept, g), 1);
ASSERT_EQ(in_degree(g.acceptEod, g), 2);
}
TEST(NFAGraph, RemoveEquivalence_Reports4) {
CompileContext cc(false, false, get_current_target(), Grey());
NGHolder g(NFA_SUFFIX);
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
NFAVertex d = add_vertex(g);
add_edge(g.start, a, g);
add_edge(a, b, g);
add_edge(a, c, g);
add_edge(c, d, g);
add_edge(d, d, g);
add_edge(b, g.accept, g);
g[b].reports.insert(0);
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
ASSERT_EQ(num_vertices(g), N_SPECIALS + 3); /* b, c should be merged */
ASSERT_EQ(in_degree(g.accept, g), 1);
ASSERT_EQ(in_degree(g.acceptEod, g), 1);
}
TEST(NFAGraph, RemoveEquivalence_Reports5) {
CompileContext cc(false, false, get_current_target(), Grey());
NGHolder g(NFA_SUFFIX);
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
NFAVertex d = add_vertex(g);
add_edge(g.start, a, g);
add_edge(a, b, g);
add_edge(a, c, g);
add_edge(c, d, g);
add_edge(d, d, g);
add_edge(b, g.acceptEod, g);
g[b].reports.insert(0);
ASSERT_TRUE(reduceGraphEquivalences(g, cc));
ASSERT_EQ(num_vertices(g), N_SPECIALS + 3); /* b, c should be merged */
ASSERT_EQ(in_degree(g.accept, g), 0);
ASSERT_EQ(in_degree(g.acceptEod, g), 2);
}

View File

@@ -0,0 +1,272 @@
/*
* 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 "gtest/gtest.h"
#include "compiler/compiler.h"
#include "grey.h"
#include "nfagraph/ng_builder.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_asserts.h"
#include "util/target_info.h"
#include "hs_compile.h"
#include "ng_find_matches.h"
using namespace std;
using namespace testing;
using namespace ue2;
#define NUM_MATCHES 4U
#define P(x,y) pair<size_t, size_t>(x, y)
#define NO_MATCH P(~0U, ~0U)
struct MatchesTestParams {
string pattern;
string input;
// max 4 matches per pattern, P(-1,-1) is "no match"
pair<size_t, size_t> matches[NUM_MATCHES];
unsigned flags;
bool notEod;
bool som;
};
// teach google-test how to print a param
void PrintTo(const MatchesTestParams &p, ::std::ostream *os) {
pair<size_t, size_t> *matches = const_cast<pair<size_t, size_t> *>(p.matches);
*os << "( \"" << p.pattern << "\", "
<< "\"" << p.input << "\", "
<< "{";
for (int i = 0; i < 4; i++) {
if (matches[i] == NO_MATCH) {
*os << "NO_MATCH,";
break;
} else {
*os << "P(" << matches[i].first << ',' << matches[i].second << "),";
}
}
*os << "}, ";
*os << "flags(" << p.flags << "), "
<< p.notEod << ", "
<< p.som
<< ")\n";
}
class MatchesTest: public TestWithParam<MatchesTestParams> {
};
static const MatchesTestParams matchesTests[] = {
// EOD and anchored patterns
// these should produce no matches
{ "^foobar", "foolish", {NO_MATCH}, 0, false, true},
{ "^foobar$", "ze foobar", {NO_MATCH}, 0, false, true},
{ "^foobar$", "foobar ", {NO_MATCH}, 0, false, true},
{ "^abc\\b", "abcde", {NO_MATCH}, 0, false, true},
{ "^a\\b", "aa", {NO_MATCH}, 0, false, true},
{ "^foobar\\b", "foobarz", {NO_MATCH}, 0, false, true},
{ "^foobar", "fooq", {NO_MATCH}, 0, false, true},
{ "^foobar", "foo", {NO_MATCH}, 0, false, true},
{ "^foobar", "fooba", {NO_MATCH}, 0, false, true},
{ "^foo *bar", "foolishness bar none ", {NO_MATCH}, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abc p", {NO_MATCH}, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abc dez", {NO_MATCH}, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abc ghi", {NO_MATCH}, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abc hij", {NO_MATCH}, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abc klm", {NO_MATCH}, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abcklmn", {NO_MATCH}, 0, false, true},
{ "^.*foobar", "foobaz", {NO_MATCH}, 0, false, true},
{ "^.*foobar", "foobaz\n", {NO_MATCH}, 0, false, true},
{ "^(foo)|(bar)", "fo baz", {NO_MATCH}, 0, false, true},
{ "^((foo)|(bar))", "fo baz", {NO_MATCH}, 0, false, true},
{ "aaaaaaaa$", "AAaaAAaa", {NO_MATCH}, 0, false, true},
{ "^foo\\z", "foo\n", {NO_MATCH}, 0, false, true},
{ "^(foo){2,}", "foo", {NO_MATCH}, 0, false, true},
// these should match
{ "^abc\\B", "abcde", { P(0,3), NO_MATCH }, 0, false, true},
{ "^abc\\b", "abc de", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^foobar", "foobar", { P(0, 6), NO_MATCH }, 0, false, true},
{ "^foobar$", "foobar", { P(0, 6), NO_MATCH }, 0, false, true},
{ "^foobar", "foobarq", { P(0, 6), NO_MATCH }, 0, false, true},
{ "^foobar\\B", "foobarz", { P(0, 6), NO_MATCH }, 0, false, true},
{ "^foo.*bar", "foobar none ", { P(0, 6), NO_MATCH }, 0, false, true},
{ "^foo.*bar", "foo bar none ", { P(0, 7), NO_MATCH }, 0, false, true},
{ "^foo.*bar", "foo bar none ", { P(0, 10), NO_MATCH }, 0, false, true},
{ "^foo.*bar", "foolishness bar none ", { P(0, 15), NO_MATCH }, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "klmny", { P(0, 5), NO_MATCH }, 0, false, true},
{ "^(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abc dexyklmnxy", { P(0, 17), NO_MATCH }, 0, false, true},
{ "^.*foobar", "abcfoobar", { P(0, 9), NO_MATCH }, 0, false, true},
{ "^((foo)|(bar))", "foobar", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^((foo)|(bar))", "foo bar", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^(foo)|(bar)", "foobaz", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^(foo)|(bar)", "foo baz", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^(f[o0]+o)|(bar)", "fo0o baz", { P(0, 4), NO_MATCH }, 0, false, true},
{ "aaaaaaaa$", "AAaaAAaa", { P(0, 8), NO_MATCH }, HS_FLAG_CASELESS, false, true},
{ "^foo\\z", "foo", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^foo\\Z", "foo", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^foo\\Z", "foo\n", { P(0, 3), NO_MATCH }, 0, false, true},
{ "^(foo){2,}", "foofoofoo", { P(0, 6), P(0, 9), NO_MATCH}, 0, false, true},
// try multiple matches per pattern
{ "^(foo)|(bar)", "foo bar", { P(0, 3), P(4, 7), NO_MATCH }, 0, false, true},
{ "^(foo)|(bar)", "foobar", { P(0, 3), P(3, 6), NO_MATCH }, 0, false, true},
{ "^(foo)+|(bar)", "foo foobar", { P(0, 3), P(7, 10), NO_MATCH }, 0, false, true},
{ "^(foo)+|(bar)", "foofoo bar", { P(0, 3), P(0, 6), P(7, 10), NO_MATCH }, 0, false, true},
{ "^(foo)|(bar)", "foobarbaz", { P(0, 3), P(3, 6), NO_MATCH }, 0, false, true},
{ "^(f[o0]+o)", "foo0obar", { P(0, 3), P(0, 5), NO_MATCH }, 0, false, true},
{ "^(f[o0]+)", "foo0obar", { P(0, 2), P(0, 3), P(0, 4), P(0, 5) }, 0, false, true},
// unanchored patterns
{ "\\b4\\B", "444", { P(0, 1), NO_MATCH }, 0, false, true},
{ "\\b\\w+\\b", "444 555", { P(0, 3), P(4, 7), NO_MATCH }, 0, false, true},
{ "foobar", "veryfoolish", {NO_MATCH}, 0, false, true},
{ "foo.*bar", "extreme foolishness bar none ", { P(8, 23), NO_MATCH }, 0, false, true},
{ "(abc\\s+dex?y)?(ghij|klmn).*?x?y", "abc deyghijfy", { P(0, 13), NO_MATCH }, 0, false, true},
{ "(abc\\s+dex?y)?(ghij|klmn).*?x?y", "wegf5tgghij34xy", { P(7, 15), NO_MATCH }, 0, false, true},
{ ".*foobar", "verylongfoobaz", {NO_MATCH}, 0, false, true},
{ ".*foobar", "foobaz\n", {NO_MATCH}, 0, false, true},
{ "(foo)|(bar)", "verylongfo baz", {NO_MATCH}, 0, false, true},
{ "(foo)?bar", "foobar", { P(0, 6), NO_MATCH }, 0, false, true},
{ "foo?bar", "foobar", { P(0, 6), NO_MATCH }, 0, false, true},
{ "(abc)|(bcd)", "abcd", { P(0, 3), P(1, 4), NO_MATCH }, 0, false, true},
{ "(abcd)|(bc)", "abcd", { P(0, 4), P(1, 3), NO_MATCH }, 0, false, true},
{ "(ab|cd)ef", "abcdef cdabef", { P(2, 6), P(9, 13), NO_MATCH }, 0, false, true},
{ "(foo)|(bar)", "verylongfoobbarbaz", { P(8, 11), P(12, 15), NO_MATCH }, 0, false, true},
{ "(a[aaaa]aa?((\\B)|[aa])){1,9}", "aaaaa", { P(0, 3), P(0, 4), P(0, 5), NO_MATCH }, 0, false, true},
{ "bar\\Z", "foobar\n", { P(3, 6), NO_MATCH }, 0, false, true},
// multi-line patterns
{ "^foo$", "foo\nbar", { P(0, 3), NO_MATCH }, HS_FLAG_MULTILINE, false, true},
{ "^bar$", "foo\nbar", { P(4, 7), NO_MATCH }, HS_FLAG_MULTILINE, false, true},
{ "^foo$", "big foo\nbar", {NO_MATCH}, HS_FLAG_MULTILINE, false, true},
{ "^foo$", "foo\nfoo", { P(0, 3), P(4, 7), NO_MATCH }, HS_FLAG_MULTILINE, false, true},
{ "\\bfoo$", "big foo\nbar", { P(4, 7), NO_MATCH }, HS_FLAG_MULTILINE, false, true},
{ "\\Bfoo$", "bigfoo\nbar", { P(3, 6), NO_MATCH }, HS_FLAG_MULTILINE, false, true},
{ "^foo\\z", "big\nfoo", { P(4, 7), NO_MATCH }, HS_FLAG_MULTILINE, false, true},
{ "^foo\\Z", "big\nfoo\n", { P(4, 7), NO_MATCH }, HS_FLAG_MULTILINE, false, true},
// utf8 patterns
{ "ab+", "\x61\x62", { P(0, 2), NO_MATCH }, HS_FLAG_UTF8, false, true},
{ "ab.+d", "\x61\x62\xf0\xa4\xad\xa2\x64", { P(0, 7), NO_MATCH }, HS_FLAG_UTF8, false, true},
// noteod patterns
{ "^foobar$", "foobar", { NO_MATCH }, 0, true, true},
{ "aaaaaaaa$", "AAaaAAaa", { NO_MATCH }, HS_FLAG_CASELESS, true, true},
{ "^foo\\z", "foo", { NO_MATCH }, 0, true, true},
{ "^foo\\Z", "foo", { NO_MATCH }, 0, true, true},
{ "^foo\\Z", "foo\n", { NO_MATCH }, 0, true, true},
// vacuous patterns (with multiline, utf8 and caseless flags)
// vacuous patterns have SOM turned off, so all SOM are zero
{ "b*", "abc", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, 0, false, false},
{ "b*", "", { P(0, 0), NO_MATCH }, 0, false, false},
{ "(aa|b*)", "a", { P(0, 0), P(0, 1), NO_MATCH }, 0, false, false},
{ "b*", "bBb", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, HS_FLAG_CASELESS, false, false},
{ "b*", "abc", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, HS_FLAG_MULTILINE, false, false},
{ "b*", "", { P(0, 0), NO_MATCH }, HS_FLAG_MULTILINE, false, false},
{ "(aa|b*)", "a", { P(0, 0), P(0, 1), NO_MATCH }, HS_FLAG_MULTILINE, false, false},
{ "b*", "bBb", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, HS_FLAG_MULTILINE | HS_FLAG_CASELESS, false, false},
{ "b*", "abc", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, HS_FLAG_UTF8, false, false},
{ "b*", "", { P(0, 0), NO_MATCH }, HS_FLAG_UTF8, false, false},
{ "(aa|b*)", "a", { P(0, 0), P(0, 1), NO_MATCH }, HS_FLAG_UTF8, false, false},
{ "b*", "bBb", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, HS_FLAG_UTF8 | HS_FLAG_CASELESS, false, false},
{ "b*", "bBb", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, HS_FLAG_UTF8 | HS_FLAG_CASELESS, false, false},
{ ".*", "\x61\xf0\xa4\xad\xa2\x64", { P(0, 0), P(0, 1), P(0, 5), P(0, 6) }, HS_FLAG_UTF8, false, false},
{ ".*", "\xf0\xa4\xad\xa2\xf0\xa4\xad\xa3\x64", { P(0, 0), P(0, 4), P(0, 8), P(0, 9) }, HS_FLAG_UTF8, false, false},
// special patterns for detecting various bugs
{ "(\\B.|a)*a", "\xf0\xa4\xad\xa2\x61", { P(0, 5), NO_MATCH }, HS_FLAG_UTF8, false, true},
{ ".*", "\xf0\xa4\xad", { P(0, 0), P(0, 1), P(0, 2), P(0, 3) }, 0, false, true},
{ "\\Bfoo", "foo", {NO_MATCH}, 0, false, true},
{ "fo\\B", "fo_", { P(0, 2), NO_MATCH}, 0, false, true},
{ "^.*", "\xee\x80\x80\n\xee\x80\x80", { P(0, 0), P(0, 3), P(0, 4), P(0, 7)}, HS_FLAG_UTF8 | HS_FLAG_MULTILINE, false, false},
// ignore highlander patterns as they can't be easily checked
};
// by default, all matches initialize to zeroes. this makes it impossible to
// test vacuous patterns, among other things, unless we specify every single
// match, which is tedious. instead, we set "no match" to NO_MATCH. if the matches
// start with NO_MATCH, everything else is set to NO_MATCH. if matches start
// with something else, look for the next NO_MATCH and replace everything after
// it with NO_MATCH's.
static
void fixMatches(const pair<size_t, size_t> in_matches[],
pair<size_t, size_t> out_matches[], unsigned size) {
bool end_matches = false;
for (unsigned i = 0; i < size; i++) {
if (in_matches[i] == NO_MATCH) {
end_matches = true;
}
if (end_matches) {
out_matches[i] = NO_MATCH;
}
else {
out_matches[i] = in_matches[i];
}
}
}
TEST_P(MatchesTest, Check) {
const MatchesTestParams &t = GetParam();
CompileContext cc(false, false, get_current_target(), Grey());
ReportManager rm(cc.grey);
ParsedExpression parsed(0, t.pattern.c_str(), t.flags, 0);
unique_ptr<NGWrapper> g = buildWrapper(rm, cc, parsed);
set<pair<size_t, size_t> > results, tmp, matches;
bool utf8 = (t.flags & HS_FLAG_UTF8) > 0;
findMatches(*g, rm, t.input, matches, t.notEod, t.som, utf8);
// fix matches and make a set out of them
pair<size_t, size_t> tmp_matches[NUM_MATCHES];
fixMatches(t.matches, tmp_matches, NUM_MATCHES);
tmp = set<pair<size_t, size_t> >(tmp_matches, tmp_matches + NUM_MATCHES);
tmp.erase(NO_MATCH);
// create a superset of pattern results and matches to pick up unexpected
// matches as well as missed matches.
results.insert(tmp.begin(), tmp.end());
results.insert(matches.begin(), matches.end());
// check if we have the same number of matches as in expected results
ASSERT_EQ(results.size(), tmp.size())<< "Pattern '" << t.pattern
<< "' against input '" << t.input << "': wrong results count";
// we already know that size of two sets is the same, so now check matches.
for (set<pair<size_t, size_t> >::const_iterator it = results.begin();
it != results.end(); ++it) {
ASSERT_EQ(1, matches.count(*it))<< "Pattern '" << t.pattern
<< "' against input '" << t.input << "'";
}
}
INSTANTIATE_TEST_CASE_P(ng_find_matches, MatchesTest, ValuesIn(matchesTests));

View File

@@ -0,0 +1,90 @@
/*
* 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 <set>
#include <vector>
#include "gtest/gtest.h"
#include "nfagraph/ng_literal_analysis.h"
using namespace std;
using namespace boost;
using namespace ue2;
struct LiteralSetCompressParams {
set<ue2_literal> input;
set<ue2_literal> output;
};
void PrintTo(const LiteralSetCompressParams &p, ::std::ostream *os) {
*os << "input: ";
set<ue2_literal>::const_iterator it, ite;
for (it = p.input.begin(), ite = p.input.end(); it != ite; ++it) {
*os << it->get_string() << " ";
}
*os << "output: ";
for (it = p.output.begin(), ite = p.output.end(); it != ite; ++it) {
*os << it->get_string() << " ";
}
}
static
vector<LiteralSetCompressParams> paramFactory() {
vector<LiteralSetCompressParams> p;
p.push_back(LiteralSetCompressParams());
p.back().input.insert(ue2_literal("foobar", false));
p.back().input.insert(ue2_literal("bar", false));
p.back().output.insert(ue2_literal("bar", false));
p.push_back(LiteralSetCompressParams());
p.back().input.insert(ue2_literal("foobar", false));
p.back().input.insert(ue2_literal("fooobar", false));
p.back().input.insert(ue2_literal("foooobar", false));
p.back().input.insert(ue2_literal("fooooobar", false));
p.back().output.insert(ue2_literal("oobar", false));
return p;
}
class LiteralSetCompressTest : public testing::TestWithParam<LiteralSetCompressParams> {
protected:
LiteralSetCompressTest() {}
};
TEST_P(LiteralSetCompressTest, Run) {
const LiteralSetCompressParams &p = GetParam();
set<ue2_literal> lits(p.input.begin(), p.input.end());
compressAndScore(lits);
ASSERT_EQ(p.output, lits);
}
INSTANTIATE_TEST_CASE_P(NFAGraph, LiteralSetCompressTest, testing::ValuesIn(paramFactory()));

View File

@@ -0,0 +1,214 @@
/*
* 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.
*/
/**
* Unit tests for checking the removeRedundancy code in nfagraph/ng_redundancy.cpp.
*/
#include "config.h"
#include "gtest/gtest.h"
#include "nfagraph_common.h"
#include "grey.h"
#include "hs.h"
#include "parser/Component.h"
#include "parser/Parser.h"
#include "compiler/compiler.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_builder.h"
#include "nfagraph/ng_redundancy.h"
#include "nfagraph/ng_edge_redundancy.h"
#include "util/target_info.h"
using namespace std;
using namespace ue2;
TEST(NFAGraph, RemoveRedundancy1) {
// Build a small graph with a redundant vertex: (a|b)c
// The character reachability should be merged into: [ab]c
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("(a|b)c", cc, 0));
ASSERT_TRUE(graph.get() != nullptr);
// Run removeRedundancy
removeRedundancy(*graph, SOM_NONE);
NFAGraph &g = graph->g;
// Our graph should only have two non-special nodes
ASSERT_EQ((size_t)N_SPECIALS + 2, num_vertices(*graph));
// Dot-star start state should be connected to itself and a single other vertex
ASSERT_EQ(2U, out_degree(graph->startDs, g));
// That single vertex should have reachability [ab]
NFAVertex v = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(graph->startDs, g); ai != ae; ++ai) {
v = *ai;
if (v != graph->startDs) break;
}
const CharReach &cr = g[v].char_reach;
ASSERT_EQ(2U, cr.count());
ASSERT_TRUE(cr.test('a'));
ASSERT_TRUE(cr.test('b'));
// There should be a single edge from v to a node with char reachability 'c'
ASSERT_EQ(1U, out_degree(v, g));
NFAVertex v2 = *(adjacent_vertices(v, g).first);
const CharReach &cr2 = g[v2].char_reach;
ASSERT_EQ(1U, cr2.count());
ASSERT_TRUE(cr2.test('c'));
// 'c' should have an edge to accept
ASSERT_TRUE(edge(v2, graph->accept, g).second);
}
TEST(NFAGraph, RemoveRedundancy2) {
// Build a small graph with a redundant vertex: a.*b?c
// The dot-star should swallow the 'b?', leaving a.*c
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("a.*b?c", cc,
HS_FLAG_DOTALL));
ASSERT_TRUE(graph.get() != nullptr);
// Run removeRedundancy
removeRedundancy(*graph, SOM_NONE);
NFAGraph &g = graph->g;
// Our graph should now have only 3 non-special vertices
ASSERT_EQ((size_t)N_SPECIALS + 3, num_vertices(*graph));
// Dot-star start state should be connected to itself and a single other vertex
ASSERT_EQ(2U, out_degree(graph->startDs, g));
// That single vertex should have reachability [a]
NFAVertex v = NFAGraph::null_vertex();
NFAGraph::adjacency_iterator ai, ae;
for (tie(ai, ae) = adjacent_vertices(graph->startDs, g); ai != ae; ++ai) {
v = *ai;
if (v != graph->startDs) break;
}
const CharReach &cr = g[v].char_reach;
ASSERT_EQ(1U, cr.count());
ASSERT_TRUE(cr.test('a'));
// 'a' should have two out edges: one to a dot with a cycle (.*) and one to 'c'
ASSERT_EQ(2U, out_degree(v, g));
NFAVertex dotstar = NFAGraph::null_vertex(), vc = NFAGraph::null_vertex();
for (tie(ai, ae) = adjacent_vertices(v, g); ai != ae; ++ai) {
const CharReach &cr2 = g[*ai].char_reach;
if (cr2.count() == 1 && cr2.test('c')) {
vc = *ai;
} else if (cr2.all()) {
dotstar = *ai;
} else {
FAIL();
}
}
ASSERT_TRUE(vc != NFAGraph::null_vertex());
ASSERT_TRUE(dotstar != NFAGraph::null_vertex());
// Dot-star node should have a self-loop and an edge to vertex 'c'
ASSERT_EQ(2U, out_degree(dotstar, g));
ASSERT_EQ(2U, in_degree(vc, g));
ASSERT_TRUE(edge(dotstar, dotstar, g).second);
ASSERT_TRUE(edge(dotstar, vc, g).second);
// 'c' should have an edge to accept
ASSERT_TRUE(edge(vc, graph->accept, g).second);
}
TEST(NFAGraph, RemoveRedundancy3) {
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("foobar.*(a|b)?teakettle",
cc, 0));
ASSERT_TRUE(graph.get() != nullptr);
unsigned countBefore = num_vertices(graph->g);
removeRedundancy(*graph, SOM_NONE);
// The '(a|b)?' construction (two states) should have disappeared, leaving
// this expr as 'foobar.*teakettle'
ASSERT_EQ(countBefore - 2, num_vertices(graph->g));
}
TEST(NFAGraph, RemoveRedundancy4) {
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("foo([A-Z]|a|b|q)", cc, 0));
ASSERT_TRUE(graph.get() != nullptr);
unsigned countBefore = num_vertices(graph->g);
removeRedundancy(*graph, SOM_NONE);
// We should end up with the alternation collapsing into one state
ASSERT_EQ(countBefore - 3, num_vertices(graph->g));
}
TEST(NFAGraph, RemoveRedundancy5) {
CompileContext cc(false, false, get_current_target(), Grey());
unique_ptr<NGWrapper> graph(constructGraphWithCC("[0-9]?badgerbrush",
cc, 0));
ASSERT_TRUE(graph.get() != nullptr);
unsigned countBefore = num_vertices(graph->g);
removeRedundancy(*graph, SOM_NONE);
// Since we don't return a start offset, the first state ('[0-9]?') is
// redundant.
ASSERT_EQ(countBefore - 1, num_vertices(graph->g));
}
TEST(NFAGraph, RemoveEdgeRedundancy1) {
CompileContext cc(false, false, get_current_target(), Grey());
auto graph = constructGraphWithCC("A+hatstand", cc, HS_FLAG_DOTALL);
ASSERT_TRUE(graph.get() != nullptr);
unsigned countBefore = num_edges(graph->g);
removeEdgeRedundancy(*graph, SOM_NONE, cc);
// One edge (the self-loop on the leading A+) should have been removed.
ASSERT_EQ(countBefore - 1, num_edges(graph->g));
}
TEST(NFAGraph, RemoveEdgeRedundancy2) {
CompileContext cc(false, false, get_current_target(), Grey());
auto graph = constructGraphWithCC("foo.*A*bar", cc, HS_FLAG_DOTALL);
ASSERT_TRUE(graph.get() != nullptr);
size_t numEdgesBefore = num_edges(graph->g);
size_t numVertsBefore = num_vertices(graph->g);
removeEdgeRedundancy(*graph, SOM_NONE, cc);
// The .* should swallow up the A* and its self-loop.
ASSERT_EQ(numEdgesBefore - 4, num_edges(graph->g));
ASSERT_EQ(numVertsBefore - 1, num_vertices(graph->g));
}

View File

@@ -0,0 +1,105 @@
/*
* 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 "nfagraph_common.h"
#include "gtest/gtest.h"
#include "nfagraph/ng_repeat.h"
#include "util/depth.h"
#include "hs_compile.h"
using namespace std;
using namespace testing;
using namespace ue2;
static UNUSED
std::ostream& operator<<(std::ostream &os, const depth &d) {
// We reimplement depth::str, as it's only available if dump support is
// compiled in.
if (d.is_unreachable()) {
os << "unr";
return os;
} else if (d.is_infinite()) {
os << "inf";
return os;
}
u32 val = d; // finite val
os << val;
return os;
}
struct PureRepeatTest {
string pattern;
depth minBound;
depth maxBound;
};
class NFAPureRepeatTest : public TestWithParam<PureRepeatTest> { };
static const PureRepeatTest pureRepeatTests[] = {
{ "^.*", 0, depth::infinity() },
{ "^.+", 1, depth::infinity() },
{ "^.", 1, 1 },
{ "^..", 2, 2 },
{ "^.?.", 1, 2 },
{ "^.{1,2}", 1, 2 },
{ "^.{1,3}", 1, 3 },
{ "^.{1,10}", 1, 10 },
{ "^.{1,200}", 1, 200 },
{ "^.{200}", 200, 200 },
{ "^.{0,}", 0, depth::infinity() },
{ "^.{1,}", 1, depth::infinity() },
{ "^.{2,}", 2, depth::infinity() },
{ "^.{10,}", 10, depth::infinity() },
{ "^.{200,}", 200, depth::infinity() },
{ "^.{5000,}", 5000, depth::infinity() },
{ "^.{0,1}", 0, 1 },
{ "^.{0,2}", 0, 2 },
{ "^.{0,100}", 0, 100 },
{ "^.{0,5000}", 0, 5000 },
{ "^x{10}x{20,30}", 30, 40 },
{ "^..?..?..?..?..?", 5, 10 }
};
INSTANTIATE_TEST_CASE_P(PureRepeat, NFAPureRepeatTest, ValuesIn(pureRepeatTests));
TEST_P(NFAPureRepeatTest, Check) {
const PureRepeatTest &t = GetParam();
SCOPED_TRACE(testing::Message() << "Pattern: " << t.pattern);
unique_ptr<NGWrapper> w(constructGraph(t.pattern, HS_FLAG_ALLOWEMPTY));
PureRepeat repeat;
bool result = isPureRepeat(*w, repeat);
ASSERT_EQ(true, result);
ASSERT_EQ(t.minBound, repeat.bounds.min);
ASSERT_EQ(t.maxBound, repeat.bounds.max);
}

View File

@@ -0,0 +1,428 @@
/*
* 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 "gtest/gtest.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_split.h"
#include "nfagraph/ng_util.h"
using namespace std;
using namespace boost;
using namespace ue2;
TEST(NFAGraph, split1) {
NGHolder src, lhs, rhs;
NFAVertex a = add_vertex(src);
NFAVertex b = add_vertex(src);
NFAVertex c = add_vertex(src);
NFAVertex d = add_vertex(src);
NFAVertex e = add_vertex(src);
NFAVertex f = add_vertex(src);
NFAVertex g = add_vertex(src);
NFAVertex h = add_vertex(src);
NFAVertex i = add_vertex(src);
src[a].char_reach = CharReach('a');
src[b].char_reach = CharReach('b');
src[c].char_reach = CharReach('c');
src[d].char_reach = CharReach('d');
src[e].char_reach = CharReach('e');
src[f].char_reach = CharReach('f');
src[g].char_reach = CharReach('g');
src[h].char_reach = CharReach('h');
src[i].char_reach = CharReach('i');
add_edge(src.start, a, src);
add_edge(f, src.acceptEod, src);
add_edge(a, b, src);
add_edge(b, c, src);
add_edge(c, d, src);
add_edge(d, e, src);
add_edge(e, f, src);
add_edge(c, g, src);
add_edge(g, b, src);
add_edge(d, h, src);
add_edge(h, c, src);
add_edge(c, i, src);
add_edge(i, c, src);
NFAVertex pivot = c;
ue2::unordered_map<NFAVertex, NFAVertex> lhs_map;
ue2::unordered_map<NFAVertex, NFAVertex> rhs_map;
splitGraph(src, pivot, &lhs, &lhs_map, &rhs, &rhs_map);
ASSERT_EQ(3U + N_SPECIALS, num_vertices(lhs));
NFAGraph::vertex_iterator vi, ve;
for (tie(vi, ve) = vertices(lhs); vi != ve; ++vi) {
if (is_special(*vi, lhs)) {
continue;
}
u32 cr = lhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE((cr >= 'a' && cr <= 'c'));
}
ASSERT_EQ(8U + N_SPECIALS, num_vertices(rhs) );
for (tie(vi, ve) = vertices(rhs); vi != ve; ++vi) {
if (is_special(*vi, rhs)) {
continue;
}
u32 cr = rhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE(cr >= 'b' && cr <= 'i');
}
}
TEST(NFAGraph, split2) {
NGHolder src, lhs, rhs;
NFAVertex a = add_vertex(src);
NFAVertex b = add_vertex(src);
NFAVertex c = add_vertex(src);
NFAVertex d = add_vertex(src);
src[a].char_reach = CharReach('a');
src[b].char_reach = CharReach('b');
src[c].char_reach = CharReach('c');
src[d].char_reach = CharReach('d');
add_edge(src.start, a, src);
add_edge(d, src.acceptEod, src);
add_edge(a, b, src);
add_edge(b, c, src);
add_edge(c, d, src);
add_edge(d, b, src);
NFAVertex pivot = c;
ue2::unordered_map<NFAVertex, NFAVertex> lhs_map;
ue2::unordered_map<NFAVertex, NFAVertex> rhs_map;
splitGraph(src, pivot, &lhs, &lhs_map, &rhs, &rhs_map);
ASSERT_EQ(3U + N_SPECIALS, num_vertices(lhs));
NFAGraph::vertex_iterator vi, ve;
for (tie(vi, ve) = vertices(lhs); vi != ve; ++vi) {
if (is_special(*vi, lhs)) {
continue;
}
u32 cr = lhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE(cr >= 'a' && cr <= 'c');
}
ASSERT_EQ(3U + N_SPECIALS, num_vertices(rhs) );
for (tie(vi, ve) = vertices(rhs); vi != ve; ++vi) {
if (is_special(*vi, rhs)) {
continue;
}
u32 cr = rhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE(cr >= 'b' && cr <= 'd');
}
}
TEST(NFAGraph, split3) {
NGHolder src, lhs, rhs;
NFAVertex a = add_vertex(src);
NFAVertex b = add_vertex(src);
NFAVertex c = add_vertex(src);
NFAVertex d = add_vertex(src);
NFAVertex e = add_vertex(src);
NFAVertex f = add_vertex(src);
NFAVertex g = add_vertex(src);
NFAVertex h = add_vertex(src);
NFAVertex i = add_vertex(src);
src[a].char_reach = CharReach('a');
src[b].char_reach = CharReach('b');
src[c].char_reach = CharReach('c');
src[d].char_reach = CharReach('d');
src[e].char_reach = CharReach('e');
src[f].char_reach = CharReach('f');
src[g].char_reach = CharReach('g');
src[h].char_reach = CharReach('h');
src[i].char_reach = CharReach('i');
add_edge(src.start, a, src);
add_edge(src.start, e, src);
add_edge(i, src.acceptEod, src);
add_edge(a, b, src);
add_edge(b, c, src);
add_edge(c, d, src);
add_edge(e, f, src);
add_edge(f, g, src);
add_edge(h, i, src);
add_edge(c, h, src);
add_edge(d, h, src);
add_edge(g, h, src);
vector<NFAVertex> pivots;
pivots.push_back(c);
pivots.push_back(d);
pivots.push_back(g);
ue2::unordered_map<NFAVertex, NFAVertex> lhs_map;
ue2::unordered_map<NFAVertex, NFAVertex> rhs_map;
splitGraph(src, pivots, &lhs, &lhs_map, &rhs, &rhs_map);
ASSERT_EQ(7U + N_SPECIALS, num_vertices(lhs));
NFAGraph::vertex_iterator vi, ve;
for (tie(vi, ve) = vertices(lhs); vi != ve; ++vi) {
if (is_special(*vi, lhs)) {
continue;
}
u32 cr = lhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE((cr >= 'a' && cr <= 'g'));
}
ASSERT_EQ(2U + N_SPECIALS, num_vertices(rhs) );
for (tie(vi, ve) = vertices(rhs); vi != ve; ++vi) {
if (is_special(*vi, rhs)) {
continue;
}
u32 cr = rhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE(cr >= 'h' && cr <= 'i');
}
}
TEST(NFAGraph, split4) {
NGHolder src, lhs, rhs;
NFAVertex a = add_vertex(src);
NFAVertex b = add_vertex(src);
NFAVertex c = add_vertex(src);
NFAVertex d = add_vertex(src);
NFAVertex e = add_vertex(src);
NFAVertex f = add_vertex(src);
NFAVertex g = add_vertex(src);
NFAVertex h = add_vertex(src);
NFAVertex i = add_vertex(src);
src[a].char_reach = CharReach('a');
src[b].char_reach = CharReach('b');
src[c].char_reach = CharReach('c');
src[d].char_reach = CharReach('d');
src[e].char_reach = CharReach('e');
src[f].char_reach = CharReach('f');
src[g].char_reach = CharReach('g');
src[h].char_reach = CharReach('h');
src[i].char_reach = CharReach('i');
add_edge(src.start, a, src);
add_edge(src.start, e, src);
add_edge(i, src.acceptEod, src);
add_edge(a, b, src);
add_edge(b, c, src);
add_edge(c, d, src);
add_edge(e, f, src);
add_edge(f, g, src);
add_edge(h, i, src);
add_edge(c, h, src);
add_edge(d, h, src);
add_edge(g, h, src);
/* loops */
add_edge(g, e, src);
add_edge(d, d, src);
vector<NFAVertex> pivots;
pivots.push_back(c);
pivots.push_back(d);
pivots.push_back(g);
ue2::unordered_map<NFAVertex, NFAVertex> lhs_map;
ue2::unordered_map<NFAVertex, NFAVertex> rhs_map;
splitGraph(src, pivots, &lhs, &lhs_map, &rhs, &rhs_map);
ASSERT_EQ(7U + N_SPECIALS, num_vertices(lhs));
NFAGraph::vertex_iterator vi, ve;
for (tie(vi, ve) = vertices(lhs); vi != ve; ++vi) {
if (is_special(*vi, lhs)) {
continue;
}
u32 cr = lhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE((cr >= 'a' && cr <= 'g'));
}
ASSERT_TRUE(edge(lhs_map[g], lhs_map[e], lhs).second);
ASSERT_TRUE(edge(lhs_map[d], lhs_map[d], lhs).second);
ASSERT_EQ(2U + N_SPECIALS, num_vertices(rhs) );
for (tie(vi, ve) = vertices(rhs); vi != ve; ++vi) {
if (is_special(*vi, rhs)) {
continue;
}
u32 cr = rhs[*vi].char_reach.find_first();
SCOPED_TRACE(cr);
ASSERT_TRUE(cr >= 'h' && cr <= 'i');
}
}
TEST(NFAGraph, cyclicVerts1) {
NGHolder g;
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
add_edge(a, b, g);
add_edge(b, a, g);
auto cyclics = findVerticesInCycles(g);
ASSERT_EQ(set<NFAVertex>({g.startDs, a, b}), cyclics);
}
TEST(NFAGraph, cyclicVerts2) {
NGHolder g;
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
NFAVertex d = add_vertex(g);
NFAVertex e = add_vertex(g);
add_edge(g.start, a, g);
add_edge(a, b, g);
add_edge(b, c, g);
add_edge(c, a, g);
add_edge(c, d, g);
add_edge(a, e, g);
auto cyclics = findVerticesInCycles(g);
ASSERT_EQ(set<NFAVertex>({g.startDs, a, b, c}), cyclics);
}
TEST(NFAGraph, cyclicVerts3) {
NGHolder g;
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
NFAVertex d = add_vertex(g);
NFAVertex e = add_vertex(g);
NFAVertex f = add_vertex(g);
NFAVertex h = add_vertex(g);
add_edge(g.start, a, g);
add_edge(g.start, d, g);
add_edge(a, b, g);
add_edge(b, c, g);
add_edge(b, f, g);
add_edge(c, a, g);
add_edge(b, d, g);
add_edge(d, e, g);
add_edge(e, b, g);
add_edge(f, h, g);
add_edge(h, h, g);
auto cyclics = findVerticesInCycles(g);
ASSERT_EQ(set<NFAVertex>({g.startDs, a, b, c, d, e, h}), cyclics);
}
TEST(NFAGraph, cyclicVerts4) {
NGHolder g;
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
NFAVertex d = add_vertex(g);
NFAVertex e = add_vertex(g);
NFAVertex f = add_vertex(g);
NFAVertex h = add_vertex(g);
add_edge(g.start, a, g);
add_edge(g.start, d, g);
add_edge(a, b, g);
add_edge(b, c, g);
add_edge(b, d, g);
add_edge(c, e, g);
add_edge(d, e, g);
add_edge(e, a, g);
add_edge(e, f, g);
add_edge(f, h, g);
auto cyclics = findVerticesInCycles(g);
ASSERT_EQ(set<NFAVertex>({g.startDs, a, b, c, d, e}), cyclics);
}
TEST(NFAGraph, cyclicVerts5) {
NGHolder g;
NFAVertex a = add_vertex(g);
NFAVertex b = add_vertex(g);
NFAVertex c = add_vertex(g);
NFAVertex d = add_vertex(g);
NFAVertex e = add_vertex(g);
add_edge(g.start, a, g);
add_edge(g.start, e, g);
add_edge(a, b, g);
add_edge(b, c, g);
add_edge(c, b, g);
add_edge(c, d, g);
add_edge(e, c, g);
auto cyclics = findVerticesInCycles(g);
ASSERT_EQ(set<NFAVertex>({g.startDs, b, c}), cyclics);
}

View File

@@ -0,0 +1,86 @@
/*
* 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 "nfagraph_common.h"
#include "gtest/gtest.h"
#include "compiler/compiler.h"
#include "nfagraph/ng.h"
#include "nfagraph/ng_builder.h"
#include "nfagraph/ng_width.h"
#include "grey.h"
#include "util/target_info.h"
#include "util/depth.h"
using namespace std;
using namespace testing;
using namespace ue2;
struct WidthTest {
string pattern;
depth minWidth;
depth maxWidth;
};
class NFAWidthTest : public TestWithParam<WidthTest> { };
static const WidthTest widthTests[] = {
{ "()", 0, 0 },
{ "a", 1, 1 },
{ "a?b", 1, 2 },
{ "foobar", 6, 6 },
{ "foo(bar)?", 3, 6 },
{ "(a|ab|abc|abcd)", 1, 4 },
{ "foo.*bar", 6, depth::infinity() },
{ "foo(bar)*", 3, depth::infinity() },
{ "foo(bar)+", 6, depth::infinity() },
{ "foo(bar){1,3}", 6, 12 },
{ "(abcd)+", 4, depth::infinity() },
{ "foo\\z", 3, 3 },
{ "^foo", 3, 3 },
{ "^foo|bar.*baz", 3, depth::infinity() },
{ "^foobar.*|baz", 3, depth::infinity() },
{ "foo(\\z|bar)", 3, 6 },
{ "foo(|bar\\z)", 3, 6 },
{ "foo.{0,15}bar", 6, 21 },
{ "foo.{0,15}.*bar", 6, depth::infinity() },
{ "(?smi)^(aa[^a]aa$|a|a+\\Z|a)", 1, depth::infinity() }
};
INSTANTIATE_TEST_CASE_P(NFAWidth, NFAWidthTest, ValuesIn(widthTests));
TEST_P(NFAWidthTest, Check) {
const WidthTest &t = GetParam();
SCOPED_TRACE(testing::Message() << "Pattern: " << t.pattern);
unique_ptr<NGWrapper> w(constructGraph(t.pattern, 0));
ASSERT_EQ(t.minWidth, findMinWidth(*w));
ASSERT_EQ(t.maxWidth, findMaxWidth(*w));
}

294
unit/internal/noodle.cpp Normal file
View File

@@ -0,0 +1,294 @@
/*
* 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 "ue2common.h"
#include "hwlm/noodle_build.h"
#include "hwlm/noodle_engine.h"
#include "hwlm/hwlm.h"
#include "util/alloc.h"
#include <cstring>
#include <vector>
#include "gtest/gtest.h"
using std::unique_ptr;
using std::vector;
using namespace ue2;
struct hlmMatchEntry {
size_t from;
size_t to;
u32 id;
hlmMatchEntry(size_t start, size_t end, u32 identifier) :
from(start), to(end), id(identifier) {}
};
typedef vector<hlmMatchEntry> hlmMatchRecord;
static
hwlmcb_rv_t hlmSimpleCallback(size_t from, size_t to, u32 id, void *context) {
hlmMatchRecord *mr = (hlmMatchRecord *)context;
DEBUG_PRINTF("match @%zu = %u,%p\n", to, id, context);
mr->push_back(hlmMatchEntry(from, to, id));
return HWLM_CONTINUE_MATCHING;
}
static
void noodleMatch(const u8 *data, size_t data_len, const u8 *lit, size_t lit_len,
char nocase, HWLMCallback cb, void *ctxt) {
auto n = noodBuildTable(lit, lit_len, nocase, 0);
ASSERT_TRUE(n != nullptr);
hwlm_error_t rv;
rv = noodExec(n.get(), data, data_len, 0, cb, ctxt);
ASSERT_EQ(HWLM_SUCCESS, rv);
}
TEST(Noodle, nood1) {
const size_t data_len = 1024;
unsigned int i, j;
hlmMatchRecord ctxt;
u8 data[data_len];
memset(data, 'a', data_len);
noodleMatch(data, data_len, (const u8 *)"a", 1, 0, hlmSimpleCallback,
&ctxt);
ASSERT_EQ(1024U, ctxt.size());
for (i = 0; i < 1024; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"A", 1, 0,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(0U, ctxt.size());
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"A", 1, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1024U, ctxt.size());
for (i = 0; i < 1024; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i, ctxt[i].to);
}
for (j = 0; j < 16; j++) {
ctxt.clear();
noodleMatch(data + j, data_len - j, (const u8 *)"A", 1, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1024 - j, ctxt.size());
for (i = 0; i < 1024 - j; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data, data_len - j, (const u8 *)"A", 1, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1024 - j, ctxt.size());
for (i = 0; i < 1024 - j; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i, ctxt[i].to);
}
}
}
TEST(Noodle, nood2) {
const size_t data_len = 1024;
unsigned int i, j;
hlmMatchRecord ctxt;
u8 data[data_len];
memset(data, 'a', data_len);
noodleMatch(data, data_len, (const u8 *)"aa", 2, 0,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1023U, ctxt.size());
for (i = 0; i < 1023; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 1, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"aA", 2, 0,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(0U, ctxt.size());
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"AA", 2, 0,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(0U, ctxt.size());
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"aa", 2, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1023U, ctxt.size());
for (i = 0; i < 1023; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 1, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"Aa", 2, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1023U, ctxt.size());
for (i = 0; i < 1023; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 1, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"AA", 2, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1023U, ctxt.size());
for (i = 0; i < 1023; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 1, ctxt[i].to);
}
for (j = 0; j < 16; j++) {
ctxt.clear();
noodleMatch(data + j, data_len - j, (const u8 *)"Aa", 2, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1023 - j, ctxt.size());
for (i = 0; i < 1023 - j; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 1, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data, data_len - j, (const u8 *)"aA", 2, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1023 - j, ctxt.size());
for (i = 0; i < 1023 - j; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 1, ctxt[i].to);
}
}
}
TEST(Noodle, noodLong) {
const size_t data_len = 1024;
unsigned int i, j;
hlmMatchRecord ctxt;
u8 data[data_len];
memset(data, 'a', data_len);
noodleMatch(data, data_len, (const u8 *)"aaaa", 4, 0,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1021U, ctxt.size());
for (i = 0; i < 1021; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 3, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"aaAA", 4, 0,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(0U, ctxt.size());
ctxt.clear();
noodleMatch(data, data_len, (const u8 *)"aaAA", 4, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1021U, ctxt.size());
for (i = 0; i < 1021; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 3, ctxt[i].to);
}
for (j = 0; j < 16; j++) {
ctxt.clear();
noodleMatch(data + j, data_len -j, (const u8 *)"AAaa", 4, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1021 - j, ctxt.size());
for (i = 0; i < 1021 - j; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 3, ctxt[i].to);
}
ctxt.clear();
noodleMatch(data + j, data_len -j, (const u8 *)"aaaA", 4, 1,
hlmSimpleCallback, &ctxt);
ASSERT_EQ(1021 - j, ctxt.size());
for (i = 0; i < 1021 - j; i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 3, ctxt[i].to);
}
}
}
TEST(Noodle, noodCutoverSingle) {
const size_t max_data_len = 128;
hlmMatchRecord ctxt;
u8 data[max_data_len + 15];
memset(data, 'a', max_data_len + 15);
for (u32 align = 0; align < 16; align++) {
for (u32 len = 0; len < max_data_len; len++) {
ctxt.clear();
noodleMatch(data + align, len, (const u8 *)"a", 1, 0,
hlmSimpleCallback, &ctxt);
EXPECT_EQ(len, ctxt.size());
for (u32 i = 0; i < ctxt.size(); i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i, ctxt[i].to);
}
}
}
}
TEST(Noodle, noodCutoverDouble) {
const size_t max_data_len = 128;
hlmMatchRecord ctxt;
u8 data[max_data_len + 15];
memset(data, 'a', max_data_len + 15);
for (u32 align = 0; align < 16; align++) {
for (u32 len = 0; len < max_data_len; len++) {
ctxt.clear();
noodleMatch(data + align, len, (const u8 *)"aa", 2, 0,
hlmSimpleCallback, &ctxt);
EXPECT_EQ(len ? len - 1 : 0U , ctxt.size());
for (u32 i = 0; i < ctxt.size(); i++) {
ASSERT_EQ(i, ctxt[i].from);
ASSERT_EQ(i + 1, ctxt[i].to);
}
}
}
}

162
unit/internal/pack_bits.cpp Normal file
View File

@@ -0,0 +1,162 @@
/*
* 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 "gtest/gtest.h"
#include "util/pack_bits.h"
#include "util/make_unique.h"
#include "ue2common.h"
#include <algorithm>
#include <memory>
#include <vector>
using std::unique_ptr;
using std::vector;
template <typename T>
class PackBits : public testing::Test {};
typedef ::testing::Types<u32, u64a> PackBitsTypes;
TYPED_TEST_CASE(PackBits, PackBitsTypes);
// Templated wrappers around the pack_bits_{32,64} functions.
template <typename T>
void pack_bits(char *out, const T *v, const u32 *bits, unsigned elements);
template <typename T>
void unpack_bits(T *v, const char *in, const u32 *bits, unsigned elements);
template <>
void pack_bits<u32>(char *out, const u32 *v, const u32 *bits,
unsigned elements) {
return pack_bits_32(out, v, bits, elements);
}
template <>
void pack_bits<u64a>(char *out, const u64a *v, const u32 *bits,
unsigned elements) {
return pack_bits_64(out, v, bits, elements);
}
template <>
void unpack_bits<u32>(u32 *v, const char *in, const u32 *bits,
unsigned elements) {
return unpack_bits_32(v, (const u8 *)in, bits, elements);
}
template <>
void unpack_bits<u64a>(u64a *v, const char *in, const u32 *bits,
unsigned elements) {
return unpack_bits_64(v, (const u8 *)in, bits, elements);
}
template <typename T>
size_t packed_size(const vector<T> &bits) {
size_t num_bits = 0;
for (size_t i = 0; i < bits.size(); i++) {
num_bits += bits[i];
}
return ROUNDUP_N(num_bits, 8U) / 8U;
}
template <typename T>
void test_pack_and_unpack(const vector<T> &v, const vector<u32> &bits) {
const u32 max_bits = sizeof(T) * 8U;
const u32 elements = bits.size();
// Temporary char array to pack into.
const size_t mem_size = packed_size(bits);
unique_ptr<char[]> mem = ue2::make_unique<char[]>(mem_size);
pack_bits<T>(&mem[0], &v[0], &bits[0], elements);
vector<T> v2(elements, 0xcafecafeu); // Output vector.
unpack_bits<T>(&v2[0], &mem[0], &bits[0], elements);
for (u32 i = 0; i < elements; i++) {
// Check that v2[i] contains the bits[i] low bits of v[i].
T mask = bits[i] == max_bits ? ~(T)0 : ((T)1U << bits[i]) - 1;
ASSERT_EQ(v[i] & mask, v2[i]) << "element " << i << " mismatch";
}
}
TYPED_TEST(PackBits, AllZeroes) {
for (u32 i = 1; i < 32; i++) {
SCOPED_TRACE(i);
vector<TypeParam> v(i, 0);
vector<u32> bits(i, sizeof(TypeParam) * 8U); // all bits
test_pack_and_unpack(v, bits);
}
}
TYPED_TEST(PackBits, AllOnes) {
for (u32 i = 1; i < 32; i++) {
SCOPED_TRACE(i);
vector<TypeParam> v(i, ~((TypeParam)0));
for (u32 num_bits = 0; num_bits < sizeof(TypeParam) * 8U; num_bits++) {
vector<u32> bits(i, num_bits);
test_pack_and_unpack(v, bits);
}
}
}
TYPED_TEST(PackBits, SomeOnes) {
for (u32 i = 1; i < 32; i++) {
SCOPED_TRACE(i);
vector<TypeParam> v(i, ~((TypeParam)0));
// One bit from v[0], two bits from v[1] ...
vector<u32> bits(i);
for (u32 j = 0; j < i; j++) {
}
test_pack_and_unpack(v, bits);
// And in reverse
std::reverse(bits.begin(), bits.end());
test_pack_and_unpack(v, bits);
}
}
TYPED_TEST(PackBits, Holes) {
for (u32 i = 1; i < 32; i++) {
SCOPED_TRACE(i);
vector<TypeParam> v(i, ~((TypeParam)0));
// No bits from odd-numbered words.
vector<u32> bits(i);
for (u32 j = 0; j < i; j++) {
bits[j] = j % 2 ? 0 : sizeof(TypeParam) * 8U;
}
test_pack_and_unpack(v, bits);
}
}

117
unit/internal/parser.cpp Normal file
View File

@@ -0,0 +1,117 @@
/*
* 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 "gtest/gtest.h"
#include "parser/Component.h"
#include "parser/Parser.h"
#include "parser/dump.h"
#include "parser/prefilter.h"
#include "parser/unsupported.h"
#include "hs.h"
#include <sstream>
using namespace ue2;
using std::ostringstream;
using std::unique_ptr;
struct PatternInfo {
const char *expr;
unsigned int flags;
};
class ParserTest : public testing::TestWithParam<PatternInfo> {};
TEST_P(ParserTest, clone) {
const PatternInfo &info = GetParam();
SCOPED_TRACE(info.expr);
// This tests that we can parse expr and then clone it, then verifies that
// the cloned component tree matches the original (using dump code for
// serialisation)
ParseMode mode(info.flags);
unique_ptr<Component> c(parse(info.expr, mode));
ASSERT_TRUE(c != nullptr);
#if defined(DUMP_SUPPORT)
ostringstream oss;
dumpTree(oss, c.get());
#endif
unique_ptr<Component> c_copy(c->clone());
ASSERT_TRUE(c_copy != nullptr);
c.reset(); // free original c
#if defined(DUMP_SUPPORT)
ostringstream oss_copy;
dumpTree(oss_copy, c_copy.get());
ASSERT_EQ(oss.str(), oss_copy.str());
#endif
}
TEST_P(ParserTest, prefilter) {
const PatternInfo &info = GetParam();
SCOPED_TRACE(info.expr);
ParseMode mode(info.flags);
unique_ptr<Component> c(parse(info.expr, mode));
ASSERT_TRUE(c != nullptr);
prefilterTree(c, mode);
ASSERT_TRUE(c != nullptr);
// Should now be supported.
ASSERT_NO_THROW(checkUnsupported(*c));
}
static const PatternInfo patterns[] = {
{ "easy literal", 0 },
{ "foo|bar|baz", 0 },
{ "(?>\\d+)foo", 0 },
{ "\\bhello\\b", 0 },
{ "a+ b+? c++", 0 },
{ "^bi-anchored$", 0 },
{ "\\C", 0 },
{ "\\X", HS_FLAG_UTF8 },
{ "\\w+", HS_FLAG_UTF8 | HS_FLAG_UCP },
{ "foo(?!bar).*baz", 0 },
{ "(hatstand.*teakettle){2,} badgerbrush(es)? \\1", 0 },
{ "a (pine)?(?(1)apple)", 0 },
{ "^(?(?=hello)[a-z]+|[a-z]{3})", 0 },
};
INSTANTIATE_TEST_CASE_P(Parser, ParserTest, testing::ValuesIn(patterns));
// teach google-test how to print a param
void PrintTo(const PatternInfo &p, ::std::ostream *os) {
*os << p.expr << ":" << p.flags;
}

248
unit/internal/partial.cpp Normal file
View File

@@ -0,0 +1,248 @@
/*
* 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 "util/partial_store.h"
#include "gtest/gtest.h"
#include "ue2common.h"
#include <cstring>
using namespace std;
TEST(partial, u32_1) {
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u32(a + 8, 0xaa, 1);
b[8] = 0xaa;
ASSERT_EQ(0xaa, partial_load_u32(a + 8, 1));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u32_2) {
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u32(a + 8, 0xaabb, 2);
b[8] = 0xbb;
b[9] = 0xaa;
ASSERT_EQ(0xaabb, partial_load_u32(a + 8, 2));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u32_3) {
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u32(a + 8, 0xaabbcc, 3);
b[8] = 0xcc;
b[9] = 0xbb;
b[10] = 0xaa;
ASSERT_EQ(0xaabbcc, partial_load_u32(a + 8, 3));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u32_4) {
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u32(a + 8, 0xaabbccdd, 4);
b[8] = 0xdd;
b[9] = 0xcc;
b[10] = 0xbb;
b[11] = 0xaa;
ASSERT_EQ(0xaabbccdd, partial_load_u32(a + 8, 4));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_1) {
u64a val = 0xaa;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 1);
b[8] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 1));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_2) {
u64a val = 0xaabb;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 2);
b[8] = 0xbb;
b[9] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 2));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_3) {
u64a val = 0xaabbcc;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 3);
b[8] = 0xcc;
b[9] = 0xbb;
b[10] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 3));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_4) {
u64a val = 0xaabbccdd;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 4);
b[8] = 0xdd;
b[9] = 0xcc;
b[10] = 0xbb;
b[11] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 4));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_5) {
u64a val = 0xaabbccdd55ULL;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 5);
b[8] = 0x55;
b[9] = 0xdd;
b[10] = 0xcc;
b[11] = 0xbb;
b[12] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 5));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_6) {
u64a val = 0xaabbccdd5566ULL;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 6);
b[8] = 0x66;
b[9] = 0x55;
b[10] = 0xdd;
b[11] = 0xcc;
b[12] = 0xbb;
b[13] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 6));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_7) {
u64a val = 0xaabbccdd556677ULL;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 7);
b[8] = 0x77;
b[9] = 0x66;
b[10] = 0x55;
b[11] = 0xdd;
b[12] = 0xcc;
b[13] = 0xbb;
b[14] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 7));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}
TEST(partial, u64a_8) {
u64a val = 0xaabbccdd55667788ULL;
u8 a[16];
memset(a, 0x1f, sizeof(a));
u8 b[16];
memcpy(b, a, sizeof(a));
partial_store_u64a(a + 8, val, 8);
b[8] = 0x88;
b[9] = 0x77;
b[10] = 0x66;
b[11] = 0x55;
b[12] = 0xdd;
b[13] = 0xcc;
b[14] = 0xbb;
b[15] = 0xaa;
ASSERT_EQ(val, partial_load_u64a(a + 8, 8));
ASSERT_EQ(0, memcmp(a, b, sizeof(a)));
}

325
unit/internal/pqueue.cpp Normal file
View File

@@ -0,0 +1,325 @@
/*
* 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 "gtest/gtest.h"
#include "ue2common.h"
#define PQ_T u32
#define PQ_COMP(pqc_items, pqc_i, pqc_j) (pqc_items[pqc_i] > pqc_items[pqc_j])
#define PQ_COMP_B(pqc_items, pqc_i, pqc_jj) (pqc_items[pqc_i] > pqc_jj)
#include "util/pqueue.h"
static void sort(u32 *items, u32 count) {
for (u32 i = 0; i < count; i++) {
pq_insert(items, i, items[i]);
}
for (u32 i = 0; i < count; i++) {
u32 top = *pq_top(items);
pq_pop(items, count - i);
items[count - i - 1] = top;
}
}
TEST(pqueue, sort1) {
u32 in[] = {1, 2, 3, 4};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort2) {
u32 in[] = {1, 2, 4, 3};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort3) {
u32 in[] = {1, 3, 2, 4};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort4) {
u32 in[] = {1, 3, 4, 2};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort5) {
u32 in[] = {1, 4, 2, 3};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort6) {
u32 in[] = {1, 4, 3, 2};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort7) {
u32 in[] = {2, 1, 3, 4};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort8) {
u32 in[] = {2, 1, 4, 3};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort9) {
u32 in[] = {2, 3, 1, 4};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort10) {
u32 in[] = {2, 3, 4, 1};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort11) {
u32 in[] = {2, 4, 1, 3};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort12) {
u32 in[] = {2, 4, 3, 1};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort13) {
u32 in[] = {3, 1, 2, 4};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort14) {
u32 in[] = {3, 1, 4, 2};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort15) {
u32 in[] = {3, 2, 1, 4};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort16) {
u32 in[] = {3, 2, 4, 1};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort17) {
u32 in[] = {3, 4, 1, 2};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort18) {
u32 in[] = {3, 4, 2, 1};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort19) {
u32 in[] = {4, 1, 2, 3};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort20) {
u32 in[] = {4, 1, 3, 2};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort21) {
u32 in[] = {4, 2, 1, 3};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort22) {
u32 in[] = {4, 2, 3, 1};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort23) {
u32 in[] = {4, 3, 1, 2};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, sort24) {
u32 in[] = {4, 3, 2, 1};
u32 out[] = {1, 2, 3, 4};
sort(in, ARRAY_LENGTH(in));
ASSERT_EQ(0, memcmp(in, out, sizeof(in)));
}
TEST(pqueue, queue1) {
u32 in[] = {1, 2, 3, 4, 5, 6, 7, 8};
u32 expected[] = {4, 5, 6, 7, 8, 3, 2, 1};
u32 temp[ARRAY_LENGTH(in)];
u32 output[ARRAY_LENGTH(in)];
u32 queue_size = 0;
u32 i = 0, o = 0;
for (; i < 4; i++) {
pq_insert(temp, queue_size, in[i]);
queue_size++;
}
while (queue_size) {
output[o++] = *pq_top(temp);
pq_pop(temp, queue_size);
queue_size--;
if (i < ARRAY_LENGTH(in)) {
pq_insert(temp, queue_size, in[i++]);
queue_size++;
}
}
// for (u32 j = 0; j < o; j++) {
// printf("%u\n", output[j]);
// }
ASSERT_EQ(0, memcmp(output, expected, sizeof(in)));
}
TEST(pqueue, queue2) {
u32 in[] = {8, 7, 6, 5, 4, 3, 2, 1};
u32 expected[] = {8, 7, 6, 5, 4, 3, 2, 1};
u32 temp[ARRAY_LENGTH(in)];
u32 output[ARRAY_LENGTH(in)];
u32 queue_size = 0;
u32 i = 0, o = 0;
for (; i < 4; i++) {
pq_insert(temp, queue_size, in[i]);
queue_size++;
}
while (queue_size) {
output[o++] = *pq_top(temp);
pq_pop(temp, queue_size);
queue_size--;
if (i < ARRAY_LENGTH(in)) {
pq_insert(temp, queue_size, in[i++]);
queue_size++;
}
}
ASSERT_EQ(0, memcmp(output, expected, sizeof(in)));
}
TEST(pqueue, queue3) {
u32 in[] = {1, 8, 2, 7, 3, 6, 4, 5};
u32 expected[] = {8, 7, 6, 4, 5, 3, 2, 1};
u32 temp[ARRAY_LENGTH(in)];
u32 output[ARRAY_LENGTH(in)];
u32 queue_size = 0;
u32 i = 0, o = 0;
for (; i < 4; i++) {
pq_insert(temp, queue_size, in[i]);
queue_size++;
}
while (queue_size) {
output[o++] = *pq_top(temp);
pq_pop(temp, queue_size);
queue_size--;
if (i < ARRAY_LENGTH(in)) {
pq_insert(temp, queue_size, in[i++]);
queue_size++;
}
}
ASSERT_EQ(0, memcmp(output, expected, sizeof(in)));
}

975
unit/internal/repeat.cpp Normal file
View File

@@ -0,0 +1,975 @@
/*
* 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 "gtest/gtest.h"
#include "nfa/limex_ring.h"
#include "nfa/repeat.h"
#include "nfa/repeatcompile.h"
#include "util/depth.h"
#include "util/make_unique.h"
#include <algorithm>
#include <memory>
#include <vector>
using namespace std;
using namespace testing;
using namespace ue2;
struct RepeatTestInfo {
enum RepeatType type;
depth repeatMin;
depth repeatMax;
};
static
ostream& operator<<(ostream &os, const RepeatInfo &info) {
os << "{" << info.repeatMin;
if (info.repeatMin == info.repeatMax) {
os << "}";
} else if (info.repeatMax == REPEAT_INF) {
os << ",}";
} else {
os << "," << info.repeatMax << "}";
}
os << " " << repeatTypeName(info.type);
os << " (period " << info.minPeriod << ")";
return os;
}
class RepeatTest : public TestWithParam<RepeatTestInfo> {
protected:
virtual void SetUp() {
test_info = GetParam();
info.type = test_info.type;
info.repeatMin = test_info.repeatMin;
info.repeatMax = test_info.repeatMax.is_finite()
? (u32)test_info.repeatMax
: REPEAT_INF;
info.minPeriod = 0;
RepeatStateInfo rsi(test_info.type, test_info.repeatMin,
test_info.repeatMax, 0);
info.packedCtrlSize = rsi.packedCtrlSize;
info.stateSize = rsi.stateSize;
info.horizon = rsi.horizon;
std::copy(rsi.packedFieldSizes.begin(), rsi.packedFieldSizes.end(),
info.packedFieldSizes);
ctrl = new RepeatControl();
state_int = new char[info.stateSize + 7]; /* state may have mmbits */
state = state_int + 7;
}
virtual void TearDown() {
delete ctrl;
delete [] state_int;
}
RepeatTestInfo test_info; // Test params
RepeatInfo info; // Repeat info structure
RepeatControl *ctrl;
char *state;
private:
char *state_int;
};
static const RepeatTestInfo repeatTests[] = {
// Fixed repeats -- ring model
{ REPEAT_RING, 2, 2 },
{ REPEAT_RING, 4, 4 },
{ REPEAT_RING, 10, 10 },
{ REPEAT_RING, 16, 16 },
{ REPEAT_RING, 20, 20 },
{ REPEAT_RING, 30, 30 },
{ REPEAT_RING, 50, 50 },
{ REPEAT_RING, 64, 64 },
{ REPEAT_RING, 65, 65 },
{ REPEAT_RING, 100, 100 },
{ REPEAT_RING, 200, 200 },
{ REPEAT_RING, 1000, 1000 },
{ REPEAT_RING, 4100, 4100 },
{ REPEAT_RING, 16000, 16000 },
// {0, N} repeats -- last model
{ REPEAT_LAST, 0, 4 },
{ REPEAT_LAST, 0, 10 },
{ REPEAT_LAST, 0, 20 },
{ REPEAT_LAST, 0, 30 },
{ REPEAT_LAST, 0, 50 },
{ REPEAT_LAST, 0, 100 },
{ REPEAT_LAST, 0, 200 },
{ REPEAT_LAST, 0, 1000 },
{ REPEAT_LAST, 0, 16000 },
// {0, N} repeats -- ring model (though we use 'last' model in practice)
{ REPEAT_RING, 0, 2 },
{ REPEAT_RING, 0, 4 },
{ REPEAT_RING, 0, 10 },
{ REPEAT_RING, 0, 20 },
{ REPEAT_RING, 0, 30 },
{ REPEAT_RING, 0, 50 },
{ REPEAT_RING, 0, 64 },
{ REPEAT_RING, 0, 65 },
{ REPEAT_RING, 0, 100 },
{ REPEAT_RING, 0, 200 },
{ REPEAT_RING, 0, 1000 },
{ REPEAT_RING, 0, 16000 },
// {N, M} repeats -- ring model
{ REPEAT_RING, 2, 3 },
{ REPEAT_RING, 1, 4 },
{ REPEAT_RING, 5, 10 },
{ REPEAT_RING, 10, 20 },
{ REPEAT_RING, 10, 50 },
{ REPEAT_RING, 50, 60 },
{ REPEAT_RING, 100, 200 },
{ REPEAT_RING, 1, 200 },
{ REPEAT_RING, 10, 16000 },
{ REPEAT_RING, 10000, 16000 },
// {N, M} repeats -- range model
{ REPEAT_RANGE, 1, 4 },
{ REPEAT_RANGE, 5, 10 },
{ REPEAT_RANGE, 10, 20 },
{ REPEAT_RANGE, 10, 50 },
{ REPEAT_RANGE, 50, 60 },
{ REPEAT_RANGE, 100, 200 },
{ REPEAT_RANGE, 1, 200 },
{ REPEAT_RANGE, 10, 16000 },
{ REPEAT_RANGE, 10000, 16000 },
// {N,M} repeats -- small bitmap model
{ REPEAT_BITMAP, 1, 2 },
{ REPEAT_BITMAP, 5, 10 },
{ REPEAT_BITMAP, 10, 20 },
{ REPEAT_BITMAP, 20, 40 },
{ REPEAT_BITMAP, 1, 63 },
{ REPEAT_BITMAP, 50, 63 },
// {N,M} repeats -- trailer model
{ REPEAT_TRAILER, 1, 2 },
{ REPEAT_TRAILER, 8, 8 },
{ REPEAT_TRAILER, 0, 8 },
{ REPEAT_TRAILER, 10, 20 },
{ REPEAT_TRAILER, 1, 32 },
{ REPEAT_TRAILER, 64, 64 },
{ REPEAT_TRAILER, 1, 64 },
{ REPEAT_TRAILER, 1, 100 },
{ REPEAT_TRAILER, 1, 2000 },
{ REPEAT_TRAILER, 50, 200 },
{ REPEAT_TRAILER, 50, 1000 },
{ REPEAT_TRAILER, 64, 1024 },
// {N,} repeats -- first model
{ REPEAT_FIRST, 0, depth::infinity() },
{ REPEAT_FIRST, 1, depth::infinity() },
{ REPEAT_FIRST, 4, depth::infinity() },
{ REPEAT_FIRST, 10, depth::infinity() },
{ REPEAT_FIRST, 50, depth::infinity() },
{ REPEAT_FIRST, 100, depth::infinity() },
{ REPEAT_FIRST, 1000, depth::infinity() },
{ REPEAT_FIRST, 3000, depth::infinity() },
{ REPEAT_FIRST, 10000, depth::infinity() }
};
INSTANTIATE_TEST_CASE_P(Repeat, RepeatTest, ValuesIn(repeatTests));
TEST_P(RepeatTest, MatchSuccess) {
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
u64a offset = 1000;
repeatStore(&info, ctrl, state, offset, 0);
for (u32 i = info.repeatMin; i <= info.repeatMax; i++) {
enum TriggerResult rv = processTugTrigger(&info, ctrl, state, offset + i);
if (rv == TRIGGER_SUCCESS_CACHE) {
rv = TRIGGER_SUCCESS;
}
ASSERT_EQ(TRIGGER_SUCCESS, rv);
}
}
TEST_P(RepeatTest, MatchNegSuccess) {
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
// Write a top at offset 1000.
const u64a offset = 1000;
repeatStore(&info, ctrl, state, offset, 0);
// Write another match at offset 1002, using is_alive=0 (i.e. the repeat
// was killed between these two tops).
repeatStore(&info, ctrl, state, offset + 2, 0);
enum TriggerResult rv;
// Match at offset + repeatMin should fail, while offset + repeatMin + 2
// should succeed.
if (info.repeatMin > 2) {
rv = processTugTrigger(&info, ctrl, state, offset + info.repeatMin);
ASSERT_EQ(TRIGGER_FAIL, rv);
}
rv = processTugTrigger(&info, ctrl, state, offset + info.repeatMin + 2);
if (rv == TRIGGER_SUCCESS_CACHE) {
rv = TRIGGER_SUCCESS;
}
ASSERT_EQ(TRIGGER_SUCCESS, rv);
}
TEST_P(RepeatTest, MatchFail) {
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
// Write a match at offset 1000.
const u64a offset = 1000;
repeatStore(&info, ctrl, state, offset, 0);
// Test for a match, if possible, at offset + repeatMin - 1.
enum TriggerResult rv;
if (info.repeatMin > 0) {
u64a testOffset = offset + info.repeatMin - 1;
rv = processTugTrigger(&info, ctrl, state, testOffset);
ASSERT_EQ(TRIGGER_FAIL, rv);
}
// Match after repeatMax should fail as well.
if (info.repeatMax != REPEAT_INF) {
u64a testOffset = offset + info.repeatMax + 1;
rv = processTugTrigger(&info, ctrl, state, testOffset);
ASSERT_EQ(TRIGGER_STALE, rv);
}
}
// Fill the ring with matches.
TEST_P(RepeatTest, FillRing) {
if (info.repeatMax == REPEAT_INF) {
return;
}
const u64a offset = 1000;
repeatStore(&info, ctrl, state, offset, 0);
for (u64a i = 1; i <= info.repeatMax; i++) {
repeatStore(&info, ctrl, state, offset + i, 1);
}
// We should be able to see matches for all of these (beyond the last top offset).
enum TriggerResult rv;
for (u64a i = offset + info.repeatMax;
i <= offset + info.repeatMax + info.repeatMin; i++) {
rv = processTugTrigger(&info, ctrl, state, i);
if (rv == TRIGGER_SUCCESS_CACHE) {
rv = TRIGGER_SUCCESS;
}
ASSERT_EQ(TRIGGER_SUCCESS, rv);
}
}
TEST_P(RepeatTest, FindTops) {
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
repeatStore(&info, ctrl, state, 1000, 0);
ASSERT_EQ(1000, repeatLastTop(&info, ctrl, state));
repeatStore(&info, ctrl, state, 2000, 1);
if (info.type == REPEAT_FIRST) {
ASSERT_EQ(1000, repeatLastTop(&info, ctrl, state));
} else {
ASSERT_EQ(2000, repeatLastTop(&info, ctrl, state));
}
repeatStore(&info, ctrl, state, 3000, 0);
ASSERT_EQ(3000, repeatLastTop(&info, ctrl, state));
}
TEST_P(RepeatTest, NextMatch) {
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
u64a top = 10000000ULL;
repeatStore(&info, ctrl, state, top, 0);
u64a i = top + info.repeatMin;
if (info.repeatMin != 0) {
// First match after the top should be at top+min.
ASSERT_EQ(i, repeatNextMatch(&info, ctrl, state, top));
}
while (i < info.repeatMax) {
ASSERT_EQ(i + 1, repeatNextMatch(&info, ctrl, state, i));
i++;
}
if (info.repeatMax != REPEAT_INF) {
ASSERT_EQ(0, repeatNextMatch(&info, ctrl, state, top + info.repeatMax));
}
}
TEST_P(RepeatTest, NextMatchFilledRepeat) {
// This test is only really appropriate for repeat models that store more
// than one top.
if (info.type != REPEAT_RING && info.type != REPEAT_RANGE) {
return;
}
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
u64a top = 10000000ULL;
repeatStore(&info, ctrl, state, top, 0);
for (u64a i = 1; i <= info.repeatMax; i++) {
repeatStore(&info, ctrl, state, top + i, 1);
}
u64a last_top = top + info.repeatMax;
u64a i = top + info.repeatMin;
if (info.repeatMin != 0) {
// First match after the initial top should be at top+min.
ASSERT_EQ(i, repeatNextMatch(&info, ctrl, state, top));
}
while (i < last_top + info.repeatMax) {
ASSERT_EQ(i + 1, repeatNextMatch(&info, ctrl, state, i));
i++;
}
if (info.repeatMax != REPEAT_INF) {
ASSERT_EQ(0, repeatNextMatch(&info, ctrl, state, last_top + info.repeatMax));
}
}
TEST_P(RepeatTest, TwoTops) {
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
// Only appropriate for tests that store more than one top.
if (info.type == REPEAT_FIRST || info.type == REPEAT_LAST) {
return;
}
const u32 top_range = info.repeatMax;
// Limit the scope of this test for runtime brevity. For small cases, we
// check every offset, but for bigger ones we use a bigger step.
const u32 iter_step = std::max(1u, top_range / 256u);
const u64a top1 = 10000ull;
for (u32 i = 1; i < top_range; i += iter_step) {
const u64a top2 = top1 + i;
SCOPED_TRACE(testing::Message() << "Tops at offsets " << top1 << " and "
<< top2);
repeatStore(&info, ctrl, state, top1, 0);
ASSERT_EQ(top1, repeatLastTop(&info, ctrl, state));
repeatStore(&info, ctrl, state, top2, 1);
ASSERT_EQ(top2, repeatLastTop(&info, ctrl, state));
// We should have those matches from the top1 match window that are
// greater than or equal to top2.
for (u64a j = std::max(top1 + info.repeatMin, top2);
j <= top1 + info.repeatMax; j++) {
ASSERT_EQ(REPEAT_MATCH, repeatHasMatch(&info, ctrl, state, j))
<< j << " should be a match due to top 1";
}
// If the two match windows don't overlap, we should have some
// non-matching positions between them.
for (u64a j = top1 + info.repeatMax + 1; j < top2 + info.repeatMin;
j++) {
ASSERT_EQ(REPEAT_NOMATCH, repeatHasMatch(&info, ctrl, state, j))
<< j << " should not be a match";
}
// We should have all the matches in the match window from top 2.
for (u64a j = top2 + info.repeatMin; j <= top2 + info.repeatMax; j++) {
ASSERT_EQ(REPEAT_MATCH, repeatHasMatch(&info, ctrl, state, j))
<< j << " should be a match due to top 2";
}
// One past the end should be stale.
u64a past_end = top2 + info.repeatMax + 1;
ASSERT_EQ(REPEAT_STALE, repeatHasMatch(&info, ctrl, state, past_end))
<< "repeat should be stale at " << past_end;
}
}
TEST_P(RepeatTest, Pack) {
SCOPED_TRACE(testing::Message() << "Repeat: " << info);
u64a offset = 1000;
repeatStore(&info, ctrl, state, offset, 0);
// We should be able to pack and then unpack the control block at any
// offset up to repeatMin and get a match at both the min and max repeats.
unique_ptr<char[]> packed = ue2::make_unique<char[]>(info.packedCtrlSize);
for (u32 i = 0; i < info.repeatMax; i++) {
SCOPED_TRACE(testing::Message() << "i=" << i);
const u64a pack_offset = offset + i;
memset(packed.get(), 0xff, info.packedCtrlSize);
repeatPack(packed.get(), &info, ctrl, pack_offset);
memset(ctrl, 0xff, sizeof(*ctrl));
repeatUnpack(packed.get(), &info, pack_offset, ctrl);
// We should have a match at every offset in [offset + repeatMin,
// offset + repeatMax]. For brevity, we just check the first one and
// the last one.
u64a first = offset + std::max(i, info.repeatMin);
u64a last = offset + info.repeatMax;
ASSERT_EQ(REPEAT_MATCH, repeatHasMatch(&info, ctrl, state, first))
<< "repeat should have match at " << first;
ASSERT_EQ(REPEAT_MATCH, repeatHasMatch(&info, ctrl, state, last))
<< "repeat should have match at " << last;
}
}
static
const u32 sparsePeriods[] = {
2,
4,
6,
8,
10,
12,
15,
18,
20,
22,
24,
26,
28,
30,
/* 40,
50,
60,
80,
100,
120,
150,
180,
200,
250,
300,
350,
400,*/
};
static
const RepeatTestInfo sparseRepeats[] = {
// Fixed repeats
{ REPEAT_SPARSE_OPTIMAL_P, 10, 10 },
{ REPEAT_SPARSE_OPTIMAL_P, 20, 20 },
{ REPEAT_SPARSE_OPTIMAL_P, 40, 40 },
{ REPEAT_SPARSE_OPTIMAL_P, 80, 80 },
{ REPEAT_SPARSE_OPTIMAL_P, 100, 100 },
{ REPEAT_SPARSE_OPTIMAL_P, 150, 150 },
{ REPEAT_SPARSE_OPTIMAL_P, 200, 200 },
{ REPEAT_SPARSE_OPTIMAL_P, 250, 250 },
{ REPEAT_SPARSE_OPTIMAL_P, 300, 300 },
{ REPEAT_SPARSE_OPTIMAL_P, 350, 350 },
{ REPEAT_SPARSE_OPTIMAL_P, 400, 400 },
{ REPEAT_SPARSE_OPTIMAL_P, 500, 500 },
{ REPEAT_SPARSE_OPTIMAL_P, 600, 600 },
{ REPEAT_SPARSE_OPTIMAL_P, 800, 800 },
{ REPEAT_SPARSE_OPTIMAL_P, 1000, 1000 },
{ REPEAT_SPARSE_OPTIMAL_P, 1500, 1500 },
{ REPEAT_SPARSE_OPTIMAL_P, 2000, 2000 },
{ REPEAT_SPARSE_OPTIMAL_P, 2500, 2500 },
{ REPEAT_SPARSE_OPTIMAL_P, 3000, 3000 },
{ REPEAT_SPARSE_OPTIMAL_P, 3500, 3500 },
{ REPEAT_SPARSE_OPTIMAL_P, 4000, 4000 },
{ REPEAT_SPARSE_OPTIMAL_P, 4500, 4500 },
{ REPEAT_SPARSE_OPTIMAL_P, 5000, 5000 },
// {N, M} repeats
{ REPEAT_SPARSE_OPTIMAL_P, 10, 20 },
{ REPEAT_SPARSE_OPTIMAL_P, 20, 40 },
{ REPEAT_SPARSE_OPTIMAL_P, 40, 80 },
{ REPEAT_SPARSE_OPTIMAL_P, 80, 100 },
{ REPEAT_SPARSE_OPTIMAL_P, 100, 120 },
{ REPEAT_SPARSE_OPTIMAL_P, 150, 180 },
{ REPEAT_SPARSE_OPTIMAL_P, 200, 400 },
{ REPEAT_SPARSE_OPTIMAL_P, 250, 500 },
{ REPEAT_SPARSE_OPTIMAL_P, 300, 400 },
{ REPEAT_SPARSE_OPTIMAL_P, 350, 500 },
{ REPEAT_SPARSE_OPTIMAL_P, 400, 500 },
{ REPEAT_SPARSE_OPTIMAL_P, 500, 600 },
{ REPEAT_SPARSE_OPTIMAL_P, 600, 700 },
{ REPEAT_SPARSE_OPTIMAL_P, 800, 1000 },
{ REPEAT_SPARSE_OPTIMAL_P, 1000, 1200 },
{ REPEAT_SPARSE_OPTIMAL_P, 1500, 1800 },
{ REPEAT_SPARSE_OPTIMAL_P, 2000, 4000 },
{ REPEAT_SPARSE_OPTIMAL_P, 2500, 3000 },
{ REPEAT_SPARSE_OPTIMAL_P, 3000, 3500 },
{ REPEAT_SPARSE_OPTIMAL_P, 3500, 4000 },
{ REPEAT_SPARSE_OPTIMAL_P, 4000, 8000 },
{ REPEAT_SPARSE_OPTIMAL_P, 4500, 8000 },
{ REPEAT_SPARSE_OPTIMAL_P, 5000, 5001 }
};
static
void test_sparse2entry(const RepeatInfo *info, RepeatControl *ctrl,
char *state, u64a second) {
SCOPED_TRACE(testing::Message() << "Repeat: " << *info);
SCOPED_TRACE(second);
if (second > info->repeatMax || second < info->minPeriod) {
return;
}
u64a offset = 1000;
u64a exit = offset + info->repeatMin;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(offset, repeatLastTop(info, ctrl, state));
u64a offset2 = 1000 + second;
u64a exit2 = offset2 + info->repeatMin;
repeatStore(info, ctrl, state, offset2, 1);
ASSERT_EQ(offset2, repeatLastTop(info, ctrl, state));
u32 range = info->repeatMax - info->repeatMin;
for (u32 i = offset2; i < offset + info->repeatMax * 2 + 100; i++) {
SCOPED_TRACE(i);
RepeatMatch r = repeatHasMatch(info, ctrl, state, i);
if ((i >= exit && i <= exit + range) ||
(i >= exit2 && i <= exit2 + range)) {
ASSERT_EQ(REPEAT_MATCH, r);
} else if (i > exit2 + range) {
ASSERT_EQ(REPEAT_STALE, r);
} else {
ASSERT_EQ(REPEAT_NOMATCH, r);
}
}
ASSERT_EQ(MAX(exit + range + 1, exit2),
repeatNextMatch(info, ctrl, state, exit + range));
ASSERT_EQ(MAX(exit + range + 2, exit2),
repeatNextMatch(info, ctrl, state, exit + range + 1));
ASSERT_EQ(exit2, repeatNextMatch(info, ctrl, state, exit2 - 1));
ASSERT_EQ(0, repeatNextMatch(info, ctrl, state, exit2 + range));
}
static
void test_sparse3entry(const RepeatInfo *info, RepeatControl *ctrl,
char *state, u64a diff) {
SCOPED_TRACE(testing::Message() << "Repeat:" << *info);
SCOPED_TRACE(diff);
if (diff * 2 > info->repeatMax || diff < info->minPeriod) {
return;
}
u64a offset = 1000;
u64a exit = offset + info->repeatMin;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(offset, repeatLastTop(info, ctrl, state));
u64a offset2 = 1000 + diff;
u64a exit2 = offset2 + info->repeatMin;
repeatStore(info, ctrl, state, offset2, 1);
ASSERT_EQ(offset2, repeatLastTop(info, ctrl, state));
u64a offset3 = 1000 + 2 * diff;
u64a exit3 = offset3 + info->repeatMin;
repeatStore(info, ctrl, state, offset3, 1);
ASSERT_EQ(offset3, repeatLastTop(info, ctrl, state));
u32 range = info->repeatMax - info->repeatMin;
for (u32 i = offset2; i < offset + info->repeatMax * 2 + 100; i++) {
SCOPED_TRACE(i);
RepeatMatch r = repeatHasMatch(info, ctrl, state, i);
if((i >= exit && i <= exit + range)||
(i >= exit2 && i <= exit2 + range) ||
(i >= exit3 && i <= exit3 + range)) {
ASSERT_EQ(REPEAT_MATCH, r);
} else if (i > exit3 + range) {
ASSERT_EQ(REPEAT_STALE, r);
} else {
ASSERT_EQ(REPEAT_NOMATCH, r);
}
}
ASSERT_EQ(MAX(exit + range + 1, exit2),
repeatNextMatch(info, ctrl, state, exit + range));
ASSERT_EQ(MAX(exit + range + 2, exit2),
repeatNextMatch(info, ctrl, state, exit + range + 1));
ASSERT_EQ(exit2, repeatNextMatch(info, ctrl, state, exit2 - 1));
ASSERT_EQ(MAX(exit2 + range + 1, exit3),
repeatNextMatch(info, ctrl, state, exit2 + range));
ASSERT_EQ(MAX(exit2 + range + 2, exit3),
repeatNextMatch(info, ctrl, state, exit2 + range + 1));
ASSERT_EQ(exit3, repeatNextMatch(info, ctrl, state, exit3 - 1));
ASSERT_EQ(0, repeatNextMatch(info, ctrl, state, exit3 + range));
}
static
void test_sparse3entryNeg(const RepeatInfo *info, RepeatControl *ctrl,
char *state, u64a diff) {
SCOPED_TRACE(testing::Message() << "Repeat:" << *info);
SCOPED_TRACE(diff);
if (diff * 2 > info->repeatMax || diff < info->minPeriod) {
return;
}
u64a offset = 1000;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(offset, repeatLastTop(info, ctrl, state));
u64a offset2 = 1000 + diff;
repeatStore(info, ctrl, state, offset2, 0);
ASSERT_EQ(offset2, repeatLastTop(info, ctrl, state));
u64a offset3 = 1000 + 2 * diff;
u64a exit3 = offset3 + info->repeatMin;
repeatStore(info, ctrl, state, offset3, 0);
ASSERT_EQ(offset3, repeatLastTop(info, ctrl, state));
u32 range = info->repeatMax - info->repeatMin;
for (u32 i = offset3; i < offset + info->repeatMax * 2 + 100; i++) {
SCOPED_TRACE(i);
RepeatMatch r = repeatHasMatch(info, ctrl, state, i);
if(i >= exit3 && i <= exit3 + range) {
ASSERT_EQ(REPEAT_MATCH, r);
} else if (i > exit3 + range) {
ASSERT_EQ(REPEAT_STALE, r);
} else {
ASSERT_EQ(REPEAT_NOMATCH, r);
}
}
}
static
void test_sparse3entryExpire(const RepeatInfo *info, RepeatControl *ctrl,
char *state, u64a diff) {
SCOPED_TRACE(testing::Message() << "Repeat:" << *info);
SCOPED_TRACE(diff);
if (diff * 2 > info->repeatMax || diff < info->minPeriod) {
return;
}
u64a offset = 1000;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(offset, repeatLastTop(info, ctrl, state));
u64a offset2 = 1000 + diff;
repeatStore(info, ctrl, state, offset2, 1);
ASSERT_EQ(offset2, repeatLastTop(info, ctrl, state));
u64a offset3 = 1000 + 2 * info->repeatMax;
u64a exit3 = offset3 + info->repeatMin;
repeatStore(info, ctrl, state, offset3, 1);
ASSERT_EQ(offset3, repeatLastTop(info, ctrl, state));
u32 range = info->repeatMax - info->repeatMin;
for (u32 i = offset3; i < offset3 + info->repeatMax + 100; i++) {
SCOPED_TRACE(i);
RepeatMatch r = repeatHasMatch(info, ctrl, state, i);
if(i >= exit3 && i <= exit3 + range) {
ASSERT_EQ(REPEAT_MATCH, r);
} else if (i > exit3 + range) {
ASSERT_EQ(REPEAT_STALE, r);
} else {
ASSERT_EQ(REPEAT_NOMATCH, r);
}
}
}
class SparseOptimalTest : public TestWithParam<tuple<u32, RepeatTestInfo> > {
protected:
virtual void SetUp() {
u32 period;
tie(period, test_info) = GetParam();
RepeatStateInfo rsi(REPEAT_SPARSE_OPTIMAL_P, test_info.repeatMin,
test_info.repeatMax, period);
ptr = new char[sizeof(RepeatInfo) +
sizeof(u64a) * (rsi.patchSize + 2)];
info = (struct RepeatInfo *)ptr;
info->type = REPEAT_SPARSE_OPTIMAL_P;
info->repeatMin = test_info.repeatMin;
info->repeatMax = test_info.repeatMax;
info->minPeriod = period;
info->packedCtrlSize = rsi.packedCtrlSize;
info->stateSize = rsi.stateSize;
info->horizon = rsi.horizon;
std::copy(rsi.packedFieldSizes.begin(), rsi.packedFieldSizes.end(),
info->packedFieldSizes);
info->patchCount = rsi.patchCount;
info->patchSize = rsi.patchSize;
info->encodingSize = rsi.encodingSize;
info->patchesOffset = rsi.patchesOffset;
u32 repeatMax = info->patchSize;
u64a *table = (u64a *)(ROUNDUP_PTR((ptr + sizeof(RepeatInfo)),
alignof(u64a)));
for (u32 i = 0; i < repeatMax + 1; i++) {
table[i] = rsi.table[i];
}
ctrl = new RepeatControl();
state_int = new char[info->stateSize + 7]; /* state may have mmbits */
state = state_int + 7;
}
virtual void TearDown() {
delete ctrl;
delete[] state_int;
delete[] ptr;
}
RepeatTestInfo test_info; // Test params
RepeatInfo *info; // Repeat info structure
RepeatControl *ctrl;
char *state;
private:
char *ptr;
char *state_int;
};
TEST_P(SparseOptimalTest, Simple1) {
SCOPED_TRACE(testing::Message() << "Repeat: " << *info);
u64a offset = 1000;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(1000U, repeatLastTop(info, ctrl, state));
for (u32 i = 0; i < info->repeatMax * 2 + 100; i++) {
SCOPED_TRACE(i);
RepeatMatch r = repeatHasMatch(info, ctrl, state, offset + i);
if (i >= info->repeatMin && i <= info->repeatMax) {
ASSERT_EQ(REPEAT_MATCH, r);
} else if (i > info->repeatMax) {
ASSERT_EQ(REPEAT_STALE, r);
} else {
ASSERT_EQ(REPEAT_NOMATCH, r);
}
}
u64a exp = 1000 + info->repeatMin;
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state, 1000));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state, 1001));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state, 1002));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state, 1003));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMin - 5));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMin - 4));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMin - 3));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMin - 2));
ASSERT_EQ(exp, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMin - 1));
ASSERT_EQ(0U, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMax));
ASSERT_EQ(0U, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMax + 1));
ASSERT_EQ(0U, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMax * 2 - 1));
ASSERT_EQ(0U, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMax * 2));
ASSERT_EQ(0U, repeatNextMatch(info, ctrl, state,
1000 + info->repeatMax * 2 + 1));
ASSERT_EQ(0U, repeatNextMatch(info, ctrl, state, 10000));
}
TEST_P(SparseOptimalTest, TwoTopsNeg) {
SCOPED_TRACE(testing::Message() << "Repeat: " << *info);
u32 patch_count = info->patchCount;
u32 patch_size = info->patchSize;
u64a offset = 1000;
u64a exit = offset + info->repeatMin;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(offset, repeatLastTop(info, ctrl, state));
u64a offset2 = 1000 + patch_count * patch_size;
u64a exit2 = offset2 + info->repeatMin;
repeatStore(info, ctrl, state, offset2, 1);
ASSERT_EQ(offset2, repeatLastTop(info, ctrl, state));
u32 range = info->repeatMax - info->repeatMin;
for (u32 i = offset2; i < offset + info->repeatMax * 2 + 100; i++) {
SCOPED_TRACE(i);
RepeatMatch r = repeatHasMatch(info, ctrl, state, i);
if (i >= exit2 && i <= exit2 + range) {
ASSERT_EQ(REPEAT_MATCH, r);
} else if (i > exit2 + range) {
ASSERT_EQ(REPEAT_STALE, r);
} else {
ASSERT_EQ(REPEAT_NOMATCH, r);
}
}
const struct RepeatRingControl *xs = (const struct RepeatRingControl *)
ctrl;
ASSERT_EQ(exit2, repeatNextMatch(info, ctrl, state,
MAX(xs->offset, exit)));
ASSERT_EQ(exit2, repeatNextMatch(info, ctrl, state,
MAX(xs->offset, exit + 1)));
ASSERT_EQ(exit2, repeatNextMatch(info, ctrl, state, exit2 - 1));
ASSERT_EQ(0, repeatNextMatch(info, ctrl, state, exit2 + range));
}
TEST_P(SparseOptimalTest, Simple2a) {
test_sparse2entry(info, ctrl, state, info->minPeriod);
}
TEST_P(SparseOptimalTest, Simple2b) {
test_sparse2entry(info, ctrl, state, info->repeatMax - 1);
}
TEST_P(SparseOptimalTest, Simple2c) {
test_sparse2entry(info, ctrl, state, info->repeatMax);
}
TEST_P(SparseOptimalTest, Simple2d) {
test_sparse2entry(info, ctrl, state, info->minPeriod + 1);
}
TEST_P(SparseOptimalTest, Simple2e) {
test_sparse2entry(info, ctrl, state, 2 * info->minPeriod - 1);
}
TEST_P(SparseOptimalTest, Simple3a) {
test_sparse3entry(info, ctrl, state, info->minPeriod);
test_sparse3entryNeg(info, ctrl, state, info->minPeriod);
test_sparse3entryExpire(info, ctrl, state, info->minPeriod);
}
TEST_P(SparseOptimalTest, Simple3b) {
test_sparse3entry(info, ctrl, state, info->repeatMax / 2 - 1);
test_sparse3entryNeg(info, ctrl, state, info->repeatMax / 2 - 1);
test_sparse3entryExpire(info, ctrl, state, info->repeatMax / 2 - 1);
}
TEST_P(SparseOptimalTest, Simple3c) {
test_sparse3entry(info, ctrl, state, info->repeatMax / 2);
test_sparse3entryNeg(info, ctrl, state, info->repeatMax / 2);
test_sparse3entryExpire(info, ctrl, state, info->repeatMax / 2);
}
TEST_P(SparseOptimalTest, Simple3d) {
test_sparse3entry(info, ctrl, state, info->minPeriod + 1);
test_sparse3entryNeg(info, ctrl, state, info->minPeriod + 1);
test_sparse3entryExpire(info, ctrl, state, info->minPeriod + 1);
}
TEST_P(SparseOptimalTest, Simple3e) {
test_sparse3entry(info, ctrl, state, 2 * info->minPeriod - 1);
test_sparse3entryNeg(info, ctrl, state, 2 * info->minPeriod - 1);
test_sparse3entryExpire(info, ctrl, state, 2 * info->minPeriod - 1);
}
TEST_P(SparseOptimalTest, ThreeTops) {
SCOPED_TRACE(testing::Message() << "Repeat: " << *info);
u32 patch_count = info->patchCount;
u32 patch_size = info->patchSize;
if (patch_count < 3) {
return;
}
u64a offset = 1000;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(offset, repeatLastTop(info, ctrl, state));
u64a offset2 = 1000 + 2 * patch_size - 1;
u64a exit2 = offset2 + info->repeatMin;
repeatStore(info, ctrl, state, offset2, 1);
ASSERT_EQ(offset2, repeatLastTop(info, ctrl, state));
u64a offset3 = 1000 + patch_count * patch_size;
u64a exit3 = offset3 + info->repeatMin;
repeatStore(info, ctrl, state, offset3, 1);
ASSERT_EQ(offset3, repeatLastTop(info, ctrl, state));
for (u32 i = offset2 + info->repeatMin;
i <= offset2 + info->repeatMax; i++) {
ASSERT_EQ(REPEAT_MATCH, repeatHasMatch(info, ctrl, state, i));
}
for (u32 i = offset2 + info->repeatMax + 1;
i < offset3 + info->repeatMin; i++) {
ASSERT_EQ(REPEAT_NOMATCH, repeatHasMatch(info, ctrl, state, i));
}
for (u32 i = offset3 + info->repeatMin;
i <= offset3 + info->repeatMax; i++) {
ASSERT_EQ(REPEAT_MATCH, repeatHasMatch(info, ctrl, state, i));
}
u32 range = info->repeatMax - info->repeatMin;
ASSERT_EQ(MAX(exit2 + range + 1, exit3),
repeatNextMatch(info, ctrl, state, exit2 + range));
ASSERT_EQ(MAX(exit2 + range + 2, exit3),
repeatNextMatch(info, ctrl, state, exit2 + range + 1));
ASSERT_EQ(exit3, repeatNextMatch(info, ctrl, state, exit3 - 1));
ASSERT_EQ(0, repeatNextMatch(info, ctrl, state, exit3 + range));
}
TEST_P(SparseOptimalTest, FillTops) {
SCOPED_TRACE(testing::Message() << "Repeat: " << *info);
u32 patch_count = info->patchCount;
u32 patch_size = info->patchSize;
u32 min_period = info->minPeriod;
u64a offset = 1000;
u64a exit = offset + info->repeatMin;
repeatStore(info, ctrl, state, offset, 0);
ASSERT_EQ(offset, repeatLastTop(info, ctrl, state));
u64a offset2;
for (u32 i = min_period; i < patch_count * patch_size; i += min_period) {
offset2 = offset + i;
repeatStore(info, ctrl, state, offset2, 1);
ASSERT_EQ(offset2, repeatLastTop(info, ctrl, state));
}
u64a exit2;
for (u32 i = 0; i < patch_count * patch_size; i += min_period) {
exit2 = exit + i;
for (u32 j = exit2 + info->repeatMin;
j <= offset + info->repeatMax; j++) {
ASSERT_EQ(REPEAT_MATCH, repeatHasMatch(info, ctrl, state, j));
}
}
}
INSTANTIATE_TEST_CASE_P(SparseOptimal, SparseOptimalTest,
Combine(ValuesIn(sparsePeriods),
ValuesIn(sparseRepeats)));

View File

@@ -0,0 +1,138 @@
/*
* 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 "gtest/gtest.h"
#include "nfagraph/ng_holder.h"
#include "rose/rose_build.h"
#include "rose/rose_build_impl.h"
#include "rose/rose_build_merge.h"
#include "util/report_manager.h"
#include "util/boundary_reports.h"
#include "util/compile_context.h"
#include "util/graph_range.h"
#include "util/make_unique.h"
#include "som/slot_manager.h"
using std::vector;
using namespace ue2;
static
std::unique_ptr<RoseBuild> constructBuilder(const Grey &grey) {
CompileContext cc(true, false, get_current_target(), grey);
ReportManager rm(cc.grey);
SomSlotManager ssm(8); // som precision
BoundaryReports boundary;
return makeRoseBuilder(rm, ssm, cc, boundary);
}
static
std::unique_ptr<NGHolder> makeSuffixGraph(ReportID report) {
auto h = ue2::make_unique<NGHolder>(NFA_SUFFIX);
NGHolder &g = *h;
NFAVertex v = add_vertex(g);
g[v].char_reach = CharReach('A', 'Z');
g[v].reports.insert(report);
add_edge(g.start, v, g);
add_edge(v, g.accept, g);
return h;
}
static
RoseVertex addVertex(RoseBuildImpl &build, RoseVertex parent, u32 lit_id) {
RoseGraph &g = build.g;
RoseVertex v = add_vertex(g);
g[v].idx = build.vertexIndex++;
g[v].min_offset = 0;
g[v].max_offset = ROSE_BOUND_INF;
g[v].literals.insert(lit_id);
RoseEdge e = add_edge(parent, v, g).first;
g[e].minBound = 0;
g[e].maxBound = ROSE_BOUND_INF;
g[e].history = ROSE_ROLE_HISTORY_NONE;
return v;
}
static
size_t numUniqueSuffixGraphs(const RoseGraph &g) {
ue2::unordered_set<const NGHolder *> seen;
for (const auto &v : vertices_range(g)) {
if (g[v].suffix) {
assert(g[v].suffix.graph);
seen.insert(g[v].suffix.graph.get());
}
}
return seen.size();
}
TEST(RoseMerge, uncalcLeaves_nonleaf) {
Grey grey;
auto build_base = constructBuilder(grey);
ASSERT_NE(nullptr, build_base);
RoseBuildImpl &build = static_cast<RoseBuildImpl &>(*build_base);
RoseGraph &g = build.g;
std::shared_ptr<NGHolder> shared_suffix = makeSuffixGraph(1000);
// foo with shared_suffix
RoseVertex v1 = addVertex(build, build.root,
build.getLiteralId(ue2_literal("foo", false), 0, ROSE_FLOATING));
g[v1].suffix.graph = shared_suffix;
// foo with shared_suffix -> bar
RoseVertex v2 = addVertex(build, build.root,
build.getLiteralId(ue2_literal("foo", false), 0, ROSE_FLOATING));
g[v2].suffix.graph = shared_suffix; // same as v1
RoseVertex v3 = addVertex(build, v2,
build.getLiteralId(ue2_literal("bar", false), 0, ROSE_FLOATING));
g[v3].reports.insert(1001);
// foo with a different suffix
RoseVertex v4 = addVertex(build, build.root,
build.getLiteralId(ue2_literal("foo", false), 0, ROSE_FLOATING));
g[v4].suffix.graph = makeSuffixGraph(2000);
ASSERT_EQ(6, num_vertices(g));
ASSERT_EQ(2, numUniqueSuffixGraphs(g));
// uncalcLeaves should leave this case alone. The suffixes on leaf nodes v1
// and v4 are not mergeable, as v1 shares its suffix with v2.
uncalcLeaves(build);
ASSERT_EQ(6, num_vertices(g));
ASSERT_EQ(2, numUniqueSuffixGraphs(g));
}

View File

@@ -0,0 +1,220 @@
/*
* 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 "gtest/gtest.h"
#include "nfa/vermicelli.h"
#define BOUND (~(VERM_BOUNDARY - 1))
TEST(RVermicelli, ExecNoMatch1) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
for (size_t j = 0; j < 16; j++) {
const u8 *begin = (const u8 *)t1 + i;
const u8 *end = (const u8 *)t1 + strlen(t1) - j;
const u8 *rv = rvermicelliExec('a', 0, begin, end);
ASSERT_EQ(begin - 1, rv);
rv = rvermicelliExec('B', 0, begin, end);
ASSERT_EQ(begin - 1, rv);
rv = rvermicelliExec('A', 1, begin, end);
ASSERT_EQ(begin - 1, rv);
}
}
}
TEST(RVermicelli, Exec1) {
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rvermicelliExec('a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 48, (size_t)rv);
rv = rvermicelliExec('A', 1, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 48, (size_t)rv);
}
}
TEST(RVermicelli, Exec2) {
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rvermicelliExec('a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 48, (size_t)rv);
rv = rvermicelliExec('A', 1, (u8 *)t1, (u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 48, (size_t)rv);
}
}
TEST(RVermicelli, Exec3) {
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbaaaaaaaaaaaaaaaaaaaaaaAbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rvermicelliExec('a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 47, (size_t)rv);
rv = rvermicelliExec('A', 1, (u8 *)t1, (u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 48, (size_t)rv);
}
}
TEST(RVermicelli, Exec4) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[16 + i] = 'a';
const u8 *rv = rvermicelliExec('a', 0, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[16 + i], (size_t)rv);
rv = rvermicelliExec('A', 1, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[16 + i], (size_t)rv);
}
}
TEST(RDoubleVermicelli, Exec1) {
char t1[] = "bbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rvermicelliDoubleExec('a', 'b', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 50, (size_t)rv);
rv = rvermicelliDoubleExec('A', 'B', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 50, (size_t)rv);
rv = rvermicelliDoubleExec('b', 'a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 49, (size_t)rv);
rv = rvermicelliDoubleExec('B', 'A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 49, (size_t)rv);
}
}
TEST(RDoubleVermicelli, Exec2) {
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbaaaaabbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rvermicelliDoubleExec('a', 'a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 52, (size_t)rv);
rv = rvermicelliDoubleExec('A', 'A', 1, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 52, (size_t)rv);
}
}
TEST(RDoubleVermicelli, Exec3) {
/* 012345678901234567890123 */
char t1[] = "bbbbbbbbbbbbbbbbbaAaaAAaaaaaaaaaaaaaaaaaabbbbbbbaaaaabbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rvermicelliDoubleExec('A', 'a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i );
ASSERT_EQ((size_t)t1 + 23, (size_t)rv);
rv = rvermicelliDoubleExec('A', 'A', 1, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 52, (size_t)rv);
rv = rvermicelliDoubleExec('A', 'A', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 22, (size_t)rv);
rv = rvermicelliDoubleExec('a', 'A', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)t1 + 21, (size_t)rv);
}
}
TEST(RDoubleVermicelli, Exec4) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[32 + i] = 'a';
t1[32 + i - 1] = 'a';
const u8 *rv = rvermicelliDoubleExec('a', 'a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[32 + i], (size_t)rv);
rv = rvermicelliDoubleExec('A', 'A', 1, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[32 + i], (size_t)rv);
}
}
TEST(RDoubleVermicelli, Exec5) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
for (size_t j = 1; j <= 16; j++) {
t1[strlen(t1) - i - j] = 'a';
const u8 *rv = rvermicelliDoubleExec('b', 'a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1) - i);
ASSERT_EQ((size_t)&t1[strlen(t1) - i - j], (size_t)rv);
rv = rvermicelliDoubleExec('B', 'A', 1, (u8 *)t1,
(u8 *)t1 + strlen(t1) -i );
ASSERT_EQ((size_t)&t1[strlen(t1) - i - j], (size_t)rv);
t1[strlen(t1) - i - j] = 'b';
}
}
}

194
unit/internal/shuffle.cpp Normal file
View File

@@ -0,0 +1,194 @@
/*
* 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 "gtest/gtest.h"
#include "util/simd_utils.h"
#include "util/shuffle.h"
#include "util/shuffle_ssse3.h"
namespace {
// Switch one bit on in a bitmask.
template<class Mask>
Mask setbit(unsigned int bit) {
union {
Mask simd;
char bytes[sizeof(Mask)];
} cf;
memset(cf.bytes, 0, sizeof(Mask));
cf.bytes[bit / 8] = 1U << (bit % 8);
return cf.simd;
}
TEST(Shuffle, ShuffleDynamic32_1) {
// Try all possible one-bit masks
for (unsigned int i = 0; i < 32; i++) {
// shuffle a single 1 bit to the front
u32 mask = 1U << i;
EXPECT_EQ(1U, shuffleDynamic32(mask, mask));
EXPECT_EQ(1U, shuffleDynamic32(~0U, mask));
// we should get zero out of these cases
EXPECT_EQ(0U, shuffleDynamic32(0, mask));
EXPECT_EQ(0U, shuffleDynamic32(~mask, mask));
// we should get zero out of all the other bit positions
for (unsigned int j = 0; (j != i && j < 32); j++) {
EXPECT_EQ(0U, shuffleDynamic32((1U << j), mask));
}
}
}
TEST(Shuffle, ShuffleDynamic32_2) {
// All 32 bits in mask are on
u32 mask = ~0U;
EXPECT_EQ(0U, shuffleDynamic32(0, mask));
EXPECT_EQ(mask, shuffleDynamic32(mask, mask));
for (unsigned int i = 0; i < 32; i++) {
EXPECT_EQ(1U << i, shuffleDynamic32(1U << i, mask));
}
}
TEST(Shuffle, ShuffleDynamic32_3) {
// Try setting every second bit
u32 mask = 0;
for (unsigned int i = 0; i < 32; i += 2) {
mask |= 1U << i;
}
// Test both cases (all even bits, all odd bits)
EXPECT_EQ((1U << 16) - 1, shuffleDynamic32(mask, mask));
EXPECT_EQ((1U << 16) - 1, shuffleDynamic32(~mask, ~mask));
EXPECT_EQ(0U, shuffleDynamic32(~mask, mask));
EXPECT_EQ(0U, shuffleDynamic32(mask, ~mask));
for (unsigned int i = 0; i < 32; i += 2) {
EXPECT_EQ(1U << (i/2), shuffleDynamic32(1U << i, mask));
EXPECT_EQ(0U, shuffleDynamic32(1U << i, ~mask));
EXPECT_EQ(1U << (i/2), shuffleDynamic32(1U << (i+1), ~mask));
EXPECT_EQ(0U, shuffleDynamic32(1U << (i+1), mask));
}
}
TEST(Shuffle, ShuffleDynamic64_1) {
// Try all possible one-bit masks
for (unsigned int i = 0; i < 64; i++) {
// shuffle a single 1 bit to the front
u64a mask = 1ULL << i;
EXPECT_EQ(1U, shuffleDynamic64(mask, mask));
EXPECT_EQ(1U, shuffleDynamic64(~0ULL, mask));
// we should get zero out of these cases
EXPECT_EQ(0U, shuffleDynamic64(0, mask));
EXPECT_EQ(0U, shuffleDynamic64(~mask, mask));
// we should get zero out of all the other bit positions
for (unsigned int j = 0; (j != i && j < 64); j++) {
EXPECT_EQ(0U, shuffleDynamic64((1ULL << j), mask));
}
}
}
TEST(Shuffle, ShuffleDynamic64_2) {
// Fill first half of mask
u64a mask = 0x00000000ffffffffULL;
EXPECT_EQ(0U, shuffleDynamic64(0, mask));
EXPECT_EQ(0xffffffffU, shuffleDynamic64(mask, mask));
for (unsigned int i = 0; i < 32; i++) {
EXPECT_EQ(1U << i, shuffleDynamic64(1ULL << i, mask));
}
// Fill second half of mask
mask = 0xffffffff00000000ULL;
EXPECT_EQ(0U, shuffleDynamic64(0, mask));
EXPECT_EQ(0xffffffffU, shuffleDynamic64(mask, mask));
for (unsigned int i = 32; i < 64; i++) {
EXPECT_EQ(1U << (i - 32), shuffleDynamic64(1ULL << i, mask));
}
// Try one in the middle
mask = 0x0000ffffffff0000ULL;
EXPECT_EQ(0U, shuffleDynamic64(0, mask));
EXPECT_EQ(0xffffffffU, shuffleDynamic64(mask, mask));
for (unsigned int i = 16; i < 48; i++) {
EXPECT_EQ(1U << (i - 16), shuffleDynamic64(1ULL << i, mask));
}
}
TEST(Shuffle, ShuffleDynamic64_3) {
// Try setting every second bit (note: 32 bits, the max we can shuffle)
u64a mask = 0;
for (unsigned int i = 0; i < 64; i += 2) {
mask |= 1ULL << i;
}
// Test both cases (all even bits, all odd bits)
EXPECT_EQ(0xffffffffU, shuffleDynamic64(mask, mask));
EXPECT_EQ(0xffffffffU, shuffleDynamic64(~mask, ~mask));
EXPECT_EQ(0U, shuffleDynamic64(~mask, mask));
EXPECT_EQ(0U, shuffleDynamic64(mask, ~mask));
for (unsigned int i = 0; i < 64; i += 2) {
EXPECT_EQ(1U << (i/2), shuffleDynamic64(1ULL << i, mask));
EXPECT_EQ(0U, shuffleDynamic64(1ULL << i, ~mask));
EXPECT_EQ(1U << (i/2), shuffleDynamic64(1ULL << (i+1), ~mask));
EXPECT_EQ(0U, shuffleDynamic64(1ULL << (i+1), mask));
}
}
static
void build_pshufb_masks_onebit(unsigned int bit, m128 *permute, m128 *compare) {
// permute mask has 0x80 in all bytes except the one we care about
memset(permute, 0x80, sizeof(*permute));
memset(compare, 0, sizeof(*compare));
char *pmsk = (char *)permute;
char *cmsk = (char *)compare;
pmsk[0] = bit/8;
cmsk[0] = ~(1 << (bit % 8));
}
TEST(Shuffle, ShufflePshufb128_1) {
// Try all possible one-bit masks
for (unsigned int i = 0; i < 128; i++) {
// shuffle a single 1 bit to the front
m128 permute, compare;
build_pshufb_masks_onebit(i, &permute, &compare);
EXPECT_EQ(1U, shufflePshufb128(setbit<m128>(i), permute, compare));
EXPECT_EQ(1U, shufflePshufb128(ones128(), permute, compare));
// we should get zero out of these cases
EXPECT_EQ(0U, shufflePshufb128(zeroes128(), permute, compare));
EXPECT_EQ(0U, shufflePshufb128(not128(setbit<m128>(i)), permute, compare));
// we should get zero out of all the other bit positions
for (unsigned int j = 0; (j != i && j < 128); j++) {
EXPECT_EQ(0U, shufflePshufb128(setbit<m128>(j), permute, compare));
}
}
}
} // namespace

977
unit/internal/shufti.cpp Normal file
View File

@@ -0,0 +1,977 @@
/*
* 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 <set>
#include "gtest/gtest.h"
#include "nfa/shufti.h"
#include "nfa/shufticompile.h"
#include "util/target_info.h"
using namespace ue2;
using std::set;
using std::pair;
using std::make_pair;
TEST(Shufti, BuildMask1) {
m128 lomask, himask;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lomask, &himask);
ASSERT_NE(-1, ret);
u8 *lo = (u8 *)&lomask;
u8 *hi = (u8 *)&himask;
for (int i = 0; i < 16; i++) {
if (i == 'a' % 16) {
ASSERT_EQ(1, lo[i]);
} else {
ASSERT_EQ(0, lo[i]);
}
if (i == 'a' >> 4) {
ASSERT_EQ(1, hi[i]);
} else {
ASSERT_EQ(0, hi[i]);
}
}
}
TEST(Shufti, BuildMask2) {
m128 lomask, himask;
CharReach chars;
chars.set('a');
chars.set('B');
int ret = shuftiBuildMasks(chars, &lomask, &himask);
ASSERT_NE(-1, ret);
u8 *lo = (u8 *)&lomask;
u8 *hi = (u8 *)&himask;
ASSERT_TRUE(lo['a' % 16] & hi['a' >> 4]);
ASSERT_TRUE(lo['B' % 16] & hi['B' >> 4]);
ASSERT_FALSE(lo['a' % 16] & hi['B' >> 4]);
ASSERT_FALSE(lo['B' % 16] & hi['a' >> 4]);
}
TEST(Shufti, BuildMask4) {
m128 lomask, himask;
CharReach chars;
chars.set('a');
chars.set('B');
chars.set('A');
chars.set('b');
int ret = shuftiBuildMasks(chars, &lomask, &himask);
ASSERT_NE(-1, ret);
u8 *lo = (u8 *)&lomask;
u8 *hi = (u8 *)&himask;
ASSERT_TRUE(lo['a' % 16] & hi['a' >> 4]);
ASSERT_TRUE(lo['A' % 16] & hi['A' >> 4]);
ASSERT_TRUE(lo['b' % 16] & hi['b' >> 4]);
ASSERT_TRUE(lo['B' % 16] & hi['B' >> 4]);
}
TEST(Shufti, ExecNoMatch1) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_LE(((size_t)t1 + strlen(t1)) & ~0xf, (size_t)rv);
}
}
TEST(Shufti, ExecNoMatch2) {
m128 lo, hi;
CharReach chars;
chars.set('a');
chars.set('B');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_LE(((size_t)t1 + strlen(t1)) & ~0xf, (size_t)rv);
}
}
TEST(Shufti, ExecNoMatch3) {
m128 lo, hi;
CharReach chars;
chars.set('V'); /* V = 0x56, e = 0x65 */
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_LE(((size_t)t1 + strlen(t1)) & ~0xf, (size_t)rv);
}
}
TEST(Shufti, ExecMatch1) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Shufti, ExecMatch2) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Shufti, ExecMatch3) {
m128 lo, hi;
CharReach chars;
chars.set('a');
chars.set('B');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbBaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Shufti, ExecMatch4) {
m128 lo, hi;
CharReach chars;
chars.set('a');
chars.set('C');
chars.set('A');
chars.set('c');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbAaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t2[] = "bbbbbbbbbbbbbbbbbCaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t3[] = "bbbbbbbbbbbbbbbbbcaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t4[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = shuftiExec(lo, hi, (u8 *)t2 + i, (u8 *)t2 + strlen(t1));
ASSERT_EQ((size_t)t2 + 17, (size_t)rv);
rv = shuftiExec(lo, hi, (u8 *)t3 + i, (u8 *)t3 + strlen(t3));
ASSERT_EQ((size_t)t3 + 17, (size_t)rv);
rv = shuftiExec(lo, hi, (u8 *)t4 + i, (u8 *)t4 + strlen(t4));
ASSERT_EQ((size_t)t4 + 17, (size_t)rv);
}
}
TEST(Shufti, ExecMatch5) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[48 - i] = 'a';
const u8 *rv = shuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
}
TEST(DoubleShufti, BuildMask1) {
m128 lo1m, hi1m, lo2m, hi2m;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a', 'B'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1m, &hi1m, &lo2m, &hi2m);
u8 *lo1 = (u8 *)&lo1m;
u8 *lo2 = (u8 *)&lo2m;
u8 *hi1 = (u8 *)&hi1m;
u8 *hi2 = (u8 *)&hi2m;
for (int i = 0; i < 16; i++) {
if (i == 'a' % 16) {
ASSERT_EQ(254, lo1[i]);
} else {
ASSERT_EQ(255, lo1[i]);
}
if (i == 'a' >> 4) {
ASSERT_EQ(254, hi1[i]);
} else {
ASSERT_EQ(255, hi1[i]);
}
if (i == 'B' % 16) {
ASSERT_EQ(254, lo2[i]);
} else {
ASSERT_EQ(255, lo2[i]);
}
if (i == 'B' >> 4) {
ASSERT_EQ(254, hi2[i]);
} else {
ASSERT_EQ(255, hi2[i]);
}
}
}
TEST(DoubleShufti, BuildMask2) {
m128 lo1m, hi1m, lo2m, hi2m;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','z'));
lits.insert(make_pair('B','z'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1m, &hi1m, &lo2m, &hi2m);
u8 *lo1 = (u8 *)&lo1m;
u8 *lo2 = (u8 *)&lo2m;
u8 *hi1 = (u8 *)&hi1m;
u8 *hi2 = (u8 *)&hi2m;
ASSERT_NE(0xff,
lo1['a' % 16] | hi1['a' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
ASSERT_NE(0xff,
lo1['B' % 16] | hi1['B' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
ASSERT_EQ(0xff,
lo1['a' % 16] | hi1['B' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
ASSERT_EQ(0xff,
lo1['B' % 16] | hi1['a' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
}
TEST(DoubleShufti, BuildMask4) {
m128 lo1m, hi1m, lo2m, hi2m;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','z'));
lits.insert(make_pair('B','z'));
lits.insert(make_pair('A','z'));
lits.insert(make_pair('b','z'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1m, &hi1m, &lo2m, &hi2m);
u8 *lo1 = (u8 *)&lo1m;
u8 *lo2 = (u8 *)&lo2m;
u8 *hi1 = (u8 *)&hi1m;
u8 *hi2 = (u8 *)&hi2m;
ASSERT_NE(0xff,
lo1['a' % 16] | hi1['a' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
ASSERT_NE(0xff,
lo1['A' % 16] | hi1['A' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
ASSERT_NE(0xff,
lo1['b' % 16] | hi1['b' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
ASSERT_NE(0xff,
lo1['B' % 16] | hi1['B' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
}
TEST(DoubleShufti, BuildMask5) {
m128 lo1m, hi1m;
m128 lo2m, hi2m;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','z'));
CharReach bytes;
bytes.set('X');
shuftiBuildDoubleMasks(bytes, lits, &lo1m, &hi1m, &lo2m, &hi2m);
u8 *lo1 = (u8 *)&lo1m;
u8 *lo2 = (u8 *)&lo2m;
u8 *hi1 = (u8 *)&hi1m;
u8 *hi2 = (u8 *)&hi2m;
ASSERT_NE(0xff,
lo1['a' % 16] | hi1['a' >> 4] | lo2['z' % 16] | hi2['z' >> 4]);
ASSERT_EQ(0xff,
lo1['a' % 16] | hi1['a' >> 4] | lo2['X' % 16] | hi2['X' >> 4]);
ASSERT_EQ(0xff,
lo1['A' % 16] | hi1['A' >> 4] | lo2['X' % 16] | hi2['X' >> 4]);
ASSERT_EQ(0xff,
lo1['b' % 16] | hi1['b' >> 4] | lo2['X' % 16] | hi2['X' >> 4]);
ASSERT_EQ(0xff,
lo1['B' % 16] | hi1['B' >> 4] | lo2['X' % 16] | hi2['X' >> 4]);
}
TEST(DoubleShufti, ExecNoMatch1) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','b'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_LE(((size_t)t1 + strlen(t1)) & ~0xf, (size_t)rv);
}
}
TEST(DoubleShufti, ExecNoMatch1b) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('b','a'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + i + 15, (size_t)rv);
}
}
TEST(DoubleShufti, ExecNoMatch2) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','b'));
lits.insert(make_pair('B','b'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_LE(((size_t)t1 + strlen(t1)) & ~0xf, (size_t)rv);
}
}
TEST(DoubleShufti, ExecNoMatch2b) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('b','a'));
lits.insert(make_pair('b','B'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + i + 15, (size_t)rv);
}
}
TEST(DoubleShufti, ExecNoMatch3) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('V','e'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
char t1[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_LE(((size_t)t1 + strlen(t1)) & ~0xf, (size_t)rv);
}
}
TEST(DoubleShufti, ExecNoMatch3b) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('e','V'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
char t1[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + i + 15, (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatch1) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','b'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatch2) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','a'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatch3) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('B','a'));
lits.insert(make_pair('a','a'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbBaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatch4) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('A','a'));
lits.insert(make_pair('a','a'));
lits.insert(make_pair('C','a'));
lits.insert(make_pair('c','a'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbAaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t2[] = "bbbbbbbbbbbbbbbbbCaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t3[] = "bbbbbbbbbbbbbbbbbcaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t4[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t2 + i, (u8 *)t2 + strlen(t2));
ASSERT_EQ((size_t)t2 + 17, (size_t)rv);
rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t3+ i, (u8 *)t3 + strlen(t3));
ASSERT_EQ((size_t)t3 + 17, (size_t)rv);
rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t4 + i, (u8 *)t4 + strlen(t4));
ASSERT_EQ((size_t)t4 + 17, (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatch4b) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','A'));
lits.insert(make_pair('a','a'));
lits.insert(make_pair('a','C'));
lits.insert(make_pair('a','c'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaAaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t2[] = "bbbbbbbbbbbbbbbbbaCaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t3[] = "bbbbbbbbbbbbbbbbbacaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t4[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t2 + i, (u8 *)t2 + strlen(t2));
ASSERT_EQ((size_t)t2 + 17, (size_t)rv);
rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t3+ i, (u8 *)t3 + strlen(t3));
ASSERT_EQ((size_t)t3 + 17, (size_t)rv);
rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t4 + i, (u8 *)t4 + strlen(t4));
ASSERT_EQ((size_t)t4 + 17, (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatch5) {
m128 lo1, hi1, lo2, hi2;
flat_set<pair<u8, u8>> lits;
lits.insert(make_pair('a','A'));
shuftiBuildDoubleMasks(CharReach(), lits, &lo1, &hi1, &lo2, &hi2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[48 - i] = 'a';
t1[48 - i + 1] = 'A';
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatchMixed1) {
m128 lo1, hi1, lo2, hi2;
CharReach onebyte;
flat_set<pair<u8, u8>> twobyte;
// just one one-byte literal
onebyte.set('a');
shuftiBuildDoubleMasks(onebyte, twobyte, &lo1, &hi1, &lo2, &hi2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[48 - i] = 'a';
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatchMixed2) {
m128 lo1, hi1, lo2, hi2;
CharReach onebyte;
flat_set<pair<u8, u8>> twobyte;
// one of each
onebyte.set('a');
twobyte.insert(make_pair('x', 'y'));
shuftiBuildDoubleMasks(onebyte, twobyte, &lo1, &hi1, &lo2, &hi2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char t2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[48 - i] = 'a';
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
for (size_t i = 0; i < 31; i++) {
t2[48 - i] = 'x';
t2[48 - i + 1] = 'y';
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t2, (u8 *)t2 + strlen(t1));
ASSERT_EQ((size_t)&t2[48 - i], (size_t)rv);
}
}
TEST(DoubleShufti, ExecMatchMixed3) {
m128 lo1, hi1, lo2, hi2;
CharReach onebyte;
flat_set<pair<u8, u8>> twobyte;
// one of each
onebyte.set('a');
twobyte.insert(make_pair('x', 'y'));
shuftiBuildDoubleMasks(onebyte, twobyte, &lo1, &hi1, &lo2, &hi2);
const int len = 420;
char t1[len + 1];
char t2[len + 2];
memset(t1, 'b', len);
memset(t2, 'b', len);
for (size_t i = 0; i < 400; i++) {
t1[len - i] = 'a';
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t1, (u8 *)t1 + len);
ASSERT_EQ((size_t)&t1[len - i], (size_t)rv);
}
for (size_t i = 0; i < 400; i++) {
t2[len - i] = 'x';
t2[len - i + 1] = 'y';
const u8 *rv = shuftiDoubleExec(lo1, hi1, lo2, hi2,
(u8 *)t2, (u8 *)t2 + len);
ASSERT_EQ((size_t)&t2[len - i], (size_t)rv);
}
}
TEST(ReverseShufti, ExecNoMatch1) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_EQ((const u8 *)(t1 - 1), rv);
}
}
TEST(ReverseShufti, ExecNoMatch2) {
m128 lo, hi;
CharReach chars;
chars.set('a');
chars.set('B');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_EQ((const u8 *)(t1 - 1), rv);
}
}
TEST(ReverseShufti, ExecNoMatch3) {
m128 lo, hi;
CharReach chars;
chars.set('V'); /* V = 0x56, e = 0x65 */
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_EQ((const u8 *)(t1 - 1), rv);
}
}
TEST(ReverseShufti, ExecMatch1) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbabbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 17, rv);
}
}
TEST(ReverseShufti, ExecMatch2) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbabbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 32, rv);
}
}
TEST(ReverseShufti, ExecMatch3) {
m128 lo, hi;
CharReach chars;
chars.set('a');
chars.set('B');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaBbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('B', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 32, rv);
}
// check that we match the 'a' bytes as well.
ASSERT_EQ('B', t1[32]);
t1[32] = 'b';
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 31, rv);
}
}
TEST(ReverseShufti, ExecMatch4) {
m128 lo, hi;
CharReach chars;
chars.set('a');
chars.set('C');
chars.set('A');
chars.set('c');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaAbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char t2[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaCbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char t3[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaacbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char t4[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len - i);
EXPECT_EQ('A', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 32, rv);
rv = rshuftiExec(lo, hi, (u8 *)t2, (u8 *)t2 + len - i);
EXPECT_EQ('C', (char)*rv);
ASSERT_EQ((const u8 *)t2 + 32, rv);
rv = rshuftiExec(lo, hi, (u8 *)t3, (u8 *)t3 + len - i);
EXPECT_EQ('c', (char)*rv);
ASSERT_EQ((const u8 *)t3 + 32, rv);
rv = rshuftiExec(lo, hi, (u8 *)t4, (u8 *)t4 + len - i);
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t4 + 32, rv);
}
}
TEST(ReverseShufti, ExecMatch5) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < len; i++) {
t1[i] = 'a';
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len);
ASSERT_EQ((const u8 *)t1 + i, rv);
}
}
TEST(ReverseShufti, ExecMatch6) {
m128 lo, hi;
CharReach chars;
chars.set('a');
int ret = shuftiBuildMasks(chars, &lo, &hi);
ASSERT_NE(-1, ret);
const size_t len = 256;
char t1[len];
memset(t1, 'b', len);
for (size_t i = 0; i < len; i++) {
t1[i] = 'a';
const u8 *rv = rshuftiExec(lo, hi, (u8 *)t1, (u8 *)t1 + len);
ASSERT_EQ((const u8 *)t1 + i, rv);
}
}

319
unit/internal/sidecar.cpp Normal file
View File

@@ -0,0 +1,319 @@
/*
* 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 "ue2common.h"
#include "sidecar/sidecar.h"
#include "sidecar/sidecar_compile.h"
#include "sidecar/sidecar_internal.h"
#include "util/alloc.h"
#include "util/charreach.h"
#include <tuple>
#include <vector>
#include "gtest/gtest.h"
using namespace testing;
using namespace ue2;
using std::vector;
using std::set;
using std::tie;
using std::tuple;
namespace {
void ns_cb(UNUSED u64a offset, u32 id, void *ctxt) {
u32 *seen = (u32 *)ctxt;
*seen |= 1U << id;
}
void set_cb(UNUSED u64a offset, u32 id, void *ctxt) {
set<u32> *seen = (set<u32> *)ctxt;
seen->insert(id);
}
TEST(Sidecar, ns1) {
const size_t data_len = 1024;
u8 data[data_len];
CharReach c_1;
c_1.set('f');
vector<CharReach> charclasses;
charclasses.push_back(c_1);
auto ns = sidecarCompile(charclasses);
ASSERT_TRUE(ns != nullptr);
ASSERT_LT(0U, sidecarSize(ns.get()));
struct sidecar_enabled *enabled
= (struct sidecar_enabled *)aligned_zmalloc(sidecarEnabledSize(ns.get()));
ASSERT_TRUE(enabled);
sidecarEnabledInit(ns.get(), enabled);
struct sidecar_scratch *scratch
= (struct sidecar_scratch *)aligned_zmalloc(sidecarScratchSize(ns.get()));
for (u32 i = 0; i < 256; i++) {
SCOPED_TRACE(i);
u32 seen = 0;
memset(data, i, data_len);
sidecarExec(ns.get(), data, data_len, enabled, scratch, 0, ns_cb, &seen);
ASSERT_EQ(0U, seen);
}
sidecarEnabledAdd(ns.get(), enabled, 0);
for (u32 i = 0; i < 256; i++) {
SCOPED_TRACE(i);
u32 seen = 0;
memset(data, i, data_len);
sidecarExec(ns.get(), data, data_len, enabled, scratch, 0, ns_cb, &seen);
if (i == 'f') {
ASSERT_EQ(1U, seen);
} else {
ASSERT_EQ(0U, seen);
}
}
aligned_free(enabled);
aligned_free(scratch);
}
const char* sidecarStrings[] = {
"f",
"a",
"A",
"ab",
"\r\n", // an old favourite
"\t\r\n",
" \r\n",
"xyz",
"z0y1",
"01234567", // 8 elements
"!@#$%^&*()", // 10 elements
"qwertyuiopasdfgh", // 16 elements
"qwertyuiopasdfghj", // 17 elements
"qwertyuiopasdfghjklzxcvb", // 24 elements
"qwertyuiopasdfghjklzxcvbnm012345", // 32 elements
"qwertyuiopasdfghjklzxcvbnm0123456" // 33 elements
};
const u32 sidecarModels[] = {
SIDECAR_8,
SIDECAR_32,
SIDECAR_64,
SIDECAR_128,
SIDECAR_256,
SIDECAR_N,
SIDECAR_S
};
// Number of elements we can handle in each model
const u32 sidecarSizes[] = {
8,
32,
64,
128,
256,
1,
8
};
// Parameterized test case for string of single-byte classes
class SidecarTest : public TestWithParam<tuple<u32, const char *>> {
protected:
virtual void SetUp() {
tie(model, chars) = GetParam();
size_t num = strlen(chars);
charclasses.resize(num);
for (size_t i = 0; i < num; i++) {
charclasses[i].set(chars[i]);
}
}
virtual bool fitsModel() {
for (size_t i = 0; i < ARRAY_LENGTH(sidecarModels); i++) {
if (sidecarModels[i] == model) {
return charclasses.size() <= sidecarSizes[i];
}
}
return false;
}
u32 model;
const char *chars;
vector<CharReach> charclasses;
};
TEST_P(SidecarTest, Individual) {
SCOPED_TRACE(chars);
// Skip this test if the model is too small
if (!fitsModel()) {
return;
}
auto ns = sidecarCompile(charclasses, model);
if (!ns && model == SIDECAR_S) { /* shufti is fussi */
return;
}
ASSERT_TRUE(ns != nullptr);
ASSERT_LT(0U, sidecarSize(ns.get()));
struct sidecar_enabled *enabled
= (struct sidecar_enabled *)aligned_zmalloc(sidecarEnabledSize(ns.get()));
ASSERT_TRUE(enabled);
sidecarEnabledInit(ns.get(), enabled);
struct sidecar_enabled *local_enabled
= (struct sidecar_enabled *)aligned_zmalloc(sidecarEnabledSize(ns.get()));
struct sidecar_scratch *scratch
= (struct sidecar_scratch *)aligned_zmalloc(sidecarScratchSize(ns.get()));
const size_t data_len = 1024;
u8 data[data_len];
// with nothing enabled, nothing should fire
for (u32 i = 0; i < 256; i++) {
SCOPED_TRACE(i);
memset(data, i, data_len);
set<u32> seen;
sidecarExec(ns.get(), data, data_len, enabled, scratch, 0, set_cb, &seen);
ASSERT_TRUE(seen.empty());
}
// test that every char class fires when enabled separately
for (u32 j = 0; j < charclasses.size(); j++) {
u32 c = chars[j];
SCOPED_TRACE(c);
// build a "compile time" enabled structure and add class j to it.
sidecarEnabledInit(ns.get(), local_enabled);
sidecarEnabledAdd(ns.get(), local_enabled, j);
// union class j into our runtime enabled structure.
sidecarEnabledUnion(ns.get(), enabled, local_enabled);
for (u32 i = 0; i < 256; i++) {
SCOPED_TRACE(i);
memset(data, i, data_len);
set<u32> seen;
sidecarExec(ns.get(), data, data_len, enabled, scratch, 0, set_cb, &seen);
if (i == c) {
ASSERT_EQ(1U, seen.size());
ASSERT_EQ(j, *seen.begin());
} else {
ASSERT_TRUE(seen.empty());
}
}
}
aligned_free(local_enabled);
aligned_free(enabled);
aligned_free(scratch);
}
TEST_P(SidecarTest, Together) {
SCOPED_TRACE(chars);
// Skip this test if the model is too small
if (!fitsModel()) {
return;
}
auto ns = sidecarCompile(charclasses, model);
if (!ns && model == SIDECAR_S) { /* shufti is fussi */
return;
}
ASSERT_TRUE(ns != nullptr);
ASSERT_LT(0U, sidecarSize(ns.get()));
struct sidecar_enabled *enabled
= (struct sidecar_enabled *)aligned_zmalloc(sidecarEnabledSize(ns.get()));
ASSERT_TRUE(enabled);
struct sidecar_enabled *local_enabled
= (struct sidecar_enabled *)aligned_zmalloc(sidecarEnabledSize(ns.get()));
struct sidecar_scratch *scratch
= (struct sidecar_scratch *)aligned_zmalloc(sidecarScratchSize(ns.get()));
const size_t data_len = 1024;
u8 data[data_len];
// with nothing enabled, nothing should fire
for (u32 i = 0; i < 256; i++) {
SCOPED_TRACE(i);
memset(data, i, data_len);
set<u32> seen;
sidecarExec(ns.get(), data, data_len, enabled, scratch, 0, set_cb, &seen);
ASSERT_TRUE(seen.empty());
}
// test that every char class fires
for (u32 j = 0; j < charclasses.size(); j++) {
// enable the whole lot
sidecarEnabledInit(ns.get(), enabled);
for (u32 i = 0; i < charclasses.size(); i++) {
// build a "compile time" enabled structure and add class j to it.
sidecarEnabledInit(ns.get(), local_enabled);
sidecarEnabledAdd(ns.get(), local_enabled, i);
// union class j into our runtime enabled structure.
sidecarEnabledUnion(ns.get(), enabled, local_enabled);
}
u32 c = chars[j];
SCOPED_TRACE(c);
for (u32 i = 0; i < 256; i++) {
SCOPED_TRACE(i);
memset(data, i, data_len);
set<u32> seen;
sidecarExec(ns.get(), data, data_len, enabled, scratch, 0, set_cb, &seen);
if (i == c) {
// seen should contain only `c'
ASSERT_EQ(1U, seen.size());
ASSERT_FALSE(seen.end() == seen.find(j));
} else {
// seen should not contain `c', and either zero or one char can
// have matched
ASSERT_GT(2U, seen.size());
ASSERT_TRUE(seen.end() == seen.find(j));
}
}
}
aligned_free(local_enabled);
aligned_free(enabled);
aligned_free(scratch);
}
INSTANTIATE_TEST_CASE_P(Sidecar, SidecarTest,
Combine(ValuesIn(sidecarModels),
ValuesIn(sidecarStrings)));
}

View File

@@ -0,0 +1,643 @@
/*
* 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 "gtest/gtest.h"
#include "util/alloc.h"
#include "util/make_unique.h"
#include "util/simd_utils.h"
using namespace std;
using namespace ue2;
namespace {
// Switch one bit on in a bitmask.
template<class Mask>
Mask setbit(unsigned int bit) {
union {
Mask simd;
char bytes[sizeof(Mask)];
} cf;
memset(cf.bytes, 0, sizeof(Mask));
unsigned int byte_idx = bit / 8;
cf.bytes[byte_idx] = 1U << (bit % 8);
return cf.simd;
}
// Parameterized tests follow!
//
// Irritatingly we have to define a whole bunch of overrides here... because
// templates. One Admiration Unit for anyone able to build a better way of
// doing this.
struct simd_zeroes {
operator m128() { return zeroes128(); }
operator m256() { return zeroes256(); }
operator m384() { return zeroes384(); }
operator m512() { return zeroes512(); }
};
struct simd_ones {
operator m128() { return ones128(); }
operator m256() { return ones256(); }
operator m384() { return ones384(); }
operator m512() { return ones512(); }
};
bool simd_diff(const m128 &a, const m128 &b) { return !!diff128(a, b); }
bool simd_diff(const m256 &a, const m256 &b) { return !!diff256(a, b); }
bool simd_diff(const m384 &a, const m384 &b) { return !!diff384(a, b); }
bool simd_diff(const m512 &a, const m512 &b) { return !!diff512(a, b); }
bool simd_isnonzero(const m128 &a) { return !!isnonzero128(a); }
bool simd_isnonzero(const m256 &a) { return !!isnonzero256(a); }
bool simd_isnonzero(const m384 &a) { return !!isnonzero384(a); }
bool simd_isnonzero(const m512 &a) { return !!isnonzero512(a); }
m128 simd_and(const m128 &a, const m128 &b) { return and128(a, b); }
m256 simd_and(const m256 &a, const m256 &b) { return and256(a, b); }
m384 simd_and(const m384 &a, const m384 &b) { return and384(a, b); }
m512 simd_and(const m512 &a, const m512 &b) { return and512(a, b); }
m128 simd_or(const m128 &a, const m128 &b) { return or128(a, b); }
m256 simd_or(const m256 &a, const m256 &b) { return or256(a, b); }
m384 simd_or(const m384 &a, const m384 &b) { return or384(a, b); }
m512 simd_or(const m512 &a, const m512 &b) { return or512(a, b); }
m128 simd_xor(const m128 &a, const m128 &b) { return xor128(a, b); }
m256 simd_xor(const m256 &a, const m256 &b) { return xor256(a, b); }
m384 simd_xor(const m384 &a, const m384 &b) { return xor384(a, b); }
m512 simd_xor(const m512 &a, const m512 &b) { return xor512(a, b); }
m128 simd_andnot(const m128 &a, const m128 &b) { return andnot128(a, b); }
m256 simd_andnot(const m256 &a, const m256 &b) { return andnot256(a, b); }
m384 simd_andnot(const m384 &a, const m384 &b) { return andnot384(a, b); }
m512 simd_andnot(const m512 &a, const m512 &b) { return andnot512(a, b); }
m128 simd_not(const m128 &a) { return not128(a); }
m256 simd_not(const m256 &a) { return not256(a); }
m384 simd_not(const m384 &a) { return not384(a); }
m512 simd_not(const m512 &a) { return not512(a); }
void simd_clearbit(m128 *a, unsigned int i) { return clearbit128(a, i); }
void simd_clearbit(m256 *a, unsigned int i) { return clearbit256(a, i); }
void simd_clearbit(m384 *a, unsigned int i) { return clearbit384(a, i); }
void simd_clearbit(m512 *a, unsigned int i) { return clearbit512(a, i); }
void simd_setbit(m128 *a, unsigned int i) { return setbit128(a, i); }
void simd_setbit(m256 *a, unsigned int i) { return setbit256(a, i); }
void simd_setbit(m384 *a, unsigned int i) { return setbit384(a, i); }
void simd_setbit(m512 *a, unsigned int i) { return setbit512(a, i); }
bool simd_testbit(const m128 *a, unsigned int i) { return testbit128(a, i); }
bool simd_testbit(const m256 *a, unsigned int i) { return testbit256(a, i); }
bool simd_testbit(const m384 *a, unsigned int i) { return testbit384(a, i); }
bool simd_testbit(const m512 *a, unsigned int i) { return testbit512(a, i); }
u32 simd_diffrich(const m128 &a, const m128 &b) { return diffrich128(a, b); }
u32 simd_diffrich(const m256 &a, const m256 &b) { return diffrich256(a, b); }
u32 simd_diffrich(const m384 &a, const m384 &b) { return diffrich384(a, b); }
u32 simd_diffrich(const m512 &a, const m512 &b) { return diffrich512(a, b); }
u32 simd_diffrich64(const m128 &a, const m128 &b) { return diffrich64_128(a, b); }
u32 simd_diffrich64(const m256 &a, const m256 &b) { return diffrich64_256(a, b); }
u32 simd_diffrich64(const m384 &a, const m384 &b) { return diffrich64_384(a, b); }
u32 simd_diffrich64(const m512 &a, const m512 &b) { return diffrich64_512(a, b); }
void simd_store(void *ptr, const m128 &a) { store128(ptr, a); }
void simd_store(void *ptr, const m256 &a) { store256(ptr, a); }
void simd_store(void *ptr, const m384 &a) { store384(ptr, a); }
void simd_store(void *ptr, const m512 &a) { store512(ptr, a); }
void simd_load(m128 *a, const void *ptr) { *a = load128(ptr); }
void simd_load(m256 *a, const void *ptr) { *a = load256(ptr); }
void simd_load(m384 *a, const void *ptr) { *a = load384(ptr); }
void simd_load(m512 *a, const void *ptr) { *a = load512(ptr); }
void simd_loadu(m128 *a, const void *ptr) { *a = loadu128(ptr); }
void simd_loadu(m256 *a, const void *ptr) { *a = loadu256(ptr); }
void simd_loadu(m384 *a, const void *ptr) { *a = loadu384(ptr); }
void simd_loadu(m512 *a, const void *ptr) { *a = loadu512(ptr); }
void simd_storebytes(void *ptr, const m128 &a, unsigned i) { storebytes128(ptr, a, i); }
void simd_storebytes(void *ptr, const m256 &a, unsigned i) { storebytes256(ptr, a, i); }
void simd_storebytes(void *ptr, const m384 &a, unsigned i) { storebytes384(ptr, a, i); }
void simd_storebytes(void *ptr, const m512 &a, unsigned i) { storebytes512(ptr, a, i); }
void simd_loadbytes(m128 *a, const void *ptr, unsigned i) { *a = loadbytes128(ptr, i); }
void simd_loadbytes(m256 *a, const void *ptr, unsigned i) { *a = loadbytes256(ptr, i); }
void simd_loadbytes(m384 *a, const void *ptr, unsigned i) { *a = loadbytes384(ptr, i); }
void simd_loadbytes(m512 *a, const void *ptr, unsigned i) { *a = loadbytes512(ptr, i); }
template<typename T>
class SimdUtilsTest : public testing::Test {
// empty
};
typedef ::testing::Types<m128, m256, m384, m512> SimdTypes;
TYPED_TEST_CASE(SimdUtilsTest, SimdTypes);
//
// The tests themselves.
//
TYPED_TEST(SimdUtilsTest, zero) {
const TypeParam zeroes = simd_zeroes();
// Should have no bits on.
char cmp[sizeof(zeroes)];
memset(cmp, 0, sizeof(zeroes));
ASSERT_EQ(0, memcmp(cmp, &zeroes, sizeof(zeroes)));
}
TYPED_TEST(SimdUtilsTest, ones) {
const TypeParam ones = simd_ones();
// Should have all bits on.
char cmp[sizeof(ones)];
memset(cmp, 0xff, sizeof(ones));
ASSERT_EQ(0, memcmp(cmp, &ones, sizeof(ones)));
}
TYPED_TEST(SimdUtilsTest, and1) {
const TypeParam zeroes = simd_zeroes();
const TypeParam ones = simd_ones();
TypeParam result;
result = simd_and(zeroes, ones);
EXPECT_FALSE(simd_diff(result, zeroes));
result = simd_and(ones, zeroes);
EXPECT_FALSE(simd_diff(result, zeroes));
result = simd_and(zeroes, zeroes);
EXPECT_FALSE(simd_diff(result, zeroes));
result = simd_and(ones, ones);
EXPECT_FALSE(simd_diff(result, ones));
}
TYPED_TEST(SimdUtilsTest, and2) {
TypeParam a, b;
memset(&a, 0x33, sizeof(a));
memset(&b, 0x55, sizeof(b));
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} c;
c.simd = simd_and(a, b);
const char expected = 0x33 & 0x55;
for (size_t i = 0; i < sizeof(c); i++) {
EXPECT_EQ(expected, c.bytes[i]);
}
}
TEST(SimdUtils, diff256) {
const unsigned total_bits = 256;
// Test identical cases
ASSERT_EQ(0U, diff256(zeroes256(), zeroes256()));
ASSERT_EQ(0U, diff256(ones256(), ones256()));
for (unsigned i = 0; i < total_bits; i++) {
m256 a = setbit<m256>(i);
m256 b = setbit<m256>(i);
ASSERT_EQ(0U, diff256(a, b));
}
// Cases that differ in one 32-bit word
for (unsigned i = 0; i < total_bits; i++) {
m256 a = setbit<m256>(i);
u32 rv = diff256(zeroes256(), a);
ASSERT_EQ(1U, rv);
}
}
TYPED_TEST(SimdUtilsTest, or1) {
const TypeParam zeroes = simd_zeroes();
const TypeParam ones = simd_ones();
TypeParam result;
result = simd_or(zeroes, ones);
EXPECT_FALSE(simd_diff(result, ones));
result = simd_or(ones, zeroes);
EXPECT_FALSE(simd_diff(result, ones));
result = simd_or(zeroes, zeroes);
EXPECT_FALSE(simd_diff(result, zeroes));
result = simd_or(ones, ones);
EXPECT_FALSE(simd_diff(result, ones));
}
TYPED_TEST(SimdUtilsTest, or2) {
TypeParam a, b;
memset(&a, 0x33, sizeof(a));
memset(&b, 0x55, sizeof(b));
for (unsigned j = 0; j < 8; j++) {
for (unsigned i = 0; i < 32; i++) {
m256 x = setbit<m256>(j*32+i);
m256 y = zeroes256();
ASSERT_EQ(1U << j, diffrich256(x, y)) << "bit " << j*32+i << " not happy";
}
}
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} c;
c.simd = simd_or(a, b);
const char expected = 0x33 | 0x55;
for (size_t i = 0; i < sizeof(c); i++) {
EXPECT_EQ(expected, c.bytes[i]);
}
}
TYPED_TEST(SimdUtilsTest, xor1) {
const TypeParam zeroes = simd_zeroes();
const TypeParam ones = simd_ones();
TypeParam result;
result = simd_xor(zeroes, ones);
EXPECT_FALSE(simd_diff(result, ones));
result = simd_xor(ones, zeroes);
EXPECT_FALSE(simd_diff(result, ones));
result = simd_xor(zeroes, zeroes);
EXPECT_FALSE(simd_diff(result, zeroes));
result = simd_xor(ones, ones);
EXPECT_FALSE(simd_diff(result, zeroes));
}
TYPED_TEST(SimdUtilsTest, xor2) {
TypeParam a, b;
memset(&a, 0x33, sizeof(a));
memset(&b, 0x55, sizeof(b));
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} c;
c.simd = simd_xor(a, b);
const char expected = 0x33 ^ 0x55;
for (size_t i = 0; i < sizeof(c); i++) {
EXPECT_EQ(expected, c.bytes[i]);
}
}
TYPED_TEST(SimdUtilsTest, andnot1) {
const TypeParam zeroes = simd_zeroes();
const TypeParam ones = simd_ones();
TypeParam result;
result = simd_andnot(zeroes, ones);
EXPECT_FALSE(simd_diff(result, ones));
result = simd_andnot(ones, zeroes);
EXPECT_FALSE(simd_diff(result, zeroes));
result = simd_andnot(zeroes, zeroes);
EXPECT_FALSE(simd_diff(result, zeroes));
result = simd_andnot(ones, ones);
EXPECT_FALSE(simd_diff(result, zeroes));
}
TYPED_TEST(SimdUtilsTest, andnot2) {
TypeParam a, b;
memset(&a, 0x33, sizeof(a));
memset(&b, 0x55, sizeof(b));
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} c;
c.simd = simd_andnot(a, b);
const char expected = ~0x33 & 0x55;
for (size_t i = 0; i < sizeof(c); i++) {
EXPECT_EQ(expected, c.bytes[i]);
}
}
TYPED_TEST(SimdUtilsTest, not1) {
const TypeParam zeroes = simd_zeroes();
const TypeParam ones = simd_ones();
TypeParam result;
result = simd_not(zeroes);
EXPECT_FALSE(simd_diff(result, ones));
result = simd_not(ones);
EXPECT_FALSE(simd_diff(result, zeroes));
}
TYPED_TEST(SimdUtilsTest, not2) {
TypeParam a;
memset(&a, 0x33, sizeof(a));
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} c;
c.simd = simd_not(a);
const char expected = ~0x33;
for (size_t i = 0; i < sizeof(c); i++) {
EXPECT_EQ(expected, c.bytes[i]);
}
}
TYPED_TEST(SimdUtilsTest, isnonzero) {
TypeParam a = simd_zeroes();
EXPECT_FALSE(simd_isnonzero(a));
a = simd_ones();
EXPECT_TRUE(simd_isnonzero(a));
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} c;
// Try every 1-bit case.
for (size_t i = 0; i < sizeof(a); i++) {
for (size_t j = 0; j < 8; j++) {
memset(&c.simd, 0, sizeof(c.simd));
c.bytes[i] = 1 << j;
EXPECT_TRUE(simd_isnonzero(c.simd));
}
}
}
TYPED_TEST(SimdUtilsTest, clearbit) {
const unsigned int total_bits = sizeof(TypeParam) * 8;
const TypeParam ones = simd_ones();
for (unsigned int i = 0; i < total_bits; i++) {
TypeParam a = simd_ones();
simd_clearbit(&a, i);
ASSERT_NE(0, simd_diff(a, ones)) << "bit " << i << " wasn't cleared";
TypeParam mask = setbit<TypeParam>(i);
ASSERT_EQ(0, simd_diff(ones, simd_or(a, mask)))
<< "clearing bit " << i << " caused collateral damage";
}
}
TYPED_TEST(SimdUtilsTest, testbit) {
const unsigned int total_bits = sizeof(TypeParam) * 8;
const TypeParam ones = simd_ones();
// First, all bits are on in 'ones'.
for (unsigned int i = 0; i < total_bits; i++) {
ASSERT_EQ(1, simd_testbit(&ones, i)) << "bit " << i << " is on";
}
// Try individual bits; only 'i' should be on.
for (unsigned int i = 0; i < total_bits; i++) {
TypeParam a = setbit<TypeParam>(i);
for (unsigned int j = 0; j < total_bits; j++) {
ASSERT_EQ(i == j ? 1 : 0, simd_testbit(&a, j)) << "bit " << i
<< " is wrong";
}
}
}
TYPED_TEST(SimdUtilsTest, setbit) {
const unsigned int total_bits = sizeof(TypeParam) * 8;
// Try individual bits; only 'i' should be on.
for (unsigned int i = 0; i < total_bits; i++) {
TypeParam a = setbit<TypeParam>(i);
TypeParam x = simd_zeroes();
simd_setbit(&x, i);
ASSERT_FALSE(simd_diff(a, x));
}
TypeParam a = simd_zeroes();
// turn on all bits
for (unsigned int i = 0; i < total_bits; i++) {
simd_setbit(&a, i);
}
ASSERT_FALSE(simd_diff(simd_ones(), a));
}
TYPED_TEST(SimdUtilsTest, diffrich) {
const unsigned total_bits = sizeof(TypeParam) * 8;
const TypeParam zeroes = simd_zeroes();
const TypeParam ones = simd_ones();
// Test identical cases
EXPECT_EQ(0U, simd_diffrich(zeroes, zeroes));
EXPECT_EQ(0U, simd_diffrich(ones, ones));
for (unsigned i = 0; i < total_bits; i++) {
TypeParam a = setbit<TypeParam>(i);
TypeParam b = setbit<TypeParam>(i);
EXPECT_EQ(0U, simd_diffrich(a, b));
}
// and nothing is on in zeroes
for (unsigned int i = 0; i < total_bits; i++) {
ASSERT_EQ(0, simd_testbit(&zeroes, i)) << "bit " << i << " is off";
}
// All-zeroes and all-ones differ in all words
EXPECT_EQ((1U << (total_bits / 32)) - 1, simd_diffrich(zeroes, ones));
// Cases that differ in one 32-bit word
for (unsigned i = 0; i < total_bits; i++) {
TypeParam a = setbit<TypeParam>(i);
u32 rv = simd_diffrich(zeroes, a);
EXPECT_EQ(1U << i / 32, rv);
}
}
TYPED_TEST(SimdUtilsTest, diffrich64) {
const unsigned total_bits = sizeof(TypeParam) * 8;
const TypeParam zeroes = simd_zeroes();
const TypeParam ones = simd_ones();
// Test identical cases
EXPECT_EQ(0U, simd_diffrich64(zeroes, zeroes));
EXPECT_EQ(0U, simd_diffrich64(ones, ones));
for (unsigned i = 0; i < total_bits; i++) {
TypeParam a = setbit<TypeParam>(i);
TypeParam b = setbit<TypeParam>(i);
EXPECT_EQ(0U, simd_diffrich64(a, b));
}
// All-zeroes and all-ones differ in all words, which will result in every
// second bit being on.
EXPECT_EQ(((1U << (total_bits / 32)) - 1) & 0x55555555u,
simd_diffrich64(zeroes, ones));
// Cases that differ in one 64-bit word
for (unsigned i = 0; i < total_bits; i++) {
TypeParam a = setbit<TypeParam>(i);
u32 rv = simd_diffrich64(zeroes, a);
EXPECT_EQ(1U << ((i / 64) * 2), rv);
}
}
// Unaligned load
TYPED_TEST(SimdUtilsTest, loadu) {
const TypeParam ones = simd_ones();
const size_t mem_len = sizeof(ones) * 2;
unique_ptr<char[]> mem_array = ue2::make_unique<char[]>(mem_len);
char *mem = mem_array.get();
for (size_t offset = 1; offset < sizeof(ones); offset++) {
memset(mem, 0, mem_len);
memset(mem + offset, 0xff, sizeof(ones));
TypeParam a;
simd_loadu(&a, mem + offset);
ASSERT_EQ(0, simd_diff(a, ones));
}
}
// Aligned load and store
TYPED_TEST(SimdUtilsTest, load_store) {
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} a;
for (size_t i = 0; i < sizeof(a); i++) {
a.bytes[i] = (char)(i % 256);
}
aligned_unique_ptr<char> mem_ptr = aligned_zmalloc_unique<char>(sizeof(a));
char *mem = mem_ptr.get();
ASSERT_EQ(0, (size_t)mem % 16U);
memset(mem, 0, sizeof(a));
simd_store(mem, a.simd);
ASSERT_EQ(0, memcmp(mem, a.bytes, sizeof(a)));
TypeParam b;
simd_load(&b, mem);
ASSERT_FALSE(simd_diff(a.simd, b));
}
// Packed load and store
TYPED_TEST(SimdUtilsTest, loadbytes_storebytes) {
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} a;
for (size_t i = 0; i < sizeof(a); i++) {
a.bytes[i] = (char)(i % 256);
}
char mem[sizeof(TypeParam)];
for (size_t i = 1; i < sizeof(TypeParam); i++) {
memset(mem, 0xff, sizeof(TypeParam));
simd_storebytes(mem, a.simd, i);
union {
TypeParam simd;
char bytes[sizeof(TypeParam)];
} b;
simd_loadbytes(&b.simd, mem, i);
// First i bytes should match a, remaining bytes are zero. (Note that
// this takes endianness into account)
for (size_t j = 0; j < sizeof(TypeParam); j++) {
size_t idx = j;
ASSERT_EQ(j < i ? a.bytes[idx] : 0, b.bytes[idx]);
}
}
}
TEST(SimdUtilsTest, movq) {
m128 simd;
simd = ones128();
u64a r = movq(simd);
ASSERT_EQ((u64a)(~0), r);
char cmp[sizeof(m128)];
memset(cmp, 0x80, sizeof(m128));
simd = set16x8(0x80);
r = movq(simd);
ASSERT_EQ(0, memcmp(cmp, &simd, sizeof(simd)));
ASSERT_EQ(0, memcmp(cmp, &r, sizeof(r)));
simd = _mm_set_epi64x(~0LL, 0x123456789abcdef);
r = movq(simd);
ASSERT_EQ(r, 0x123456789abcdef);
}
TEST(SimdUtilsTest, set16x8) {
char cmp[sizeof(m128)];
for (unsigned i = 0; i < 256; i++) {
m128 simd = set16x8(i);
memset(cmp, i, sizeof(simd));
ASSERT_EQ(0, memcmp(cmp, &simd, sizeof(simd)));
}
}
#if defined(__AVX2__)
TEST(SimdUtilsTest, set32x8) {
char cmp[sizeof(m256)];
for (unsigned i = 0; i < 256; i++) {
m256 simd = set32x8(i);
memset(cmp, i, sizeof(simd));
ASSERT_EQ(0, memcmp(cmp, &simd, sizeof(simd)));
}
}
TEST(SimdUtilsTest, set2x128) {
char cmp[sizeof(m256)];
for (unsigned i = 0; i < 256; i++) {
m128 x = set16x8(i);
m256 y = set32x8(i);
m256 z = set2x128(x);
memset(cmp, i, sizeof(z));
ASSERT_EQ(0, memcmp(cmp, &z, sizeof(z)));
ASSERT_EQ(0, memcmp(&y, &z, sizeof(z)));
}
}
#endif
} // namespace

View File

@@ -0,0 +1,410 @@
/*
* 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 "gtest/gtest.h"
#include "util/state_compress.h"
#include <vector>
#include <tuple>
using namespace std;
TEST(state_compress, u32) {
char buf[sizeof(u32)] = { 0 };
vector<tuple<u32, u32, int> > tests = {
make_tuple(0U, 0x1U, 1),
make_tuple(1U, 0x1U, 1),
make_tuple(0x80008000, 0x80808080, 1),
make_tuple(0x9f008000, 0x80808080, 1),
make_tuple(0x9fab8dfe, 0xff0fff71, 3),
make_tuple(0x9fab8dfe, 0xffefff71, 4),
make_tuple(0xf0f0f0f0, 0x0f0f0f0f, 4),
};
for (const auto &e : tests) {
u32 val;
u32 mask;
int len;
tie(val, mask, len) = e;
storecompressed32(&buf, &val, &mask, len);
u32 val_out;
loadcompressed32(&val_out, &buf, &mask, len);
EXPECT_EQ(val & mask, val_out);
}
}
TEST(state_compress, u64a) {
char buf[sizeof(u64a)] = { 0 };
vector<tuple<u64a, u64a, int> > tests = {
make_tuple(0ULL, 0x1ULL, 1),
make_tuple(1ULL, 0x1ULL, 1),
make_tuple(0x80008000ULL, 0x80808080ULL, 1),
make_tuple(0x9f008000ULL, 0x80808080ULL, 1),
make_tuple(0x9fab8dfeULL, 0xff0fff71ULL, 3),
make_tuple(0x9fab8dfeULL, 0xffefff71ULL, 4),
make_tuple(0xf0f0f0f0ULL, 0x0f0f0f0fULL, 4),
make_tuple(0x0123456789abcdefULL, 0x0123456789abcdefULL, 5),
make_tuple(0x0123456789abcdefULL, 0xfedcba9876543210ULL, 6),
make_tuple(0x0123456789abcdefULL, 0x0123456789abcdefULL, 7),
make_tuple(0x0123456789abcdefULL, 0xffffffffffffffffULL, 8),
};
for (const auto &e : tests) {
u64a val;
u64a mask;
int len;
tie(val, mask, len) = e;
storecompressed64(&buf, &val, &mask, len);
u64a val_out;
loadcompressed64(&val_out, &buf, &mask, len);
EXPECT_EQ(val & mask, val_out);
}
}
TEST(state_compress, m128_1) {
char buf[sizeof(m128)] = { 0 };
for (u32 i = 0; i < 16; i++) {
char mask_raw[16] = { 0 };
char val_raw[16] = { 0 };
memset(val_raw, (i << 4) + 3, 16);
mask_raw[i] = 0xff;
val_raw[i] = i;
mask_raw[15 - i] = 0xff;
val_raw[15 - i] = i;
m128 val;
m128 mask;
memcpy(&val, val_raw, sizeof(val));
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed128(&buf, &val, &mask, 0);
m128 val_out;
loadcompressed128(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff128(and128(val, mask), val_out));
mask_raw[i] = 0x0f;
mask_raw[15 - i] = 0x0f;
memcpy(&mask, mask_raw, sizeof(mask));
val_raw[i] = 9;
storecompressed128(&buf, &val, &mask, 0);
loadcompressed128(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff128(and128(val, mask), val_out));
}
}
TEST(state_compress, m128_2) {
char buf[sizeof(m128)] = { 0 };
char val_raw[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
m128 val;
memcpy(&val, val_raw, sizeof(val));
for (u32 i = 0; i < 16; i++) {
char mask_raw[16];
memset(mask_raw, 0x7f, sizeof(mask_raw));
mask_raw[i] = 0;
m128 mask;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed128(&buf, &val, &mask, 0);
m128 val_out;
loadcompressed128(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff128(and128(val, mask), val_out));
for (u32 j = i + 1; j < 16; j++) {
mask_raw[j] = 0;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed128(&buf, &val, &mask, 0);
loadcompressed128(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff128(and128(val, mask), val_out));
mask_raw[j] = 0x7f;
}
}
}
TEST(state_compress, m256_1) {
char buf[sizeof(m256)] = { 0 };
for (u32 i = 0; i < 32; i++) {
char mask_raw[32] = { 0 };
char val_raw[32] = { 0 };
memset(val_raw, (i << 3) + 3, 32);
mask_raw[i] = 0xff;
val_raw[i] = i;
mask_raw[31 - i] = 0xff;
val_raw[31 - i] = i;
m256 val;
m256 mask;
memcpy(&val, val_raw, sizeof(val));
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed256(&buf, &val, &mask, 0);
m256 val_out;
loadcompressed256(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff256(and256(val, mask), val_out));
mask_raw[i] = 0x7;
mask_raw[31 - i] = 0x1f;
memcpy(&mask, mask_raw, sizeof(mask));
val_raw[i] = 5;
storecompressed256(&buf, &val, &mask, 0);
loadcompressed256(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff256(and256(val, mask), val_out));
}
}
TEST(state_compress, m256_2) {
char buf[sizeof(m256)] = { 0 };
char val_raw[32] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P' };
m256 val;
memcpy(&val, val_raw, sizeof(val));
for (u32 i = 0; i < 32; i++) {
char mask_raw[32];
memset(mask_raw, 0x7f, sizeof(mask_raw));
mask_raw[i] = 0;
m256 mask;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed256(&buf, &val, &mask, 0);
m256 val_out;
loadcompressed256(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff256(and256(val, mask), val_out));
for (u32 j = i + 1; j < 32; j++) {
mask_raw[j] = 0;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed256(&buf, &val, &mask, 0);
loadcompressed256(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff256(and256(val, mask), val_out));
mask_raw[j] = 0x7f;
}
}
}
TEST(state_compress, m384_1) {
char buf[sizeof(m384)] = { 0 };
for (u32 i = 0; i < 48; i++) {
char mask_raw[48] = { 0 };
char val_raw[48] = { 0 };
memset(val_raw, (i << 2) + 3, 48);
mask_raw[i] = 0xff;
val_raw[i] = i;
mask_raw[47 - i] = 0xff;
val_raw[47 - i] = i;
m384 val;
m384 mask;
memcpy(&val, val_raw, sizeof(val));
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed384(&buf, &val, &mask, 0);
m384 val_out;
loadcompressed384(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff384(and384(val, mask), val_out));
mask_raw[i] = 0x3;
mask_raw[47 - i] = 0x2f;
memcpy(&mask, mask_raw, sizeof(mask));
val_raw[i] = 3;
storecompressed384(&buf, &val, &mask, 0);
loadcompressed384(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff384(and384(val, mask), val_out));
}
}
TEST(state_compress, m384_2) {
char buf[sizeof(m384)] = { 0 };
char val_raw[48] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'Q', 'R', ' ', '-', ',', '"', ';', ':' };
m384 val;
memcpy(&val, val_raw, sizeof(val));
for (u32 i = 0; i < 48; i++) {
char mask_raw[48];
memset(mask_raw, 0x7f, sizeof(mask_raw));
mask_raw[i] = 0;
m384 mask;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed384(&buf, &val, &mask, 0);
m384 val_out;
loadcompressed384(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff384(and384(val, mask), val_out));
for (u32 j = i + 1; j < 48; j++) {
mask_raw[j] = 0;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed384(&buf, &val, &mask, 0);
loadcompressed384(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff384(and384(val, mask), val_out));
mask_raw[j] = 0x7f;
}
}
}
TEST(state_compress, m512_1) {
char buf[sizeof(m512)] = { 0 };
for (u32 i = 0; i < 64; i++) {
char mask_raw[64] = { 0 };
char val_raw[64] = { 0 };
memset(val_raw, (i << 2) + 3, 64);
mask_raw[i] = 0xff;
val_raw[i] = i;
mask_raw[63 - i] = 0xff;
val_raw[63 - i] = i;
m512 val;
m512 mask;
memcpy(&val, val_raw, sizeof(val));
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed512(&buf, &val, &mask, 0);
m512 val_out;
loadcompressed512(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff512(and512(val, mask), val_out));
mask_raw[i] = 0x3;
mask_raw[63 - i] = 0x2f;
memcpy(&mask, mask_raw, sizeof(mask));
val_raw[i] = 3;
storecompressed512(&buf, &val, &mask, 0);
loadcompressed512(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff512(and512(val, mask), val_out));
}
}
TEST(state_compress, m512_2) {
char buf[sizeof(m512)] = { 0 };
char val_raw[64] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'Q', 'R', ' ', '-', ',', '"', ';', ':',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n' };
m512 val;
memcpy(&val, val_raw, sizeof(val));
for (u32 i = 0; i < 64; i++) {
char mask_raw[64];
memset(mask_raw, 0x7f, sizeof(mask_raw));
mask_raw[i] = 0;
m512 mask;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed512(&buf, &val, &mask, 0);
m512 val_out;
loadcompressed512(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff512(and512(val, mask), val_out));
for (u32 j = i + 1; j < 64; j++) {
mask_raw[j] = 0;
memcpy(&mask, mask_raw, sizeof(mask));
storecompressed512(&buf, &val, &mask, 0);
loadcompressed512(&val_out, &buf, &mask, 0);
EXPECT_TRUE(!diff512(and512(val, mask), val_out));
mask_raw[j] = 0x7f;
}
}
}

616
unit/internal/truffle.cpp Normal file
View File

@@ -0,0 +1,616 @@
/*
* 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 "gtest/gtest.h"
#include "nfa/truffle.h"
#include "nfa/trufflecompile.h"
#include "util/charreach.h"
#include "util/simd_utils.h"
using namespace ue2;
TEST(Truffle, CompileDot) {
m128 mask1, mask2;
memset(&mask1, 0, sizeof(mask1));
memset(&mask2, 0, sizeof(mask2));
CharReach chars;
chars.setall();
truffleBuildMasks(chars, &mask1, &mask2);
CharReach out = truffle2cr(mask1, mask2);
ASSERT_EQ(out, chars);
}
TEST(Truffle, CompileChars) {
m128 mask1, mask2;
CharReach chars;
// test one char at a time
for (u32 c = 0; c < 256; ++c) {
mask1 = zeroes128();
mask2 = zeroes128();
chars.clear();
chars.set((u8)c);
truffleBuildMasks(chars, &mask1, &mask2);
CharReach out = truffle2cr(mask1, mask2);
ASSERT_EQ(out, chars);
}
// set all chars up to dot
for (u32 c = 0; c < 256; ++c) {
mask1 = zeroes128();
mask2 = zeroes128();
chars.set((u8)c);
truffleBuildMasks(chars, &mask1, &mask2);
CharReach out = truffle2cr(mask1, mask2);
ASSERT_EQ(out, chars);
}
// unset all chars from dot
for (u32 c = 0; c < 256; ++c) {
mask1 = zeroes128();
mask2 = zeroes128();
chars.clear((u8)c);
truffleBuildMasks(chars, &mask1, &mask2);
CharReach out = truffle2cr(mask1, mask2);
ASSERT_EQ(out, chars);
}
}
TEST(Truffle, ExecNoMatch1) {
m128 mask1, mask2;
memset(&mask1, 0, sizeof(mask1));
memset(&mask2, 0, sizeof(mask2));
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\xff";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + strlen(t1), (size_t)rv);
}
}
TEST(Truffle, ExecNoMatch2) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
chars.set('B');
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + strlen(t1), (size_t)rv);
}
}
TEST(Truffle, ExecNoMatch3) {
m128 mask1, mask2;
CharReach chars;
chars.set('V'); /* V = 0x56, e = 0x65 */
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + strlen(t1), (size_t)rv);
}
}
TEST(Truffle, ExecMiniMatch0) {
m128 lo, hi;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &lo, &hi);
char t1[] = "a";
const u8 *rv = truffleExec(lo, hi, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1, (size_t)rv);
}
TEST(Truffle, ExecMiniMatch1) {
m128 lo, hi;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &lo, &hi);
char t1[] = "bbbbbbbabbb";
const u8 *rv = truffleExec(lo, hi, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 7, (size_t)rv);
}
TEST(Truffle, ExecMiniMatch2) {
m128 lo, hi;
CharReach chars;
chars.set(0);
truffleBuildMasks(chars, &lo, &hi);
char t1[] = "bbbbbbb\0bbb";
const u8 *rv = truffleExec(lo, hi, (u8 *)t1, (u8 *)t1 + 11);
ASSERT_EQ((size_t)t1 + 7, (size_t)rv);
}
TEST(Truffle, ExecMiniMatch3) {
m128 lo, hi;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &lo, &hi);
char t1[] = "\0\0\0\0\0\0\0a\0\0\0";
const u8 *rv = truffleExec(lo, hi, (u8 *)t1, (u8 *)t1 + 11);
ASSERT_EQ((size_t)t1 + 7, (size_t)rv);
}
TEST(Truffle, ExecMatchBig) {
m128 lo, hi;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &lo, &hi);
std::array<u8, 400> t1;
t1.fill('b');
t1[120] = 'a';
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(lo, hi, (u8 *)t1.data() + i, (u8 *)t1.data() + 399);
ASSERT_LE(((size_t)t1.data() + 120) & ~0xf, (size_t)rv);
}
}
TEST(Truffle, ExecMatch1) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Truffle, ExecMatch2) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Truffle, ExecMatch3) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
chars.set('B');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbBaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Truffle, ExecMatch4) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
chars.set('C');
chars.set('A');
chars.set('c');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbAaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t2[] = "bbbbbbbbbbbbbbbbbCaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t3[] = "bbbbbbbbbbbbbbbbbcaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
char t4[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1 + i, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = truffleExec(mask1, mask2, (u8 *)t2 + i, (u8 *)t2 + strlen(t1));
ASSERT_EQ((size_t)t2 + 17, (size_t)rv);
rv = truffleExec(mask1, mask2, (u8 *)t3 + i, (u8 *)t3 + strlen(t3));
ASSERT_EQ((size_t)t3 + 17, (size_t)rv);
rv = truffleExec(mask1, mask2, (u8 *)t4 + i, (u8 *)t4 + strlen(t4));
ASSERT_EQ((size_t)t4 + 17, (size_t)rv);
}
}
TEST(Truffle, ExecMatch5) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[48 - i] = 'a';
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
}
TEST(Truffle, ExecMatch6) {
m128 mask1, mask2;
CharReach chars;
// [0-Z] - includes some graph chars
chars.setRange('0', 'Z');
truffleBuildMasks(chars, &mask1, &mask2);
std::array<u8, 128> t1;
t1.fill('*'); // it's full of stars!
for (u8 c = '0'; c <= 'Z'; c++) {
t1[17] = c;
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1.data(), (u8 *)t1.data() + 128);
ASSERT_EQ((size_t)t1.data() + 17, (size_t)rv);
}
}
TEST(Truffle, ExecMatch7) {
m128 mask1, mask2;
CharReach chars;
// hi bits
chars.setRange(127, 255);
truffleBuildMasks(chars, &mask1, &mask2);
std::array<u8, 128> t1;
t1.fill('*'); // it's full of stars!
for (unsigned int c = 127; c <= 255; c++) {
t1[40] = (u8)c;
const u8 *rv = truffleExec(mask1, mask2, (u8 *)t1.data(), (u8 *)t1.data() + 128);
ASSERT_EQ((size_t)t1.data() + 40, (size_t)rv);
}
}
TEST(ReverseTruffle, ExecNoMatch1) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_EQ((const u8 *)(t1 - 1), rv);
}
}
TEST(ReverseTruffle, ExecNoMatch2) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
chars.set('B');
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_EQ((const u8 *)(t1 - 1), rv);
}
}
TEST(ReverseTruffle, ExecNoMatch3) {
m128 mask1, mask2;
CharReach chars;
chars.set('V'); /* V = 0x56, e = 0x65 */
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_EQ((const u8 *)(t1 - 1), rv);
}
}
TEST(ReverseTruffle, ExecMiniMatch0) {
m128 lo, hi;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &lo, &hi);
char t1[] = "a";
const u8 *rv = rtruffleExec(lo, hi, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1, (size_t)rv);
}
TEST(ReverseTruffle, ExecMiniMatch1) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbabbbb";
size_t len = strlen(t1);
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 7, rv);
}
TEST(ReverseTruffle, ExecMiniMatch2) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "babbbbbabbbb";
size_t len = strlen(t1);
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 7, rv);
}
TEST(ReverseTruffle, ExecMatch1) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbabbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 17, rv);
}
}
TEST(ReverseTruffle, ExecMatch2) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbabbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 32, rv);
}
}
TEST(ReverseTruffle, ExecMatch3) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
chars.set('B');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaBbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('B', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 32, rv);
}
// check that we match the 'a' bytes as well.
ASSERT_EQ('B', t1[32]);
t1[32] = 'b';
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
ASSERT_NE((const u8 *)t1 - 1, rv); // not found
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 31, rv);
}
}
TEST(ReverseTruffle, ExecMatch4) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
chars.set('C');
chars.set('A');
chars.set('c');
truffleBuildMasks(chars, &mask1, &mask2);
/* 0123456789012345678901234567890 */
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaAbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char t2[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaCbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char t3[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaacbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
char t4[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < 16; i++) {
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len - i);
EXPECT_EQ('A', (char)*rv);
ASSERT_EQ((const u8 *)t1 + 32, rv);
rv = rtruffleExec(mask1, mask2, (u8 *)t2, (u8 *)t2 + len - i);
EXPECT_EQ('C', (char)*rv);
ASSERT_EQ((const u8 *)t2 + 32, rv);
rv = rtruffleExec(mask1, mask2, (u8 *)t3, (u8 *)t3 + len - i);
EXPECT_EQ('c', (char)*rv);
ASSERT_EQ((const u8 *)t3 + 32, rv);
rv = rtruffleExec(mask1, mask2, (u8 *)t4, (u8 *)t4 + len - i);
EXPECT_EQ('a', (char)*rv);
ASSERT_EQ((const u8 *)t4 + 32, rv);
}
}
TEST(ReverseTruffle, ExecMatch5) {
m128 mask1, mask2;
CharReach chars;
chars.set('a');
truffleBuildMasks(chars, &mask1, &mask2);
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
size_t len = strlen(t1);
for (size_t i = 0; i < len; i++) {
t1[i] = 'a';
const u8 *rv = rtruffleExec(mask1, mask2, (u8 *)t1, (u8 *)t1 + len);
ASSERT_EQ((const u8 *)t1 + i, rv);
}
}

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.
*/
#include "config.h"
#include "gtest/gtest.h"
#include "util/unaligned.h"
TEST(Unaligned, StoreLoad16) {
char data[3];
memset(data, 0, sizeof(data));
char *ptr = &data[1];
unaligned_store_u16(ptr, (u16)~0u);
ASSERT_EQ((u16)~0u, unaligned_load_u16(ptr));
unaligned_store_u16(ptr, 0u);
ASSERT_EQ(0u, unaligned_load_u16(ptr));
unaligned_store_u16(ptr, (u16)0xf0f0u);
ASSERT_EQ(0xf0f0u, unaligned_load_u16(ptr));
for (u32 i = 0; i < 16; i++) {
const u16 val = 0x1u << i;
memset(data, 0xff, sizeof(data));
unaligned_store_u16(ptr, val);
ASSERT_EQ(val, unaligned_load_u16(ptr));
}
}
TEST(Unaligned, StoreLoad32) {
char data[5];
memset(data, 0, sizeof(data));
char *ptr = &data[1];
unaligned_store_u32(ptr, ~0u);
ASSERT_EQ(~0u, unaligned_load_u32(ptr));
unaligned_store_u32(ptr, 0u);
ASSERT_EQ(0u, unaligned_load_u32(ptr));
unaligned_store_u32(ptr, 0xf0f0f0f0u);
ASSERT_EQ(0xf0f0f0f0u, unaligned_load_u32(ptr));
for (u32 i = 0; i < 32; i++) {
const u32 val = 0x1u << i;
memset(data, 0xff, sizeof(data));
unaligned_store_u32(ptr, val);
ASSERT_EQ(val, unaligned_load_u32(ptr));
}
}
TEST(Unaligned, StoreLoad64) {
char data[9];
memset(data, 0, sizeof(data));
char *ptr = &data[1];
unaligned_store_u64a(ptr, ~0ull);
ASSERT_EQ(~0ull, unaligned_load_u64a(ptr));
unaligned_store_u64a(ptr, 0ull);
ASSERT_EQ(0ull, unaligned_load_u64a(ptr));
unaligned_store_u64a(ptr, 0xf0f0f0f0f0f0f0f0ull);
ASSERT_EQ(0xf0f0f0f0f0f0f0f0ull, unaligned_load_u64a(ptr));
for (u32 i = 0; i < 64; i++) {
const u64a val = 0x1ull << i;
memset(data, 0xff, sizeof(data));
unaligned_store_u64a(ptr, val);
ASSERT_EQ(val, unaligned_load_u64a(ptr));
}
}

View File

@@ -0,0 +1,295 @@
/*
* 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 "gtest/gtest.h"
#include "config.h"
#include "ue2common.h"
#include "util/unicode_set.h"
using namespace ue2;
TEST(unicode_set, basic_1) {
CodePointSet cps;
EXPECT_TRUE(cps.none());
EXPECT_EQ(INVALID_UNICODE, cps.at(0));
cps.set('a');
EXPECT_FALSE(cps.none());
EXPECT_EQ(1U, cps.count());
EXPECT_EQ('a', cps.at(0));
EXPECT_EQ(INVALID_UNICODE, cps.at(1));
cps.unset('a');
EXPECT_TRUE(cps.none());
EXPECT_EQ(0U, cps.count());
EXPECT_EQ(INVALID_UNICODE, cps.at(0));
}
TEST(unicode_set, range_1) {
CodePointSet cps;
cps.setRange('b', 'y');
EXPECT_EQ(24U, cps.count());
EXPECT_EQ('b', cps.at(0));
EXPECT_EQ('y', cps.at(23));
EXPECT_EQ(INVALID_UNICODE, cps.at(24));
cps.setRange('B', 'Y');
EXPECT_EQ(48U, cps.count());
EXPECT_EQ('B', cps.at(0));
EXPECT_EQ('Y', cps.at(23));
EXPECT_EQ('b', cps.at(24));
EXPECT_EQ('y', cps.at(47));
EXPECT_EQ(INVALID_UNICODE, cps.at(48));
CodePointSet cps2 = cps;
cps2.unsetRange('B', 'y');
EXPECT_TRUE(cps2.none());
cps2 = cps;
EXPECT_FALSE(cps2.none());
cps2.unsetRange(0, 'y');
EXPECT_TRUE(cps2.none());
cps2 = cps;
EXPECT_FALSE(cps2.none());
cps2.unsetRange('B', MAX_UNICODE);
EXPECT_TRUE(cps2.none());
}
TEST(unicode_set, range_2) {
CodePointSet cps;
cps.setRange('A', 'Z');
EXPECT_EQ(26U, cps.count());
EXPECT_EQ('A', cps.at(0));
EXPECT_EQ('Z', cps.at(25));
EXPECT_EQ(INVALID_UNICODE, cps.at(26));
CodePointSet cps2 = cps;
cps2.setRange('E', 'K');
EXPECT_TRUE(cps == cps2);
EXPECT_FALSE(cps != cps2);
cps2.setRange('a', 'z');
EXPECT_EQ(52U, cps2.count());
EXPECT_EQ('A', cps2.at(0));
EXPECT_EQ('Z', cps2.at(25));
EXPECT_EQ('a', cps2.at(26));
EXPECT_EQ('z', cps2.at(51));
EXPECT_EQ(INVALID_UNICODE, cps2.at(52));
CodePointSet cps3 = cps2;
cps3.setRange('a', 'f');
EXPECT_TRUE(cps3 == cps2);
cps3.set('g');
EXPECT_TRUE(cps3 == cps2);
cps3.setRange('g', 'z');
EXPECT_TRUE(cps3 == cps2);
EXPECT_TRUE(cps3.isSubset(cps2));
EXPECT_TRUE(cps2.isSubset(cps3));
EXPECT_TRUE(cps3.isSubset(cps));
EXPECT_FALSE(cps.isSubset(cps3));
}
TEST(unicode_set, range_3) {
CodePointSet cps;
cps.setRange(1000, 10000);
EXPECT_EQ(9001, cps.count());
EXPECT_EQ(1000, cps.at(0));
EXPECT_EQ(10000, cps.at(9000));
cps.unsetRange(2000, 3000);
EXPECT_EQ(8000, cps.count());
EXPECT_EQ(1000, cps.at(0));
EXPECT_EQ(1999, cps.at(999));
EXPECT_EQ(3001, cps.at(1000));
EXPECT_EQ(10000, cps.at(7999));
cps.setRange(1500, 3500);
EXPECT_EQ(9001, cps.count());
EXPECT_EQ(1000, cps.at(0));
EXPECT_EQ(10000, cps.at(9000));
}
TEST(unicode_set, subset) {
CodePointSet a;
a.setRange(0, 99);
CodePointSet b;
b.setRange(100, 199);
CodePointSet c;
c.setRange(200, 299);
CodePointSet ab;
ab.setRange(0, 199);
CodePointSet ac;
ac.setRange(0, 99);
ac.setRange(200, 299);
CodePointSet bc;
bc.setRange(100, 299);
CodePointSet abc;
abc.setRange(0, 299);
CodePointSet e;
EXPECT_TRUE(abc.isSubset(a));
EXPECT_TRUE(abc.isSubset(b));
EXPECT_TRUE(abc.isSubset(c));
EXPECT_TRUE(abc.isSubset(e));
EXPECT_TRUE(abc.isSubset(ab));
EXPECT_TRUE(abc.isSubset(ac));
EXPECT_TRUE(abc.isSubset(bc));
EXPECT_TRUE(abc.isSubset(abc));
EXPECT_TRUE(ab.isSubset(a));
EXPECT_TRUE(ab.isSubset(b));
EXPECT_FALSE(ab.isSubset(c));
EXPECT_TRUE(ab.isSubset(e));
EXPECT_TRUE(ab.isSubset(ab));
EXPECT_FALSE(ab.isSubset(ac));
EXPECT_FALSE(ab.isSubset(bc));
EXPECT_FALSE(ab.isSubset(abc));
EXPECT_TRUE(ac.isSubset(a));
EXPECT_FALSE(ac.isSubset(b));
EXPECT_TRUE(ac.isSubset(c));
EXPECT_TRUE(ac.isSubset(e));
EXPECT_FALSE(ac.isSubset(ab));
EXPECT_TRUE(ac.isSubset(ac));
EXPECT_FALSE(ac.isSubset(bc));
EXPECT_FALSE(ac.isSubset(abc));
EXPECT_FALSE(bc.isSubset(a));
EXPECT_TRUE(bc.isSubset(b));
EXPECT_TRUE(bc.isSubset(c));
EXPECT_TRUE(bc.isSubset(e));
EXPECT_FALSE(bc.isSubset(ab));
EXPECT_FALSE(bc.isSubset(ac));
EXPECT_TRUE(bc.isSubset(bc));
EXPECT_FALSE(bc.isSubset(abc));
EXPECT_TRUE(a.isSubset(a));
EXPECT_FALSE(a.isSubset(b));
EXPECT_FALSE(a.isSubset(c));
EXPECT_TRUE(a.isSubset(e));
EXPECT_FALSE(a.isSubset(ab));
EXPECT_FALSE(a.isSubset(ac));
EXPECT_FALSE(a.isSubset(bc));
EXPECT_FALSE(a.isSubset(abc));
EXPECT_FALSE(b.isSubset(a));
EXPECT_TRUE(b.isSubset(b));
EXPECT_FALSE(b.isSubset(c));
EXPECT_TRUE(b.isSubset(e));
EXPECT_FALSE(b.isSubset(ab));
EXPECT_FALSE(b.isSubset(ac));
EXPECT_FALSE(b.isSubset(bc));
EXPECT_FALSE(b.isSubset(abc));
EXPECT_FALSE(c.isSubset(a));
EXPECT_FALSE(c.isSubset(b));
EXPECT_TRUE(c.isSubset(c));
EXPECT_TRUE(c.isSubset(e));
EXPECT_FALSE(c.isSubset(ab));
EXPECT_FALSE(c.isSubset(ac));
EXPECT_FALSE(c.isSubset(bc));
EXPECT_FALSE(c.isSubset(abc));
EXPECT_FALSE(e.isSubset(a));
EXPECT_FALSE(e.isSubset(b));
EXPECT_FALSE(e.isSubset(c));
EXPECT_TRUE(e.isSubset(e));
EXPECT_FALSE(e.isSubset(ab));
EXPECT_FALSE(e.isSubset(ac));
EXPECT_FALSE(e.isSubset(bc));
EXPECT_FALSE(e.isSubset(abc));
}
TEST(unicode_set, set_op) {
CodePointSet a;
a.setRange(0, 99);
CodePointSet b;
b.setRange(100, 199);
CodePointSet c;
c.setRange(200, 299);
CodePointSet ab;
ab.setRange(0, 199);
CodePointSet ac;
ac.setRange(0, 99);
ac.setRange(200, 299);
CodePointSet bc;
bc.setRange(100, 299);
CodePointSet abc;
abc.setRange(0, 299);
CodePointSet e;
CodePointSet t = a;
t |= a;
EXPECT_TRUE(t == a);
t |= e;
EXPECT_TRUE(t == a);
t |= b;
EXPECT_TRUE(t == ab);
t |= b;
EXPECT_TRUE(t == ab);
t |= a;
EXPECT_TRUE(t == ab);
t |= ac;
EXPECT_TRUE(t == abc);
t -= e;
EXPECT_TRUE(t == abc);
t -= b;
EXPECT_TRUE(t == ac);
t -= b;
EXPECT_TRUE(t == ac);
t -= bc;
EXPECT_TRUE(t == a);
t -= abc;
EXPECT_TRUE(t == e);
}

View File

@@ -0,0 +1,202 @@
/*
* 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 "gtest/gtest.h"
#include "config.h"
#include "ue2common.h"
#include "util/uniform_ops.h"
#include "util/alloc.h"
using namespace ue2;
TEST(Uniform, Sizes) {
// We likely depend on the size of the structures in various different
// places in UE2, and it's conceivable that they could vary by
// platform/compiler.
EXPECT_EQ(1U, sizeof(u8));
EXPECT_EQ(2U, sizeof(u16));
EXPECT_EQ(4U, sizeof(u32));
EXPECT_EQ(8U, sizeof(u64a));
EXPECT_EQ(16U, sizeof(m128));
EXPECT_EQ(32U, sizeof(m256));
EXPECT_EQ(64U, sizeof(m512));
}
TEST(Uniform, loadstore_u8) {
for (int i = 0; i < 8; i++) {
u8 in = 1 << i;
const char *cin = (const char *)(&in);
u8 out = load_u8(cin);
EXPECT_EQ(in, out);
char ALIGN_DIRECTIVE stored[1];
store_u8(stored, in);
EXPECT_EQ(0, memcmp(stored, &in, sizeof(in)));
}
}
TEST(Uniform, loadstore_u16) {
for (int i = 0; i < 16; i++) {
u16 in = 1 << i;
const char *cin = (const char *)(&in);
u16 out = load_u16(cin);
EXPECT_EQ(in, out);
void *stored = aligned_zmalloc(2);
store_u16(stored, in);
EXPECT_EQ(0, memcmp(stored, &in, sizeof(in)));
aligned_free(stored);
}
}
TEST(Uniform, loadstore_u32) {
for (int i = 0; i < 32; i++) {
u32 in = 1 << i;
const char *cin = (const char *)(&in);
u32 out = load_u32(cin);
EXPECT_EQ(in, out);
void *stored = aligned_zmalloc(32/8);
store_u32(stored, in);
EXPECT_EQ(0, memcmp(stored, &in, sizeof(in)));
aligned_free(stored);
}
}
TEST(Uniform, loadstore_u64a) {
for (int i = 0; i < 64; i++) {
u64a in = 1ULL << i;
const char *cin = (const char *)(&in);
u64a out = load_u64a(cin);
EXPECT_EQ(in, out);
void *stored = aligned_zmalloc(64/8);
store_u64a(stored, in);
EXPECT_EQ(0, memcmp(stored, &in, sizeof(in)));
aligned_free(stored);
}
}
TEST(Uniform, loadstore_m128) {
union {
m128 simd;
u32 words[128/32];
} in;
for (int i = 0; i < 128; i++) {
memset(&in, 0, sizeof(in));
in.words[i/32] = 1 << (i % 32);
const char *cin = (const char *)(&in);
m128 out = load_m128(cin);
EXPECT_EQ(0, memcmp(&out, &in, sizeof(out)));
void *stored = aligned_zmalloc(128/8);
store_m128(stored, in.simd);
EXPECT_EQ(0, memcmp(stored, &in, sizeof(in)));
aligned_free(stored);
}
}
TEST(Uniform, loadstore_m256) {
union {
m256 simd;
u32 words[256/32];
} in;
for (int i = 0; i < 256; i++) {
memset(&in, 0, sizeof(in));
in.words[i/32] = 1 << (i % 32);
const char *cin = (const char *)(&in);
m256 out = load_m256(cin);
EXPECT_EQ(0, memcmp(&out, &in, sizeof(out)));
void *stored = aligned_zmalloc(256/8);
store_m256(stored, in.simd);
EXPECT_EQ(0, memcmp(stored, &in, sizeof(in)));
aligned_free(stored);
}
}
TEST(Uniform, loadstore_m512) {
union {
m512 simd;
u32 words[512/32];
} in;
for (int i = 0; i < 512; i++) {
memset(&in, 0, sizeof(in));
in.words[i/32] = 1 << (i % 32);
const char *cin = (const char *)(&in);
m512 out = load_m512(cin);
EXPECT_EQ(0, memcmp(&out, &in, sizeof(out)));
void *stored = aligned_zmalloc(512/8);
store_m512(stored, in.simd);
EXPECT_EQ(0, memcmp(stored, &in, sizeof(in)));
aligned_free(stored);
}
}
TEST(Uniform, testbit_u32) {
for (u32 i = 0; i < 32; i++) {
u32 v = 0;
EXPECT_EQ((char)0, testbit_u32(&v, i));
v |= 1ULL << i;
EXPECT_EQ((char)1, testbit_u32(&v, i));
v = ~v;
EXPECT_EQ((char)0, testbit_u32(&v, i));
v |= 1ULL << i;
EXPECT_EQ((char)1, testbit_u32(&v, i));
}
}
TEST(Uniform, testbit_u64a) {
for (u32 i = 0; i < 64; i++) {
u64a v = 0;
EXPECT_EQ((char)0, testbit_u64a(&v, i));
v |= 1ULL << i;
EXPECT_EQ((char)1, testbit_u64a(&v, i));
v = ~v;
EXPECT_EQ((char)0, testbit_u64a(&v, i));
v |= 1ULL << i;
EXPECT_EQ((char)1, testbit_u64a(&v, i));
}
}
TEST(Uniform, clearbit_u32) {
for (u32 i = 0; i < 32; i++) {
u32 v = ~0U;
clearbit_u32(&v, i);
EXPECT_EQ((char)0, testbit_u32(&v, i));
v = ~v;
clearbit_u32(&v, i);
EXPECT_EQ(0U, v);
}
}
TEST(Uniform, clearbit_u64a) {
for (u32 i = 0; i < 64; i++) {
u64a v = ~0ULL;
clearbit_u64a(&v, i);
EXPECT_EQ((char)0, testbit_u64a(&v, i));
v = ~v;
clearbit_u64a(&v, i);
EXPECT_EQ(0ULL, v);
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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 "parser/utf8_validate.h"
#include "ue2common.h"
#include "util/ue2string.h"
#include "gtest/gtest.h"
using namespace testing;
using namespace ue2;
struct ValidUtf8TestInfo {
std::string str;
bool is_valid;
};
// Helper for gtest.
static
void PrintTo(const ValidUtf8TestInfo &t, ::std::ostream *os) {
*os << "(" << t.str << ", " << t.is_valid << ")";
}
static ValidUtf8TestInfo valid_utf8_tests[] = {
// Trivial ASCII.
{"foobar", true},
{"0", true},
{"\x7e", true},
{"hatstand\tteakettle\tbadgerbrush\n", true},
// Some valid UTF-8: Movie titles!
{"À bout de souffle", true},
{"拳銃は俺のパスポート", true},
{"大醉俠", true},
{"龙门客栈", true},
{"공동경비구역", true},
{"জলসাঘর", true},
// Invalid one-byte caseS.
{"\x7f", false},
// These bytes should never appear in a UTF-8 stream.
{"\xc0", false},
{"\xc1", false},
{"\xf5", false},
{"\xf6", false},
{"\xf7", false},
{"\xf8", false},
{"\xf9", false},
{"\xfa", false},
{"\xfc", false},
{"\xfd", false},
{"\xfe", false},
{"\xff", false},
{"\xff", false},
// Examples from RFC-3629 section 7.
{"\x41\xe2\x89\xa2\xce\x91\x2e", true},
{"\xed\x95\x9c\xea\xb5\xad\xec\x96\xb4", true},
{"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e", true},
{"\xef\xbb\xbf\xf0\xa3\x8e\xb4", true},
// Examples from RFC-3629 section 10. (security concerns)
{"/../", true},
{"\x2f\xc0\xae\x2e\x2f", false}, // overlong
// Overlong encodings
{"\xc0\xc1", false}, // 'a' as two bytes
{"\xe0\x80\xc1", false}, // 'a' as three bytes
{"\xf0\x80\x80\xc1", false}, // 'a' as four bytes
/* invalid continuing bytes */
{"\xd1\xf1", false},
{"\xef\xbf\xf1", false},
{"\xef\xf1\xbf", false},
{"\xf1\xf1\xf1\xf1", false},
{"\xf1\xbf\xbf\xf1", false},
{"\xf1\xbf\xf1\xbf", false},
{"\xf1\xf1\xbf\xbf", false},
// UTF-16 surrogates
{"\xed\xa0\x80", false}, // U+D800
{"\xed\xb2\x80", false}, // U+DC80
};
class ValidUtf8Test : public TestWithParam<ValidUtf8TestInfo> {};
INSTANTIATE_TEST_CASE_P(ValidUtf8, ValidUtf8Test, ValuesIn(valid_utf8_tests));
TEST_P(ValidUtf8Test, check) {
const auto &info = GetParam();
ASSERT_EQ(info.is_valid, isValidUtf8(info.str.c_str()))
<< "String is: " << escapeString(info.str) << std::endl;
}

View File

@@ -0,0 +1,400 @@
/*
* 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 "gtest/gtest.h"
#include "config.h"
#include "ue2common.h"
#include "util/ue2string.h"
using namespace ue2;
TEST(string, case_iter1) {
const char * const expected[] = {
"3FOO-BAR",
"3FOO-BAr",
"3FOO-BaR",
"3FOO-Bar",
"3FOO-bAR",
"3FOO-bAr",
"3FOO-baR",
"3FOO-bar",
"3FOo-BAR",
"3FOo-BAr",
"3FOo-BaR",
"3FOo-Bar",
"3FOo-bAR",
"3FOo-bAr",
"3FOo-baR",
"3FOo-bar",
"3FoO-BAR",
"3FoO-BAr",
"3FoO-BaR",
"3FoO-Bar",
"3FoO-bAR",
"3FoO-bAr",
"3FoO-baR",
"3FoO-bar",
"3Foo-BAR",
"3Foo-BAr",
"3Foo-BaR",
"3Foo-Bar",
"3Foo-bAR",
"3Foo-bAr",
"3Foo-baR",
"3Foo-bar",
"3fOO-BAR",
"3fOO-BAr",
"3fOO-BaR",
"3fOO-Bar",
"3fOO-bAR",
"3fOO-bAr",
"3fOO-baR",
"3fOO-bar",
"3fOo-BAR",
"3fOo-BAr",
"3fOo-BaR",
"3fOo-Bar",
"3fOo-bAR",
"3fOo-bAr",
"3fOo-baR",
"3fOo-bar",
"3foO-BAR",
"3foO-BAr",
"3foO-BaR",
"3foO-Bar",
"3foO-bAR",
"3foO-bAr",
"3foO-baR",
"3foO-bar",
"3foo-BAR",
"3foo-BAr",
"3foo-BaR",
"3foo-Bar",
"3foo-bAR",
"3foo-bAr",
"3foo-baR",
"3foo-bar",
};
case_iter it = caseIterateBegin(ue2_literal(expected[17], true));
case_iter ite = caseIterateEnd();
u32 i = 0;
for(; it != ite; ++it, ++i) {
EXPECT_EQ(expected[i], *it);
}
EXPECT_EQ(ARRAY_LENGTH(expected), i);
}
TEST(string, case_iter2) {
case_iter it = caseIterateBegin(ue2_literal("4A", true));
case_iter ite = caseIterateEnd();
EXPECT_TRUE(ite != it);
EXPECT_EQ("4A", *it);
++it;
EXPECT_TRUE(ite != it);
EXPECT_EQ("4a", *it);
++it;
EXPECT_TRUE(!(ite != it));
}
TEST(string, case_iter3a) {
ue2_literal src;
src.push_back('a', false);
src.push_back('B', true);
src.push_back('c', false);
case_iter it = caseIterateBegin(src);
case_iter ite = caseIterateEnd();
EXPECT_TRUE(ite != it);
EXPECT_EQ("aBc", *it);
++it;
EXPECT_TRUE(ite != it);
EXPECT_EQ("abc", *it);
++it;
EXPECT_TRUE(!(ite != it));
}
TEST(string, case_iter3b) {
ue2_literal src;
src.push_back('A', false);
src.push_back('B', true);
src.push_back('C', false);
case_iter it = caseIterateBegin(src);
case_iter ite = caseIterateEnd();
EXPECT_TRUE(ite != it);
EXPECT_EQ("ABC", *it);
++it;
EXPECT_TRUE(ite != it);
EXPECT_EQ("AbC", *it);
++it;
EXPECT_TRUE(!(ite != it));
}
TEST(string, case_iter3c) {
ue2_literal src;
src.push_back('A', false);
src.push_back('B', true);
src.push_back('C', true);
case_iter it = caseIterateBegin(src);
case_iter ite = caseIterateEnd();
EXPECT_TRUE(ite != it);
EXPECT_EQ("ABC", *it);
++it;
EXPECT_TRUE(ite != it);
EXPECT_EQ("ABc", *it);
++it;
EXPECT_TRUE(ite != it);
EXPECT_EQ("AbC", *it);
++it;
EXPECT_TRUE(ite != it);
EXPECT_EQ("Abc", *it);
++it;
EXPECT_TRUE(!(ite != it));
}
TEST(string, selfOverlap) {
EXPECT_EQ(0U, maxStringSelfOverlap("abc", 0));
EXPECT_EQ(0U, maxStringSelfOverlap("abc", 1));
EXPECT_EQ(0U, maxStringSelfOverlap("abcA", 0));
EXPECT_EQ(1U, maxStringSelfOverlap("abcA", 1));
EXPECT_EQ(3U, maxStringSelfOverlap("aaaa", 0));
EXPECT_EQ(1U, maxStringSelfOverlap("aaAa", 0));
EXPECT_EQ(3U, maxStringSelfOverlap("aaAa", 1));
EXPECT_EQ(1U, maxStringSelfOverlap("aaba", 0));
EXPECT_EQ(2U, maxStringSelfOverlap("aabaa", 0));
EXPECT_EQ(3U, maxStringSelfOverlap("aabaab", 0));
}
TEST(string, reverse0) {
ue2_literal empty;
EXPECT_TRUE(empty == reverse_literal(empty));
}
TEST(string, reverse1) {
ue2_literal fwd("0123456789", false);
ue2_literal bwd("9876543210", false);
EXPECT_TRUE(bwd == reverse_literal(fwd));
}
TEST(string, reverse2) {
ue2_literal fwd;
fwd.push_back('0', false);
fwd.push_back('a', false);
fwd.push_back('b', true);
fwd.push_back('c', true);
ue2_literal bwd;
bwd.push_back('c', true);
bwd.push_back('b', true);
bwd.push_back('a', false);
bwd.push_back('0', false);
EXPECT_TRUE(bwd == reverse_literal(fwd));
}
TEST(string, iterator_op1) {
ue2_literal abcdef("abcdef", false);
ue2_literal::const_iterator b = abcdef.begin();
ue2_literal::const_iterator e = abcdef.end();
EXPECT_TRUE(b < e);
EXPECT_FALSE(b > e);
EXPECT_TRUE(e > b);
EXPECT_FALSE(e < b);
EXPECT_TRUE(e == e);
EXPECT_TRUE(b == b);
EXPECT_FALSE(e == b);
EXPECT_TRUE(b != e);
EXPECT_FALSE(b != b);
EXPECT_FALSE(e != e);
}
TEST(string, iterator_op2) {
ue2_literal empty;
ue2_literal::const_iterator b = empty.begin();
ue2_literal::const_iterator e = empty.end();
EXPECT_FALSE(b > e);
EXPECT_FALSE(b < e);
EXPECT_TRUE(e == e);
EXPECT_TRUE(b == b);
EXPECT_FALSE(e != b);
}
TEST(string, eq) {
ue2_literal empty1;
ue2_literal empty2;
ue2_literal abc_cs("abc", false);
ue2_literal abc_nc("abc", true);
ue2_literal abc_ucs("ABC", false);
ue2_literal abc_unc("ABC", true);
EXPECT_TRUE(empty1 == empty2);
EXPECT_FALSE(empty1 != empty2);
EXPECT_TRUE(abc_cs != abc_nc);
EXPECT_TRUE(abc_cs != empty1);
EXPECT_TRUE(abc_cs != abc_nc);
EXPECT_TRUE(abc_cs != abc_ucs);
EXPECT_TRUE(abc_cs != abc_unc);
EXPECT_FALSE(abc_cs == abc_nc);
EXPECT_FALSE(abc_cs == empty1);
EXPECT_FALSE(abc_cs == abc_nc);
EXPECT_FALSE(abc_cs == abc_ucs);
EXPECT_FALSE(abc_cs == abc_unc);
EXPECT_TRUE(abc_nc != abc_ucs);
EXPECT_TRUE(abc_nc == abc_unc);
EXPECT_FALSE(abc_nc == abc_ucs);
EXPECT_FALSE(abc_nc != abc_unc);
EXPECT_TRUE(abc_ucs != abc_unc);
EXPECT_FALSE(abc_ucs == abc_unc);
}
TEST(string, contains_cs) {
ue2_literal abc123("abc123", false);
EXPECT_TRUE(contains(abc123, CharReach('a')));
EXPECT_TRUE(contains(abc123, CharReach('b')));
EXPECT_TRUE(contains(abc123, CharReach('c')));
EXPECT_TRUE(contains(abc123, CharReach('1')));
EXPECT_TRUE(contains(abc123, CharReach('2')));
EXPECT_TRUE(contains(abc123, CharReach('3')));
EXPECT_TRUE(!contains(abc123, CharReach('A')));
EXPECT_TRUE(!contains(abc123, CharReach('B')));
EXPECT_TRUE(!contains(abc123, CharReach('C')));
EXPECT_TRUE(!contains(abc123, CharReach(0x11)));
EXPECT_TRUE(!contains(abc123, CharReach(0x12)));
EXPECT_TRUE(!contains(abc123, CharReach(0x13)));
EXPECT_TRUE(!contains(abc123, CharReach(0)));
EXPECT_TRUE(!contains(abc123, CharReach('X')));
EXPECT_TRUE(!contains(abc123, CharReach('\n')));
EXPECT_TRUE(contains(abc123, CharReach::dot()));
EXPECT_TRUE(contains(abc123, CharReach("Aa")));
EXPECT_TRUE(contains(abc123, CharReach("Bb")));
EXPECT_TRUE(contains(abc123, CharReach("XYZ1")));
EXPECT_TRUE(contains(abc123, CharReach("3~")));
EXPECT_TRUE(!contains(abc123, CharReach("45X")));
EXPECT_TRUE(!contains(abc123, CharReach("Bx")));
}
TEST(string, contains_nc) {
ue2_literal abc123("abc123", true);
EXPECT_TRUE(contains(abc123, CharReach('a')));
EXPECT_TRUE(contains(abc123, CharReach('b')));
EXPECT_TRUE(contains(abc123, CharReach('c')));
EXPECT_TRUE(contains(abc123, CharReach('1')));
EXPECT_TRUE(contains(abc123, CharReach('2')));
EXPECT_TRUE(contains(abc123, CharReach('3')));
EXPECT_TRUE(contains(abc123, CharReach('A')));
EXPECT_TRUE(contains(abc123, CharReach('B')));
EXPECT_TRUE(contains(abc123, CharReach('C')));
EXPECT_TRUE(!contains(abc123, CharReach(0x11)));
EXPECT_TRUE(!contains(abc123, CharReach(0x12)));
EXPECT_TRUE(!contains(abc123, CharReach(0x13)));
EXPECT_TRUE(!contains(abc123, CharReach(0)));
EXPECT_TRUE(!contains(abc123, CharReach('X')));
EXPECT_TRUE(!contains(abc123, CharReach('\n')));
EXPECT_TRUE(contains(abc123, CharReach::dot()));
EXPECT_TRUE(contains(abc123, CharReach("Aa")));
EXPECT_TRUE(contains(abc123, CharReach("Bb")));
EXPECT_TRUE(contains(abc123, CharReach("XYZ1")));
EXPECT_TRUE(contains(abc123, CharReach("3~")));
EXPECT_TRUE(!contains(abc123, CharReach("45X")));
EXPECT_TRUE(contains(abc123, CharReach("Bx")));
}
TEST(string, contains_empty) {
ue2_literal empty;
EXPECT_TRUE(!contains(empty, CharReach('a')));
EXPECT_TRUE(!contains(empty, CharReach::dot()));
}
TEST(string, cat) {
ue2_literal abc("abc", false);
ue2_literal empty;
ue2_literal def("def", true);
ue2_literal abcdef;
abcdef.push_back('a', false);
abcdef.push_back('b', false);
abcdef.push_back('c', false);
abcdef.push_back('D', true);
abcdef.push_back('e', true);
abcdef.push_back('f', true);
EXPECT_EQ(empty, empty + empty);
EXPECT_EQ(abc, abc + empty);
EXPECT_EQ(abc, empty + abc);
EXPECT_EQ(abcdef, abc + def);
}
TEST(string, erase) {
ue2_literal abcdefghi("abcdefghi", false);
ue2_literal abc = abcdefghi;
abc.erase(3, 10);
EXPECT_EQ(ue2_literal("abc", false), abc);
ue2_literal def = abcdefghi;
def.erase(0, 3);
def.erase(3);
EXPECT_EQ(ue2_literal("def", false), def);
ue2_literal bcdefghi = abcdefghi;
bcdefghi.erase(0, 1);
EXPECT_EQ(ue2_literal("bcdefghi", false), bcdefghi);
}
TEST(string, erase_nc) {
ue2_literal abcdef;
abcdef.push_back('a', false);
abcdef.push_back('b', false);
abcdef.push_back('c', false);
abcdef.push_back('D', true);
abcdef.push_back('e', true);
abcdef.push_back('f', true);
ue2_literal abc = abcdef;
abc.erase(3, 6);
EXPECT_EQ(ue2_literal("abc", false), abc);
ue2_literal def = abcdef;
def.erase(0, 3);
EXPECT_EQ(ue2_literal("def", true), def);
}

View File

@@ -0,0 +1,347 @@
/*
* 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 "gtest/gtest.h"
#include "nfa/vermicelli.h"
#define BOUND (~(VERM_BOUNDARY - 1))
TEST(Vermicelli, ExecNoMatch1) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
for (size_t j = 0; j < 16; j++) {
const u8 *rv = vermicelliExec('a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j), (size_t)rv);
rv = vermicelliExec('B', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j), (size_t)rv);
rv = vermicelliExec('A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j), (size_t)rv);
}
}
}
TEST(Vermicelli, Exec1) {
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = vermicelliExec('a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = vermicelliExec('A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Vermicelli, Exec2) {
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = vermicelliExec('a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = vermicelliExec('A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Vermicelli, Exec3) {
char t1[] = "bbbbbbbbbbbbbbbbbAaaaaaaaaaaaaaaaaaaaaaabbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = vermicelliExec('a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 18, (size_t)rv);
rv = vermicelliExec('A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(Vermicelli, Exec4) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[48 - i] = 'a';
const u8 *rv = vermicelliExec('a', 0, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
rv = vermicelliExec('A', 1, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
}
TEST(DoubleVermicelli, ExecNoMatch1) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
for (size_t j = 0; j < 16; j++) {
const u8 *rv = vermicelliDoubleExec('a', 'b', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j - 1) & BOUND, (size_t)rv);
rv = vermicelliDoubleExec('B', 'b', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j - 1) & BOUND, (size_t)rv);
rv = vermicelliDoubleExec('A', 'B', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j - 1) & BOUND, (size_t)rv);
rv = vermicelliDoubleExec('b', 'B', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j - 1) & BOUND, (size_t)rv);
rv = vermicelliDoubleExec('B', 'A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j - 1) & BOUND, (size_t)rv);
}
}
}
TEST(DoubleVermicelli, Exec1) {
char t1[] = "bbbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = vermicelliDoubleExec('a', 'b', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 18, (size_t)rv);
rv = vermicelliDoubleExec('A', 'B', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 18, (size_t)rv);
rv = vermicelliDoubleExec('b', 'a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = vermicelliDoubleExec('B', 'A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(DoubleVermicelli, Exec2) {
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbaaaaabbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = vermicelliDoubleExec('a', 'a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = vermicelliDoubleExec('A', 'A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(DoubleVermicelli, Exec3) {
/* 012345678901234567890123 */
char t1[] = "bbbbbbbbbbbbbbbbbaAaaAAaaaaaaaaaaaaaaaaaabbbbbbbaaaaabbbbbbbb";
for (size_t i = 0; i < 16; i++) {
const u8 *rv = vermicelliDoubleExec('A', 'a', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 18, (size_t)rv);
rv = vermicelliDoubleExec('A', 'A', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = vermicelliDoubleExec('A', 'A', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 21, (size_t)rv);
rv = vermicelliDoubleExec('a', 'A', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(DoubleVermicelli, Exec4) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
t1[48 - i] = 'a';
t1[48 - i + 1] = 'a';
const u8 *rv = vermicelliDoubleExec('a', 'a', 0, (u8 *)t1,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
rv = vermicelliDoubleExec('A', 'A', 1, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
}
TEST(Vermicelli, noodEarlyExit) {
// searches that should fail
static const u8 *lowerAlpha = (const u8*) "abcdefghijklmnopqrstuvwxyz";
const u8 *la_end = lowerAlpha + 26;
EXPECT_EQ(la_end, vermicelliExec('0', 0, lowerAlpha, la_end));
EXPECT_EQ(la_end, vermicelliExec('A', 0, lowerAlpha, la_end));
// single byte match
for (unsigned i = 0; i < 26; i++) {
u8 byte = lowerAlpha[i];
SCOPED_TRACE(byte);
EXPECT_EQ(lowerAlpha + i, vermicelliExec(byte, 0, lowerAlpha, la_end));
byte = toupper(lowerAlpha[i]);
EXPECT_EQ(lowerAlpha + i, vermicelliExec(byte, 1, lowerAlpha, la_end));
}
}
TEST(NVermicelli, ExecNoMatch1) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
SCOPED_TRACE(i);
for (size_t j = 0; j < 16; j++) {
SCOPED_TRACE(j);
const u8 *rv = nvermicelliExec('b', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j), (size_t)rv);
rv = nvermicelliExec('B', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1) - j);
ASSERT_EQ(((size_t)t1 + strlen(t1) - j), (size_t)rv);
}
}
}
TEST(NVermicelli, Exec1) {
char t1[] = "bbbbbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
SCOPED_TRACE(i);
const u8 *rv = nvermicelliExec('b', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = nvermicelliExec('B', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(NVermicelli, Exec2) {
char t1[] = "bbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
SCOPED_TRACE(i);
const u8 *rv = nvermicelliExec('b', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = nvermicelliExec('B', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
}
}
TEST(NVermicelli, Exec3) {
char t1[] = "bbbbbbbbbbbbbbbbbBaaaaaaaaaaaaaaaaaaaaaabbbbbbbbabbbbbbbbbbbb";
for (size_t i = 0; i < 16; i++) {
SCOPED_TRACE(i);
const u8 *rv = nvermicelliExec('b', 0, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 17, (size_t)rv);
rv = nvermicelliExec('B', 1, (u8 *)t1 + i,
(u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)t1 + 18, (size_t)rv);
}
}
TEST(NVermicelli, Exec4) {
char t1[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
for (size_t i = 0; i < 31; i++) {
SCOPED_TRACE(i);
t1[48 - i] = 'a';
const u8 *rv = nvermicelliExec('b', 0, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
rv = nvermicelliExec('B', 1, (u8 *)t1, (u8 *)t1 + strlen(t1));
ASSERT_EQ((size_t)&t1[48 - i], (size_t)rv);
}
}