]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0053: Vims list concatenation is inefficient v9.2.0053
authorYasuhiro Matsumoto <mattn.jp@gmail.com>
Wed, 25 Feb 2026 19:31:37 +0000 (19:31 +0000)
committerChristian Brabandt <cb@256bit.org>
Wed, 25 Feb 2026 19:31:37 +0000 (19:31 +0000)
Problem:  Vims list concatenation is inefficient
Solution: Use a single allocation of len1 + len2 using
          list_alloc_with_items() (Yasuhiro Matsumoto).

Replace list_copy() + list_extend() (N+1 individual mallocs) with a
single list_alloc_with_items(len1+len2) call.  This reduces the number
of memory allocations from O(N) to O(1) for the list '+' operator.

closes: #19495

Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/list.c
src/version.c

index 253b1be9ffbd3ef0c476691dbd48b932a14bab0b..310d8516b89a4a013fb4ea13e1cb2c015b0f2fd8 100644 (file)
@@ -1232,22 +1232,62 @@ list_extend(list_T *l1, list_T *l2, listitem_T *bef)
 list_concat(list_T *l1, list_T *l2, typval_T *tv)
 {
     list_T     *l;
+    int                len1 = l1 == NULL ? 0 : l1->lv_len;
+    int                len2 = l2 == NULL ? 0 : l2->lv_len;
+    long       totallen = (long)len1 + (long)len2;
+    int                i;
+    listitem_T *item;
 
-    // make a copy of the first list.
-    if (l1 == NULL)
+    if (totallen == 0)
+    {
        l = list_alloc();
-    else
-       l = list_copy(l1, FALSE, TRUE, 0);
+       if (l == NULL)
+           return FAIL;
+       ++l->lv_refcount;
+       tv->v_type = VAR_LIST;
+       tv->v_lock = 0;
+       tv->vval.v_list = l;
+       return OK;
+    }
+    if (totallen > INT_MAX)
+       return FAIL;
+
+    // allocate all items at once for efficiency
+    l = list_alloc_with_items((int)totallen);
     if (l == NULL)
        return FAIL;
+
+    i = 0;
+    if (len1 > 0)
+    {
+       CHECK_LIST_MATERIALIZE(l1);
+       for (item = l1->lv_first; item != NULL && !got_int;
+               item = item->li_next)
+       {
+           typval_T new_tv;
+
+           copy_tv(&item->li_tv, &new_tv);
+           list_set_item(l, i++, &new_tv);
+       }
+    }
+    if (len2 > 0)
+    {
+       CHECK_LIST_MATERIALIZE(l2);
+       for (item = l2->lv_first; item != NULL && !got_int;
+               item = item->li_next)
+       {
+           typval_T new_tv;
+
+           copy_tv(&item->li_tv, &new_tv);
+           list_set_item(l, i++, &new_tv);
+       }
+    }
+
+    ++l->lv_refcount;
     tv->v_type = VAR_LIST;
     tv->v_lock = 0;
     tv->vval.v_list = l;
-    if (l1 == NULL)
-       ++l->lv_refcount;
-
-    // append all items from the second list
-    return list_extend(l, l2, NULL);
+    return OK;
 }
 
     list_T *
@@ -1482,7 +1522,7 @@ list_join_inner(
 {
     int                i;
     join_T     *p;
-    int                sumlen = 0;
+    long       sumlen = 0;
     int                first = TRUE;
     char_u     *tofree;
     char_u     numbuf[NUMBUFLEN];
@@ -1500,7 +1540,7 @@ list_join_inner(
            return FAIL;
 
        s.length = STRLEN(s.string);
-       sumlen += (int)s.length;
+       sumlen += (long)s.length;
 
        (void)ga_grow(join_gap, 1);
        p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++);
@@ -1526,8 +1566,8 @@ list_join_inner(
     // multiple copy operations.  Add 2 for a tailing ']' and NUL.
     seplen = STRLEN(sep);
     if (join_gap->ga_len >= 2)
-       sumlen += (int)seplen * (join_gap->ga_len - 1);
-    if (ga_grow(gap, sumlen + 2) == FAIL)
+       sumlen += (long)seplen * (join_gap->ga_len - 1);
+    if (sumlen > INT_MAX - 2 || ga_grow(gap, (int)sumlen + 2) == FAIL)
        return FAIL;
 
     for (i = 0; i < join_gap->ga_len && !got_int; ++i)
@@ -1847,11 +1887,16 @@ f_list2str(typval_T *argvars, typval_T *rettv)
        }
        ga_append(&ga, NUL);
     }
-    else if (ga_grow(&ga, list_len(l) + 1) == OK)
+    else
     {
-       FOR_ALL_LIST_ITEMS(l, li)
-           ga_append(&ga, tv_get_number(&li->li_tv));
-       ga_append(&ga, NUL);
+       long len = (long)list_len(l) + 1;
+
+       if (len <= INT_MAX && ga_grow(&ga, (int)len) == OK)
+       {
+           FOR_ALL_LIST_ITEMS(l, li)
+               ga_append(&ga, tv_get_number(&li->li_tv));
+           ga_append(&ga, NUL);
+       }
     }
 
     rettv->v_type = VAR_STRING;
index 36d32a15acaeabb21bf75639d44de11d3ab523a5..fe8ce15a1268fbd5bad3e57fc0b0fae8c5dd859a 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    53,
 /**/
     52,
 /**/