]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0157: Vim9: concatenation can be improved v9.2.0157
authorJohn Marriott <basilisk@internode.on.net>
Fri, 13 Mar 2026 21:26:55 +0000 (21:26 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 13 Mar 2026 21:26:55 +0000 (21:26 +0000)
Problem:  Vim9: concatenation can be improved
Solution: Cache string segments lengths in exe_concat() and avoid
          strlen() calls (John Marriott).

closes: #19647

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/version.c
src/vim9execute.c

index 3676c461bc139e7111115f0bb7abdcf7a2fcb152..dd050e2b31483fff1e18f540e27b718ce9baa0f8 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    157,
 /**/
     156,
 /**/
index c58992f5dc91db546f22d820f15bf5f5f070f6a8..76d160cd921414708195576be28f27422aa73b0d 100644 (file)
@@ -133,28 +133,57 @@ ufunc_argcount(ufunc_T *ufunc)
 exe_concat(int count, ectx_T *ectx)
 {
     int                idx;
-    int                len = 0;
-    typval_T   *tv;
+    size_t     len = 0;
     garray_T   ga;
+    typedef struct
+    {
+       typval_T    *tv;
+       size_t      length;
+    } string_segment_T;
+    enum
+    {
+       STRING_SEGMENT_CACHE_SIZE = 10
+    };
+    string_segment_T   fixed_string_segment_tab[STRING_SEGMENT_CACHE_SIZE];
+    string_segment_T   *string_segment_tab = &fixed_string_segment_tab[0];     // an array of cached typevals and lengths
+    string_segment_T    *segment;
+
+    if (count > (int)ARRAY_LENGTH(fixed_string_segment_tab))
+    {
+       // make an array big enough to store the length of each string segment
+       string_segment_tab = ALLOC_MULT(string_segment_T, count);
+       if (string_segment_tab == NULL)
+           return FAIL;
+    }
 
     ga_init2(&ga, sizeof(char), 1);
     // Preallocate enough space for the whole string to avoid having to grow
     // and copy.
     for (idx = 0; idx < count; ++idx)
     {
-       tv = STACK_TV_BOT(idx - count);
-       if (tv->vval.v_string != NULL)
-           len += (int)STRLEN(tv->vval.v_string);
+       segment = &string_segment_tab[idx];
+       segment->tv = STACK_TV_BOT(idx - count);
+       if (segment->tv->vval.v_string != NULL)
+       {
+           segment->length = STRLEN(segment->tv->vval.v_string);
+           len += segment->length;
+       }
+       else
+           segment->length = 0;    // Ensure clean state for the second pass
     }
 
-    if (ga_grow(&ga, len + 1) == FAIL)
+    if (ga_grow(&ga, (int)len + 1) == FAIL)
+    {
+       if (string_segment_tab != fixed_string_segment_tab)
+           vim_free(string_segment_tab);
        return FAIL;
+    }
 
     for (idx = 0; idx < count; ++idx)
     {
-       tv = STACK_TV_BOT(idx - count);
-       ga_concat(&ga, tv->vval.v_string);
-       clear_tv(tv);
+       segment = &string_segment_tab[idx];
+       ga_concat_len(&ga, segment->tv->vval.v_string, segment->length);
+       clear_tv(segment->tv);
     }
 
     // add a terminating NUL
@@ -163,6 +192,9 @@ exe_concat(int count, ectx_T *ectx)
     ectx->ec_stack.ga_len -= count - 1;
     STACK_TV_BOT(-1)->vval.v_string = ga.ga_data;
 
+    if (string_segment_tab != fixed_string_segment_tab)
+       vim_free(string_segment_tab);
+
     return OK;
 }