1/*
2 * Copyright (c) 2015-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 "catchup.h"
30#include "match.h"
31#include "program_runtime.h"
32#include "rose.h"
33#include "util/bitutils.h"
34#include "util/fatbit.h"
35
36#if defined(DEBUG) || defined(DUMP_SUPPORT)
37#include "util/compare.h"
38/** A debugging crutch: print a hex-escaped version of the match for our
39 * perusal. The start and end offsets are stream offsets. */
40static UNUSED
41void printMatch(const struct core_info *ci, u64a start, u64a end) {
42 assert(start <= end);
43 assert(end <= ci->buf_offset + ci->len);
44
45 printf("'");
46 u64a i = start;
47 for (; i <= MIN(ci->buf_offset, end); i++) {
48 u64a h_idx = ci->buf_offset - i;
49 u8 c = h_idx >= ci->hlen ? '?' : ci->hbuf[ci->hlen - h_idx - 1];
50 if (ourisprint(c) && c != '\'') {
51 printf("%c", c);
52 } else {
53 printf("\\x%02x", c);
54 }
55 }
56 for (; i <= end; i++) {
57 u64a b_idx = i - ci->buf_offset - 1;
58 u8 c = b_idx >= ci->len ? '?' : ci->buf[b_idx];
59 if (ourisprint(c) && c != '\'') {
60 printf("%c", c);
61 } else {
62 printf("\\x%02x", c);
63 }
64 }
65 printf("'");
66}
67#endif
68
69hwlmcb_rv_t roseDelayRebuildCallback(size_t end, u32 id,
70 struct hs_scratch *scratch) {
71 struct RoseContext *tctx = &scratch->tctxt;
72 struct core_info *ci = &scratch->core_info;
73 const struct RoseEngine *t = ci->rose;
74 size_t rb_len = MIN(ci->hlen, t->delayRebuildLength);
75
76 u64a real_end = ci->buf_offset - rb_len + end + 1; // index after last byte
77
78#ifdef DEBUG
79 DEBUG_PRINTF("REBUILD MATCH id=%u end offset@%llu]: ", id, real_end);
80 u64a start = real_end < 8 ? 1 : real_end - 7;
81 printMatch(ci, start, real_end);
82 printf("\n");
83#endif
84
85 DEBUG_PRINTF("STATE groups=0x%016llx\n", tctx->groups);
86
87 assert(id && id < t->size); // id is a program offset
88 const u64a som = 0;
89 const u8 flags = 0;
90 UNUSED hwlmcb_rv_t rv =
91 roseRunProgram(t, scratch, id, som, real_end, flags);
92 assert(rv != HWLM_TERMINATE_MATCHING);
93
94 /* we are just repopulating the delay queue, groups should be
95 * already set from the original scan. */
96
97 return tctx->groups;
98}
99
100static really_inline
101hwlmcb_rv_t ensureMpvQueueFlushed(const struct RoseEngine *t,
102 struct hs_scratch *scratch, u32 qi, s64a loc,
103 char in_chained) {
104 return ensureQueueFlushed_i(t, scratch, qi, loc, 1, in_chained);
105}
106
107hwlmcb_rv_t roseHandleChainMatch(const struct RoseEngine *t,
108 struct hs_scratch *scratch, u32 event,
109 u64a top_squash_distance, u64a end,
110 char in_catchup) {
111 assert(event == MQE_TOP || event >= MQE_TOP_FIRST);
112 struct core_info *ci = &scratch->core_info;
113
114 u8 *aa = getActiveLeafArray(t, scratch->core_info.state);
115 u32 aaCount = t->activeArrayCount;
116 struct fatbit *activeQueues = scratch->aqa;
117 u32 qCount = t->queueCount;
118
119 const u32 qi = 0; /* MPV is always queue 0 if it exists */
120 struct mq *q = &scratch->queues[qi];
121 const struct NfaInfo *info = getNfaInfoByQueue(t, qi);
122
123 s64a loc = (s64a)end - ci->buf_offset;
124 assert(loc <= (s64a)ci->len && loc >= -(s64a)ci->hlen);
125
126 if (!mmbit_set(aa, aaCount, qi)) {
127 initQueue(q, qi, t, scratch);
128 nfaQueueInitState(q->nfa, q);
129 pushQueueAt(q, 0, MQE_START, loc);
130 fatbit_set(activeQueues, qCount, qi);
131 } else if (info->no_retrigger) {
132 DEBUG_PRINTF("yawn\n");
133 /* nfa only needs one top; we can go home now */
134 return HWLM_CONTINUE_MATCHING;
135 } else if (!fatbit_set(activeQueues, qCount, qi)) {
136 initQueue(q, qi, t, scratch);
137 loadStreamState(q->nfa, q, 0);
138 pushQueueAt(q, 0, MQE_START, 0);
139 } else if (isQueueFull(q)) {
140 DEBUG_PRINTF("queue %u full -> catching up nfas\n", qi);
141 /* we know it is a chained nfa and the suffixes/outfixes must already
142 * be known to be consistent */
143 if (ensureMpvQueueFlushed(t, scratch, qi, loc, in_catchup)
144 == HWLM_TERMINATE_MATCHING) {
145 DEBUG_PRINTF("terminating...\n");
146 return HWLM_TERMINATE_MATCHING;
147 }
148 }
149
150 if (top_squash_distance) {
151 assert(q->cur < q->end);
152 struct mq_item *last = &q->items[q->end - 1];
153 if (last->type == event
154 && last->location >= loc - (s64a)top_squash_distance) {
155 last->location = loc;
156 goto event_enqueued;
157 }
158 }
159
160 pushQueue(q, event, loc);
161
162event_enqueued:
163 if (q_cur_loc(q) == (s64a)ci->len) {
164 /* we may not run the nfa; need to ensure state is fine */
165 DEBUG_PRINTF("empty run\n");
166 pushQueueNoMerge(q, MQE_END, loc);
167 char alive = nfaQueueExec(q->nfa, q, loc);
168 if (alive) {
169 scratch->tctxt.mpv_inactive = 0;
170 q->cur = q->end = 0;
171 pushQueueAt(q, 0, MQE_START, loc);
172 } else {
173 mmbit_unset(aa, aaCount, qi);
174 fatbit_unset(scratch->aqa, qCount, qi);
175 }
176 }
177
178 DEBUG_PRINTF("added mpv event at %lld\n", loc);
179 scratch->tctxt.next_mpv_offset = 0; /* the top event may result in matches
180 * earlier than expected */
181 return HWLM_CONTINUE_MATCHING;
182}
183
184int roseAnchoredCallback(u64a start, u64a end, u32 id, void *ctx) {
185 struct hs_scratch *scratch = ctx;
186 assert(scratch && scratch->magic == SCRATCH_MAGIC);
187 struct RoseContext *tctxt = &scratch->tctxt;
188 struct core_info *ci = &scratch->core_info;
189 const struct RoseEngine *t = ci->rose;
190
191 u64a real_end = ci->buf_offset + end; // index after last byte
192
193 DEBUG_PRINTF("MATCH id=%u offsets=[???,%llu]\n", id, real_end);
194 DEBUG_PRINTF("STATE groups=0x%016llx\n", tctxt->groups);
195
196 if (can_stop_matching(scratch)) {
197 DEBUG_PRINTF("received a match when we're already dead!\n");
198 return MO_HALT_MATCHING;
199 }
200
201 /* delayed literals need to be delivered before real literals; however
202 * delayed literals only come from the floating table so if we are going
203 * to deliver a literal here it must be too early for a delayed literal */
204
205 /* no history checks from anchored region and we are before the flush
206 * boundary */
207
208 if (real_end <= t->floatingMinLiteralMatchOffset) {
209 roseFlushLastByteHistory(t, scratch, real_end);
210 tctxt->lastEndOffset = real_end;
211 }
212
213 // Note that the "id" we have been handed is the program offset.
214 const u8 flags = ROSE_PROG_FLAG_IN_ANCHORED;
215 if (roseRunProgram(t, scratch, id, start, real_end, flags)
216 == HWLM_TERMINATE_MATCHING) {
217 assert(can_stop_matching(scratch));
218 DEBUG_PRINTF("caller requested termination\n");
219 return MO_HALT_MATCHING;
220 }
221
222 DEBUG_PRINTF("DONE groups=0x%016llx\n", tctxt->groups);
223
224 return MO_CONTINUE_MATCHING;
225}
226
227/**
228 * \brief Run the program for the given literal ID, with the interpreter
229 * inlined into this call.
230 *
231 * Assumes not in_anchored.
232 */
233static really_inline
234hwlmcb_rv_t roseProcessMatchInline(const struct RoseEngine *t,
235 struct hs_scratch *scratch, u64a end,
236 u32 id) {
237 DEBUG_PRINTF("id=%u\n", id);
238 assert(id && id < t->size); // id is an offset into bytecode
239 const u64a som = 0;
240 const u8 flags = 0;
241 if (!scratch->pure) {
242 return roseRunProgram(t, scratch, id, som, end, flags);
243 } else {
244 return roseRunProgram_l(t, scratch, id, som, end, flags);
245 }
246}
247
248static rose_inline
249hwlmcb_rv_t playDelaySlot(const struct RoseEngine *t,
250 struct hs_scratch *scratch,
251 struct fatbit **delaySlots, u32 vicIndex,
252 u64a offset) {
253 /* assert(!tctxt->in_anchored); */
254 assert(vicIndex < DELAY_SLOT_COUNT);
255 const struct fatbit *vicSlot = delaySlots[vicIndex];
256 u32 delay_count = t->delay_count;
257
258 if (offset < t->floatingMinLiteralMatchOffset) {
259 DEBUG_PRINTF("too soon\n");
260 return HWLM_CONTINUE_MATCHING;
261 }
262
263 struct RoseContext *tctxt = &scratch->tctxt;
264 roseFlushLastByteHistory(t, scratch, offset);
265 tctxt->lastEndOffset = offset;
266
267 const u32 *programs = getByOffset(t, t->delayProgramOffset);
268
269 for (u32 it = fatbit_iterate(vicSlot, delay_count, MMB_INVALID);
270 it != MMB_INVALID; it = fatbit_iterate(vicSlot, delay_count, it)) {
271 UNUSED rose_group old_groups = tctxt->groups;
272
273 DEBUG_PRINTF("DELAYED MATCH id=%u offset=%llu\n", it, offset);
274 const u64a som = 0;
275 const u8 flags = 0;
276 hwlmcb_rv_t rv = roseRunProgram(t, scratch, programs[it], som, offset,
277 flags);
278 DEBUG_PRINTF("DONE groups=0x%016llx\n", tctxt->groups);
279
280 /* delayed literals can't safely set groups.
281 * However we may be setting groups that successors already have
282 * worked out that we don't need to match the group */
283 DEBUG_PRINTF("groups in %016llx out %016llx\n", old_groups,
284 tctxt->groups);
285
286 if (rv == HWLM_TERMINATE_MATCHING) {
287 return HWLM_TERMINATE_MATCHING;
288 }
289 }
290
291 return HWLM_CONTINUE_MATCHING;
292}
293
294static really_inline
295hwlmcb_rv_t flushAnchoredLiteralAtLoc(const struct RoseEngine *t,
296 struct hs_scratch *scratch,
297 u32 curr_loc) {
298 struct RoseContext *tctxt = &scratch->tctxt;
299 struct fatbit *curr_row = getAnchoredLiteralLog(scratch)[curr_loc - 1];
300 u32 region_width = t->anchored_count;
301
302 const u32 *programs = getByOffset(t, t->anchoredProgramOffset);
303
304 DEBUG_PRINTF("report matches at curr loc\n");
305 for (u32 it = fatbit_iterate(curr_row, region_width, MMB_INVALID);
306 it != MMB_INVALID; it = fatbit_iterate(curr_row, region_width, it)) {
307 DEBUG_PRINTF("it = %u/%u\n", it, region_width);
308
309 rose_group old_groups = tctxt->groups;
310 DEBUG_PRINTF("ANCH REPLAY MATCH id=%u offset=%u\n", it, curr_loc);
311 const u64a som = 0;
312 const u8 flags = 0;
313 hwlmcb_rv_t rv = roseRunProgram(t, scratch, programs[it], som, curr_loc,
314 flags);
315 DEBUG_PRINTF("DONE groups=0x%016llx\n", tctxt->groups);
316
317 /* anchored literals can't safely set groups.
318 * However we may be setting groups that successors already
319 * have worked out that we don't need to match the group */
320 DEBUG_PRINTF("groups in %016llx out %016llx\n", old_groups,
321 tctxt->groups);
322 tctxt->groups &= old_groups;
323
324 if (rv == HWLM_TERMINATE_MATCHING) {
325 return HWLM_TERMINATE_MATCHING;
326 }
327 }
328
329 /* clear row; does not invalidate iteration */
330 bf64_unset(&scratch->al_log_sum, curr_loc - 1);
331
332 return HWLM_CONTINUE_MATCHING;
333}
334
335static really_inline
336u32 anchored_it_begin(struct hs_scratch *scratch) {
337 struct RoseContext *tctxt = &scratch->tctxt;
338 if (tctxt->lastEndOffset >= scratch->anchored_literal_region_len) {
339 return MMB_INVALID;
340 }
341 u32 begin = tctxt->lastEndOffset;
342 begin--;
343
344 return bf64_iterate(scratch->al_log_sum, begin);
345}
346
347static really_inline
348hwlmcb_rv_t flushAnchoredLiterals(const struct RoseEngine *t,
349 struct hs_scratch *scratch,
350 u32 *anchored_it_param, u64a to_off) {
351 struct RoseContext *tctxt = &scratch->tctxt;
352 u32 anchored_it = *anchored_it_param;
353 /* catch up any remaining anchored matches */
354 for (; anchored_it != MMB_INVALID && anchored_it < to_off;
355 anchored_it = bf64_iterate(scratch->al_log_sum, anchored_it)) {
356 assert(anchored_it < scratch->anchored_literal_region_len);
357 DEBUG_PRINTF("loc_it = %u\n", anchored_it);
358 u32 curr_off = anchored_it + 1;
359 roseFlushLastByteHistory(t, scratch, curr_off);
360 tctxt->lastEndOffset = curr_off;
361
362 if (flushAnchoredLiteralAtLoc(t, scratch, curr_off)
363 == HWLM_TERMINATE_MATCHING) {
364 return HWLM_TERMINATE_MATCHING;
365 }
366 }
367
368 *anchored_it_param = anchored_it;
369 return HWLM_CONTINUE_MATCHING;
370}
371
372static really_inline
373hwlmcb_rv_t playVictims(const struct RoseEngine *t, struct hs_scratch *scratch,
374 u32 *anchored_it, u64a lastEnd, u64a victimDelaySlots,
375 struct fatbit **delaySlots) {
376 while (victimDelaySlots) {
377 u32 vic = findAndClearLSB_64(&victimDelaySlots);
378 DEBUG_PRINTF("vic = %u\n", vic);
379 u64a vicOffset = vic + (lastEnd & ~(u64a)DELAY_MASK);
380
381 if (flushAnchoredLiterals(t, scratch, anchored_it, vicOffset)
382 == HWLM_TERMINATE_MATCHING) {
383 return HWLM_TERMINATE_MATCHING;
384 }
385
386 if (playDelaySlot(t, scratch, delaySlots, vic % DELAY_SLOT_COUNT,
387 vicOffset) == HWLM_TERMINATE_MATCHING) {
388 return HWLM_TERMINATE_MATCHING;
389 }
390 }
391
392 return HWLM_CONTINUE_MATCHING;
393}
394
395/* call flushQueuedLiterals instead */
396hwlmcb_rv_t flushQueuedLiterals_i(const struct RoseEngine *t,
397 struct hs_scratch *scratch, u64a currEnd) {
398 struct RoseContext *tctxt = &scratch->tctxt;
399 u64a lastEnd = tctxt->delayLastEndOffset;
400 DEBUG_PRINTF("flushing backed up matches @%llu up from %llu\n", currEnd,
401 lastEnd);
402
403 assert(currEnd != lastEnd); /* checked in main entry point */
404
405 u32 anchored_it = anchored_it_begin(scratch);
406
407 if (!tctxt->filledDelayedSlots) {
408 DEBUG_PRINTF("no delayed, no flush\n");
409 goto anchored_leftovers;
410 }
411
412 {
413 struct fatbit **delaySlots = getDelaySlots(scratch);
414
415 u32 lastIndex = lastEnd & DELAY_MASK;
416 u32 currIndex = currEnd & DELAY_MASK;
417
418 int wrapped = (lastEnd | DELAY_MASK) < currEnd;
419
420 u64a victimDelaySlots; /* needs to be twice as wide as the number of
421 * slots. */
422
423 DEBUG_PRINTF("hello %08x\n", tctxt->filledDelayedSlots);
424 if (!wrapped) {
425 victimDelaySlots = tctxt->filledDelayedSlots;
426
427 DEBUG_PRINTF("unwrapped %016llx %08x\n", victimDelaySlots,
428 tctxt->filledDelayedSlots);
429 /* index vars < 32 so 64bit shifts are safe */
430
431 /* clear all slots at last index and below, */
432 victimDelaySlots &= ~((1LLU << (lastIndex + 1)) - 1);
433
434 /* clear all slots above curr index */
435 victimDelaySlots &= (1LLU << (currIndex + 1)) - 1;
436
437 tctxt->filledDelayedSlots &= ~victimDelaySlots;
438
439 DEBUG_PRINTF("unwrapped %016llx %08x\n", victimDelaySlots,
440 tctxt->filledDelayedSlots);
441 } else {
442 DEBUG_PRINTF("wrapped %08x\n", tctxt->filledDelayedSlots);
443
444 /* 1st half: clear all slots at last index and below, */
445 u64a first_half = tctxt->filledDelayedSlots;
446 first_half &= ~((1ULL << (lastIndex + 1)) - 1);
447 tctxt->filledDelayedSlots &= (1ULL << (lastIndex + 1)) - 1;
448
449 u64a second_half = tctxt->filledDelayedSlots;
450
451 if (currEnd > lastEnd + DELAY_SLOT_COUNT) {
452 /* 2nd half: clear all slots above last index */
453 second_half &= (1ULL << (lastIndex + 1)) - 1;
454 } else {
455 /* 2nd half: clear all slots above curr index */
456 second_half &= (1ULL << (currIndex + 1)) - 1;
457 }
458 tctxt->filledDelayedSlots &= ~second_half;
459
460 victimDelaySlots = first_half | (second_half << DELAY_SLOT_COUNT);
461
462 DEBUG_PRINTF("-- %016llx %016llx = %016llx (li %u)\n", first_half,
463 second_half, victimDelaySlots, lastIndex);
464 }
465
466 if (playVictims(t, scratch, &anchored_it, lastEnd, victimDelaySlots,
467 delaySlots) == HWLM_TERMINATE_MATCHING) {
468 return HWLM_TERMINATE_MATCHING;
469 }
470 }
471
472anchored_leftovers:;
473 hwlmcb_rv_t rv = flushAnchoredLiterals(t, scratch, &anchored_it, currEnd);
474 tctxt->delayLastEndOffset = currEnd;
475 return rv;
476}
477
478static really_inline
479hwlmcb_rv_t roseCallback_i(size_t end, u32 id, struct hs_scratch *scratch) {
480 struct RoseContext *tctx = &scratch->tctxt;
481 const struct RoseEngine *t = scratch->core_info.rose;
482
483 u64a real_end = end + tctx->lit_offset_adjust;
484
485#if defined(DEBUG)
486 DEBUG_PRINTF("MATCH id=%u end offset@%llu: ", id, real_end);
487 u64a start = real_end < 8 ? 1 : real_end - 7;
488 printMatch(&scratch->core_info, start, real_end);
489 printf("\n");
490#endif
491 DEBUG_PRINTF("last end %llu\n", tctx->lastEndOffset);
492
493 DEBUG_PRINTF("STATE groups=0x%016llx\n", tctx->groups);
494
495 if (can_stop_matching(scratch)) {
496 DEBUG_PRINTF("received a match when we're already dead!\n");
497 return HWLM_TERMINATE_MATCHING;
498 }
499
500 hwlmcb_rv_t rv = flushQueuedLiterals(t, scratch, real_end);
501 /* flushDelayed may have advanced tctx->lastEndOffset */
502
503 if (real_end >= t->floatingMinLiteralMatchOffset) {
504 roseFlushLastByteHistory(t, scratch, real_end);
505 tctx->lastEndOffset = real_end;
506 }
507
508 if (rv == HWLM_TERMINATE_MATCHING) {
509 return HWLM_TERMINATE_MATCHING;
510 }
511
512 rv = roseProcessMatchInline(t, scratch, real_end, id);
513
514 DEBUG_PRINTF("DONE groups=0x%016llx\n", tctx->groups);
515
516 if (rv != HWLM_TERMINATE_MATCHING) {
517 return tctx->groups;
518 }
519
520 assert(can_stop_matching(scratch));
521 DEBUG_PRINTF("user requested halt\n");
522 return HWLM_TERMINATE_MATCHING;
523}
524
525hwlmcb_rv_t roseCallback(size_t end, u32 id, struct hs_scratch *scratch) {
526 return roseCallback_i(end, id, scratch);
527}
528
529hwlmcb_rv_t roseFloatingCallback(size_t end, u32 id,
530 struct hs_scratch *scratch) {
531 const struct RoseEngine *t = scratch->core_info.rose;
532
533 return roseCallback_i(end, id, scratch) & t->floating_group_mask;
534}
535
536/**
537 * \brief Execute a boundary report program.
538 *
539 * Returns MO_HALT_MATCHING if the stream is exhausted or the user has
540 * instructed us to halt, or MO_CONTINUE_MATCHING otherwise.
541 */
542int roseRunBoundaryProgram(const struct RoseEngine *rose, u32 program,
543 u64a stream_offset, struct hs_scratch *scratch) {
544 DEBUG_PRINTF("running boundary program at offset %u\n", program);
545
546 if (can_stop_matching(scratch)) {
547 DEBUG_PRINTF("can stop matching\n");
548 return MO_HALT_MATCHING;
549 }
550
551 if (rose->hasSom && scratch->deduper.current_report_offset == ~0ULL) {
552 /* we cannot delay the initialization of the som deduper logs any longer
553 * as we are reporting matches. This is done explicitly as we are
554 * shortcutting the som handling in the vacuous repeats as we know they
555 * all come from non-som patterns. */
556 fatbit_clear(scratch->deduper.som_log[0]);
557 fatbit_clear(scratch->deduper.som_log[1]);
558 scratch->deduper.som_log_dirty = 0;
559 }
560
561 // Keep assertions in program report path happy. At offset zero, there can
562 // have been no earlier reports. At EOD, all earlier reports should have
563 // been handled and we will have been caught up to the stream offset by the
564 // time we are running boundary report programs.
565 scratch->tctxt.minMatchOffset = stream_offset;
566
567 const u64a som = 0;
568 const u8 flags = 0;
569 hwlmcb_rv_t rv = roseRunProgram(rose, scratch, program, som, stream_offset,
570 flags);
571 if (rv == HWLM_TERMINATE_MATCHING) {
572 return MO_HALT_MATCHING;
573 }
574
575 return MO_CONTINUE_MATCHING;
576}
577
578/**
579 * \brief Execute a flush combination program.
580 *
581 * Returns MO_HALT_MATCHING if the stream is exhausted or the user has
582 * instructed us to halt, or MO_CONTINUE_MATCHING otherwise.
583 */
584int roseRunFlushCombProgram(const struct RoseEngine *rose,
585 struct hs_scratch *scratch, u64a end) {
586 hwlmcb_rv_t rv = roseRunProgram(rose, scratch, rose->flushCombProgramOffset,
587 0, end, 0);
588 if (rv == HWLM_TERMINATE_MATCHING) {
589 return MO_HALT_MATCHING;
590 }
591 return MO_CONTINUE_MATCHING;
592}
593
594int roseReportAdaptor(u64a start, u64a end, ReportID id, void *context) {
595 struct hs_scratch *scratch = context;
596 assert(scratch && scratch->magic == SCRATCH_MAGIC);
597
598 DEBUG_PRINTF("id=%u matched at [%llu,%llu]\n", id, start, end);
599
600 const struct RoseEngine *rose = scratch->core_info.rose;
601
602 // Our match ID is the program offset.
603 const u32 program = id;
604 const u8 flags = ROSE_PROG_FLAG_SKIP_MPV_CATCHUP;
605 hwlmcb_rv_t rv =
606 roseRunProgram(rose, scratch, program, start, end, flags);
607 if (rv == HWLM_TERMINATE_MATCHING) {
608 return MO_HALT_MATCHING;
609 }
610
611 return can_stop_matching(scratch) ? MO_HALT_MATCHING : MO_CONTINUE_MATCHING;
612}
613