1 /*
2 * Copyright (c) 2016, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Intel Corporation nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30
31 #include "test_util.h"
32
33 #include "hs.h"
34 #include "gtest/gtest.h"
35
36 #include <memory>
37
38 using namespace std;
39
40 struct RescanContext {
RescanContextRescanContext41 RescanContext(const hs_database_t *db_in, hs_scratch_t *scratch_in)
42 : db(db_in), scratch(scratch_in) {}
43 const hs_database_t *db;
44 hs_scratch_t *scratch;
45 size_t matches = 0;
46 };
47
48 struct HyperscanDatabaseDeleter {
operator ()HyperscanDatabaseDeleter49 void operator()(hs_database_t *db) const {
50 hs_error_t err = hs_free_database(db);
51 EXPECT_EQ(HS_SUCCESS, err);
52 }
53 };
54
55 unique_ptr<hs_database_t, HyperscanDatabaseDeleter>
makeDatabase(const char * expression,unsigned int flags,unsigned int mode)56 makeDatabase(const char *expression, unsigned int flags, unsigned int mode) {
57 hs_database_t *db = nullptr;
58 hs_compile_error_t *compile_err = nullptr;
59 hs_error_t err = hs_compile(expression, flags, mode, nullptr, &db,
60 &compile_err);
61 EXPECT_EQ(HS_SUCCESS, err);
62
63 return unique_ptr<hs_database_t, HyperscanDatabaseDeleter>(db);
64 }
65
66 // Generic block mode test that uses the given scan callback.
67 static
runBlockTest(match_event_handler cb_func)68 void runBlockTest(match_event_handler cb_func) {
69 auto db = makeDatabase("foo.*bar", 0, HS_MODE_BLOCK);
70 ASSERT_NE(nullptr, db.get());
71
72 hs_scratch_t *scratch = nullptr;
73 hs_error_t err = hs_alloc_scratch(db.get(), &scratch);
74 ASSERT_EQ(HS_SUCCESS, err);
75 ASSERT_TRUE(scratch != nullptr);
76
77 RescanContext rc(db.get(), scratch);
78 const string data = "___foo___bar_";
79
80 err = hs_scan(db.get(), data.c_str(), data.length(), 0, scratch,
81 cb_func, &rc);
82 ASSERT_EQ(HS_SUCCESS, err);
83 ASSERT_EQ(1, rc.matches);
84
85 // teardown
86 err = hs_free_scratch(scratch);
87 ASSERT_EQ(HS_SUCCESS, err);
88 }
89
90 // Generic streaming mode test that uses the given scan callback.
91 static
runStreamingTest(match_event_handler cb_func)92 void runStreamingTest(match_event_handler cb_func) {
93 auto db = makeDatabase("foo.*bar", 0, HS_MODE_STREAM);
94 ASSERT_NE(nullptr, db.get());
95
96 hs_scratch_t *scratch = nullptr;
97 hs_error_t err = hs_alloc_scratch(db.get(), &scratch);
98 ASSERT_EQ(HS_SUCCESS, err);
99 ASSERT_TRUE(scratch != nullptr);
100
101 hs_stream_t *stream = nullptr;
102 err = hs_open_stream(db.get(), 0, &stream);
103 ASSERT_EQ(HS_SUCCESS, err);
104 ASSERT_TRUE(stream != nullptr);
105
106 RescanContext rc(db.get(), scratch);
107 const string data = "___foo___bar_";
108
109 err = hs_scan_stream(stream, data.c_str(), data.length(), 0, scratch,
110 cb_func, &rc);
111 ASSERT_EQ(HS_SUCCESS, err);
112 ASSERT_EQ(1, rc.matches);
113
114 // teardown
115 hs_close_stream(stream, scratch, nullptr, nullptr);
116 err = hs_free_scratch(scratch);
117 ASSERT_EQ(HS_SUCCESS, err);
118 }
119
120 // Generic vectored mode test that uses the given scan callback.
121 static
runVectoredTest(match_event_handler cb_func)122 void runVectoredTest(match_event_handler cb_func) {
123 auto db = makeDatabase("foo.*bar", 0, HS_MODE_VECTORED);
124 ASSERT_NE(nullptr, db.get());
125
126 hs_scratch_t *scratch = nullptr;
127 hs_error_t err = hs_alloc_scratch(db.get(), &scratch);
128 ASSERT_EQ(HS_SUCCESS, err);
129 ASSERT_TRUE(scratch != nullptr);
130
131 RescanContext rc(db.get(), scratch);
132 const string data1 = "___foo_";
133 const string data2 = "bar_";
134
135 const char *vec[] = {data1.c_str(), data2.c_str()};
136 const unsigned int len[] = {unsigned(data1.length()),
137 unsigned(data2.length())};
138
139 err = hs_scan_vector(db.get(), vec, len, 2, 0, scratch, cb_func, &rc);
140 ASSERT_EQ(HS_SUCCESS, err);
141 ASSERT_EQ(1, rc.matches);
142
143 // teardown
144 err = hs_free_scratch(scratch);
145 ASSERT_EQ(HS_SUCCESS, err);
146 }
147
148 static
rescan_block_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)149 int rescan_block_cb(unsigned, unsigned long long, unsigned long long, unsigned,
150 void *ctx) {
151 RescanContext *rctx = (RescanContext *)ctx;
152 rctx->matches++;
153
154 const string data = "___foo___bar_";
155
156 hs_error_t err = hs_scan(rctx->db, data.c_str(), data.length(), 0,
157 rctx->scratch, dummy_cb, nullptr);
158 EXPECT_EQ(HS_SCRATCH_IN_USE, err);
159 return 0;
160 }
161
162
163 // Attempt to use in-use scratch inside block mode callback.
TEST(ScratchInUse,Block)164 TEST(ScratchInUse, Block) {
165 runBlockTest(rescan_block_cb);
166 }
167
168 static
rescan_stream_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)169 int rescan_stream_cb(unsigned, unsigned long long, unsigned long long, unsigned,
170 void *ctx) {
171 RescanContext *rctx = (RescanContext *)ctx;
172 rctx->matches++;
173
174 const string data = "___foo___bar_";
175
176 hs_stream_t *stream = nullptr;
177 hs_error_t err = hs_open_stream(rctx->db, 0, &stream);
178 EXPECT_EQ(HS_SUCCESS, err);
179 EXPECT_TRUE(stream != nullptr);
180 if (stream == nullptr) {
181 return 1;
182 }
183
184 err = hs_scan_stream(stream, data.c_str(), data.length(), 0,
185 rctx->scratch, dummy_cb, nullptr);
186 EXPECT_EQ(HS_SCRATCH_IN_USE, err);
187
188 hs_close_stream(stream, nullptr, nullptr, nullptr);
189 return 0;
190 }
191
192 // Attempt to use in-use scratch inside streaming mode callback.
TEST(ScratchInUse,Streaming)193 TEST(ScratchInUse, Streaming) {
194 runStreamingTest(rescan_stream_cb);
195 }
196
197 static
rescan_vector_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)198 int rescan_vector_cb(unsigned, unsigned long long, unsigned long long, unsigned,
199 void *ctx) {
200 RescanContext *rctx = (RescanContext *)ctx;
201 rctx->matches++;
202
203 const string data1 = "___foo_";
204 const string data2 = "bar_";
205
206 const char *vec[] = {data1.c_str(), data2.c_str()};
207 const unsigned int len[] = {unsigned(data1.length()),
208 unsigned(data2.length())};
209
210 hs_error_t err = hs_scan_vector(rctx->db, vec, len, 2, 0, rctx->scratch,
211 dummy_cb, nullptr);
212 EXPECT_EQ(HS_SCRATCH_IN_USE, err);
213 return 0;
214 }
215
216 // Attempt to use in-use scratch inside vectored mode callback.
TEST(ScratchInUse,Vectored)217 TEST(ScratchInUse, Vectored) {
218 runVectoredTest(rescan_vector_cb);
219 }
220
221 static
rescan_realloc_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)222 int rescan_realloc_cb(unsigned, unsigned long long, unsigned long long,
223 unsigned, void *ctx) {
224 RescanContext *rctx = (RescanContext *)ctx;
225 rctx->matches++;
226
227 auto db = makeDatabase("another db", 0, HS_MODE_BLOCK);
228 hs_error_t err = hs_alloc_scratch(db.get(), &rctx->scratch);
229 EXPECT_EQ(HS_SCRATCH_IN_USE, err);
230 return 0;
231 }
232
233 // Attempt to use hs_alloc_scratch on in-use scratch inside callback (block
234 // scan).
TEST(ScratchInUse,ReallocScratchBlock)235 TEST(ScratchInUse, ReallocScratchBlock) {
236 runBlockTest(rescan_realloc_cb);
237 }
238
239 // Attempt to use hs_alloc_scratch on in-use scratch inside callback (streaming
240 // scan).
TEST(ScratchInUse,ReallocScratchStreaming)241 TEST(ScratchInUse, ReallocScratchStreaming) {
242 runStreamingTest(rescan_realloc_cb);
243 }
244
245 // Attempt to use hs_alloc_scratch on in-use scratch inside callback (vectored
246 // scan).
TEST(ScratchInUse,ReallocScratchVector)247 TEST(ScratchInUse, ReallocScratchVector) {
248 runVectoredTest(rescan_realloc_cb);
249 }
250
251 static
rescan_free_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)252 int rescan_free_cb(unsigned, unsigned long long, unsigned long long,
253 unsigned, void *ctx) {
254 RescanContext *rctx = (RescanContext *)ctx;
255 rctx->matches++;
256
257 hs_error_t err = hs_free_scratch(rctx->scratch);
258 EXPECT_EQ(HS_SCRATCH_IN_USE, err);
259 return 0;
260 }
261
262 // Attempt to use hs_free_scratch on in-use scratch inside callback (block
263 // scan).
TEST(ScratchInUse,FreeScratchBlock)264 TEST(ScratchInUse, FreeScratchBlock) {
265 runBlockTest(rescan_free_cb);
266 }
267
268 // Attempt to use hs_free_scratch on in-use scratch inside callback (streaming
269 // scan).
TEST(ScratchInUse,FreeScratchStreaming)270 TEST(ScratchInUse, FreeScratchStreaming) {
271 runStreamingTest(rescan_free_cb);
272 }
273
274 // Attempt to use hs_free_scratch on in-use scratch inside callback (vectored
275 // scan).
TEST(ScratchInUse,FreeScratchVector)276 TEST(ScratchInUse, FreeScratchVector) {
277 runVectoredTest(rescan_free_cb);
278 }
279