1 /*
2  * Copyright (c) 2018-2019, 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 <algorithm>
30 #include <array>
31 #include <iostream>
32 #include <vector>
33 
34 #include "gtest/gtest.h"
35 #include "hs.h"
36 #include "config.h"
37 #include "test_util.h"
38 
39 using namespace std;
40 
TEST(LogicalCombination,SingleComb1)41 TEST(LogicalCombination, SingleComb1) {
42     hs_database_t *db = nullptr;
43     hs_compile_error_t *compile_err = nullptr;
44     CallBackContext c;
45     string data = "abcdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
46     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
47                           "ijkl[mMn]", "(101 & 102 & 103) | (104 & !105)"};
48     unsigned flags[] = {0, 0, 0, 0, 0, HS_FLAG_COMBINATION};
49     unsigned ids[] = {101, 102, 103, 104, 105, 1001};
50     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
51                                       nullptr, &db, &compile_err);
52 
53     ASSERT_EQ(HS_SUCCESS, err);
54     ASSERT_TRUE(db != nullptr);
55 
56     hs_scratch_t *scratch = nullptr;
57     err = hs_alloc_scratch(db, &scratch);
58     ASSERT_EQ(HS_SUCCESS, err);
59     ASSERT_TRUE(scratch != nullptr);
60 
61     c.halt = 0;
62     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
63                   (void *)&c);
64     ASSERT_EQ(HS_SUCCESS, err);
65     ASSERT_EQ(16U, c.matches.size());
66     ASSERT_EQ(MatchRecord(3, 101), c.matches[0]);
67     ASSERT_EQ(MatchRecord(6, 102), c.matches[1]);
68     ASSERT_EQ(MatchRecord(18, 103), c.matches[2]);
69     ASSERT_EQ(MatchRecord(18, 1001), c.matches[3]);
70     ASSERT_EQ(MatchRecord(21, 101), c.matches[4]);
71     ASSERT_EQ(MatchRecord(21, 1001), c.matches[5]);
72     ASSERT_EQ(MatchRecord(25, 102), c.matches[6]);
73     ASSERT_EQ(MatchRecord(25, 1001), c.matches[7]);
74     ASSERT_EQ(MatchRecord(38, 104), c.matches[8]);
75     ASSERT_EQ(MatchRecord(38, 1001), c.matches[9]);
76     ASSERT_EQ(MatchRecord(39, 104), c.matches[10]);
77     ASSERT_EQ(MatchRecord(39, 1001), c.matches[11]);
78     ASSERT_EQ(MatchRecord(48, 105), c.matches[12]);
79     ASSERT_EQ(MatchRecord(48, 1001), c.matches[13]);
80     ASSERT_EQ(MatchRecord(53, 102), c.matches[14]);
81     ASSERT_EQ(MatchRecord(53, 1001), c.matches[15]);
82 
83     hs_free_database(db);
84     err = hs_free_scratch(scratch);
85     ASSERT_EQ(HS_SUCCESS, err);
86 }
87 
TEST(LogicalCombination,SingleCombQuietSub1)88 TEST(LogicalCombination, SingleCombQuietSub1) {
89     hs_database_t *db = nullptr;
90     hs_compile_error_t *compile_err = nullptr;
91     CallBackContext c;
92     string data = "abcdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
93     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
94                           "ijkl[mMn]", "(101 & 102 & 103) | (104 & !105)"};
95     unsigned flags[] = {HS_FLAG_QUIET, HS_FLAG_QUIET, HS_FLAG_QUIET,
96                         HS_FLAG_QUIET, 0, HS_FLAG_COMBINATION};
97     unsigned ids[] = {101, 102, 103, 104, 105, 1001};
98     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
99                                       nullptr, &db, &compile_err);
100 
101     ASSERT_EQ(HS_SUCCESS, err);
102     ASSERT_TRUE(db != nullptr);
103 
104     hs_scratch_t *scratch = nullptr;
105     err = hs_alloc_scratch(db, &scratch);
106     ASSERT_EQ(HS_SUCCESS, err);
107     ASSERT_TRUE(scratch != nullptr);
108 
109     c.halt = 0;
110     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
111                   (void *)&c);
112     ASSERT_EQ(HS_SUCCESS, err);
113     ASSERT_EQ(8U, c.matches.size());
114     ASSERT_EQ(MatchRecord(18, 1001), c.matches[0]);
115     ASSERT_EQ(MatchRecord(21, 1001), c.matches[1]);
116     ASSERT_EQ(MatchRecord(25, 1001), c.matches[2]);
117     ASSERT_EQ(MatchRecord(38, 1001), c.matches[3]);
118     ASSERT_EQ(MatchRecord(39, 1001), c.matches[4]);
119     ASSERT_EQ(MatchRecord(48, 105), c.matches[5]);
120     ASSERT_EQ(MatchRecord(48, 1001), c.matches[6]);
121     ASSERT_EQ(MatchRecord(53, 1001), c.matches[7]);
122 
123     hs_free_database(db);
124     err = hs_free_scratch(scratch);
125     ASSERT_EQ(HS_SUCCESS, err);
126 }
127 
TEST(LogicalCombination,MultiCombQuietSub1)128 TEST(LogicalCombination, MultiCombQuietSub1) {
129     hs_database_t *db = nullptr;
130     hs_compile_error_t *compile_err = nullptr;
131     CallBackContext c;
132     string data = "abcdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
133     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
134                           "ijkl[mMn]", "(101 & 102 & 103) | (104 & !105)",
135                           "!101 & 102", "!(!101 | 102)", "101 & !102"};
136     unsigned flags[] = {HS_FLAG_QUIET, HS_FLAG_QUIET, HS_FLAG_QUIET,
137                         HS_FLAG_QUIET, 0, HS_FLAG_COMBINATION,
138                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION,
139                         HS_FLAG_COMBINATION};
140     unsigned ids[] = {101, 102, 103, 104, 105, 1001, 1002, 1003, 1004};
141     hs_error_t err = hs_compile_multi(expr, flags, ids, 9, HS_MODE_NOSTREAM,
142                                       nullptr, &db, &compile_err);
143 
144     ASSERT_EQ(HS_SUCCESS, err);
145     ASSERT_TRUE(db != nullptr);
146 
147     hs_scratch_t *scratch = nullptr;
148     err = hs_alloc_scratch(db, &scratch);
149     ASSERT_EQ(HS_SUCCESS, err);
150     ASSERT_TRUE(scratch != nullptr);
151 
152     c.halt = 0;
153     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
154                   (void *)&c);
155     ASSERT_EQ(HS_SUCCESS, err);
156     ASSERT_EQ(10U, c.matches.size());
157     ASSERT_EQ(MatchRecord(3, 1003), c.matches[0]);
158     ASSERT_EQ(MatchRecord(3, 1004), c.matches[1]);
159     ASSERT_EQ(MatchRecord(18, 1001), c.matches[2]);
160     ASSERT_EQ(MatchRecord(21, 1001), c.matches[3]);
161     ASSERT_EQ(MatchRecord(25, 1001), c.matches[4]);
162     ASSERT_EQ(MatchRecord(38, 1001), c.matches[5]);
163     ASSERT_EQ(MatchRecord(39, 1001), c.matches[6]);
164     ASSERT_EQ(MatchRecord(48, 105), c.matches[7]);
165     ASSERT_EQ(MatchRecord(48, 1001), c.matches[8]);
166     ASSERT_EQ(MatchRecord(53, 1001), c.matches[9]);
167 
168     hs_free_database(db);
169     err = hs_free_scratch(scratch);
170     ASSERT_EQ(HS_SUCCESS, err);
171 }
172 
TEST(LogicalCombination,MultiHighlanderCombQuietSub1)173 TEST(LogicalCombination, MultiHighlanderCombQuietSub1) {
174     hs_database_t *db = nullptr;
175     hs_compile_error_t *compile_err = nullptr;
176     CallBackContext c;
177     string data = "abcdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
178     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
179                           "ijkl[mMn]", "(101 & 102 & 103) | (104 & !105)",
180                           "!101 & 102", "!(!101 | 102)", "101 & !102"};
181     unsigned flags[] = {HS_FLAG_QUIET, HS_FLAG_QUIET, HS_FLAG_QUIET,
182                         HS_FLAG_QUIET, 0,
183                         HS_FLAG_COMBINATION | HS_FLAG_SINGLEMATCH,
184                         HS_FLAG_COMBINATION,
185                         HS_FLAG_COMBINATION | HS_FLAG_SINGLEMATCH,
186                         HS_FLAG_COMBINATION | HS_FLAG_SINGLEMATCH};
187     unsigned ids[] = {101, 102, 103, 104, 105, 1001, 1002, 1003, 1004};
188     hs_error_t err = hs_compile_multi(expr, flags, ids, 9, HS_MODE_NOSTREAM,
189                                       nullptr, &db, &compile_err);
190 
191     ASSERT_EQ(HS_SUCCESS, err);
192     ASSERT_TRUE(db != nullptr);
193 
194     hs_scratch_t *scratch = nullptr;
195     err = hs_alloc_scratch(db, &scratch);
196     ASSERT_EQ(HS_SUCCESS, err);
197     ASSERT_TRUE(scratch != nullptr);
198 
199     c.halt = 0;
200     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
201                   (void *)&c);
202     ASSERT_EQ(HS_SUCCESS, err);
203     ASSERT_EQ(4U, c.matches.size());
204     ASSERT_EQ(MatchRecord(3, 1003), c.matches[0]);
205     ASSERT_EQ(MatchRecord(3, 1004), c.matches[1]);
206     ASSERT_EQ(MatchRecord(18, 1001), c.matches[2]);
207     ASSERT_EQ(MatchRecord(48, 105), c.matches[3]);
208 
209     hs_free_database(db);
210     err = hs_free_scratch(scratch);
211     ASSERT_EQ(HS_SUCCESS, err);
212 }
213 
TEST(LogicalCombination,MultiQuietCombQuietSub1)214 TEST(LogicalCombination, MultiQuietCombQuietSub1) {
215     hs_database_t *db = nullptr;
216     hs_compile_error_t *compile_err = nullptr;
217     CallBackContext c;
218     string data = "abcdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
219     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
220                           "ijkl[mMn]", "(101 & 102 & 103) | (104 & !105)",
221                           "!101 & 102", "!(!101 | 102)", "101 & !102"};
222     unsigned flags[] = {HS_FLAG_QUIET, HS_FLAG_QUIET, HS_FLAG_QUIET,
223                         HS_FLAG_QUIET, 0, HS_FLAG_COMBINATION | HS_FLAG_QUIET,
224                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION,
225                         HS_FLAG_COMBINATION | HS_FLAG_QUIET};
226     unsigned ids[] = {101, 102, 103, 104, 105, 1001, 1002, 1003, 1004};
227     hs_error_t err = hs_compile_multi(expr, flags, ids, 9, HS_MODE_NOSTREAM,
228                                       nullptr, &db, &compile_err);
229 
230     ASSERT_EQ(HS_SUCCESS, err);
231     ASSERT_TRUE(db != nullptr);
232 
233     hs_scratch_t *scratch = nullptr;
234     err = hs_alloc_scratch(db, &scratch);
235     ASSERT_EQ(HS_SUCCESS, err);
236     ASSERT_TRUE(scratch != nullptr);
237 
238     c.halt = 0;
239     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
240                   (void *)&c);
241     ASSERT_EQ(HS_SUCCESS, err);
242     ASSERT_EQ(2U, c.matches.size());
243     ASSERT_EQ(MatchRecord(3, 1003), c.matches[0]);
244     ASSERT_EQ(MatchRecord(48, 105), c.matches[1]);
245 
246     hs_free_database(db);
247     err = hs_free_scratch(scratch);
248     ASSERT_EQ(HS_SUCCESS, err);
249 }
250 
TEST(LogicalCombination,SingleComb2)251 TEST(LogicalCombination, SingleComb2) {
252     hs_database_t *db = nullptr;
253     hs_compile_error_t *compile_err = nullptr;
254     CallBackContext c;
255     string data = "abbdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
256     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
257                           "ijkl[mMn]", "(201 | 202 & 203) & (!204 | 205)"};
258     unsigned flags[] = {0, 0, 0, 0, 0, HS_FLAG_COMBINATION};
259     unsigned ids[] = {201, 202, 203, 204, 205, 1002};
260     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
261                                       nullptr, &db, &compile_err);
262 
263     ASSERT_EQ(HS_SUCCESS, err);
264     ASSERT_TRUE(db != nullptr);
265 
266     hs_scratch_t *scratch = nullptr;
267     err = hs_alloc_scratch(db, &scratch);
268     ASSERT_EQ(HS_SUCCESS, err);
269     ASSERT_TRUE(scratch != nullptr);
270 
271     c.halt = 0;
272     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
273                   (void *)&c);
274     ASSERT_EQ(HS_SUCCESS, err);
275     ASSERT_EQ(13U, c.matches.size());
276     ASSERT_EQ(MatchRecord(6, 202), c.matches[0]);
277     ASSERT_EQ(MatchRecord(18, 203), c.matches[1]);
278     ASSERT_EQ(MatchRecord(18, 1002), c.matches[2]);
279     ASSERT_EQ(MatchRecord(21, 201), c.matches[3]);
280     ASSERT_EQ(MatchRecord(21, 1002), c.matches[4]);
281     ASSERT_EQ(MatchRecord(25, 202), c.matches[5]);
282     ASSERT_EQ(MatchRecord(25, 1002), c.matches[6]);
283     ASSERT_EQ(MatchRecord(38, 204), c.matches[7]);
284     ASSERT_EQ(MatchRecord(39, 204), c.matches[8]);
285     ASSERT_EQ(MatchRecord(48, 205), c.matches[9]);
286     ASSERT_EQ(MatchRecord(48, 1002), c.matches[10]);
287     ASSERT_EQ(MatchRecord(53, 202), c.matches[11]);
288     ASSERT_EQ(MatchRecord(53, 1002), c.matches[12]);
289 
290     hs_free_database(db);
291     err = hs_free_scratch(scratch);
292     ASSERT_EQ(HS_SUCCESS, err);
293 }
294 
TEST(LogicalCombination,SingleCombQuietSub2)295 TEST(LogicalCombination, SingleCombQuietSub2) {
296     hs_database_t *db = nullptr;
297     hs_compile_error_t *compile_err = nullptr;
298     CallBackContext c;
299     string data = "abbdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
300     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
301                           "ijkl[mMn]", "(201 | 202 & 203) & (!204 | 205)"};
302     unsigned flags[] = {0, HS_FLAG_QUIET, HS_FLAG_QUIET, 0, HS_FLAG_QUIET,
303                         HS_FLAG_COMBINATION};
304     unsigned ids[] = {201, 202, 203, 204, 205, 1002};
305     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
306                                       nullptr, &db, &compile_err);
307 
308     ASSERT_EQ(HS_SUCCESS, err);
309     ASSERT_TRUE(db != nullptr);
310 
311     hs_scratch_t *scratch = nullptr;
312     err = hs_alloc_scratch(db, &scratch);
313     ASSERT_EQ(HS_SUCCESS, err);
314     ASSERT_TRUE(scratch != nullptr);
315 
316     c.halt = 0;
317     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
318                   (void *)&c);
319     ASSERT_EQ(HS_SUCCESS, err);
320     ASSERT_EQ(8U, c.matches.size());
321     ASSERT_EQ(MatchRecord(18, 1002), c.matches[0]);
322     ASSERT_EQ(MatchRecord(21, 201), c.matches[1]);
323     ASSERT_EQ(MatchRecord(21, 1002), c.matches[2]);
324     ASSERT_EQ(MatchRecord(25, 1002), c.matches[3]);
325     ASSERT_EQ(MatchRecord(38, 204), c.matches[4]);
326     ASSERT_EQ(MatchRecord(39, 204), c.matches[5]);
327     ASSERT_EQ(MatchRecord(48, 1002), c.matches[6]);
328     ASSERT_EQ(MatchRecord(53, 1002), c.matches[7]);
329 
330     hs_free_database(db);
331     err = hs_free_scratch(scratch);
332     ASSERT_EQ(HS_SUCCESS, err);
333 }
334 
TEST(LogicalCombination,SingleComb3)335 TEST(LogicalCombination, SingleComb3) {
336     hs_database_t *db = nullptr;
337     hs_compile_error_t *compile_err = nullptr;
338     CallBackContext c;
339     string data = "abcijklndefxxfoobarrrghabcxdefxteakettleeeeexxxxijklnxxdef";
340     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
341                           "ijkl[mMn]", "((301 | 302) & 303) & (304 | 305)"};
342     unsigned flags[] = {0, 0, 0, 0, 0, HS_FLAG_COMBINATION};
343     unsigned ids[] = {301, 302, 303, 304, 305, 1003};
344     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
345                                       nullptr, &db, &compile_err);
346 
347     ASSERT_EQ(HS_SUCCESS, err);
348     ASSERT_TRUE(db != nullptr);
349 
350     hs_scratch_t *scratch = nullptr;
351     err = hs_alloc_scratch(db, &scratch);
352     ASSERT_EQ(HS_SUCCESS, err);
353     ASSERT_TRUE(scratch != nullptr);
354 
355     c.halt = 0;
356     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
357                   (void *)&c);
358     ASSERT_EQ(HS_SUCCESS, err);
359     ASSERT_EQ(17U, c.matches.size());
360     ASSERT_EQ(MatchRecord(3, 301), c.matches[0]);
361     ASSERT_EQ(MatchRecord(8, 305), c.matches[1]);
362     ASSERT_EQ(MatchRecord(11, 302), c.matches[2]);
363     ASSERT_EQ(MatchRecord(23, 303), c.matches[3]);
364     ASSERT_EQ(MatchRecord(23, 1003), c.matches[4]);
365     ASSERT_EQ(MatchRecord(26, 301), c.matches[5]);
366     ASSERT_EQ(MatchRecord(26, 1003), c.matches[6]);
367     ASSERT_EQ(MatchRecord(30, 302), c.matches[7]);
368     ASSERT_EQ(MatchRecord(30, 1003), c.matches[8]);
369     ASSERT_EQ(MatchRecord(43, 304), c.matches[9]);
370     ASSERT_EQ(MatchRecord(43, 1003), c.matches[10]);
371     ASSERT_EQ(MatchRecord(44, 304), c.matches[11]);
372     ASSERT_EQ(MatchRecord(44, 1003), c.matches[12]);
373     ASSERT_EQ(MatchRecord(53, 305), c.matches[13]);
374     ASSERT_EQ(MatchRecord(53, 1003), c.matches[14]);
375     ASSERT_EQ(MatchRecord(58, 302), c.matches[15]);
376     ASSERT_EQ(MatchRecord(58, 1003), c.matches[16]);
377 
378     hs_free_database(db);
379     err = hs_free_scratch(scratch);
380     ASSERT_EQ(HS_SUCCESS, err);
381 }
382 
TEST(LogicalCombination,SingleCombQuietSub3)383 TEST(LogicalCombination, SingleCombQuietSub3) {
384     hs_database_t *db = nullptr;
385     hs_compile_error_t *compile_err = nullptr;
386     CallBackContext c;
387     string data = "abcijklndefxxfoobarrrghabcxdefxteakettleeeeexxxxijklnxxdef";
388     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
389                           "ijkl[mMn]", "((301 | 302) & 303) & (304 | 305)"};
390     unsigned flags[] = {HS_FLAG_QUIET, HS_FLAG_QUIET, 0, HS_FLAG_QUIET,
391                         HS_FLAG_QUIET, HS_FLAG_COMBINATION};
392     unsigned ids[] = {301, 302, 303, 304, 305, 1003};
393     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
394                                       nullptr, &db, &compile_err);
395 
396     ASSERT_EQ(HS_SUCCESS, err);
397     ASSERT_TRUE(db != nullptr);
398 
399     hs_scratch_t *scratch = nullptr;
400     err = hs_alloc_scratch(db, &scratch);
401     ASSERT_EQ(HS_SUCCESS, err);
402     ASSERT_TRUE(scratch != nullptr);
403 
404     c.halt = 0;
405     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
406                   (void *)&c);
407     ASSERT_EQ(HS_SUCCESS, err);
408     ASSERT_EQ(8U, c.matches.size());
409     ASSERT_EQ(MatchRecord(23, 303), c.matches[0]);
410     ASSERT_EQ(MatchRecord(23, 1003), c.matches[1]);
411     ASSERT_EQ(MatchRecord(26, 1003), c.matches[2]);
412     ASSERT_EQ(MatchRecord(30, 1003), c.matches[3]);
413     ASSERT_EQ(MatchRecord(43, 1003), c.matches[4]);
414     ASSERT_EQ(MatchRecord(44, 1003), c.matches[5]);
415     ASSERT_EQ(MatchRecord(53, 1003), c.matches[6]);
416     ASSERT_EQ(MatchRecord(58, 1003), c.matches[7]);
417 
418     hs_free_database(db);
419     err = hs_free_scratch(scratch);
420     ASSERT_EQ(HS_SUCCESS, err);
421 }
422 
TEST(LogicalCombination,MultiCombDupSub4)423 TEST(LogicalCombination, MultiCombDupSub4) {
424     hs_database_t *db = nullptr;
425     hs_compile_error_t *compile_err = nullptr;
426     CallBackContext c;
427     string data = "abbdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
428     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
429                           "ijkl[mMn]", "(201 & 202 & 203) | (204 & !205)",
430                           "(201 | 202 & 203) & (!204 | 205)",
431                           "((201 | 202) & 203) & (204 | 205)"};
432     unsigned flags[] = {0, 0, 0, 0, 0, HS_FLAG_COMBINATION,
433                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION};
434     unsigned ids[] = {201, 202, 203, 204, 205, 1001, 1002, 1003};
435     hs_error_t err = hs_compile_multi(expr, flags, ids, 8, HS_MODE_NOSTREAM,
436                                       nullptr, &db, &compile_err);
437 
438     ASSERT_EQ(HS_SUCCESS, err);
439     ASSERT_TRUE(db != nullptr);
440 
441     hs_scratch_t *scratch = nullptr;
442     err = hs_alloc_scratch(db, &scratch);
443     ASSERT_EQ(HS_SUCCESS, err);
444     ASSERT_TRUE(scratch != nullptr);
445 
446     c.halt = 0;
447     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
448                   (void *)&c);
449     ASSERT_EQ(HS_SUCCESS, err);
450     ASSERT_EQ(23U, c.matches.size());
451     ASSERT_EQ(MatchRecord(6, 202), c.matches[0]);
452     ASSERT_EQ(MatchRecord(18, 203), c.matches[1]);
453     ASSERT_EQ(MatchRecord(18, 1002), c.matches[2]);
454     ASSERT_EQ(MatchRecord(21, 201), c.matches[3]);
455     ASSERT_EQ(MatchRecord(21, 1001), c.matches[4]);
456     ASSERT_EQ(MatchRecord(21, 1002), c.matches[5]);
457     ASSERT_EQ(MatchRecord(25, 202), c.matches[6]);
458     ASSERT_EQ(MatchRecord(25, 1001), c.matches[7]);
459     ASSERT_EQ(MatchRecord(25, 1002), c.matches[8]);
460     ASSERT_EQ(MatchRecord(38, 204), c.matches[9]);
461     ASSERT_EQ(MatchRecord(38, 1001), c.matches[10]);
462     ASSERT_EQ(MatchRecord(38, 1003), c.matches[11]);
463     ASSERT_EQ(MatchRecord(39, 204), c.matches[12]);
464     ASSERT_EQ(MatchRecord(39, 1001), c.matches[13]);
465     ASSERT_EQ(MatchRecord(39, 1003), c.matches[14]);
466     ASSERT_EQ(MatchRecord(48, 205), c.matches[15]);
467     ASSERT_EQ(MatchRecord(48, 1001), c.matches[16]);
468     ASSERT_EQ(MatchRecord(48, 1002), c.matches[17]);
469     ASSERT_EQ(MatchRecord(48, 1003), c.matches[18]);
470     ASSERT_EQ(MatchRecord(53, 202), c.matches[19]);
471     ASSERT_EQ(MatchRecord(53, 1001), c.matches[20]);
472     ASSERT_EQ(MatchRecord(53, 1002), c.matches[21]);
473     ASSERT_EQ(MatchRecord(53, 1003), c.matches[22]);
474 
475     hs_free_database(db);
476     err = hs_free_scratch(scratch);
477     ASSERT_EQ(HS_SUCCESS, err);
478 }
479 
TEST(LogicalCombination,MultiCombQuietDupSub4)480 TEST(LogicalCombination, MultiCombQuietDupSub4) {
481     hs_database_t *db = nullptr;
482     hs_compile_error_t *compile_err = nullptr;
483     CallBackContext c;
484     string data = "abbdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef";
485     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
486                           "ijkl[mMn]", "(201 & 202 & 203) | (204 & !205)",
487                           "(201 | 202 & 203) & (!204 | 205)",
488                           "((201 | 202) & 203) & (204 | 205)"};
489     unsigned flags[] = {HS_FLAG_QUIET, HS_FLAG_QUIET, HS_FLAG_QUIET, 0,
490                         HS_FLAG_QUIET, HS_FLAG_COMBINATION,
491                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION};
492     unsigned ids[] = {201, 202, 203, 204, 205, 1001, 1002, 1003};
493     hs_error_t err = hs_compile_multi(expr, flags, ids, 8, HS_MODE_NOSTREAM,
494                                       nullptr, &db, &compile_err);
495 
496     ASSERT_EQ(HS_SUCCESS, err);
497     ASSERT_TRUE(db != nullptr);
498 
499     hs_scratch_t *scratch = nullptr;
500     err = hs_alloc_scratch(db, &scratch);
501     ASSERT_EQ(HS_SUCCESS, err);
502     ASSERT_TRUE(scratch != nullptr);
503 
504     c.halt = 0;
505     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
506                   (void *)&c);
507     ASSERT_EQ(HS_SUCCESS, err);
508     ASSERT_EQ(17U, c.matches.size());
509     ASSERT_EQ(MatchRecord(18, 1002), c.matches[0]);
510     ASSERT_EQ(MatchRecord(21, 1001), c.matches[1]);
511     ASSERT_EQ(MatchRecord(21, 1002), c.matches[2]);
512     ASSERT_EQ(MatchRecord(25, 1001), c.matches[3]);
513     ASSERT_EQ(MatchRecord(25, 1002), c.matches[4]);
514     ASSERT_EQ(MatchRecord(38, 204), c.matches[5]);
515     ASSERT_EQ(MatchRecord(38, 1001), c.matches[6]);
516     ASSERT_EQ(MatchRecord(38, 1003), c.matches[7]);
517     ASSERT_EQ(MatchRecord(39, 204), c.matches[8]);
518     ASSERT_EQ(MatchRecord(39, 1001), c.matches[9]);
519     ASSERT_EQ(MatchRecord(39, 1003), c.matches[10]);
520     ASSERT_EQ(MatchRecord(48, 1001), c.matches[11]);
521     ASSERT_EQ(MatchRecord(48, 1002), c.matches[12]);
522     ASSERT_EQ(MatchRecord(48, 1003), c.matches[13]);
523     ASSERT_EQ(MatchRecord(53, 1001), c.matches[14]);
524     ASSERT_EQ(MatchRecord(53, 1002), c.matches[15]);
525     ASSERT_EQ(MatchRecord(53, 1003), c.matches[16]);
526 
527     hs_free_database(db);
528     err = hs_free_scratch(scratch);
529     ASSERT_EQ(HS_SUCCESS, err);
530 }
531 
TEST(LogicalCombination,MultiCombUniSub5)532 TEST(LogicalCombination, MultiCombUniSub5) {
533     hs_database_t *db = nullptr;
534     hs_compile_error_t *compile_err = nullptr;
535     CallBackContext c;
536     string data = "abcdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef"
537                   "-----------------------------------------------"
538                   "cbbfedxxgoogleeecncbaxfedxhaystacksssssxxxxijkloxxfed"
539                   "-----------------------------------------------"
540                   "cabijklRfeexxgoobarrrjpcabxfeexshockwaveeeeexxxxijklsxxfee"
541                   "------------------------------------------";
542     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
543                           "ijkl[mMn]", "cba", "fed", "google.*cn",
544                           "haystacks{4,8}", "ijkl[oOp]", "cab", "fee",
545                           "goobar.*jp", "shockwave{4,6}", "ijkl[rRs]",
546                           "(101 & 102 & 103) | (104 & !105)",
547                           "(201 | 202 & 203) & (!204 | 205)",
548                           "((301 | 302) & 303) & (304 | 305)"};
549     unsigned flags[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
550                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION,
551                         HS_FLAG_COMBINATION};
552     unsigned ids[] = {101, 102, 103, 104, 105, 201, 202, 203, 204, 205, 301,
553                       302, 303, 304, 305, 1001, 1002, 1003};
554     hs_error_t err = hs_compile_multi(expr, flags, ids, 18, HS_MODE_NOSTREAM,
555                                       nullptr, &db, &compile_err);
556 
557     ASSERT_EQ(HS_SUCCESS, err);
558     ASSERT_TRUE(db != nullptr);
559 
560     hs_scratch_t *scratch = nullptr;
561     err = hs_alloc_scratch(db, &scratch);
562     ASSERT_EQ(HS_SUCCESS, err);
563     ASSERT_TRUE(scratch != nullptr);
564 
565     c.halt = 0;
566     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
567                   (void *)&c);
568     ASSERT_EQ(HS_SUCCESS, err);
569     ASSERT_EQ(46U, c.matches.size());
570     ASSERT_EQ(MatchRecord(3, 101), c.matches[0]);
571     ASSERT_EQ(MatchRecord(6, 102), c.matches[1]);
572     ASSERT_EQ(MatchRecord(18, 103), c.matches[2]);
573     ASSERT_EQ(MatchRecord(18, 1001), c.matches[3]);
574     ASSERT_EQ(MatchRecord(21, 101), c.matches[4]);
575     ASSERT_EQ(MatchRecord(21, 1001), c.matches[5]);
576     ASSERT_EQ(MatchRecord(25, 102), c.matches[6]);
577     ASSERT_EQ(MatchRecord(25, 1001), c.matches[7]);
578     ASSERT_EQ(MatchRecord(38, 104), c.matches[8]);
579     ASSERT_EQ(MatchRecord(38, 1001), c.matches[9]);
580     ASSERT_EQ(MatchRecord(39, 104), c.matches[10]);
581     ASSERT_EQ(MatchRecord(39, 1001), c.matches[11]);
582     ASSERT_EQ(MatchRecord(48, 105), c.matches[12]);
583     ASSERT_EQ(MatchRecord(48, 1001), c.matches[13]);
584     ASSERT_EQ(MatchRecord(53, 102), c.matches[14]);
585     ASSERT_EQ(MatchRecord(53, 1001), c.matches[15]);
586     ASSERT_EQ(MatchRecord(106, 202), c.matches[16]);
587     ASSERT_EQ(MatchRecord(118, 203), c.matches[17]);
588     ASSERT_EQ(MatchRecord(118, 1002), c.matches[18]);
589     ASSERT_EQ(MatchRecord(121, 201), c.matches[19]);
590     ASSERT_EQ(MatchRecord(121, 1002), c.matches[20]);
591     ASSERT_EQ(MatchRecord(125, 202), c.matches[21]);
592     ASSERT_EQ(MatchRecord(125, 1002), c.matches[22]);
593     ASSERT_EQ(MatchRecord(138, 204), c.matches[23]);
594     ASSERT_EQ(MatchRecord(139, 204), c.matches[24]);
595     ASSERT_EQ(MatchRecord(148, 205), c.matches[25]);
596     ASSERT_EQ(MatchRecord(148, 1002), c.matches[26]);
597     ASSERT_EQ(MatchRecord(153, 202), c.matches[27]);
598     ASSERT_EQ(MatchRecord(153, 1002), c.matches[28]);
599     ASSERT_EQ(MatchRecord(203, 301), c.matches[29]);
600     ASSERT_EQ(MatchRecord(208, 305), c.matches[30]);
601     ASSERT_EQ(MatchRecord(211, 302), c.matches[31]);
602     ASSERT_EQ(MatchRecord(223, 303), c.matches[32]);
603     ASSERT_EQ(MatchRecord(223, 1003), c.matches[33]);
604     ASSERT_EQ(MatchRecord(226, 301), c.matches[34]);
605     ASSERT_EQ(MatchRecord(226, 1003), c.matches[35]);
606     ASSERT_EQ(MatchRecord(230, 302), c.matches[36]);
607     ASSERT_EQ(MatchRecord(230, 1003), c.matches[37]);
608     ASSERT_EQ(MatchRecord(243, 304), c.matches[38]);
609     ASSERT_EQ(MatchRecord(243, 1003), c.matches[39]);
610     ASSERT_EQ(MatchRecord(244, 304), c.matches[40]);
611     ASSERT_EQ(MatchRecord(244, 1003), c.matches[41]);
612     ASSERT_EQ(MatchRecord(253, 305), c.matches[42]);
613     ASSERT_EQ(MatchRecord(253, 1003), c.matches[43]);
614     ASSERT_EQ(MatchRecord(258, 302), c.matches[44]);
615     ASSERT_EQ(MatchRecord(258, 1003), c.matches[45]);
616 
617     hs_free_database(db);
618     err = hs_free_scratch(scratch);
619     ASSERT_EQ(HS_SUCCESS, err);
620 }
621 
TEST(LogicalCombination,MultiCombQuietUniSub5)622 TEST(LogicalCombination, MultiCombQuietUniSub5) {
623     hs_database_t *db = nullptr;
624     hs_compile_error_t *compile_err = nullptr;
625     CallBackContext c;
626     string data = "abcdefxxfoobarrrghabcxdefxteakettleeeeexxxxijklmxxdef"
627                   "-----------------------------------------------"
628                   "cbbfedxxgoogleeecncbaxfedxhaystacksssssxxxxijkloxxfed"
629                   "-----------------------------------------------"
630                   "cabijklRfeexxgoobarrrjpcabxfeexshockwaveeeeexxxxijklsxxfee"
631                   "------------------------------------------";
632     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
633                           "ijkl[mMn]", "cba", "fed", "google.*cn",
634                           "haystacks{4,8}", "ijkl[oOp]", "cab", "fee",
635                           "goobar.*jp", "shockwave{4,6}", "ijkl[rRs]",
636                           "(101 & 102 & 103) | (104 & !105)",
637                           "(201 | 202 & 203) & (!204 | 205)",
638                           "((301 | 302) & 303) & (304 | 305)"};
639     unsigned flags[] = {0, HS_FLAG_QUIET, HS_FLAG_QUIET, HS_FLAG_QUIET, 0,
640                         HS_FLAG_QUIET, 0, HS_FLAG_QUIET, 0, HS_FLAG_QUIET,
641                         HS_FLAG_QUIET, HS_FLAG_QUIET, 0, HS_FLAG_QUIET, 0,
642                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION,
643                         HS_FLAG_COMBINATION};
644     unsigned ids[] = {101, 102, 103, 104, 105, 201, 202, 203, 204, 205, 301,
645                       302, 303, 304, 305, 1001, 1002, 1003};
646     hs_error_t err = hs_compile_multi(expr, flags, ids, 18, HS_MODE_NOSTREAM,
647                                       nullptr, &db, &compile_err);
648 
649     ASSERT_EQ(HS_SUCCESS, err);
650     ASSERT_TRUE(db != nullptr);
651 
652     hs_scratch_t *scratch = nullptr;
653     err = hs_alloc_scratch(db, &scratch);
654     ASSERT_EQ(HS_SUCCESS, err);
655     ASSERT_TRUE(scratch != nullptr);
656 
657     c.halt = 0;
658     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
659                   (void *)&c);
660     ASSERT_EQ(HS_SUCCESS, err);
661     ASSERT_EQ(30U, c.matches.size());
662     ASSERT_EQ(MatchRecord(3, 101), c.matches[0]);
663     ASSERT_EQ(MatchRecord(18, 1001), c.matches[1]);
664     ASSERT_EQ(MatchRecord(21, 101), c.matches[2]);
665     ASSERT_EQ(MatchRecord(21, 1001), c.matches[3]);
666     ASSERT_EQ(MatchRecord(25, 1001), c.matches[4]);
667     ASSERT_EQ(MatchRecord(38, 1001), c.matches[5]);
668     ASSERT_EQ(MatchRecord(39, 1001), c.matches[6]);
669     ASSERT_EQ(MatchRecord(48, 105), c.matches[7]);
670     ASSERT_EQ(MatchRecord(48, 1001), c.matches[8]);
671     ASSERT_EQ(MatchRecord(53, 1001), c.matches[9]);
672     ASSERT_EQ(MatchRecord(106, 202), c.matches[10]);
673     ASSERT_EQ(MatchRecord(118, 1002), c.matches[11]);
674     ASSERT_EQ(MatchRecord(121, 1002), c.matches[12]);
675     ASSERT_EQ(MatchRecord(125, 202), c.matches[13]);
676     ASSERT_EQ(MatchRecord(125, 1002), c.matches[14]);
677     ASSERT_EQ(MatchRecord(138, 204), c.matches[15]);
678     ASSERT_EQ(MatchRecord(139, 204), c.matches[16]);
679     ASSERT_EQ(MatchRecord(148, 1002), c.matches[17]);
680     ASSERT_EQ(MatchRecord(153, 202), c.matches[18]);
681     ASSERT_EQ(MatchRecord(153, 1002), c.matches[19]);
682     ASSERT_EQ(MatchRecord(208, 305), c.matches[20]);
683     ASSERT_EQ(MatchRecord(223, 303), c.matches[21]);
684     ASSERT_EQ(MatchRecord(223, 1003), c.matches[22]);
685     ASSERT_EQ(MatchRecord(226, 1003), c.matches[23]);
686     ASSERT_EQ(MatchRecord(230, 1003), c.matches[24]);
687     ASSERT_EQ(MatchRecord(243, 1003), c.matches[25]);
688     ASSERT_EQ(MatchRecord(244, 1003), c.matches[26]);
689     ASSERT_EQ(MatchRecord(253, 305), c.matches[27]);
690     ASSERT_EQ(MatchRecord(253, 1003), c.matches[28]);
691     ASSERT_EQ(MatchRecord(258, 1003), c.matches[29]);
692 
693     hs_free_database(db);
694     err = hs_free_scratch(scratch);
695     ASSERT_EQ(HS_SUCCESS, err);
696 }
697 
TEST(LogicalCombination,SingleCombPurelyNegative6)698 TEST(LogicalCombination, SingleCombPurelyNegative6) {
699     hs_database_t *db = nullptr;
700     hs_compile_error_t *compile_err = nullptr;
701     CallBackContext c;
702     string data = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
703     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
704                           "ijkl[mMn]", "(!201 | 202 & 203) & (!204 | 205)"};
705     unsigned flags[] = {0, 0, 0, 0, 0, HS_FLAG_COMBINATION};
706     unsigned ids[] = {201, 202, 203, 204, 205, 1002};
707     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
708                                       nullptr, &db, &compile_err);
709 
710     ASSERT_EQ(HS_SUCCESS, err);
711     ASSERT_TRUE(db != nullptr);
712 
713     hs_scratch_t *scratch = nullptr;
714     err = hs_alloc_scratch(db, &scratch);
715     ASSERT_EQ(HS_SUCCESS, err);
716     ASSERT_TRUE(scratch != nullptr);
717 
718     c.halt = 0;
719     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
720                   (void *)&c);
721     ASSERT_EQ(HS_SUCCESS, err);
722     ASSERT_EQ(1U, c.matches.size());
723     ASSERT_EQ(MatchRecord(53, 1002), c.matches[0]);
724 
725     hs_free_database(db);
726     err = hs_free_scratch(scratch);
727     ASSERT_EQ(HS_SUCCESS, err);
728 }
729 
TEST(LogicalCombination,SingleCombQuietPurelyNegative6)730 TEST(LogicalCombination, SingleCombQuietPurelyNegative6) {
731     hs_database_t *db = nullptr;
732     hs_compile_error_t *compile_err = nullptr;
733     CallBackContext c;
734     string data = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
735     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
736                           "ijkl[mMn]", "(!201 | 202 & 203) & (!204 | 205)"};
737     unsigned flags[] = {0, 0, 0, 0, 0, HS_FLAG_COMBINATION | HS_FLAG_QUIET};
738     unsigned ids[] = {201, 202, 203, 204, 205, 1002};
739     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_NOSTREAM,
740                                       nullptr, &db, &compile_err);
741 
742     ASSERT_EQ(HS_SUCCESS, err);
743     ASSERT_TRUE(db != nullptr);
744 
745     hs_scratch_t *scratch = nullptr;
746     err = hs_alloc_scratch(db, &scratch);
747     ASSERT_EQ(HS_SUCCESS, err);
748     ASSERT_TRUE(scratch != nullptr);
749 
750     c.halt = 0;
751     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
752                   (void *)&c);
753     ASSERT_EQ(HS_SUCCESS, err);
754     ASSERT_EQ(0U, c.matches.size());
755 
756     hs_free_database(db);
757     err = hs_free_scratch(scratch);
758     ASSERT_EQ(HS_SUCCESS, err);
759 }
760 
TEST(LogicalCombination,MultiCombPurelyNegativeUniSub6)761 TEST(LogicalCombination, MultiCombPurelyNegativeUniSub6) {
762     hs_database_t *db = nullptr;
763     hs_compile_error_t *compile_err = nullptr;
764     CallBackContext c;
765     string data = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
766                   "-----------------------------------------------"
767                   "xxxfedxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
768                   "-----------------------------------------------"
769                   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
770                   "------------------------------------------";
771     const char *expr[] = {"abc", "def", "foobar.*gh", "teakettle{4,10}",
772                           "ijkl[mMn]", "cba", "fed", "google.*cn",
773                           "haystacks{4,8}", "ijkl[oOp]", "cab", "fee",
774                           "goobar.*jp", "shockwave{4,6}", "ijkl[rRs]",
775                           "(101 & 102 & 103) | (!104 & !105)",
776                           "(!201 | 202 & 203) & (!204 | 205)",
777                           "((301 | 302) & 303) & (304 | 305)"};
778     unsigned flags[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
779                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION,
780                         HS_FLAG_COMBINATION};
781     unsigned ids[] = {101, 102, 103, 104, 105, 201, 202, 203, 204, 205, 301,
782                       302, 303, 304, 305, 1001, 1002, 1003};
783     hs_error_t err = hs_compile_multi(expr, flags, ids, 18, HS_MODE_NOSTREAM,
784                                       nullptr, &db, &compile_err);
785 
786     ASSERT_EQ(HS_SUCCESS, err);
787     ASSERT_TRUE(db != nullptr);
788 
789     hs_scratch_t *scratch = nullptr;
790     err = hs_alloc_scratch(db, &scratch);
791     ASSERT_EQ(HS_SUCCESS, err);
792     ASSERT_TRUE(scratch != nullptr);
793 
794     c.halt = 0;
795     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
796                   (void *)&c);
797     ASSERT_EQ(HS_SUCCESS, err);
798     ASSERT_EQ(3U, c.matches.size());
799     ASSERT_EQ(MatchRecord(106, 202), c.matches[0]);
800     ASSERT_EQ(MatchRecord(106, 1002), c.matches[1]);
801     ASSERT_EQ(MatchRecord(300, 1001), c.matches[2]);
802 
803     hs_free_database(db);
804     err = hs_free_scratch(scratch);
805     ASSERT_EQ(HS_SUCCESS, err);
806 }
807 
TEST(LogicalCombination,MultiCombPurelyNegativeUniSubEOD6)808 TEST(LogicalCombination, MultiCombPurelyNegativeUniSubEOD6) {
809     hs_database_t *db = nullptr;
810     hs_compile_error_t *compile_err = nullptr;
811     CallBackContext c;
812     string data = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
813                   "-----------------------------------------------"
814                   "xdefedxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
815                   "-----------------------------------------------"
816                   "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
817                   "-------------------------------------defed";
818     const char *expr[] = {"abc", "defed", "foobar.*gh", "teakettle{4,10}",
819                           "ijkl[mMn]", "cba", "fed", "google.*cn",
820                           "haystacks{4,8}", "ijkl[oOp]", "cab", "fee",
821                           "goobar.*jp", "shockwave{4,6}", "ijkl[rRs]",
822                           "(101 & 102 & 103) | (!104 & !105)",
823                           "(!201 | 202 & 203) & (!204 | 205)",
824                           "((301 | 302) & 303) & (304 | 305)"};
825     unsigned flags[] = {0, 0, 0, 0, 0, 0, HS_FLAG_MULTILINE,
826                         0, 0, 0, 0, 0, 0, 0, 0,
827                         HS_FLAG_COMBINATION, HS_FLAG_COMBINATION,
828                         HS_FLAG_COMBINATION};
829     unsigned ids[] = {101, 102, 103, 104, 105, 201, 202, 203, 204, 205, 301,
830                       302, 303, 304, 305, 1001, 1002, 1003};
831     hs_error_t err = hs_compile_multi(expr, flags, ids, 18, HS_MODE_NOSTREAM,
832                                       nullptr, &db, &compile_err);
833 
834     ASSERT_EQ(HS_SUCCESS, err);
835     ASSERT_TRUE(db != nullptr);
836 
837     hs_scratch_t *scratch = nullptr;
838     err = hs_alloc_scratch(db, &scratch);
839     ASSERT_EQ(HS_SUCCESS, err);
840     ASSERT_TRUE(scratch != nullptr);
841 
842     c.halt = 0;
843     err = hs_scan(db, data.c_str(), data.size(), 0, scratch, record_cb,
844                   (void *)&c);
845     ASSERT_EQ(HS_SUCCESS, err);
846     ASSERT_EQ(8U, c.matches.size());
847     ASSERT_EQ(MatchRecord(106, 102), c.matches[0]);
848     ASSERT_EQ(MatchRecord(106, 202), c.matches[1]);
849     ASSERT_EQ(MatchRecord(106, 1001), c.matches[2]);
850     ASSERT_EQ(MatchRecord(106, 1002), c.matches[3]);
851     ASSERT_EQ(MatchRecord(300, 102), c.matches[4]);
852     ASSERT_EQ(MatchRecord(300, 202), c.matches[5]);
853     ASSERT_EQ(MatchRecord(300, 1001), c.matches[6]);
854     ASSERT_EQ(MatchRecord(300, 1002), c.matches[7]);
855 
856     hs_free_database(db);
857     err = hs_free_scratch(scratch);
858     ASSERT_EQ(HS_SUCCESS, err);
859 }
860 
TEST(LogicalCombination,MultiCombStream1)861 TEST(LogicalCombination, MultiCombStream1) {
862     hs_database_t *db = nullptr;
863     hs_compile_error_t *compile_err = nullptr;
864     CallBackContext c;
865     string data[] = {"xxxxxxxabcxxxxxxxdefxxxghixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
866                      "xxxxxxxxxxxxxxxxghixxxxxxxxxxxabcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
867                      "xxxxxxxxxxxxxxxxdefxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
868                      "xxxxxxxxxxxxxxxxxyzxxxxxxxxxxxxxxxxxxxxxghixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
869                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
870                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
871                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
872                      "xxxxxghixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
873                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
874                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzxy",
875                      "z"};
876     const char *expr[] = {"abc", "def", "xyz", "zxyz",
877                           "101 & 102", "201 & !202"};
878     unsigned flags[] = {0, 0, 0, 0, HS_FLAG_COMBINATION, HS_FLAG_COMBINATION};
879     unsigned ids[] = {101, 102, 201, 202, 1001, 1002};
880     hs_error_t err = hs_compile_multi(expr, flags, ids, 6, HS_MODE_STREAM,
881                                       nullptr, &db, &compile_err);
882 
883     ASSERT_EQ(HS_SUCCESS, err);
884     ASSERT_TRUE(db != nullptr);
885 
886     hs_scratch_t *scratch = nullptr;
887     err = hs_alloc_scratch(db, &scratch);
888     ASSERT_EQ(HS_SUCCESS, err);
889     ASSERT_TRUE(scratch != nullptr);
890 
891     hs_stream_t *stream = nullptr;
892     err = hs_open_stream(db, 0, &stream);
893     ASSERT_EQ(HS_SUCCESS, err);
894     ASSERT_TRUE(stream != nullptr);
895 
896     c.halt = 0;
897     int i;
898     for (i = 0; i < 11; i++) {
899         err = hs_scan_stream(stream, data[i].c_str(), data[i].size(), 0,
900                              scratch, record_cb, (void *)&c);
901         ASSERT_EQ(HS_SUCCESS, err);
902     }
903     err = hs_close_stream(stream, scratch, dummy_cb, nullptr);
904     ASSERT_EQ(HS_SUCCESS, err);
905 
906     ASSERT_EQ(11U, c.matches.size());
907     ASSERT_EQ(MatchRecord(10, 101), c.matches[0]);
908     ASSERT_EQ(MatchRecord(20, 102), c.matches[1]);
909     ASSERT_EQ(MatchRecord(20, 1001), c.matches[2]);
910     ASSERT_EQ(MatchRecord(109, 101), c.matches[3]);
911     ASSERT_EQ(MatchRecord(109, 1001), c.matches[4]);
912     ASSERT_EQ(MatchRecord(171, 102), c.matches[5]);
913     ASSERT_EQ(MatchRecord(171, 1001), c.matches[6]);
914     ASSERT_EQ(MatchRecord(247, 201), c.matches[7]);
915     ASSERT_EQ(MatchRecord(247, 1002), c.matches[8]);
916     ASSERT_EQ(MatchRecord(761, 201), c.matches[9]);
917     ASSERT_EQ(MatchRecord(761, 202), c.matches[10]);
918 
919     hs_free_database(db);
920     err = hs_free_scratch(scratch);
921     ASSERT_EQ(HS_SUCCESS, err);
922 }
923