From: Willy Tarreau Date: Mon, 19 Oct 2026 16:22:49 +0000 (+0200) Subject: MINOR: tools: add find_word_index_in_array() to find a keyword X-Git-Tag: v3.5-dev10~88 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=113005dd1b0a0b4ae203f5ccfed1630af0d3cfb4;p=haproxy.git MINOR: tools: add find_word_index_in_array() to find a keyword This will be used to find a word among a set in an array of arbitrary size and layout provided that the words are always accessible via a pointer at a fixed location in each item. The goal here is to simplify call places which want to match a word among a list, find the associated entry, and propose a list of possible matches. This will return the following values depending on the looked up word: 0 = not found 1 = found a different one which looks like a candidate 2 = found exact match When non-zero is returned, an index pointer will designate the associated entry in the array so that the caller can find the context of the sought word, or at least report the mostly similar one in the error message, like this: size_t idx; match = find_word_index_in_array(kw_array, sizeof(kw_array[0]), -1, offsetof(struct keyword, name), word, &idx); if (match != 2) { memprintf(err, "Invalid keyword '%s'.", word); if (match == 1) memprintf(err, "%s Maybe you meant '%s' ?", *err, kw_array[idx].name); goto fail; } --- diff --git a/include/haproxy/tools.h b/include/haproxy/tools.h index da95ba79e..40e655dbd 100644 --- a/include/haproxy/tools.h +++ b/include/haproxy/tools.h @@ -1020,6 +1020,8 @@ void update_word_fingerprint(uint8_t *fp, const char *word); void make_word_fingerprint_with_len(uint8_t *fp, struct ist word); void make_word_fingerprint(uint8_t *fp, const char *word); int word_fingerprint_distance(const uint8_t *fp1, const uint8_t *fp2); +int find_word_index_in_array(const void *array, size_t item_sz, size_t nbelem, + size_t ofs, const char *word, size_t *idx); /* debugging macro to emit messages using write() on fd #-1 so that strace sees * them. It relies on variadic macros with optional arguments so that any diff --git a/src/tools.c b/src/tools.c index 40c3a22a7..16936985b 100644 --- a/src/tools.c +++ b/src/tools.c @@ -7011,6 +7011,62 @@ struct ist env_suggest(struct ist word) return best_name; } +/* Look up word among array of words , made of elements of size + * . The pointer to the word to find in the array must be at offset + * in the element. If is >= 0, no word will be looked up past + * this number of elements. In any case, a NULL pointer to a word marks the + * end of the list. On return, if is not NULL, it will be set to the + * index of the element found, the closest one, or won't be touched if no + * element is found. The return value is: + * 0 : no element found + * 1 : approximate match at position + * 2 : exact match at position + */ +int find_word_index_in_array(const void *array, size_t item_sz, size_t nbelem, + size_t ofs, const char *word, size_t *idx) +{ + uint8_t word_sig[1024]; + uint8_t list_sig[1024]; + const char *best_ptr = NULL; + int dist, best_dist = INT_MAX; + const char **match; + size_t elem, best_elem = 0; + + make_word_fingerprint(word_sig, word); + + for (elem = 0; elem < nbelem; elem++) { + match = (const char **)((const char *)array + ofs + elem * item_sz); + if (!*match) + break; + + if (strcmp(word, *match) == 0) { + /* found exact match */ + if (idx) + *idx = elem; + return 2; + } + make_word_fingerprint(list_sig, *match); + dist = word_fingerprint_distance(word_sig, list_sig); + if (dist < best_dist) { + best_dist = dist; + best_ptr = *match; + best_elem = elem; + } + } + + if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr))) + best_ptr = NULL; + + if (best_ptr) { + /* found approximate match */ + if (idx) + *idx = best_elem; + return 1; + } + + /* not found */ + return 0; +} /* This function hashes a word, scramble is the anonymizing key, returns * the hashed word when the key (scramble) != 0, else returns the word.