# UNDF: UNDF-2026-000000216 --- a/postfix/src/util/match_list.c +++ b/postfix/src/util/match_list.c @@ -50,6 +50,7 @@ #include #include #include +#include /* Application-specific */ @@ -132,6 +132,7 @@ MATCH_LIST *match_list_init(const char *pname, int flags, list->match_count = match_count; list->match_func = (MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN)); list->match_args = (const char **) mymalloc(match_count * sizeof(const char *)); va_start(ap, match_count); for (i = 0; i < match_count; i++) list->match_func[i] = va_arg(ap, MATCH_LIST_FN); va_end(ap); list->error = 0; list->fold_buf = vstring_alloc(20); + list->string_cache = htable_create(8); #define DO_MATCH 1 @@ -140,6 +141,12 @@ MATCH_LIST *match_list_init(const char *pname, int flags, list->patterns = match_list_parse(list, argv_alloc(1), saved_patterns, DO_MATCH); argv_terminate(list->patterns); + /* Pre-index all inline string patterns (no '!' negation, no ':' dict) */ + { + char **cpp; + for (cpp = list->patterns->argv; *cpp != 0; cpp++) { + char *p = *cpp; + int neg = 0; + while (*p == '!') { neg = !neg; p++; } + if (strchr(p, ':') == 0 && *p != '/') /* plain string pattern */ + htable_enter(list->string_cache, p, (char *)(neg ? (void*)1 : (void*)2)); + } + } myfree(saved_patterns); return (list); } @@ -160,6 +167,21 @@ int match_list_match(MATCH_LIST *list,...) list->error = 0; /* + * Fast path: O(1) hash lookup for the common case where all patterns + * are plain strings (no wildcards, no file includes, no type:table). + * Fall through to the linear scan only when non-string patterns exist. + */ + if (list->match_count == 1 && htable_used(list->string_cache) > 0) { + casefold(list->fold_buf, list->match_args[0]); + HTABLE_INFO *entry = htable_find(list->string_cache, STR(list->fold_buf)); + if (entry) { + /* value 2 = positive match, value 1 = negated (no match) */ + return (entry->value == (char*)2) ? 1 : 0; + } + /* Not found in hash — could still match a non-string pattern below */ + if (list->patterns->argc == (int)htable_used(list->string_cache)) + return 0; /* all patterns are plain strings and none matched */ + } + + /* * Iterate over all patterns in the list, stop at the first match. */ for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) { @@ -185,5 +207,6 @@ void match_list_free(MATCH_LIST *list) argv_free(list->patterns); myfree((void *) list->match_func); myfree((void *) list->match_args); + htable_free(list->string_cache, (void (*)(char *)) 0); vstring_free(list->fold_buf); myfree((void *) list); } --- a/postfix/src/util/match_list.h +++ b/postfix/src/util/match_list.h @@ -38,6 +38,7 @@ typedef int (*MATCH_LIST_FN) (struct MATCH_LIST *, const char *, const char *); typedef struct MATCH_LIST { const char *pname; /* parameter name for error reports */ int flags; /* MATCH_FLAG_XXX */ + struct HTABLE *string_cache; /* O(1) index for inline string patterns */ ARGV *patterns; /* one pattern per element */ int match_count; /* number of match functions */ MATCH_LIST_FN *match_func; /* match functions */