/** * \brief Run the program for the given literal ID, with the interpreter * out of line. * * Assumes not in_anchored. */ static really_inline hwlmcb_rv_t roseProcessMatch(const struct RoseEngine *t, struct hs_scratch *scratch, u64a end, size_t match_len, u32 id) { DEBUG_PRINTF("id=%u\n", id); const u32 *programs = getByOffset(t, t->litProgramOffset); assert(id < t->literalCount); const u64a som = 0; const u8 flags = 0; return roseRunProgram(t, scratch, programs[id], som, end, match_len, flags); }
int roseAnchoredCallback(u64a start, u64a end, u32 id, void *ctx) { struct hs_scratch *scratch = ctx; assert(scratch && scratch->magic == SCRATCH_MAGIC); struct RoseContext *tctxt = &scratch->tctxt; struct core_info *ci = &scratch->core_info; const struct RoseEngine *t = ci->rose; u64a real_end = ci->buf_offset + end; // index after last byte DEBUG_PRINTF("MATCH id=%u offsets=[???,%llu]\n", id, real_end); DEBUG_PRINTF("STATE groups=0x%016llx\n", tctxt->groups); if (can_stop_matching(scratch)) { DEBUG_PRINTF("received a match when we're already dead!\n"); return MO_HALT_MATCHING; } const size_t match_len = 0; /* delayed literals need to be delivered before real literals; however * delayed literals only come from the floating table so if we are going * to deliver a literal here it must be too early for a delayed literal */ /* no history checks from anchored region and we are before the flush * boundary */ if (real_end <= t->floatingMinLiteralMatchOffset) { roseFlushLastByteHistory(t, scratch, real_end); tctxt->lastEndOffset = real_end; } const u32 *programs = getByOffset(t, t->litProgramOffset); assert(id < t->literalCount); const u8 flags = ROSE_PROG_FLAG_IN_ANCHORED; if (roseRunProgram(t, scratch, programs[id], start, real_end, match_len, flags) == HWLM_TERMINATE_MATCHING) { assert(can_stop_matching(scratch)); DEBUG_PRINTF("caller requested termination\n"); return MO_HALT_MATCHING; } DEBUG_PRINTF("DONE groups=0x%016llx\n", tctxt->groups); if (real_end > t->floatingMinLiteralMatchOffset) { recordAnchoredLiteralMatch(t, scratch, id, real_end); } return MO_CONTINUE_MATCHING; }
/** * \brief Match callback adaptor used for matches from pure-literal cases. * * Literal match IDs in this path run limited Rose programs that do not use * Rose state (which is not initialised in the pure-literal path). They can * still, for example, check lookarounds or literal masks. */ hwlmcb_rv_t rosePureLiteralCallback(size_t start, size_t end, u32 id, void *context) { DEBUG_PRINTF("start=%zu, end=%zu, id=%u\n", start, end, id); struct hs_scratch *scratch = context; struct core_info *ci = &scratch->core_info; const u64a real_end = (u64a)end + ci->buf_offset + 1; const u64a som = 0; const size_t match_len = end - start + 1; const struct RoseEngine *rose = ci->rose; const u32 *programs = getByOffset(rose, rose->litProgramOffset); assert(id < rose->literalCount); const u8 flags = 0; return roseRunProgram(rose, scratch, programs[id], som, real_end, match_len, flags); }
static really_inline void do_rebuild(const struct RoseEngine *t, struct hs_scratch *scratch) { assert(t->drmatcherOffset); assert(!can_stop_matching(scratch)); const struct HWLM *hwlm = getByOffset(t, t->drmatcherOffset); size_t len = MIN(scratch->core_info.hlen, t->delayRebuildLength); const u8 *buf = scratch->core_info.hbuf + scratch->core_info.hlen - len; DEBUG_PRINTF("BEGIN FLOATING REBUILD over %zu bytes\n", len); scratch->core_info.status &= ~STATUS_DELAY_DIRTY; hwlmExec(hwlm, buf, len, 0, roseDelayRebuildCallback, scratch, scratch->tctxt.groups); assert(!can_stop_matching(scratch)); }
hwlmcb_rv_t roseDelayRebuildCallback(size_t start, size_t end, u32 id, void *ctx) { struct hs_scratch *scratch = ctx; struct RoseContext *tctx = &scratch->tctxt; struct core_info *ci = &scratch->core_info; const struct RoseEngine *t = ci->rose; size_t rb_len = MIN(ci->hlen, t->delayRebuildLength); u64a real_end = ci->buf_offset - rb_len + end + 1; // index after last byte #ifdef DEBUG DEBUG_PRINTF("REBUILD MATCH id=%u offsets=[%llu,%llu]: ", id, start + ci->buf_offset - rb_len, real_end); printMatch(ci, start + ci->buf_offset - rb_len, real_end); printf("\n"); #endif DEBUG_PRINTF("STATE groups=0x%016llx\n", tctx->groups); const u32 *delayRebuildPrograms = getByOffset(t, t->litDelayRebuildProgramOffset); assert(id < t->literalCount); const u32 program = delayRebuildPrograms[id]; if (program) { const u64a som = 0; const size_t match_len = end - start + 1; const u8 flags = 0; UNUSED hwlmcb_rv_t rv = roseRunProgram(t, scratch, program, som, real_end, match_len, flags); assert(rv != HWLM_TERMINATE_MATCHING); } /* we are just repopulating the delay queue, groups should be * already set from the original scan. */ return tctx->groups; }
static rose_inline void runEagerPrefixesStream(const struct RoseEngine *t, struct hs_scratch *scratch) { if (!t->eagerIterOffset || scratch->core_info.buf_offset >= EAGER_STOP_OFFSET) { return; } char *state = scratch->core_info.state; u8 *ara = getActiveLeftArray(t, state); /* indexed by offsets into * left_table */ const u32 arCount = t->activeLeftCount; const u32 qCount = t->queueCount; const struct LeftNfaInfo *left_table = getLeftTable(t); const struct mmbit_sparse_iter *it = getByOffset(t, t->eagerIterOffset); struct mmbit_sparse_state si_state[MAX_SPARSE_ITER_STATES]; u32 idx = 0; u32 ri = mmbit_sparse_iter_begin(ara, arCount, &idx, it, si_state); for (; ri != MMB_INVALID; ri = mmbit_sparse_iter_next(ara, arCount, ri, &idx, it, si_state)) { const struct LeftNfaInfo *left = left_table + ri; u32 qi = ri + t->leftfixBeginQueue; DEBUG_PRINTF("leftfix %u of %u, maxLag=%u\n", ri, arCount, left->maxLag); assert(!fatbit_isset(scratch->aqa, qCount, qi)); assert(left->eager); assert(!left->infix); struct mq *q = scratch->queues + qi; const struct NFA *nfa = getNfaByQueue(t, qi); s64a loc = MIN(scratch->core_info.len, EAGER_STOP_OFFSET - scratch->core_info.buf_offset); fatbit_set(scratch->aqa, qCount, qi); initRoseQueue(t, qi, left, scratch); if (scratch->core_info.buf_offset) { s64a sp = left->transient ? -(s64a)scratch->core_info.hlen : -(s64a)loadRoseDelay(t, state, left); pushQueueAt(q, 0, MQE_START, sp); if (scratch->core_info.buf_offset + sp > 0) { loadStreamState(nfa, q, sp); /* if the leftfix fix is currently in a match state, we cannot * advance it. */ if (nfaInAnyAcceptState(nfa, q)) { continue; } pushQueueAt(q, 1, MQE_END, loc); } else { pushQueueAt(q, 1, MQE_TOP, sp); pushQueueAt(q, 2, MQE_END, loc); nfaQueueInitState(q->nfa, q); } } else { pushQueueAt(q, 0, MQE_START, 0); pushQueueAt(q, 1, MQE_TOP, 0); pushQueueAt(q, 2, MQE_END, loc); nfaQueueInitState(nfa, q); } char alive = nfaQueueExecToMatch(q->nfa, q, loc); if (!alive) { DEBUG_PRINTF("queue %u dead, squashing\n", qi); mmbit_unset(ara, arCount, ri); fatbit_unset(scratch->aqa, qCount, qi); scratch->tctxt.groups &= left->squash_mask; } else if (q->cur == q->end) { assert(alive != MO_MATCHES_PENDING); /* unlike in block mode we cannot squash groups if there is no match * in this block as we need the groups on for later stream writes */ /* TODO: investigate possibility of a method to suppress groups for * a single stream block. */ DEBUG_PRINTF("queue %u finished, nfa lives\n", qi); q->cur = q->end = 0; pushQueueAt(q, 0, MQE_START, loc); } else { assert(alive == MO_MATCHES_PENDING); DEBUG_PRINTF("queue %u unfinished, nfa lives\n", qi); q->end--; /* remove end item */ } } }
static rose_inline int roseEodRunIterator(const struct RoseEngine *t, u8 *state, u64a offset, struct hs_scratch *scratch) { if (!t->eodIterOffset) { return MO_CONTINUE_MATCHING; } const struct RoseRole *roleTable = getRoleTable(t); const struct RosePred *predTable = getPredTable(t); const struct RoseIterMapping *iterMapBase = getByOffset(t, t->eodIterMapOffset); const struct mmbit_sparse_iter *it = getByOffset(t, t->eodIterOffset); assert(ISALIGNED(iterMapBase)); assert(ISALIGNED(it)); // Sparse iterator state was allocated earlier struct mmbit_sparse_state *s = scratch->sparse_iter_state; struct fatbit *handled_roles = scratch->handled_roles; const u32 numStates = t->rolesWithStateCount; void *role_state = getRoleState(state); u32 idx = 0; u32 i = mmbit_sparse_iter_begin(role_state, numStates, &idx, it, s); fatbit_clear(handled_roles); for (; i != MMB_INVALID; i = mmbit_sparse_iter_next(role_state, numStates, i, &idx, it, s)) { DEBUG_PRINTF("pred state %u (iter idx=%u) is on\n", i, idx); const struct RoseIterMapping *iterMap = iterMapBase + idx; const struct RoseIterRole *roles = getByOffset(t, iterMap->offset); assert(ISALIGNED(roles)); DEBUG_PRINTF("%u roles to consider\n", iterMap->count); for (u32 j = 0; j != iterMap->count; j++) { u32 role = roles[j].role; assert(role < t->roleCount); DEBUG_PRINTF("checking role %u, pred %u:\n", role, roles[j].pred); const struct RoseRole *tr = roleTable + role; if (fatbit_isset(handled_roles, t->roleCount, role)) { DEBUG_PRINTF("role %u already handled by the walk, skip\n", role); continue; } // Special case: if this role is a trivial case (pred type simple) // we don't need to check any history and we already know the pred // role is on. if (tr->flags & ROSE_ROLE_PRED_SIMPLE) { DEBUG_PRINTF("pred type is simple, no need for checks\n"); } else { assert(roles[j].pred < t->predCount); const struct RosePred *tp = predTable + roles[j].pred; if (!roseCheckPredHistory(tp, offset)) { continue; } } /* mark role as handled so we don't touch it again in this walk */ fatbit_set(handled_roles, t->roleCount, role); DEBUG_PRINTF("fire report for role %u, report=%u\n", role, tr->reportId); int rv = scratch->tctxt.cb(offset, tr->reportId, scratch->tctxt.userCtx); if (rv == MO_HALT_MATCHING) { return MO_HALT_MATCHING; } } } return MO_CONTINUE_MATCHING; }