patch 9.1.1645: fuzzy.c can be further improved

Commit: 
https://github.com/vim/vim/commit/59799f3afac310f3dca409e295b2ce2036867eaf
Author: glepnir <glephun...@gmail.com>
Date:   Sun Aug 17 21:40:45 2025 +0200

    patch 9.1.1645: fuzzy.c can be further improved
    
    Problem:  fuzzy.c can be further improved
    Solution: Fix memory leak and refactor it (glepnir).
    
    Optimize performance and memory allocation:
    
    - Fix memory leak in fuzzy_match_in_list.
    - using single memory allocation in match_positions
    - Improve has_match performance and add null pointer checks
    
    closes: #18012
    
    Signed-off-by: glepnir <glephun...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/fuzzy.c b/src/fuzzy.c
index 47407644e..15e3c4cdb 100644
--- a/src/fuzzy.c
+++ b/src/fuzzy.c
@@ -216,7 +216,7 @@ fuzzy_match_in_list(
     long       max_matches)
 {
     long           len;
-    fuzzyItem_T            *items;
+    fuzzyItem_T            *items = NULL;
     listitem_T     *li;
     long           match_count = 0;
     int_u          matches[FUZZY_MATCH_MAX_LEN];
@@ -235,14 +235,15 @@ fuzzy_match_in_list(
     FOR_ALL_LIST_ITEMS(l, li)
     {
        int             score;
-       char_u          *itemstr;
+       char_u          *itemstr = NULL;
+       char_u          *itemstr_copy = NULL;
        typval_T        rettv;
        int             itemstr_allocate = FALSE;
+       list_T          *match_positions = NULL;
 
        if (max_matches > 0 && match_count >= max_matches)
            break;
 
-       itemstr = NULL;
        rettv.v_type = VAR_UNKNOWN;
        if (li->li_tv.v_type == VAR_STRING)     // list of strings
            itemstr = li->li_tv.vval.v_string;
@@ -279,34 +280,45 @@ fuzzy_match_in_list(
                && fuzzy_match(itemstr, str, matchseq, &score, matches,
                                                FUZZY_MATCH_MAX_LEN))
        {
-           items[match_count].idx = match_count;
-           items[match_count].item = li;
-           items[match_count].score = score;
-           items[match_count].pat = str;
-           items[match_count].startpos = matches[0];
-           items[match_count].itemstr = itemstr_allocate
-                                           ? vim_strsave(itemstr) : itemstr;
-           items[match_count].itemstr_allocated = itemstr_allocate;
+           if (itemstr_allocate)
+           {
+               itemstr_copy = vim_strsave(itemstr);
+               if (itemstr_copy == NULL)
+               {
+                   clear_tv(&rettv);
+                   continue;
+               }
+           }
+           else
+               itemstr_copy = itemstr;
 
            // Copy the list of matching positions in itemstr to a list, if
            // "retmatchpos" is set.
            if (retmatchpos)
            {
-               int     j = 0;
-               char_u  *p;
+               match_positions = list_alloc();
+               if (match_positions == NULL)
+               {
+                   if (itemstr_allocate && itemstr_copy)
+                       vim_free(itemstr_copy);
+                   clear_tv(&rettv);
+                   continue;
+               }
 
-               items[match_count].lmatchpos = list_alloc();
-               if (items[match_count].lmatchpos == NULL)
-                   goto done;
+               // Fill position information
+               int     j = 0;
+               char_u  *p = str;
+               int     success = TRUE;
 
-               p = str;
-               while (*p != NUL && j < FUZZY_MATCH_MAX_LEN)
+               while (*p != NUL && j < FUZZY_MATCH_MAX_LEN && success)
                {
                    if (!VIM_ISWHITE(PTR2CHAR(p)) || matchseq)
                    {
-                       if (list_append_number(items[match_count].lmatchpos,
-                                   matches[j]) == FAIL)
-                           goto done;
+                       if (list_append_number(match_positions, matches[j]) == 
FAIL)
+                       {
+                           success = FALSE;
+                           break;
+                       }
                        j++;
                    }
                    if (has_mbyte)
@@ -314,7 +326,25 @@ fuzzy_match_in_list(
                    else
                        ++p;
                }
+
+               if (!success)
+               {
+                   list_free(match_positions);
+                   if (itemstr_allocate && itemstr_copy)
+                       vim_free(itemstr_copy);
+                   clear_tv(&rettv);
+                   continue;
+               }
            }
+           items[match_count].idx = match_count;
+           items[match_count].item = li;
+           items[match_count].score = score;
+           items[match_count].pat = str;
+           items[match_count].startpos = matches[0];
+           items[match_count].itemstr = itemstr_copy;
+           items[match_count].itemstr_allocated = itemstr_allocate;
+           items[match_count].lmatchpos = match_positions;
+
            ++match_count;
        }
        clear_tv(&rettv);
@@ -888,8 +918,6 @@ fuzzymatches_to_strmatches(
        int             count,
        int             funcsort)
 {
-    int                i;
-
     if (count <= 0)
        goto theend;
 
@@ -906,7 +934,7 @@ fuzzymatches_to_strmatches(
     else
        fuzzy_match_str_sort((void *)fuzmatch, (size_t)count);
 
-    for (i = 0; i < count; i++)
+    for (int i = 0; i < count; i++)
        (*matches)[i] = fuzmatch[i].str;
 
 theend:
@@ -933,33 +961,36 @@ theend:
     static int
 has_match(char_u *needle, char_u *haystack)
 {
-    while (*needle != NUL)
+    if (!needle || !haystack || !*needle)
+       return FAIL;
+
+    char_u *n_ptr = needle;
+    char_u *h_ptr = haystack;
+
+    while (*n_ptr)
     {
-       int n_char = mb_ptr2char(needle);
-       char_u *p = haystack;
-       int h_char;
-       int matched = FALSE;
+       int n_char = mb_ptr2char(n_ptr);
+       int found = FALSE;
 
-       while (*p != NUL)
+       while (*h_ptr)
        {
-           h_char = mb_ptr2char(p);
-
-           if (n_char == h_char
-                   || MB_TOUPPER(n_char) == h_char)
+           int h_char = mb_ptr2char(h_ptr);
+           if (h_char == n_char || h_char == MB_TOUPPER(n_char))
            {
-               matched = TRUE;
+               found = TRUE;
+               h_ptr += mb_ptr2len(h_ptr);
                break;
            }
-           p += mb_ptr2len(p);
+           h_ptr += mb_ptr2len(h_ptr);
        }
 
-       if (!matched)
-           return 0;
+       if (!found)
+           return FAIL;
 
-       needle += mb_ptr2len(needle);
-       haystack = p + mb_ptr2len(p);
+       n_ptr += mb_ptr2len(n_ptr);
     }
-    return 1;
+
+    return OK;
 }
 
 typedef struct match_struct
@@ -993,8 +1024,7 @@ compute_bonus_codepoint(int last_c, int c)
 }
 
     static void
-setup_match_struct(match_struct *match, char_u *needle,
-       char_u *haystack)
+setup_match_struct(match_struct *match, char_u *needle, char_u *haystack)
 {
     int i = 0;
     char_u *p = needle;
@@ -1073,7 +1103,7 @@ match_row(const match_struct *match, int row, score_t 
*curr_D,
     static score_t
 match_positions(char_u *needle, char_u *haystack, int_u *positions)
 {
-    if (!*needle)
+    if (!needle || !haystack || !*needle)
        return SCORE_MIN;
 
     match_struct match;
@@ -1095,21 +1125,28 @@ match_positions(char_u *needle, char_u *haystack, int_u 
*positions)
        // matches needle. If the lengths of the strings are equal the
        // strings themselves must also be equal (ignoring case).
        if (positions)
+       {
            for (int i = 0; i < n; i++)
                positions[i] = i;
+       }
        return SCORE_MAX;
     }
 
-    // D[][] Stores the best score for this position ending with a match.
-    // M[][] Stores the best possible score at this position.
-    score_t (*D)[MATCH_MAX_LEN], (*M)[MATCH_MAX_LEN];
-    M = alloc(sizeof(score_t) * MATCH_MAX_LEN * n);
-    if (!M)
+    // ensure n * MATCH_MAX_LEN * 2 won't overflow
+    if ((size_t)n > (SIZE_MAX / sizeof(score_t)) / MATCH_MAX_LEN / 2)
        return SCORE_MIN;
-    D = alloc(sizeof(score_t) * MATCH_MAX_LEN * n);
-    if (!D)
+
+    // Allocate for both D and M matrices in one contiguous block
+    score_t *block = (score_t*)alloc(sizeof(score_t) * MATCH_MAX_LEN * n * 2);
+    if (!block)
        return SCORE_MIN;
 
+    // D[][] Stores the best score for this position ending with a match.
+    // M[][] Stores the best possible score at this position.
+    score_t (*D)[MATCH_MAX_LEN] = (score_t(*)[MATCH_MAX_LEN])block;
+    score_t (*M)[MATCH_MAX_LEN] = (score_t(*)[MATCH_MAX_LEN])(block
+                                                       + MATCH_MAX_LEN * n);
+
     match_row(&match, 0, D[0], M[0], D[0], M[0]);
     for (int i = 1; i < n; i++)
        match_row(&match, i, D[i], M[i], D[i - 1], M[i - 1]);
@@ -1144,9 +1181,6 @@ match_positions(char_u *needle, char_u *haystack, int_u 
*positions)
     }
 
     score_t result = M[n - 1][m - 1];
-
-    vim_free(M);
-    vim_free(D);
-
+    vim_free(block);
     return result;
 }
diff --git a/src/version.c b/src/version.c
index 71f390b9c..fe5d37cda 100644
--- a/src/version.c
+++ b/src/version.c
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1645,
 /**/
     1644,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1unjJP-003Q2R-Ny%40256bit.org.

Raspunde prin e-mail lui