// Saves out stream state for all our active suffix NFAs. static rose_inline void roseSaveNfaStreamState(const struct RoseEngine *t, char *state, struct hs_scratch *scratch) { struct mq *queues = scratch->queues; u8 *aa = getActiveLeafArray(t, state); u32 aaCount = t->activeArrayCount; if (scratch->tctxt.mpv_inactive) { DEBUG_PRINTF("mpv is dead as a doornail\n"); /* mpv if it exists is queue 0 */ mmbit_unset(aa, aaCount, 0); } for (u32 qi = mmbit_iterate(aa, aaCount, MMB_INVALID); qi != MMB_INVALID; qi = mmbit_iterate(aa, aaCount, qi)) { DEBUG_PRINTF("saving stream state for qi=%u\n", qi); struct mq *q = queues + qi; // If it's active, it should have an active queue (as we should have // done some work!) assert(fatbit_isset(scratch->aqa, t->queueCount, qi)); const struct NFA *nfa = getNfaByQueue(t, qi); saveStreamState(nfa, q, q_cur_loc(q)); } }
static really_inline int can_never_match(const struct RoseEngine *t, char *state, struct hs_scratch *scratch, size_t length, u64a offset) { struct RoseContext *tctxt = &scratch->tctxt; if (tctxt->groups) { DEBUG_PRINTF("still has active groups\n"); return 0; } if (offset + length <= t->anchoredDistance) { /* not < as may have eod */ DEBUG_PRINTF("still in anchored region\n"); return 0; } if (t->lastByteHistoryIterOffset) { /* last byte history is hard */ DEBUG_PRINTF("last byte history\n"); return 0; } if (mmbit_any(getActiveLeafArray(t, state), t->activeArrayCount)) { DEBUG_PRINTF("active leaf\n"); return 0; } return 1; }
static really_inline void roseEodExec_i(const struct RoseEngine *t, u8 *state, u64a offset, struct hs_scratch *scratch, const char is_streaming) { assert(t); assert(scratch->core_info.buf || scratch->core_info.hbuf); assert(!scratch->core_info.buf || !scratch->core_info.hbuf); assert(!can_stop_matching(scratch)); // Fire the special EOD event literal. if (t->hasEodEventLiteral) { DEBUG_PRINTF("firing eod event id %u at offset %llu\n", t->eodLiteralId, offset); const struct core_info *ci = &scratch->core_info; size_t len = ci->buf ? ci->len : ci->hlen; assert(len || !ci->buf); /* len may be 0 if no history is required * (bounds checks only can lead to this) */ roseRunEvent(len, t->eodLiteralId, &scratch->tctxt); if (can_stop_matching(scratch)) { DEBUG_PRINTF("user told us to stop\n"); return; } } roseCheckNfaEod(t, state, scratch, offset, is_streaming); if (!t->eodIterOffset && !t->ematcherOffset) { DEBUG_PRINTF("no eod accepts\n"); return; } // Handle pending EOD reports. int itrv = roseEodRunIterator(t, state, offset, scratch); if (itrv == MO_HALT_MATCHING) { return; } // Run the EOD anchored matcher if there is one. if (t->ematcherOffset) { assert(t->ematcherRegionSize); // Unset the reports we just fired so we don't fire them again below. mmbit_clear(getRoleState(state), t->rolesWithStateCount); mmbit_clear(getActiveLeafArray(t, state), t->activeArrayCount); sidecar_enabled_populate(t, scratch, state); hwlmcb_rv_t rv = roseEodRunMatcher(t, offset, scratch, is_streaming); if (rv == HWLM_TERMINATE_MATCHING) { return; } cleanupAfterEodMatcher(t, state, offset, scratch); // Fire any new EOD reports. roseEodRunIterator(t, state, offset, scratch); roseCheckEodSuffixes(t, state, offset, scratch); } }
static rose_inline void roseCheckNfaEod(const struct RoseEngine *t, u8 *state, struct hs_scratch *scratch, u64a offset, const char is_streaming) { /* data, len is used for state decompress, should be full available data */ const u8 *aa = getActiveLeafArray(t, state); const u32 aaCount = t->activeArrayCount; u8 key = 0; if (is_streaming) { const u8 *eod_data = scratch->core_info.hbuf; size_t eod_len = scratch->core_info.hlen; key = eod_len ? eod_data[eod_len - 1] : 0; } for (u32 qi = mmbit_iterate(aa, aaCount, MMB_INVALID); qi != MMB_INVALID; qi = mmbit_iterate(aa, aaCount, qi)) { const struct NfaInfo *info = getNfaInfoByQueue(t, qi); const struct NFA *nfa = getNfaByInfo(t, info); if (!nfaAcceptsEod(nfa)) { DEBUG_PRINTF("nfa %u does not accept eod\n", qi); continue; } DEBUG_PRINTF("checking nfa %u\n", qi); char *fstate = scratch->fullState + info->fullStateOffset; const char *sstate = (const char *)state + info->stateOffset; if (is_streaming) { // Decompress stream state. nfaExpandState(nfa, fstate, sstate, offset, key); } nfaCheckFinalState(nfa, fstate, sstate, offset, scratch->tctxt.cb, scratch->tctxt.cb_som, scratch->tctxt.userCtx); } }
static rose_inline void roseCheckEodSuffixes(const struct RoseEngine *t, u8 *state, u64a offset, struct hs_scratch *scratch) { const u8 *aa = getActiveLeafArray(t, state); const u32 aaCount = t->activeArrayCount; UNUSED u32 qCount = t->queueCount; for (u32 qi = mmbit_iterate(aa, aaCount, MMB_INVALID); qi != MMB_INVALID; qi = mmbit_iterate(aa, aaCount, qi)) { const struct NfaInfo *info = getNfaInfoByQueue(t, qi); const struct NFA *nfa = getNfaByInfo(t, info); assert(nfaAcceptsEod(nfa)); DEBUG_PRINTF("checking nfa %u\n", qi); assert(fatbit_isset(scratch->aqa, qCount, qi)); /* we have just been triggered */ char *fstate = scratch->fullState + info->fullStateOffset; const char *sstate = (const char *)state + info->stateOffset; struct mq *q = scratch->queues + qi; pushQueueNoMerge(q, MQE_END, scratch->core_info.len); q->context = NULL; /* rose exec is used as we don't want to / can't raise matches in the * history buffer. */ char rv = nfaQueueExecRose(q->nfa, q, MO_INVALID_IDX); if (rv) { /* nfa is still alive */ nfaCheckFinalState(nfa, fstate, sstate, offset, scratch->tctxt.cb, scratch->tctxt.cb_som, scratch->tctxt.userCtx); } } }
hwlmcb_rv_t roseHandleChainMatch(const struct RoseEngine *t, struct hs_scratch *scratch, u32 event, u64a top_squash_distance, u64a end, char in_catchup) { assert(event == MQE_TOP || event >= MQE_TOP_FIRST); struct core_info *ci = &scratch->core_info; u8 *aa = getActiveLeafArray(t, scratch->core_info.state); u32 aaCount = t->activeArrayCount; struct fatbit *activeQueues = scratch->aqa; u32 qCount = t->queueCount; const u32 qi = 0; /* MPV is always queue 0 if it exists */ struct mq *q = &scratch->queues[qi]; const struct NfaInfo *info = getNfaInfoByQueue(t, qi); s64a loc = (s64a)end - ci->buf_offset; assert(loc <= (s64a)ci->len && loc >= -(s64a)ci->hlen); if (!mmbit_set(aa, aaCount, qi)) { initQueue(q, qi, t, scratch); nfaQueueInitState(q->nfa, q); pushQueueAt(q, 0, MQE_START, loc); fatbit_set(activeQueues, qCount, qi); } else if (info->no_retrigger) { DEBUG_PRINTF("yawn\n"); /* nfa only needs one top; we can go home now */ return HWLM_CONTINUE_MATCHING; } else if (!fatbit_set(activeQueues, qCount, qi)) { initQueue(q, qi, t, scratch); loadStreamState(q->nfa, q, 0); pushQueueAt(q, 0, MQE_START, 0); } else if (isQueueFull(q)) { DEBUG_PRINTF("queue %u full -> catching up nfas\n", qi); /* we know it is a chained nfa and the suffixes/outfixes must already * be known to be consistent */ if (ensureMpvQueueFlushed(t, scratch, qi, loc, in_catchup) == HWLM_TERMINATE_MATCHING) { DEBUG_PRINTF("terminating...\n"); return HWLM_TERMINATE_MATCHING; } } if (top_squash_distance) { assert(q->cur != q->end); struct mq_item *last = &q->items[q->end - 1]; if (last->type == event && last->location >= loc - (s64a)top_squash_distance) { last->location = loc; goto event_enqueued; } } pushQueue(q, event, loc); event_enqueued: if (q_cur_loc(q) == (s64a)ci->len) { /* we may not run the nfa; need to ensure state is fine */ DEBUG_PRINTF("empty run\n"); pushQueueNoMerge(q, MQE_END, loc); char alive = nfaQueueExec(q->nfa, q, loc); if (alive) { scratch->tctxt.mpv_inactive = 0; q->cur = q->end = 0; pushQueueAt(q, 0, MQE_START, loc); } else { mmbit_unset(aa, aaCount, qi); fatbit_unset(scratch->aqa, qCount, qi); } } DEBUG_PRINTF("added mpv event at %lld\n", loc); scratch->tctxt.next_mpv_offset = 0; /* the top event may result in matches * earlier than expected */ return HWLM_CONTINUE_MATCHING; }