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 /** \file
30 * \brief Runtime functions.
31 */
32
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "allocator.h"
37 #include "hs_compile.h" /* for HS_MODE_* flags */
38 #include "hs_runtime.h"
39 #include "hs_internal.h"
40 #include "hwlm/hwlm.h"
41 #include "nfa/mcclellan.h"
42 #include "nfa/nfa_api.h"
43 #include "nfa/nfa_api_util.h"
44 #include "nfa/nfa_internal.h"
45 #include "nfa/nfa_rev_api.h"
46 #include "nfa/sheng.h"
47 #include "smallwrite/smallwrite_internal.h"
48 #include "rose/rose.h"
49 #include "rose/runtime.h"
50 #include "database.h"
51 #include "report.h"
52 #include "scratch.h"
53 #include "som/som_runtime.h"
54 #include "som/som_stream.h"
55 #include "state.h"
56 #include "stream_compress.h"
57 #include "ue2common.h"
58 #include "util/exhaust.h"
59 #include "util/multibit.h"
60
61 static really_inline
prefetch_data(const char * data,unsigned length)62 void prefetch_data(const char *data, unsigned length) {
63 __builtin_prefetch(data);
64 __builtin_prefetch(data + length/2);
65 __builtin_prefetch(data + length - 24);
66 }
67
68 /** dummy event handler for use when user does not provide one */
69 static
null_onEvent(UNUSED unsigned id,UNUSED unsigned long long from,UNUSED unsigned long long to,UNUSED unsigned flags,UNUSED void * ctxt)70 int HS_CDECL null_onEvent(UNUSED unsigned id, UNUSED unsigned long long from,
71 UNUSED unsigned long long to, UNUSED unsigned flags,
72 UNUSED void *ctxt) {
73 return 0;
74 }
75
76 static really_inline
getHistoryAmount(const struct RoseEngine * t,u64a offset)77 u32 getHistoryAmount(const struct RoseEngine *t, u64a offset) {
78 return MIN(t->historyRequired, offset);
79 }
80
81 static really_inline
getHistory(char * state,const struct RoseEngine * t,u64a offset)82 u8 *getHistory(char *state, const struct RoseEngine *t, u64a offset) {
83 return (u8 *)state + t->stateOffsets.history + t->historyRequired
84 - MIN(t->historyRequired, offset);
85 }
86
87 /** \brief Sanity checks for scratch space.
88 *
89 * Although more at home in scratch.c, it is located here to be closer to its
90 * callers.
91 */
92 static really_inline
validScratch(const struct RoseEngine * t,const struct hs_scratch * s)93 char validScratch(const struct RoseEngine *t, const struct hs_scratch *s) {
94 if (!ISALIGNED_CL(s)) {
95 DEBUG_PRINTF("bad alignment %p\n", s);
96 return 0;
97 }
98
99 if (s->magic != SCRATCH_MAGIC) {
100 DEBUG_PRINTF("bad magic 0x%x\n", s->magic);
101 return 0;
102 }
103
104 if (t->mode == HS_MODE_BLOCK && t->stateOffsets.end > s->bStateSize) {
105 DEBUG_PRINTF("bad state size\n");
106 return 0;
107 }
108
109 if (t->queueCount > s->queueCount) {
110 DEBUG_PRINTF("bad queue count\n");
111 return 0;
112 }
113
114 /* TODO: add quick rose sanity checks */
115
116 return 1;
117 }
118
119 static really_inline
populateCoreInfo(struct hs_scratch * s,const struct RoseEngine * rose,char * state,match_event_handler onEvent,void * userCtx,const char * data,size_t length,const u8 * history,size_t hlen,u64a offset,u8 status,UNUSED unsigned int flags)120 void populateCoreInfo(struct hs_scratch *s, const struct RoseEngine *rose,
121 char *state, match_event_handler onEvent, void *userCtx,
122 const char *data, size_t length, const u8 *history,
123 size_t hlen, u64a offset, u8 status,
124 UNUSED unsigned int flags) {
125 assert(rose);
126 s->core_info.userContext = userCtx;
127 s->core_info.userCallback = onEvent ? onEvent : null_onEvent;
128 s->core_info.rose = rose;
129 s->core_info.state = state; /* required for chained queues + evec */
130
131 s->core_info.exhaustionVector = state + rose->stateOffsets.exhausted;
132 s->core_info.status = status;
133 s->core_info.buf = (const u8 *)data;
134 s->core_info.len = length;
135 s->core_info.hbuf = history;
136 s->core_info.hlen = hlen;
137 s->core_info.buf_offset = offset;
138
139 /* and some stuff not actually in core info */
140 s->som_set_now_offset = ~0ULL;
141 s->deduper.current_report_offset = ~0ULL;
142 s->deduper.som_log_dirty = 1; /* som logs have not been cleared */
143 s->fdr_conf = NULL;
144
145 // Rose program execution (used for some report paths) depends on these
146 // values being initialised.
147 s->tctxt.lastMatchOffset = 0;
148 s->tctxt.minMatchOffset = offset;
149 s->tctxt.minNonMpvMatchOffset = offset;
150 }
151
152 #define STATUS_VALID_BITS \
153 (STATUS_TERMINATED | STATUS_EXHAUSTED | STATUS_DELAY_DIRTY | STATUS_ERROR)
154
155 /** \brief Retrieve status bitmask from stream state. */
156 static really_inline
getStreamStatus(const char * state)157 u8 getStreamStatus(const char *state) {
158 u8 status = *(const u8 *)(state + ROSE_STATE_OFFSET_STATUS_FLAGS);
159 assert((status & ~STATUS_VALID_BITS) == 0);
160 return status;
161 }
162
163 /** \brief Store status bitmask to stream state. */
164 static really_inline
setStreamStatus(char * state,u8 status)165 void setStreamStatus(char *state, u8 status) {
166 assert((status & ~STATUS_VALID_BITS) == 0);
167 *(u8 *)(state + ROSE_STATE_OFFSET_STATUS_FLAGS) = status;
168 }
169
170 /** \brief Initialise SOM state. Used in both block and streaming mode. */
171 static really_inline
initSomState(const struct RoseEngine * rose,char * state)172 void initSomState(const struct RoseEngine *rose, char *state) {
173 assert(rose && state);
174 const u32 somCount = rose->somLocationCount;
175 mmbit_clear((u8 *)state + rose->stateOffsets.somValid, somCount);
176 mmbit_clear((u8 *)state + rose->stateOffsets.somWritable, somCount);
177 }
178
179 static really_inline
rawBlockExec(const struct RoseEngine * rose,struct hs_scratch * scratch)180 void rawBlockExec(const struct RoseEngine *rose, struct hs_scratch *scratch) {
181 assert(rose);
182 assert(scratch);
183
184 initSomState(rose, scratch->core_info.state);
185
186 DEBUG_PRINTF("blockmode scan len=%zu\n", scratch->core_info.len);
187
188 roseBlockExec(rose, scratch);
189 }
190
191 static really_inline
pureLiteralInitScratch(struct hs_scratch * scratch,u64a offset)192 void pureLiteralInitScratch(struct hs_scratch *scratch, u64a offset) {
193 // Some init has already been done.
194 assert(offset == scratch->core_info.buf_offset);
195
196 scratch->tctxt.lit_offset_adjust = offset + 1;
197 scratch->tctxt.lastEndOffset = offset;
198 scratch->tctxt.delayLastEndOffset = offset;
199 scratch->tctxt.filledDelayedSlots = 0;
200 scratch->al_log_sum = 0;
201 }
202
203 static really_inline
pureLiteralBlockExec(const struct RoseEngine * rose,struct hs_scratch * scratch)204 void pureLiteralBlockExec(const struct RoseEngine *rose,
205 struct hs_scratch *scratch) {
206 assert(rose);
207 assert(scratch);
208
209 const struct HWLM *ftable = getFLiteralMatcher(rose);
210 initSomState(rose, scratch->core_info.state);
211 const u8 *buffer = scratch->core_info.buf;
212 size_t length = scratch->core_info.len;
213 DEBUG_PRINTF("rose engine %d\n", rose->runtimeImpl);
214
215 pureLiteralInitScratch(scratch, 0);
216 scratch->tctxt.groups = rose->initialGroups;
217
218 hwlmExec(ftable, buffer, length, 0, roseCallback, scratch,
219 rose->initialGroups & rose->floating_group_mask);
220 }
221
222 static really_inline
initOutfixQueue(struct mq * q,u32 qi,const struct RoseEngine * t,struct hs_scratch * scratch)223 void initOutfixQueue(struct mq *q, u32 qi, const struct RoseEngine *t,
224 struct hs_scratch *scratch) {
225 const struct NfaInfo *info = getNfaInfoByQueue(t, qi);
226 q->nfa = getNfaByInfo(t, info);
227 q->end = 0;
228 q->cur = 0;
229 q->state = scratch->fullState + info->fullStateOffset;
230 q->streamState = (char *)scratch->core_info.state + info->stateOffset;
231 q->offset = scratch->core_info.buf_offset;
232 q->buffer = scratch->core_info.buf;
233 q->length = scratch->core_info.len;
234 q->history = scratch->core_info.hbuf;
235 q->hlength = scratch->core_info.hlen;
236 q->cb = roseReportAdaptor;
237 q->context = scratch;
238 q->report_current = 0;
239
240 DEBUG_PRINTF("qi=%u, offset=%llu, fullState=%u, streamState=%u, "
241 "state=%u\n", qi, q->offset, info->fullStateOffset,
242 info->stateOffset, *(u32 *)q->state);
243 }
244
245 static never_inline
soleOutfixBlockExec(const struct RoseEngine * t,struct hs_scratch * scratch)246 void soleOutfixBlockExec(const struct RoseEngine *t,
247 struct hs_scratch *scratch) {
248 assert(t);
249 assert(scratch);
250
251 initSomState(t, scratch->core_info.state);
252 assert(t->outfixEndQueue == 1);
253 assert(!t->amatcherOffset);
254 assert(!t->ematcherOffset);
255 assert(!t->fmatcherOffset);
256
257 const struct NFA *nfa = getNfaByQueue(t, 0);
258
259 size_t len = nfaRevAccelCheck(nfa, scratch->core_info.buf,
260 scratch->core_info.len);
261 if (!len) {
262 return;
263 }
264
265 struct mq *q = scratch->queues;
266 initOutfixQueue(q, 0, t, scratch);
267 q->length = len; /* adjust for rev_accel */
268 nfaQueueInitState(nfa, q);
269 pushQueueAt(q, 0, MQE_START, 0);
270 pushQueueAt(q, 1, MQE_TOP, 0);
271 pushQueueAt(q, 2, MQE_END, scratch->core_info.len);
272
273 char rv = nfaQueueExec(q->nfa, q, scratch->core_info.len);
274
275 if (rv && nfaAcceptsEod(nfa) && len == scratch->core_info.len) {
276 nfaCheckFinalState(nfa, q->state, q->streamState, q->length, q->cb,
277 scratch);
278 }
279 }
280
281 static rose_inline
runSmallWriteEngine(const struct SmallWriteEngine * smwr,struct hs_scratch * scratch)282 void runSmallWriteEngine(const struct SmallWriteEngine *smwr,
283 struct hs_scratch *scratch) {
284 assert(smwr);
285 assert(scratch);
286
287 const u8 *buffer = scratch->core_info.buf;
288 size_t length = scratch->core_info.len;
289
290 DEBUG_PRINTF("USING SMALL WRITE\n");
291
292 if (length <= smwr->start_offset) {
293 DEBUG_PRINTF("too short\n");
294 return;
295 }
296
297 const struct NFA *nfa = getSmwrNfa(smwr);
298
299 size_t local_alen = length - smwr->start_offset;
300 const u8 *local_buffer = buffer + smwr->start_offset;
301
302 assert(isDfaType(nfa->type));
303 if (nfa->type == MCCLELLAN_NFA_8) {
304 nfaExecMcClellan8_B(nfa, smwr->start_offset, local_buffer,
305 local_alen, roseReportAdaptor, scratch);
306 } else if (nfa->type == MCCLELLAN_NFA_16) {
307 nfaExecMcClellan16_B(nfa, smwr->start_offset, local_buffer,
308 local_alen, roseReportAdaptor, scratch);
309 } else {
310 nfaExecSheng_B(nfa, smwr->start_offset, local_buffer,
311 local_alen, roseReportAdaptor, scratch);
312 }
313 }
314
315 HS_PUBLIC_API
hs_scan(const hs_database_t * db,const char * data,unsigned length,unsigned flags,hs_scratch_t * scratch,match_event_handler onEvent,void * userCtx)316 hs_error_t HS_CDECL hs_scan(const hs_database_t *db, const char *data,
317 unsigned length, unsigned flags,
318 hs_scratch_t *scratch, match_event_handler onEvent,
319 void *userCtx) {
320 if (unlikely(!scratch || !data)) {
321 return HS_INVALID;
322 }
323
324 hs_error_t err = validDatabase(db);
325 if (unlikely(err != HS_SUCCESS)) {
326 return err;
327 }
328
329 const struct RoseEngine *rose = hs_get_bytecode(db);
330 if (unlikely(!ISALIGNED_16(rose))) {
331 return HS_INVALID;
332 }
333
334 if (unlikely(rose->mode != HS_MODE_BLOCK)) {
335 return HS_DB_MODE_ERROR;
336 }
337
338 if (unlikely(!validScratch(rose, scratch))) {
339 return HS_INVALID;
340 }
341
342 if (unlikely(markScratchInUse(scratch))) {
343 return HS_SCRATCH_IN_USE;
344 }
345
346 if (rose->minWidth > length) {
347 DEBUG_PRINTF("minwidth=%u > length=%u\n", rose->minWidth, length);
348 unmarkScratchInUse(scratch);
349 return HS_SUCCESS;
350 }
351
352 prefetch_data(data, length);
353
354 /* populate core info in scratch */
355 populateCoreInfo(scratch, rose, scratch->bstate, onEvent, userCtx, data,
356 length, NULL, 0, 0, 0, flags);
357
358 clearEvec(rose, scratch->core_info.exhaustionVector);
359 if (rose->ckeyCount) {
360 scratch->core_info.logicalVector = scratch->bstate +
361 rose->stateOffsets.logicalVec;
362 scratch->core_info.combVector = scratch->bstate +
363 rose->stateOffsets.combVec;
364 scratch->tctxt.lastCombMatchOffset = 0;
365 clearLvec(rose, scratch->core_info.logicalVector,
366 scratch->core_info.combVector);
367 }
368
369 if (!length) {
370 if (rose->boundary.reportZeroEodOffset) {
371 roseRunBoundaryProgram(rose, rose->boundary.reportZeroEodOffset, 0,
372 scratch);
373 }
374 goto set_retval;
375 }
376
377 if (rose->boundary.reportZeroOffset) {
378 int rv = roseRunBoundaryProgram(rose, rose->boundary.reportZeroOffset,
379 0, scratch);
380 if (rv == MO_HALT_MATCHING) {
381 goto set_retval;
382 }
383 }
384
385 if (rose->minWidthExcludingBoundaries > length) {
386 DEBUG_PRINTF("minWidthExcludingBoundaries=%u > length=%u\n",
387 rose->minWidthExcludingBoundaries, length);
388 goto done_scan;
389 }
390
391 // Similarly, we may have a maximum width (for engines constructed entirely
392 // of bi-anchored patterns).
393 if (rose->maxBiAnchoredWidth != ROSE_BOUND_INF
394 && length > rose->maxBiAnchoredWidth) {
395 DEBUG_PRINTF("block len=%u longer than maxBAWidth=%u\n", length,
396 rose->maxBiAnchoredWidth);
397 goto done_scan;
398 }
399
400 // Is this a small write case?
401 if (rose->smallWriteOffset) {
402 const struct SmallWriteEngine *smwr = getSmallWrite(rose);
403 assert(smwr);
404
405 // Apply the small write engine if and only if the block (buffer) is
406 // small enough. Otherwise, we allow rose &co to deal with it.
407 if (length < smwr->largestBuffer) {
408 DEBUG_PRINTF("Attempting small write of block %u bytes long.\n",
409 length);
410 runSmallWriteEngine(smwr, scratch);
411 goto done_scan;
412 }
413 }
414
415 switch (rose->runtimeImpl) {
416 default:
417 assert(0);
418 case ROSE_RUNTIME_FULL_ROSE:
419 rawBlockExec(rose, scratch);
420 break;
421 case ROSE_RUNTIME_PURE_LITERAL:
422 pureLiteralBlockExec(rose, scratch);
423 break;
424 case ROSE_RUNTIME_SINGLE_OUTFIX:
425 soleOutfixBlockExec(rose, scratch);
426 break;
427 }
428
429 done_scan:
430 if (unlikely(internal_matching_error(scratch))) {
431 unmarkScratchInUse(scratch);
432 return HS_UNKNOWN_ERROR;
433 } else if (told_to_stop_matching(scratch)) {
434 unmarkScratchInUse(scratch);
435 return HS_SCAN_TERMINATED;
436 }
437
438 if (rose->hasSom) {
439 int halt = flushStoredSomMatches(scratch, ~0ULL);
440 if (halt) {
441 unmarkScratchInUse(scratch);
442 return HS_SCAN_TERMINATED;
443 }
444 }
445
446 if (rose->boundary.reportEodOffset) {
447 roseRunBoundaryProgram(rose, rose->boundary.reportEodOffset, length,
448 scratch);
449 }
450
451 set_retval:
452 if (unlikely(internal_matching_error(scratch))) {
453 unmarkScratchInUse(scratch);
454 return HS_UNKNOWN_ERROR;
455 }
456
457 if (rose->lastFlushCombProgramOffset) {
458 if (roseRunLastFlushCombProgram(rose, scratch, length)
459 == MO_HALT_MATCHING) {
460 if (unlikely(internal_matching_error(scratch))) {
461 unmarkScratchInUse(scratch);
462 return HS_UNKNOWN_ERROR;
463 }
464 unmarkScratchInUse(scratch);
465 return HS_SCAN_TERMINATED;
466 }
467 }
468
469 DEBUG_PRINTF("done. told_to_stop_matching=%d\n",
470 told_to_stop_matching(scratch));
471 hs_error_t rv = told_to_stop_matching(scratch) ? HS_SCAN_TERMINATED
472 : HS_SUCCESS;
473 unmarkScratchInUse(scratch);
474 return rv;
475 }
476
477 static really_inline
maintainHistoryBuffer(const struct RoseEngine * rose,char * state,const char * buffer,size_t length)478 void maintainHistoryBuffer(const struct RoseEngine *rose, char *state,
479 const char *buffer, size_t length) {
480 if (!rose->historyRequired) {
481 return;
482 }
483
484 // Hopefully few of our users are scanning no data.
485 if (unlikely(length == 0)) {
486 DEBUG_PRINTF("zero-byte scan\n");
487 return;
488 }
489
490 char *his_state = state + rose->stateOffsets.history;
491
492 if (length < rose->historyRequired) {
493 size_t shortfall = rose->historyRequired - length;
494 memmove(his_state, his_state + rose->historyRequired - shortfall,
495 shortfall);
496 }
497 size_t amount = MIN(rose->historyRequired, length);
498
499 memcpy(his_state + rose->historyRequired - amount, buffer + length - amount,
500 amount);
501 #ifdef DEBUG_HISTORY
502 printf("History [%u] : ", rose->historyRequired);
503 for (size_t i = 0; i < rose->historyRequired; i++) {
504 printf(" %02hhx", his_state[i]);
505 }
506 printf("\n");
507 #endif
508 }
509
510 static really_inline
init_stream(struct hs_stream * s,const struct RoseEngine * rose,char init_history)511 void init_stream(struct hs_stream *s, const struct RoseEngine *rose,
512 char init_history) {
513 char *state = getMultiState(s);
514
515 if (init_history) {
516 // Make absolutely sure that the 16 bytes leading up to the end of the
517 // history buffer are initialised, as we rely on this (regardless of the
518 // actual values used) in FDR.
519 char *hist_end =
520 state + rose->stateOffsets.history + rose->historyRequired;
521 assert(hist_end - 16 >= (const char *)s);
522 memset(hist_end - 16, 0x5a, 16);
523 }
524
525 s->rose = rose;
526 s->offset = 0;
527
528 setStreamStatus(state, 0);
529 roseInitState(rose, state);
530
531 clearEvec(rose, state + rose->stateOffsets.exhausted);
532 if (rose->ckeyCount) {
533 clearLvec(rose, state + rose->stateOffsets.logicalVec,
534 state + rose->stateOffsets.combVec);
535 }
536
537 // SOM state multibit structures.
538 initSomState(rose, state);
539 }
540
541 HS_PUBLIC_API
hs_open_stream(const hs_database_t * db,UNUSED unsigned flags,hs_stream_t ** stream)542 hs_error_t HS_CDECL hs_open_stream(const hs_database_t *db,
543 UNUSED unsigned flags,
544 hs_stream_t **stream) {
545 if (unlikely(!stream)) {
546 return HS_INVALID;
547 }
548
549 *stream = NULL;
550
551 hs_error_t err = validDatabase(db);
552 if (unlikely(err != HS_SUCCESS)) {
553 return err;
554 }
555
556 const struct RoseEngine *rose = hs_get_bytecode(db);
557 if (unlikely(!ISALIGNED_16(rose))) {
558 return HS_INVALID;
559 }
560
561 if (unlikely(rose->mode != HS_MODE_STREAM)) {
562 return HS_DB_MODE_ERROR;
563 }
564
565 size_t stateSize = rose->stateOffsets.end;
566 struct hs_stream *s = hs_stream_alloc(sizeof(struct hs_stream) + stateSize);
567 if (unlikely(!s)) {
568 return HS_NOMEM;
569 }
570
571 init_stream(s, rose, 1);
572
573 *stream = s;
574 return HS_SUCCESS;
575 }
576
577
578 static really_inline
rawEodExec(hs_stream_t * id,hs_scratch_t * scratch)579 void rawEodExec(hs_stream_t *id, hs_scratch_t *scratch) {
580 const struct RoseEngine *rose = id->rose;
581
582 if (can_stop_matching(scratch)) {
583 DEBUG_PRINTF("stream already broken\n");
584 return;
585 }
586
587 if (isAllExhausted(rose, scratch->core_info.exhaustionVector)) {
588 DEBUG_PRINTF("stream exhausted\n");
589 return;
590 }
591
592 roseStreamEodExec(rose, id->offset, scratch);
593 }
594
595 static never_inline
soleOutfixEodExec(hs_stream_t * id,hs_scratch_t * scratch)596 void soleOutfixEodExec(hs_stream_t *id, hs_scratch_t *scratch) {
597 const struct RoseEngine *t = id->rose;
598
599 if (can_stop_matching(scratch)) {
600 DEBUG_PRINTF("stream already broken\n");
601 return;
602 }
603
604 if (isAllExhausted(t, scratch->core_info.exhaustionVector)) {
605 DEBUG_PRINTF("stream exhausted\n");
606 return;
607 }
608
609 assert(t->outfixEndQueue == 1);
610 assert(!t->amatcherOffset);
611 assert(!t->ematcherOffset);
612 assert(!t->fmatcherOffset);
613
614 const struct NFA *nfa = getNfaByQueue(t, 0);
615
616 struct mq *q = scratch->queues;
617 initOutfixQueue(q, 0, t, scratch);
618 if (!scratch->core_info.buf_offset) {
619 DEBUG_PRINTF("buf_offset is zero\n");
620 return; /* no vacuous engines */
621 }
622
623 nfaExpandState(nfa, q->state, q->streamState, q->offset,
624 queue_prev_byte(q, 0));
625
626 assert(nfaAcceptsEod(nfa));
627 nfaCheckFinalState(nfa, q->state, q->streamState, q->offset, q->cb,
628 scratch);
629 }
630
631 static really_inline
report_eod_matches(hs_stream_t * id,hs_scratch_t * scratch,match_event_handler onEvent,void * context)632 void report_eod_matches(hs_stream_t *id, hs_scratch_t *scratch,
633 match_event_handler onEvent, void *context) {
634 DEBUG_PRINTF("--- report eod matches at offset %llu\n", id->offset);
635 assert(onEvent);
636
637 const struct RoseEngine *rose = id->rose;
638 char *state = getMultiState(id);
639 u8 status = getStreamStatus(state);
640
641 if (status & (STATUS_TERMINATED | STATUS_EXHAUSTED | STATUS_ERROR)) {
642 DEBUG_PRINTF("stream is broken, just freeing storage\n");
643 return;
644 }
645
646 populateCoreInfo(scratch, rose, state, onEvent, context, NULL, 0,
647 getHistory(state, rose, id->offset),
648 getHistoryAmount(rose, id->offset), id->offset, status, 0);
649
650 if (rose->ckeyCount) {
651 scratch->core_info.logicalVector = state +
652 rose->stateOffsets.logicalVec;
653 scratch->core_info.combVector = state + rose->stateOffsets.combVec;
654 if (!id->offset) {
655 scratch->tctxt.lastCombMatchOffset = id->offset;
656 }
657 }
658
659 if (rose->somLocationCount) {
660 loadSomFromStream(scratch, id->offset);
661 }
662
663 if (!id->offset) {
664 if (rose->boundary.reportZeroEodOffset) {
665 int rv = roseRunBoundaryProgram(
666 rose, rose->boundary.reportZeroEodOffset, 0, scratch);
667 if (rv == MO_HALT_MATCHING) {
668 return;
669 }
670 }
671 } else {
672 if (rose->boundary.reportEodOffset) {
673 int rv = roseRunBoundaryProgram(
674 rose, rose->boundary.reportEodOffset, id->offset, scratch);
675 if (rv == MO_HALT_MATCHING) {
676 return;
677 }
678 }
679
680 if (rose->requiresEodCheck) {
681 switch (rose->runtimeImpl) {
682 default:
683 case ROSE_RUNTIME_PURE_LITERAL:
684 assert(0);
685 case ROSE_RUNTIME_FULL_ROSE:
686 rawEodExec(id, scratch);
687 break;
688 case ROSE_RUNTIME_SINGLE_OUTFIX:
689 soleOutfixEodExec(id, scratch);
690 break;
691 }
692 }
693 }
694
695 if (rose->hasSom && !told_to_stop_matching(scratch)) {
696 int halt = flushStoredSomMatches(scratch, ~0ULL);
697 if (halt) {
698 DEBUG_PRINTF("told to stop matching\n");
699 scratch->core_info.status |= STATUS_TERMINATED;
700 }
701 }
702
703 if (rose->lastFlushCombProgramOffset && !told_to_stop_matching(scratch)) {
704 if (roseRunLastFlushCombProgram(rose, scratch, id->offset)
705 == MO_HALT_MATCHING) {
706 DEBUG_PRINTF("told to stop matching\n");
707 scratch->core_info.status |= STATUS_TERMINATED;
708 }
709 }
710 }
711
712 HS_PUBLIC_API
hs_copy_stream(hs_stream_t ** to_id,const hs_stream_t * from_id)713 hs_error_t HS_CDECL hs_copy_stream(hs_stream_t **to_id,
714 const hs_stream_t *from_id) {
715 if (!to_id) {
716 return HS_INVALID;
717 }
718
719 *to_id = NULL;
720
721 if (!from_id || !from_id->rose) {
722 return HS_INVALID;
723 }
724
725 const struct RoseEngine *rose = from_id->rose;
726 size_t stateSize = sizeof(struct hs_stream) + rose->stateOffsets.end;
727
728 struct hs_stream *s = hs_stream_alloc(stateSize);
729 if (!s) {
730 return HS_NOMEM;
731 }
732
733 memcpy(s, from_id, stateSize);
734
735 *to_id = s;
736
737 return HS_SUCCESS;
738 }
739
740 HS_PUBLIC_API
hs_reset_and_copy_stream(hs_stream_t * to_id,const hs_stream_t * from_id,hs_scratch_t * scratch,match_event_handler onEvent,void * context)741 hs_error_t HS_CDECL hs_reset_and_copy_stream(hs_stream_t *to_id,
742 const hs_stream_t *from_id,
743 hs_scratch_t *scratch,
744 match_event_handler onEvent,
745 void *context) {
746 if (!from_id || !from_id->rose) {
747 return HS_INVALID;
748 }
749
750 if (!to_id || to_id->rose != from_id->rose) {
751 return HS_INVALID;
752 }
753
754 if (to_id == from_id) {
755 return HS_INVALID;
756 }
757
758 if (onEvent) {
759 if (!scratch || !validScratch(to_id->rose, scratch)) {
760 return HS_INVALID;
761 }
762 if (unlikely(markScratchInUse(scratch))) {
763 return HS_SCRATCH_IN_USE;
764 }
765 report_eod_matches(to_id, scratch, onEvent, context);
766 if (unlikely(internal_matching_error(scratch))) {
767 unmarkScratchInUse(scratch);
768 return HS_UNKNOWN_ERROR;
769 }
770 unmarkScratchInUse(scratch);
771 }
772
773 size_t stateSize
774 = sizeof(struct hs_stream) + from_id->rose->stateOffsets.end;
775
776 memcpy(to_id, from_id, stateSize);
777
778 return HS_SUCCESS;
779 }
780
781 static really_inline
rawStreamExec(struct hs_stream * stream_state,struct hs_scratch * scratch)782 void rawStreamExec(struct hs_stream *stream_state, struct hs_scratch *scratch) {
783 assert(stream_state);
784 assert(scratch);
785 assert(!can_stop_matching(scratch));
786
787 DEBUG_PRINTF("::: streaming rose ::: offset = %llu len = %zu\n",
788 stream_state->offset, scratch->core_info.len);
789
790 const struct RoseEngine *rose = stream_state->rose;
791 assert(rose);
792 roseStreamExec(rose, scratch);
793
794 if (!told_to_stop_matching(scratch) &&
795 isAllExhausted(rose, scratch->core_info.exhaustionVector)) {
796 DEBUG_PRINTF("stream exhausted\n");
797 scratch->core_info.status |= STATUS_EXHAUSTED;
798 }
799 }
800
801 static really_inline
pureLiteralStreamExec(struct hs_stream * stream_state,struct hs_scratch * scratch)802 void pureLiteralStreamExec(struct hs_stream *stream_state,
803 struct hs_scratch *scratch) {
804 assert(stream_state);
805 assert(scratch);
806 assert(!can_stop_matching(scratch));
807
808 const struct RoseEngine *rose = stream_state->rose;
809 const struct HWLM *ftable = getFLiteralMatcher(rose);
810
811 size_t len2 = scratch->core_info.len;
812
813 DEBUG_PRINTF("::: streaming rose ::: offset = %llu len = %zu\n",
814 stream_state->offset, scratch->core_info.len);
815
816 pureLiteralInitScratch(scratch, stream_state->offset);
817 scratch->tctxt.groups = loadGroups(rose, scratch->core_info.state);
818
819 // Pure literal cases don't have floatingMinDistance set, so we always
820 // start the match region at zero.
821 const size_t start = 0;
822
823 hwlmExecStreaming(ftable, len2, start, roseCallback, scratch,
824 rose->initialGroups & rose->floating_group_mask);
825
826 if (!told_to_stop_matching(scratch) &&
827 isAllExhausted(rose, scratch->core_info.exhaustionVector)) {
828 DEBUG_PRINTF("stream exhausted\n");
829 scratch->core_info.status |= STATUS_EXHAUSTED;
830 }
831 }
832
833 static never_inline
soleOutfixStreamExec(struct hs_stream * stream_state,struct hs_scratch * scratch)834 void soleOutfixStreamExec(struct hs_stream *stream_state,
835 struct hs_scratch *scratch) {
836 assert(stream_state);
837 assert(scratch);
838 assert(!can_stop_matching(scratch));
839
840 const struct RoseEngine *t = stream_state->rose;
841 assert(t->outfixEndQueue == 1);
842 assert(!t->amatcherOffset);
843 assert(!t->ematcherOffset);
844 assert(!t->fmatcherOffset);
845
846 const struct NFA *nfa = getNfaByQueue(t, 0);
847
848 struct mq *q = scratch->queues;
849 initOutfixQueue(q, 0, t, scratch);
850 if (!scratch->core_info.buf_offset) {
851 nfaQueueInitState(nfa, q);
852 pushQueueAt(q, 0, MQE_START, 0);
853 pushQueueAt(q, 1, MQE_TOP, 0);
854 pushQueueAt(q, 2, MQE_END, scratch->core_info.len);
855 } else {
856 nfaExpandState(nfa, q->state, q->streamState, q->offset,
857 queue_prev_byte(q, 0));
858 pushQueueAt(q, 0, MQE_START, 0);
859 pushQueueAt(q, 1, MQE_END, scratch->core_info.len);
860 }
861
862 if (nfaQueueExec(q->nfa, q, scratch->core_info.len)) {
863 nfaQueueCompressState(nfa, q, scratch->core_info.len);
864 } else if (!told_to_stop_matching(scratch)) {
865 scratch->core_info.status |= STATUS_EXHAUSTED;
866 }
867 }
868
869 static inline
hs_scan_stream_internal(hs_stream_t * id,const char * data,unsigned length,UNUSED unsigned flags,hs_scratch_t * scratch,match_event_handler onEvent,void * context)870 hs_error_t hs_scan_stream_internal(hs_stream_t *id, const char *data,
871 unsigned length, UNUSED unsigned flags,
872 hs_scratch_t *scratch,
873 match_event_handler onEvent, void *context) {
874 assert(id);
875 assert(scratch);
876
877 if (unlikely(!data)) {
878 return HS_INVALID;
879 }
880
881 const struct RoseEngine *rose = id->rose;
882 char *state = getMultiState(id);
883
884 u8 status = getStreamStatus(state);
885 if (status & (STATUS_TERMINATED | STATUS_EXHAUSTED | STATUS_ERROR)) {
886 DEBUG_PRINTF("stream is broken, halting scan\n");
887 if (status & STATUS_ERROR) {
888 return HS_UNKNOWN_ERROR;
889 } else if (status & STATUS_TERMINATED) {
890 return HS_SCAN_TERMINATED;
891 } else {
892 return HS_SUCCESS;
893 }
894 }
895
896 // We avoid doing any work if the user has given us zero bytes of data to
897 // scan. Arguably we should define some semantics for how we treat vacuous
898 // cases here.
899 if (unlikely(length == 0)) {
900 DEBUG_PRINTF("zero length block\n");
901 return HS_SUCCESS;
902 }
903
904 u32 historyAmount = getHistoryAmount(rose, id->offset);
905 populateCoreInfo(scratch, rose, state, onEvent, context, data, length,
906 getHistory(state, rose, id->offset), historyAmount,
907 id->offset, status, flags);
908 if (rose->ckeyCount) {
909 scratch->core_info.logicalVector = state +
910 rose->stateOffsets.logicalVec;
911 scratch->core_info.combVector = state + rose->stateOffsets.combVec;
912 if (!id->offset) {
913 scratch->tctxt.lastCombMatchOffset = id->offset;
914 }
915 }
916 assert(scratch->core_info.hlen <= id->offset
917 && scratch->core_info.hlen <= rose->historyRequired);
918
919 prefetch_data(data, length);
920
921 if (rose->somLocationCount) {
922 loadSomFromStream(scratch, id->offset);
923 }
924
925 if (!id->offset && rose->boundary.reportZeroOffset) {
926 DEBUG_PRINTF("zero reports\n");
927 int rv = roseRunBoundaryProgram(rose, rose->boundary.reportZeroOffset,
928 0, scratch);
929 if (rv == MO_HALT_MATCHING) {
930 DEBUG_PRINTF("halting scan\n");
931 setStreamStatus(state, scratch->core_info.status);
932 if (told_to_stop_matching(scratch)) {
933 return HS_SCAN_TERMINATED;
934 } else {
935 assert(scratch->core_info.status & STATUS_EXHAUSTED);
936 return HS_SUCCESS;
937 }
938 }
939 }
940
941 switch (rose->runtimeImpl) {
942 default:
943 assert(0);
944 case ROSE_RUNTIME_FULL_ROSE:
945 rawStreamExec(id, scratch);
946 break;
947 case ROSE_RUNTIME_PURE_LITERAL:
948 pureLiteralStreamExec(id, scratch);
949 break;
950 case ROSE_RUNTIME_SINGLE_OUTFIX:
951 soleOutfixStreamExec(id, scratch);
952 }
953
954 if (rose->hasSom && !told_to_stop_matching(scratch)) {
955 int halt = flushStoredSomMatches(scratch, ~0ULL);
956 if (halt) {
957 scratch->core_info.status |= STATUS_TERMINATED;
958 }
959 }
960
961 setStreamStatus(state, scratch->core_info.status);
962
963 if (unlikely(internal_matching_error(scratch))) {
964 return HS_UNKNOWN_ERROR;
965 } else if (likely(!can_stop_matching(scratch))) {
966 maintainHistoryBuffer(rose, state, data, length);
967 id->offset += length; /* maintain offset */
968
969 if (rose->somLocationCount) {
970 storeSomToStream(scratch, id->offset);
971 }
972 } else if (told_to_stop_matching(scratch)) {
973 return HS_SCAN_TERMINATED;
974 }
975
976 return HS_SUCCESS;
977 }
978
979 HS_PUBLIC_API
hs_scan_stream(hs_stream_t * id,const char * data,unsigned length,unsigned flags,hs_scratch_t * scratch,match_event_handler onEvent,void * context)980 hs_error_t HS_CDECL hs_scan_stream(hs_stream_t *id, const char *data,
981 unsigned length, unsigned flags,
982 hs_scratch_t *scratch,
983 match_event_handler onEvent, void *context) {
984 if (unlikely(!id || !scratch || !data ||
985 !validScratch(id->rose, scratch))) {
986 return HS_INVALID;
987 }
988
989 if (unlikely(markScratchInUse(scratch))) {
990 return HS_SCRATCH_IN_USE;
991 }
992 hs_error_t rv = hs_scan_stream_internal(id, data, length, flags, scratch,
993 onEvent, context);
994 unmarkScratchInUse(scratch);
995 return rv;
996 }
997
998 HS_PUBLIC_API
hs_close_stream(hs_stream_t * id,hs_scratch_t * scratch,match_event_handler onEvent,void * context)999 hs_error_t HS_CDECL hs_close_stream(hs_stream_t *id, hs_scratch_t *scratch,
1000 match_event_handler onEvent,
1001 void *context) {
1002 if (!id) {
1003 return HS_INVALID;
1004 }
1005
1006 if (onEvent) {
1007 if (!scratch || !validScratch(id->rose, scratch)) {
1008 return HS_INVALID;
1009 }
1010 if (unlikely(markScratchInUse(scratch))) {
1011 return HS_SCRATCH_IN_USE;
1012 }
1013 report_eod_matches(id, scratch, onEvent, context);
1014 if (unlikely(internal_matching_error(scratch))) {
1015 unmarkScratchInUse(scratch);
1016 return HS_UNKNOWN_ERROR;
1017 }
1018 unmarkScratchInUse(scratch);
1019 }
1020
1021 hs_stream_free(id);
1022
1023 return HS_SUCCESS;
1024 }
1025
1026 HS_PUBLIC_API
hs_reset_stream(hs_stream_t * id,UNUSED unsigned int flags,hs_scratch_t * scratch,match_event_handler onEvent,void * context)1027 hs_error_t HS_CDECL hs_reset_stream(hs_stream_t *id, UNUSED unsigned int flags,
1028 hs_scratch_t *scratch,
1029 match_event_handler onEvent,
1030 void *context) {
1031 if (!id) {
1032 return HS_INVALID;
1033 }
1034
1035 if (onEvent) {
1036 if (!scratch || !validScratch(id->rose, scratch)) {
1037 return HS_INVALID;
1038 }
1039 if (unlikely(markScratchInUse(scratch))) {
1040 return HS_SCRATCH_IN_USE;
1041 }
1042 report_eod_matches(id, scratch, onEvent, context);
1043 if (unlikely(internal_matching_error(scratch))) {
1044 unmarkScratchInUse(scratch);
1045 return HS_UNKNOWN_ERROR;
1046 }
1047 unmarkScratchInUse(scratch);
1048 }
1049
1050 // history already initialised
1051 init_stream(id, id->rose, 0);
1052
1053 return HS_SUCCESS;
1054 }
1055
1056 HS_PUBLIC_API
hs_stream_size(const hs_database_t * db,size_t * stream_size)1057 hs_error_t HS_CDECL hs_stream_size(const hs_database_t *db,
1058 size_t *stream_size) {
1059 if (!stream_size) {
1060 return HS_INVALID;
1061 }
1062
1063 hs_error_t ret = validDatabase(db);
1064 if (ret != HS_SUCCESS) {
1065 return ret;
1066 }
1067
1068 const struct RoseEngine *rose = hs_get_bytecode(db);
1069 if (!ISALIGNED_16(rose)) {
1070 return HS_INVALID;
1071 }
1072
1073 if (rose->mode != HS_MODE_STREAM) {
1074 return HS_DB_MODE_ERROR;
1075 }
1076
1077 u32 base_stream_size = rose->stateOffsets.end;
1078
1079 // stream state plus the hs_stream struct itself
1080 *stream_size = base_stream_size + sizeof(struct hs_stream);
1081
1082 return HS_SUCCESS;
1083 }
1084
1085 #if defined(DEBUG) || defined(DUMP_SUPPORT)
1086 #include "util/compare.h"
1087 // A debugging crutch: print a hex-escaped version of the match for our
1088 // perusal.
1089 static UNUSED
dumpData(const char * data,size_t len)1090 void dumpData(const char *data, size_t len) {
1091 DEBUG_PRINTF("BUFFER:");
1092 for (size_t i = 0; i < len; i++) {
1093 u8 c = data[i];
1094 if (ourisprint(c) && c != '\'') {
1095 printf("%c", c);
1096 } else {
1097 printf("\\x%02x", c);
1098 }
1099 }
1100 printf("\n");
1101 }
1102 #endif
1103
1104 HS_PUBLIC_API
hs_scan_vector(const hs_database_t * db,const char * const * data,const unsigned int * length,unsigned int count,UNUSED unsigned int flags,hs_scratch_t * scratch,match_event_handler onEvent,void * context)1105 hs_error_t HS_CDECL hs_scan_vector(const hs_database_t *db,
1106 const char * const * data,
1107 const unsigned int *length,
1108 unsigned int count,
1109 UNUSED unsigned int flags,
1110 hs_scratch_t *scratch,
1111 match_event_handler onEvent, void *context) {
1112 if (unlikely(!scratch || !data || !length)) {
1113 return HS_INVALID;
1114 }
1115
1116 hs_error_t err = validDatabase(db);
1117 if (unlikely(err != HS_SUCCESS)) {
1118 return err;
1119 }
1120
1121 const struct RoseEngine *rose = hs_get_bytecode(db);
1122 if (unlikely(!ISALIGNED_16(rose))) {
1123 return HS_INVALID;
1124 }
1125
1126 if (unlikely(rose->mode != HS_MODE_VECTORED)) {
1127 return HS_DB_MODE_ERROR;
1128 }
1129
1130 if (unlikely(!validScratch(rose, scratch))) {
1131 return HS_INVALID;
1132 }
1133
1134 if (unlikely(markScratchInUse(scratch))) {
1135 return HS_SCRATCH_IN_USE;
1136 }
1137
1138 hs_stream_t *id = (hs_stream_t *)(scratch->bstate);
1139
1140 init_stream(id, rose, 1); /* open stream */
1141
1142 for (u32 i = 0; i < count; i++) {
1143 DEBUG_PRINTF("block %u/%u offset=%llu len=%u\n", i, count, id->offset,
1144 length[i]);
1145 #ifdef DEBUG
1146 dumpData(data[i], length[i]);
1147 #endif
1148 hs_error_t ret
1149 = hs_scan_stream_internal(id, data[i], length[i], 0, scratch,
1150 onEvent, context);
1151 if (ret != HS_SUCCESS) {
1152 unmarkScratchInUse(scratch);
1153 return ret;
1154 }
1155 }
1156
1157 /* close stream */
1158 if (onEvent) {
1159 report_eod_matches(id, scratch, onEvent, context);
1160
1161 if (unlikely(internal_matching_error(scratch))) {
1162 unmarkScratchInUse(scratch);
1163 return HS_UNKNOWN_ERROR;
1164 } else if (told_to_stop_matching(scratch)) {
1165 unmarkScratchInUse(scratch);
1166 return HS_SCAN_TERMINATED;
1167 }
1168 }
1169
1170 unmarkScratchInUse(scratch);
1171
1172 return HS_SUCCESS;
1173 }
1174
1175 HS_PUBLIC_API
hs_compress_stream(const hs_stream_t * stream,char * buf,size_t buf_space,size_t * used_space)1176 hs_error_t HS_CDECL hs_compress_stream(const hs_stream_t *stream, char *buf,
1177 size_t buf_space, size_t *used_space) {
1178 if (unlikely(!stream || !used_space)) {
1179 return HS_INVALID;
1180 }
1181
1182 if (unlikely(buf_space && !buf)) {
1183 return HS_INVALID;
1184 }
1185
1186 const struct RoseEngine *rose = stream->rose;
1187
1188 size_t stream_size = size_compress_stream(rose, stream);
1189
1190 DEBUG_PRINTF("require %zu [orig %zu]\n", stream_size,
1191 rose->stateOffsets.end + sizeof(struct hs_stream));
1192 *used_space = stream_size;
1193
1194 if (buf_space < stream_size) {
1195 return HS_INSUFFICIENT_SPACE;
1196 }
1197 compress_stream(buf, stream_size, rose, stream);
1198
1199 return HS_SUCCESS;
1200 }
1201
1202 HS_PUBLIC_API
hs_expand_stream(const hs_database_t * db,hs_stream_t ** stream,const char * buf,size_t buf_size)1203 hs_error_t HS_CDECL hs_expand_stream(const hs_database_t *db,
1204 hs_stream_t **stream,
1205 const char *buf, size_t buf_size) {
1206 if (unlikely(!stream || !buf)) {
1207 return HS_INVALID;
1208 }
1209
1210 *stream = NULL;
1211
1212 hs_error_t err = validDatabase(db);
1213 if (unlikely(err != HS_SUCCESS)) {
1214 return err;
1215 }
1216
1217 const struct RoseEngine *rose = hs_get_bytecode(db);
1218 if (unlikely(!ISALIGNED_16(rose))) {
1219 return HS_INVALID;
1220 }
1221
1222 if (unlikely(rose->mode != HS_MODE_STREAM)) {
1223 return HS_DB_MODE_ERROR;
1224 }
1225
1226 size_t stream_size = rose->stateOffsets.end + sizeof(struct hs_stream);
1227
1228 struct hs_stream *s = hs_stream_alloc(stream_size);
1229 if (unlikely(!s)) {
1230 return HS_NOMEM;
1231 }
1232
1233 if (!expand_stream(s, rose, buf, buf_size)) {
1234 hs_stream_free(s);
1235 return HS_INVALID;
1236 }
1237
1238 *stream = s;
1239 return HS_SUCCESS;
1240 }
1241
1242 HS_PUBLIC_API
hs_reset_and_expand_stream(hs_stream_t * to_stream,const char * buf,size_t buf_size,hs_scratch_t * scratch,match_event_handler onEvent,void * context)1243 hs_error_t HS_CDECL hs_reset_and_expand_stream(hs_stream_t *to_stream,
1244 const char *buf, size_t buf_size,
1245 hs_scratch_t *scratch,
1246 match_event_handler onEvent,
1247 void *context) {
1248 if (unlikely(!to_stream || !buf)) {
1249 return HS_INVALID;
1250 }
1251
1252 const struct RoseEngine *rose = to_stream->rose;
1253
1254 if (onEvent) {
1255 if (!scratch || !validScratch(to_stream->rose, scratch)) {
1256 return HS_INVALID;
1257 }
1258 if (unlikely(markScratchInUse(scratch))) {
1259 return HS_SCRATCH_IN_USE;
1260 }
1261 report_eod_matches(to_stream, scratch, onEvent, context);
1262 if (unlikely(internal_matching_error(scratch))) {
1263 unmarkScratchInUse(scratch);
1264 return HS_UNKNOWN_ERROR;
1265 }
1266 unmarkScratchInUse(scratch);
1267 }
1268
1269 if (expand_stream(to_stream, rose, buf, buf_size)) {
1270 return HS_SUCCESS;
1271 } else {
1272 return HS_INVALID;
1273 }
1274 }
1275