vectorscan/unit/internal/sidecar.cpp
2015-10-30 11:28:37 +11:00

313 lines
9.3 KiB
C++

/*
* 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()));
auto enabled =
aligned_zmalloc_unique<sidecar_enabled>(sidecarEnabledSize(ns.get()));
sidecarEnabledInit(ns.get(), enabled.get());
auto scratch =
aligned_zmalloc_unique<sidecar_scratch>(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.get(), scratch.get(), 0,
ns_cb, &seen);
ASSERT_EQ(0U, seen);
}
sidecarEnabledAdd(ns.get(), enabled.get(), 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.get(), scratch.get(), 0,
ns_cb, &seen);
if (i == 'f') {
ASSERT_EQ(1U, seen);
} else {
ASSERT_EQ(0U, seen);
}
}
}
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()));
auto enabled =
aligned_zmalloc_unique<sidecar_enabled>(sidecarEnabledSize(ns.get()));
sidecarEnabledInit(ns.get(), enabled.get());
auto local_enabled =
aligned_zmalloc_unique<sidecar_enabled>(sidecarEnabledSize(ns.get()));
auto scratch =
aligned_zmalloc_unique<sidecar_scratch>(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.get(), scratch.get(), 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.get());
sidecarEnabledAdd(ns.get(), local_enabled.get(), j);
// union class j into our runtime enabled structure.
sidecarEnabledUnion(ns.get(), enabled.get(), local_enabled.get());
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.get(), scratch.get(),
0, set_cb, &seen);
if (i == c) {
ASSERT_EQ(1U, seen.size());
ASSERT_EQ(j, *seen.begin());
} else {
ASSERT_TRUE(seen.empty());
}
}
}
}
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()));
auto enabled =
aligned_zmalloc_unique<sidecar_enabled>(sidecarEnabledSize(ns.get()));
sidecarEnabledInit(ns.get(), enabled.get());
auto local_enabled =
aligned_zmalloc_unique<sidecar_enabled>(sidecarEnabledSize(ns.get()));
auto scratch =
aligned_zmalloc_unique<sidecar_scratch>(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.get(), scratch.get(), 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.get());
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.get());
sidecarEnabledAdd(ns.get(), local_enabled.get(), i);
// union class j into our runtime enabled structure.
sidecarEnabledUnion(ns.get(), enabled.get(), local_enabled.get());
}
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.get(), scratch.get(),
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));
}
}
}
}
INSTANTIATE_TEST_CASE_P(Sidecar, SidecarTest,
Combine(ValuesIn(sidecarModels),
ValuesIn(sidecarStrings)));
}