Skip to content

Commit

Permalink
detect: merge sorted lists instead of qsort
Browse files Browse the repository at this point in the history
Ticket: OISF#6299

Simply because it is faster (just linear).

This is for merging match_array into tx_candidates
  • Loading branch information
catenacyber authored and victorjulien committed Jan 30, 2024
1 parent 9240ae2 commit 5bb8800
Showing 1 changed file with 78 additions and 17 deletions.
95 changes: 78 additions & 17 deletions src/detect.c
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,81 @@ static inline void StoreDetectFlags(DetectTransaction *tx, const uint8_t flow_fl
}
}

// Merge 'state' rules from the regular prefilter
// updates array_idx on the way
static inline void RuleMatchCandidateMergeStateRules(
DetectEngineThreadCtx *det_ctx, uint32_t *array_idx)
{
// Now, we will merge 2 sorted lists :
// the one in det_ctx->tx_candidates
// and the one in det_ctx->match_array
// For match_array, we take only the relevant elements where s->app_inspect != NULL

// Basically, we iterate at the same time over the 2 lists
// comparing and taking an element from either.

// Trick is to do so in place in det_ctx->tx_candidates,
// so as to minimize the number of moves in det_ctx->tx_candidates.
// For this, the algorithm traverses the lists in reverse order.
// Otherwise, if the first element of match_array was to be put before
// all tx_candidates, we would need to shift all tx_candidates

// Retain the number of elements sorted in tx_candidates before merge
uint32_t j = *array_idx;
// First loop only counting the number of elements to add
for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
const Signature *s = det_ctx->match_array[i];
if (s->app_inspect != NULL) {
(*array_idx)++;
}
}
// Future number of elements in tx_candidates after merge
uint32_t k = *array_idx;

if (k == j) {
// no new element from match_array to merge in tx_candidates
return;
}

// variable i is for all elements of match_array (even not relevant ones)
// variable j is for elements of tx_candidates before merge
// variable k is for elements of tx_candidates after merge
for (uint32_t i = det_ctx->match_array_cnt; i > 0;) {
const Signature *s = det_ctx->match_array[i - 1];
if (s->app_inspect == NULL) {
// no relevant element, get the next one from match_array
i--;
continue;
}
// we have one element from match_array to merge in tx_candidates
k--;
if (j > 0) {
// j > 0 means there is still at least one element in tx_candidates to merge
const RuleMatchCandidateTx *s0 = &det_ctx->tx_candidates[j - 1];
if (s->num <= s0->id) {
// get next element from previous tx_candidates
j--;
// take the element from tx_candidates before merge
det_ctx->tx_candidates[k].s = det_ctx->tx_candidates[j].s;
det_ctx->tx_candidates[k].id = det_ctx->tx_candidates[j].id;
det_ctx->tx_candidates[k].flags = det_ctx->tx_candidates[j].flags;
det_ctx->tx_candidates[k].stream_reset = det_ctx->tx_candidates[j].stream_reset;
continue;
}
} // otherwise
// get next element from match_array
i--;
// take the element from match_array
det_ctx->tx_candidates[k].s = s;
det_ctx->tx_candidates[k].id = s->num;
det_ctx->tx_candidates[k].flags = NULL;
det_ctx->tx_candidates[k].stream_reset = 0;
}
// Even if k > 0 or j > 0, the loop is over. (Note that j == k now)
// The remaining elements in tx_candidates up to k were already sorted
// and come before any other element later in the list
}

static void DetectRunTx(ThreadVars *tv,
DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx,
Expand Down Expand Up @@ -1369,24 +1444,10 @@ static void DetectRunTx(ThreadVars *tv,
}

/* merge 'state' rules from the regular prefilter */
#ifdef PROFILING
uint32_t x = array_idx;
for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
const Signature *s = det_ctx->match_array[i];
if (s->app_inspect != NULL) {
const SigIntId id = s->num;
det_ctx->tx_candidates[array_idx].s = s;
det_ctx->tx_candidates[array_idx].id = id;
det_ctx->tx_candidates[array_idx].flags = NULL;
det_ctx->tx_candidates[array_idx].stream_reset = 0;
array_idx++;

SCLogDebug("%p/%"PRIu64" rule %u (%u) added from 'match' list",
tx.tx_ptr, tx.tx_id, s->id, id);
}
}
do_sort = (array_idx > x); // sort if match added anything
SCLogDebug("%p/%" PRIu64 " rules added from 'match' list: %u", tx.tx_ptr, tx.tx_id,
array_idx - x);
#endif
RuleMatchCandidateMergeStateRules(det_ctx, &array_idx);

/* merge stored state into results */
if (tx.de_state != NULL) {
Expand Down

0 comments on commit 5bb8800

Please sign in to comment.