patch 9.2.0053: Vims list concatenation is inefficient

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>
This commit is contained in:
Yasuhiro Matsumoto
2026-02-25 19:31:37 +00:00
committed by Christian Brabandt
parent b834c3f23b
commit 048079f6da
2 changed files with 64 additions and 17 deletions
+62 -17
View 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;
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
53,
/**/
52,
/**/