mirror of
https://github.com/VectorCamp/vectorscan.git
synced 2025-11-15 17:02:14 +03:00
Initial commit of Hyperscan
This commit is contained in:
121
unit/CMakeLists.txt
Normal file
121
unit/CMakeLists.txt
Normal 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
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
20061
unit/gtest/gtest.h
Normal file
File diff suppressed because it is too large
Load Diff
151
unit/hyperscan/allocators.cpp
Normal file
151
unit/hyperscan/allocators.cpp
Normal 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);
|
||||
}
|
||||
2241
unit/hyperscan/arg_checks.cpp
Normal file
2241
unit/hyperscan/arg_checks.cpp
Normal file
File diff suppressed because it is too large
Load Diff
402
unit/hyperscan/bad_patterns.cpp
Normal file
402
unit/hyperscan/bad_patterns.cpp
Normal 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(®ex, &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);
|
||||
}
|
||||
129
unit/hyperscan/bad_patterns.txt
Normal file
129
unit/hyperscan/bad_patterns.txt
Normal 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
1513
unit/hyperscan/behaviour.cpp
Normal file
File diff suppressed because it is too large
Load Diff
118
unit/hyperscan/expr_info.cpp
Normal file
118
unit/hyperscan/expr_info.cpp
Normal 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
158
unit/hyperscan/extparam.cpp
Normal 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);
|
||||
}
|
||||
194
unit/hyperscan/identical.cpp
Normal file
194
unit/hyperscan/identical.cpp
Normal 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
36
unit/hyperscan/main.cpp
Normal 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
326
unit/hyperscan/multi.cpp
Normal 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
356
unit/hyperscan/order.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
289
unit/hyperscan/scratch_op.cpp
Normal file
289
unit/hyperscan/scratch_op.cpp
Normal 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
|
||||
486
unit/hyperscan/serialize.cpp
Normal file
486
unit/hyperscan/serialize.cpp
Normal 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
622
unit/hyperscan/single.cpp
Normal 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
200
unit/hyperscan/som.cpp
Normal 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));
|
||||
|
||||
767
unit/hyperscan/stream_op.cpp
Normal file
767
unit/hyperscan/stream_op.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
234
unit/hyperscan/test_util.cpp
Normal file
234
unit/hyperscan/test_util.cpp
Normal 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
120
unit/hyperscan/test_util.h
Normal 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
461
unit/internal/bitfield.cpp
Normal 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
414
unit/internal/bitutils.cpp
Normal 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
385
unit/internal/charreach.cpp
Normal 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
107
unit/internal/compare.cpp
Normal 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));
|
||||
|
||||
67
unit/internal/database.cpp
Normal file
67
unit/internal/database.cpp
Normal 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
280
unit/internal/depth.cpp
Normal 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
828
unit/internal/fdr.cpp
Normal 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
545
unit/internal/fdr_flood.cpp
Normal 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()));
|
||||
|
||||
211
unit/internal/fdr_loadval.cpp
Normal file
211
unit/internal/fdr_loadval.cpp
Normal 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
403
unit/internal/flat_map.cpp
Normal 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
394
unit/internal/flat_set.cpp
Normal 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
273
unit/internal/graph.cpp
Normal 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
258
unit/internal/lbr.cpp
Normal 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
482
unit/internal/limex_nfa.cpp
Normal 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
36
unit/internal/main.cpp
Normal 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();
|
||||
}
|
||||
62
unit/internal/masked_move.cpp
Normal file
62
unit/internal/masked_move.cpp
Normal 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
1286
unit/internal/multi_bit.cpp
Normal file
File diff suppressed because it is too large
Load Diff
61
unit/internal/nfagraph_common.h
Normal file
61
unit/internal/nfagraph_common.h
Normal 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
|
||||
81
unit/internal/nfagraph_comp.cpp
Normal file
81
unit/internal/nfagraph_comp.cpp
Normal 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());
|
||||
}
|
||||
655
unit/internal/nfagraph_equivalence.cpp
Normal file
655
unit/internal/nfagraph_equivalence.cpp
Normal 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);
|
||||
}
|
||||
272
unit/internal/nfagraph_find_matches.cpp
Normal file
272
unit/internal/nfagraph_find_matches.cpp
Normal 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));
|
||||
90
unit/internal/nfagraph_literal_analysis.cpp
Normal file
90
unit/internal/nfagraph_literal_analysis.cpp
Normal 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()));
|
||||
214
unit/internal/nfagraph_redundancy.cpp
Normal file
214
unit/internal/nfagraph_redundancy.cpp
Normal 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));
|
||||
}
|
||||
105
unit/internal/nfagraph_repeat.cpp
Normal file
105
unit/internal/nfagraph_repeat.cpp
Normal 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);
|
||||
}
|
||||
428
unit/internal/nfagraph_util.cpp
Normal file
428
unit/internal/nfagraph_util.cpp
Normal 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);
|
||||
}
|
||||
86
unit/internal/nfagraph_width.cpp
Normal file
86
unit/internal/nfagraph_width.cpp
Normal 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
294
unit/internal/noodle.cpp
Normal 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
162
unit/internal/pack_bits.cpp
Normal 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
117
unit/internal/parser.cpp
Normal 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
248
unit/internal/partial.cpp
Normal 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
325
unit/internal/pqueue.cpp
Normal 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
975
unit/internal/repeat.cpp
Normal 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)));
|
||||
138
unit/internal/rose_build_merge.cpp
Normal file
138
unit/internal/rose_build_merge.cpp
Normal 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));
|
||||
}
|
||||
220
unit/internal/rvermicelli.cpp
Normal file
220
unit/internal/rvermicelli.cpp
Normal 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
194
unit/internal/shuffle.cpp
Normal 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
977
unit/internal/shufti.cpp
Normal 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
319
unit/internal/sidecar.cpp
Normal 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)));
|
||||
|
||||
}
|
||||
643
unit/internal/simd_utils.cpp
Normal file
643
unit/internal/simd_utils.cpp
Normal 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
|
||||
410
unit/internal/state_compress.cpp
Normal file
410
unit/internal/state_compress.cpp
Normal 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
616
unit/internal/truffle.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
92
unit/internal/unaligned.cpp
Normal file
92
unit/internal/unaligned.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
295
unit/internal/unicode_set.cpp
Normal file
295
unit/internal/unicode_set.cpp
Normal 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);
|
||||
}
|
||||
202
unit/internal/uniform_ops.cpp
Normal file
202
unit/internal/uniform_ops.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
122
unit/internal/utf8_validate.cpp
Normal file
122
unit/internal/utf8_validate.cpp
Normal 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;
|
||||
}
|
||||
400
unit/internal/util_string.cpp
Normal file
400
unit/internal/util_string.cpp
Normal 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);
|
||||
}
|
||||
347
unit/internal/vermicelli.cpp
Normal file
347
unit/internal/vermicelli.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user