mirror of
https://github.com/openappsec/openappsec.git
synced 2025-06-28 16:41:02 +03:00
462 lines
20 KiB
C++
462 lines
20 KiB
C++
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Thin NFA I/S
|
|
// ------------
|
|
// The thin NFA allows building and executing an automaton for string search, using the
|
|
// Aho-Corasick algorithm.
|
|
// The resulting automaton is built in a compact representation. Some states are "full" - they
|
|
// have an explicit transition per character. Others are "partial" - they have some explicit transitions,
|
|
// plus a "default transition". This is an epsilon-transition. For characters which don't have an
|
|
// explicit transition, we follow the default transition, and look up the same character there.
|
|
//
|
|
// Source files
|
|
// ------------
|
|
// kiss_thin_nfa.c (this file) - execution code.
|
|
// kiss_thin_nfa_build.c - allocation and destruction code. Contains code which is common to compilation
|
|
// and serialization/deserialization. All objects which are part of the comipled automaton are created here.
|
|
// kiss_thin_nfa_compile.c - compilation code. Contains the logic that converts a set of strings into an automaton.
|
|
// kiss_thin_nfa_analyze.c - Validation and dump. Code that reads the BNFA and tries to make sense of it.
|
|
// kiss_thin_nfa_impl.h - internal header file. APIs and definitions between the different source files.
|
|
|
|
|
|
// ********************* INCLUDES **************************
|
|
#include "kiss_thin_nfa_impl.h"
|
|
|
|
// Internal execution flags passed to kiss_dfa_exec_one_buf:
|
|
#define KISS_PM_EXEC_LAST_BUFF 0x00000001 // This is the last buffer (preset buffer or the last buffer in vbuf)
|
|
|
|
|
|
// The runtime status of the Thin NFA
|
|
struct kiss_bnfa_runtime_s {
|
|
KissThinNFA *nfa_h; // The NFA we're executing
|
|
kiss_bnfa_comp_offset_t last_bnfa_offset; // Last state reached by exec_one_buf
|
|
std::vector<std::pair<uint, uint>> *matches; // The matches we've found so far
|
|
u_int scanned_so_far; // The length of all buffers before the current buffer
|
|
};
|
|
|
|
|
|
// Critical code path debugging - enabled only in debug mode.
|
|
#define THIN_NFA_TRACE_TRANS(runtime, next_off, ch, op) \
|
|
thinnfa_debug_perf( \
|
|
"%s: Transition by 0x%02x to %d - %s\n", \
|
|
FILE_LINE, \
|
|
ch, \
|
|
kiss_bnfa_offset_decompress(next_off), \
|
|
op \
|
|
)
|
|
|
|
#define TRANSLATE_CHAR_IF_NEEED(do_char_trans, char_trans_table, ch) \
|
|
((u_char)((do_char_trans) ? ((char_trans_table)[ch]) : (ch)))
|
|
|
|
// Given a match for a pattern at a given position, insert an entry to the match list.
|
|
// We may add more than one entry, depending on the number of matching patterns.
|
|
//
|
|
// Parameters:
|
|
// runtime - the current status of Thin NFA execution.
|
|
// one_buf_offset - the offset of the match, within the buffer currently scanned.
|
|
// Together with runtime->scanned_so_far we can get the real match offset.
|
|
// one_buf_len - the length of the buffer currently scanned. Used for $ processing.
|
|
// exec_flags - the flags used.
|
|
static CP_INLINE void
|
|
kiss_thin_nfa_handle_match(struct kiss_bnfa_runtime_s *runtime, u_int pat_arr_offset,
|
|
u_int one_buf_offset, u_int one_buf_len, u_int exec_flags)
|
|
{
|
|
static const char rname[] = "kiss_thin_nfa_handle_match";
|
|
u_int match_pos;
|
|
const kiss_thin_nfa_pattern_array_t *pat_arr;
|
|
const kiss_thin_nfa_pattern_t *curr_id;
|
|
const kiss_thin_nfa_pattern_t *pat_end;
|
|
|
|
// Where was the match? one_buf_offset is already moved beyond the characeter that caused the match,
|
|
// so we subtract one to get this character's offset.
|
|
match_pos = runtime->scanned_so_far + (one_buf_offset - 1);
|
|
pat_arr = kiss_thin_nfa_offset_to_pat_array_ptr(runtime->nfa_h, pat_arr_offset);
|
|
// Go over the patterns and add them to the match queue.
|
|
pat_end = &(pat_arr->pattern[pat_arr->n_patterns]);
|
|
thinnfa_debug_perf((
|
|
"%s: Going over %u patterns, starting from offset %u\n",
|
|
rname,
|
|
pat_arr->n_patterns,
|
|
pat_arr_offset
|
|
));
|
|
for (curr_id = &(pat_arr->pattern[0]); curr_id != pat_end; curr_id++) {
|
|
thinnfa_debug(("%s: Match for pattern ID %d at %d len %d\n", rname, curr_id->id, match_pos, curr_id->len));
|
|
|
|
// Handle ^ - An N byte pattern at the start of the buffer would match at byte N-1.
|
|
// NOTE: If the anchored state optimization is implemented in compilation, this test isn't needed.
|
|
if ((curr_id->pattern_id_flags & KISS_PM_LSS_AT_BUF_START) && (match_pos != curr_id->len - 1)) {
|
|
thinnfa_debug_perf(("%s: Not match because of ^ %d\n", rname, curr_id->id));
|
|
continue;
|
|
}
|
|
|
|
// Handle $ - We must match at the buffer end, and it must be the last buffer
|
|
if ((curr_id->pattern_id_flags & KISS_PM_LSS_AT_BUF_END) &&
|
|
!((one_buf_offset == one_buf_len) && (exec_flags & KISS_PM_EXEC_LAST_BUFF))) {
|
|
thinnfa_debug_perf(("%s: Not match because of $ %d\n", rname, curr_id->id));
|
|
continue;
|
|
}
|
|
runtime->matches->emplace_back(curr_id->id, match_pos);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// Wrapper to kiss_thin_nfa_handle_match, gets the state offset, not the ID.
|
|
static CP_INLINE void
|
|
kiss_thin_nfa_handle_match_state(struct kiss_bnfa_runtime_s *runtime, kiss_bnfa_comp_offset_t cur_offset,
|
|
u_int one_buf_offset, u_int one_buf_len, u_int exec_flags)
|
|
{
|
|
const kiss_bnfa_state_t *state = kiss_bnfa_comp_offset_to_state(
|
|
runtime->nfa_h->bnfa,
|
|
cur_offset,
|
|
KISS_BNFA_STATE_MATCH
|
|
);
|
|
kiss_thin_nfa_handle_match(runtime, state->match.match_id, one_buf_offset, one_buf_len, exec_flags);
|
|
}
|
|
|
|
// Calculate the next state's offset, given a state and character. Good for full states only.
|
|
// Faster than kiss_thin_nfa_get_next_offset. An offset peremeter is compressed 16-bit offset
|
|
// The returned offset is also compressed
|
|
static CP_INLINE kiss_bnfa_comp_offset_t
|
|
kiss_thin_nfa_get_next_offset_full(const kiss_bnfa_state_t *bnfa, kiss_bnfa_comp_offset_t offset,
|
|
unsigned char char_to_find)
|
|
{
|
|
const kiss_bnfa_state_t *state = kiss_bnfa_comp_offset_to_state(bnfa, offset, KISS_BNFA_STATE_FULL);
|
|
return (kiss_bnfa_comp_offset_t)state->full.transitions[char_to_find];
|
|
}
|
|
|
|
|
|
// Calculate the next state's offset, given a state and character. Good for partial states only.
|
|
// Also indicates whether the buffer position should be incremented (i.e. if an explicit transition was found)
|
|
static CP_INLINE kiss_bnfa_comp_offset_t
|
|
kiss_thin_nfa_get_next_offset_partial(const kiss_bnfa_state_t *bnfa, kiss_bnfa_comp_offset_t offset,
|
|
unsigned char char_to_find, BOOL *inc_pos)
|
|
{
|
|
const kiss_bnfa_state_t *state = kiss_bnfa_comp_offset_to_state(bnfa, offset, KISS_BNFA_STATE_PARTIAL);
|
|
u_int trans_num = state->partial.trans_num;
|
|
u_int i;
|
|
|
|
// Simple linear search is fast for a few transitions. If we have many, we use a full state anyway.
|
|
for (i = 0; i < trans_num; i++) {
|
|
const struct kiss_bnfa_partial_transition_s *tran = &state->partial.transitions[i];
|
|
// Smaller? Keep looking. Larger? Give up (transitions are sorted).
|
|
if (tran->tran_char < char_to_find) continue;
|
|
if (tran->tran_char > char_to_find) break;
|
|
|
|
// Found the character (explicit transition) - consume a characeter and move the automaton
|
|
*inc_pos = TRUE;
|
|
return tran->next_state_offset;
|
|
}
|
|
|
|
// No explicit transition found - move to the fail state, without consuming a character.
|
|
*inc_pos = FALSE;
|
|
return state->partial.fail_state_offset;
|
|
}
|
|
|
|
|
|
// Calculate the next state's offset, when the current is a match state.
|
|
// Doesn't consume a character (epsilon transition)
|
|
static CP_INLINE kiss_bnfa_comp_offset_t
|
|
kiss_thin_nfa_get_next_offset_match(CP_MAYBE_UNUSED const kiss_bnfa_state_t *bnfa, kiss_bnfa_comp_offset_t offset)
|
|
{
|
|
// After a match state we just move to the next consecutive state.
|
|
return offset + (sizeof(kiss_bnfa_match_state_t) / KISS_BNFA_STATE_ALIGNMENT);
|
|
}
|
|
|
|
#define PARALLEL_SCANS_NUM 4 // 4 heads scanning the buffer
|
|
#define UNROLL_FACTOR 4 // Advance each head 4 bytes per loop
|
|
|
|
|
|
// Move one head of the state machine. bnfa_offset must not be a match state.
|
|
static CP_INLINE kiss_bnfa_comp_offset_t
|
|
parallel_scan_advance_one(const kiss_bnfa_state_t *bnfa, kiss_bnfa_comp_offset_t bnfa_offset, const unsigned char ch)
|
|
{
|
|
while (bnfa_offset >= 0) {
|
|
BOOL inc_pos;
|
|
// Partial state - Look for an explicit transition, or use the fail state
|
|
bnfa_offset = kiss_thin_nfa_get_next_offset_partial(bnfa, bnfa_offset, ch, &inc_pos);
|
|
if (inc_pos) {
|
|
// Found an explicit transition - can move to the next state.
|
|
return bnfa_offset;
|
|
}
|
|
}
|
|
|
|
// Full state (either we started with full, or the fail state chain reached one)
|
|
return kiss_thin_nfa_get_next_offset_full(bnfa, bnfa_offset, ch);
|
|
}
|
|
|
|
|
|
// Check if all heads are on a full state.
|
|
// If they are - advance all heads and return TRUE.
|
|
// If they aren't - do nothing and return FALSE.
|
|
static CP_INLINE BOOL
|
|
parallel_scan_advance_if_full(
|
|
const kiss_bnfa_state_t *bnfa,
|
|
kiss_bnfa_comp_offset_t *bnfa_offsets,
|
|
const unsigned char **buf_pos
|
|
)
|
|
{
|
|
kiss_bnfa_comp_offset_t offsets_and;
|
|
|
|
// If the bitwise AND of 4 offsets (PARALLEL_SCANS_NUM) is negative, they're all negaitve, so all states are full.
|
|
offsets_and = bnfa_offsets[0] & bnfa_offsets[1] & bnfa_offsets[2] & bnfa_offsets[3];
|
|
if (CP_UNLIKELY(offsets_and >= 0)) return FALSE;
|
|
|
|
// All states are full - make 4 transitions (PARALLEL_SCANS_NUM).
|
|
bnfa_offsets[0] = kiss_thin_nfa_get_next_offset_full(bnfa, bnfa_offsets[0], *(buf_pos[0]));
|
|
buf_pos[0]++;
|
|
bnfa_offsets[1] = kiss_thin_nfa_get_next_offset_full(bnfa, bnfa_offsets[1], *(buf_pos[1]));
|
|
buf_pos[1]++;
|
|
bnfa_offsets[2] = kiss_thin_nfa_get_next_offset_full(bnfa, bnfa_offsets[2], *(buf_pos[2]));
|
|
buf_pos[2]++;
|
|
bnfa_offsets[3] = kiss_thin_nfa_get_next_offset_full(bnfa, bnfa_offsets[3], *(buf_pos[3]));
|
|
buf_pos[3]++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Repeat parallel_scan_advance_if_full up to 4 times (UNROLL_FACTOR).
|
|
// Retrurn TRUE if all 4 were done, FALSE if stopped earlier.
|
|
static CP_INLINE BOOL
|
|
parallel_scan_advance_if_full_unroll(
|
|
const kiss_bnfa_state_t *bnfa,
|
|
kiss_bnfa_comp_offset_t *bnfa_offsets,
|
|
const unsigned char **buf_pos
|
|
)
|
|
{
|
|
if (!parallel_scan_advance_if_full(bnfa, bnfa_offsets, buf_pos)) return FALSE;
|
|
if (!parallel_scan_advance_if_full(bnfa, bnfa_offsets, buf_pos)) return FALSE;
|
|
if (!parallel_scan_advance_if_full(bnfa, bnfa_offsets, buf_pos)) return FALSE;
|
|
if (!parallel_scan_advance_if_full(bnfa, bnfa_offsets, buf_pos)) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Find the offset where each head should start and stop
|
|
static void
|
|
calc_head_buf_range(const u_char *buffer, u_int len, const u_char **head_start_pos, const u_char **head_end_pos)
|
|
{
|
|
static const char rname[] = "calc_head_buf_range";
|
|
const u_char *orig_buf = buffer;
|
|
u_int len_per_head = len / PARALLEL_SCANS_NUM;
|
|
u_int rem = len % PARALLEL_SCANS_NUM;
|
|
u_int i;
|
|
|
|
for (i=0; i<PARALLEL_SCANS_NUM; i++) {
|
|
u_int head_len = len_per_head;
|
|
// Give each head its share, late heads get a part of the remainder.
|
|
// The "Handle remainders" loop below assumes the last head has the largest part.
|
|
if (i >= PARALLEL_SCANS_NUM-rem) head_len++;
|
|
head_start_pos[i] = buffer;
|
|
buffer += head_len;
|
|
head_end_pos[i] = buffer;
|
|
thinnfa_debug(("%s: Head %u gets range %ld:%ld\n", rname,
|
|
i, head_start_pos[i]-orig_buf, head_end_pos[i]-orig_buf));
|
|
}
|
|
}
|
|
|
|
// Set the initial BNFA offset for each head
|
|
static void
|
|
set_head_bnfa_offset(
|
|
struct kiss_bnfa_runtime_s *runtime,
|
|
kiss_bnfa_comp_offset_t *bnfa_pos,
|
|
const u_char **buf_pos,
|
|
const u_char *buffer
|
|
)
|
|
{
|
|
const KissThinNFA *nfa_h = runtime->nfa_h;
|
|
kiss_bnfa_comp_offset_t init_off = kiss_bnfa_offset_compress(nfa_h->min_bnfa_offset);
|
|
u_int i;
|
|
|
|
if (nfa_h->flags & KISS_THIN_NFA_HAS_ANCHOR) {
|
|
// Start from the root (next full state after the anchored root)
|
|
init_off++;
|
|
}
|
|
|
|
// Heads that scan from the beginning of the buffer, will start at previous buffer's ending state.
|
|
// The rest start anew.
|
|
// Several scanning heads will start at buffer's beginning when buffer's size is less than PARALLEL_SCANS_NUM
|
|
for (i=0; i<PARALLEL_SCANS_NUM; i++) {
|
|
if (buf_pos[i] - buffer == 0) {
|
|
bnfa_pos[i] = runtime->last_bnfa_offset;
|
|
} else {
|
|
bnfa_pos[i] = init_off;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Run Thin NFA parallely on a single buffer.
|
|
static CP_INLINE void
|
|
kiss_thin_nfa_exec_one_buf_parallel_ex(
|
|
struct kiss_bnfa_runtime_s *runtime,
|
|
const u_char *buffer,
|
|
u_int len, u_int flags,
|
|
BOOL do_char_trans,
|
|
u_char *char_trans_table
|
|
)
|
|
{
|
|
const kiss_bnfa_state_t *bnfa = runtime->nfa_h->bnfa;
|
|
const unsigned char *end, *buf_pos[PARALLEL_SCANS_NUM], *head_end_pos[PARALLEL_SCANS_NUM];
|
|
kiss_bnfa_comp_offset_t bnfa_offset[PARALLEL_SCANS_NUM];
|
|
u_int i;
|
|
u_int overlap_bytes;
|
|
int overlap_head_mask;
|
|
|
|
// set starting position, ending position and state for each scanning head
|
|
calc_head_buf_range(buffer, len, buf_pos, head_end_pos);
|
|
set_head_bnfa_offset(runtime, bnfa_offset, buf_pos, buffer);
|
|
|
|
end = buffer + len;
|
|
|
|
// unroll 16 (PARALLEL_SCANS_NUM * UNROLL_FACTOR) times, while we have at least 4 input bytes to process.
|
|
while (buf_pos[PARALLEL_SCANS_NUM-1] + UNROLL_FACTOR <= end) {
|
|
// Fastpath - Advance all heads up to 4 chars, as long as they're all on a full state.
|
|
if (CP_LIKELY(parallel_scan_advance_if_full_unroll(bnfa, bnfa_offset, buf_pos))) continue;
|
|
|
|
// At least one head is on partial or match - advance all 4 by their type.
|
|
for (i=0; i<PARALLEL_SCANS_NUM; i++) {
|
|
if (bnfa_offset[i] < 0) {
|
|
// Semi-fastpath. When we reach this loop, normally 3 of 4 heads are on a full state.
|
|
bnfa_offset[i] = kiss_thin_nfa_get_next_offset_full(bnfa, bnfa_offset[i], *(buf_pos[i]));
|
|
(buf_pos[i])++;
|
|
continue;
|
|
}
|
|
|
|
if (kiss_bnfa_state_type(bnfa, bnfa_offset[i]) == KISS_BNFA_STATE_MATCH) {
|
|
// Handle a match
|
|
kiss_thin_nfa_handle_match_state(runtime, bnfa_offset[i], (u_int)(buf_pos[i] - buffer), len, flags);
|
|
bnfa_offset[i] = kiss_thin_nfa_get_next_offset_match(bnfa, bnfa_offset[i]);
|
|
}
|
|
// Advance to the next state
|
|
bnfa_offset[i] = parallel_scan_advance_one(bnfa, bnfa_offset[i],
|
|
TRANSLATE_CHAR_IF_NEEED(do_char_trans, char_trans_table, *(buf_pos[i])));
|
|
(buf_pos[i])++;
|
|
}
|
|
}
|
|
|
|
// Handle remainders (the above loop jumps 4 chars at a time, so it may leave up to 3 unscanned)
|
|
while (buf_pos[PARALLEL_SCANS_NUM-1] < end) {
|
|
// Advance only heads that haven't reached their end position
|
|
for (i=0; i<PARALLEL_SCANS_NUM; i++) {
|
|
if (buf_pos[i] >= head_end_pos[i]) continue;
|
|
if (kiss_bnfa_state_type(bnfa, bnfa_offset[i]) == KISS_BNFA_STATE_MATCH) {
|
|
// Handle a match
|
|
kiss_thin_nfa_handle_match_state(runtime, bnfa_offset[i], (u_int)(buf_pos[i] - buffer), len, flags);
|
|
bnfa_offset[i] = kiss_thin_nfa_get_next_offset_match(bnfa, bnfa_offset[i]);
|
|
}
|
|
// Advance to the next state
|
|
bnfa_offset[i] = parallel_scan_advance_one(bnfa, bnfa_offset[i],
|
|
TRANSLATE_CHAR_IF_NEEED(do_char_trans, char_trans_table, *(buf_pos[i])));
|
|
(buf_pos[i])++;
|
|
}
|
|
}
|
|
|
|
// Handle overlap - advance all heads into the next head's range, as long as there's a chance
|
|
// for a match which started in this head's range.
|
|
overlap_head_mask = (1<<(PARALLEL_SCANS_NUM-1))-1; // All heads except the last
|
|
for (overlap_bytes = 0; overlap_head_mask!=0; overlap_bytes++) {
|
|
// Advance each head (except the last) as long as overlap is needed for it
|
|
for (i=0; i<PARALLEL_SCANS_NUM-1; i++) {
|
|
int my_mask = (1<<i);
|
|
u_int state_depth;
|
|
|
|
// Did we stop this head's overlap already?
|
|
if (!(overlap_head_mask & my_mask)) continue;
|
|
|
|
// Stop the overlap if the state is not as deep as the overlap, or the buffer ended.
|
|
state_depth = kiss_bnfa_offset_to_depth(runtime->nfa_h, bnfa_offset[i]);
|
|
if ((state_depth <= overlap_bytes) || (buf_pos[i] >= end)) {
|
|
overlap_head_mask &= ~my_mask;
|
|
continue;
|
|
}
|
|
|
|
// Advance the state machine, including match handling
|
|
if (kiss_bnfa_state_type(bnfa, bnfa_offset[i]) == KISS_BNFA_STATE_MATCH) {
|
|
// Handle a match
|
|
kiss_thin_nfa_handle_match_state(runtime, bnfa_offset[i], (u_int)(buf_pos[i] - buffer), len, flags);
|
|
bnfa_offset[i] = kiss_thin_nfa_get_next_offset_match(bnfa, bnfa_offset[i]);
|
|
}
|
|
// Advance to the next state
|
|
bnfa_offset[i] = parallel_scan_advance_one(bnfa, bnfa_offset[i],
|
|
TRANSLATE_CHAR_IF_NEEED(do_char_trans, char_trans_table, *(buf_pos[i])));
|
|
(buf_pos[i])++;
|
|
}
|
|
}
|
|
|
|
// We may have stopped on a match state. If so - handle and advance
|
|
for (i=0; i<PARALLEL_SCANS_NUM; i++) {
|
|
if (kiss_bnfa_state_type(bnfa, bnfa_offset[i]) == KISS_BNFA_STATE_MATCH) {
|
|
// Handle a match
|
|
kiss_thin_nfa_handle_match_state(runtime, bnfa_offset[i], (u_int)(buf_pos[i] - buffer), len, flags);
|
|
bnfa_offset[i] = kiss_thin_nfa_get_next_offset_match(bnfa, bnfa_offset[i]);
|
|
}
|
|
}
|
|
|
|
// The next scan should start at the state where the current scan ended.
|
|
// If multiple heads reached the buffer end, use the one with the lowest index,
|
|
// because it has covered more data than other heads that reached the buffer end.
|
|
for (i=0; i<PARALLEL_SCANS_NUM; i++) {
|
|
if (buf_pos[i] == buf_pos[PARALLEL_SCANS_NUM-1]) {
|
|
runtime->last_bnfa_offset = bnfa_offset[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// Execute a thin NFA on a buffer.
|
|
// Parameters:
|
|
// nfa_h - the NFA handle
|
|
// buf - a buffer to scan.
|
|
// matches - output - will be filled with a kiss_pmglob_match_data element for each match.
|
|
void
|
|
kiss_thin_nfa_exec(KissThinNFA *nfa_h, const Buffer& buf, std::vector<std::pair<uint, uint>> &matches)
|
|
{
|
|
struct kiss_bnfa_runtime_s bnfa_runtime;
|
|
|
|
dbgAssert(nfa_h != nullptr)
|
|
<< AlertInfo(AlertTeam::CORE, "pattern matcher")
|
|
<< "kiss_thin_nfa_exec() was called with null handle";
|
|
|
|
if (buf.size() == 0) {
|
|
return;
|
|
}
|
|
|
|
// Set the runtime status structure
|
|
bnfa_runtime.nfa_h = nfa_h;
|
|
bnfa_runtime.last_bnfa_offset = kiss_bnfa_offset_compress(nfa_h->min_bnfa_offset); // The initial state
|
|
bnfa_runtime.matches = &matches;
|
|
bnfa_runtime.scanned_so_far = 0;
|
|
|
|
auto segments = buf.segRange();
|
|
for( auto iter = segments.begin(); iter != segments.end(); iter++ ) {
|
|
const u_char * data = iter->data();
|
|
u_int len = iter->size();
|
|
u_int flags = ((iter+1)==segments.end()) ? KISS_PM_EXEC_LAST_BUFF : 0;
|
|
if (nfa_h->flags & KISS_THIN_NFA_USE_CHAR_XLATION) {
|
|
kiss_thin_nfa_exec_one_buf_parallel_ex(&bnfa_runtime, data, len, flags, TRUE, nfa_h->xlation_tab);
|
|
} else {
|
|
kiss_thin_nfa_exec_one_buf_parallel_ex(&bnfa_runtime, data, len, flags, FALSE, nullptr);
|
|
}
|
|
bnfa_runtime.scanned_so_far += len;
|
|
}
|
|
|
|
return;
|
|
}
|