mirror of
https://github.com/VectorCamp/vectorscan.git
synced 2025-09-30 11:44:28 +03:00
Initial commit of Hyperscan
This commit is contained 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)));
|
Reference in New Issue
Block a user