commit 18c5f06c9f6068b9a26a1ed4ab0f66b91db85cd9
parent 6435c61bd61ce910da6659394d918f7f36e932ba
Author: zeertzjq <zeertzjq@outlook.com>
Date: Fri, 27 Feb 2026 07:32:08 +0800
vim-patch:partial:9.2.0068: Inefficient use of list_append_string() (#38083)
Problem: Inefficient use of list_append_string()
Solution: Pass string length to list_append_string() where it is known
(John Marriott).
closes: vim/vim#19491
https://github.com/vim/vim/commit/455d62e38a75572bccc43e42d20b5db3c4b22ec3
N/A patches:
vim-patch:9.2.0063: memory leak in type_name_list_or_dict()
vim-patch:9.2.0065: memory leak in invoke_sync_listeners()
vim-patch:9.2.0066: memory leak in build_drop_cmd()
vim-patch:9.2.0067: memory leak in dict_extend_func()
Co-authored-by: John Marriott <basilisk@internode.on.net>
Diffstat:
5 files changed, 45 insertions(+), 31 deletions(-)
diff --git a/src/nvim/clipboard.c b/src/nvim/clipboard.c
@@ -209,7 +209,7 @@ void set_clipboard(int name, yankreg_T *reg)
list_T *const lines = tv_list_alloc((ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise));
for (size_t i = 0; i < reg->y_size; i++) {
- tv_list_append_string(lines, reg->y_array[i].data, -1);
+ tv_list_append_string(lines, reg->y_array[i].data, (int)reg->y_array[i].size);
}
char regtype;
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
@@ -611,12 +611,16 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, bool retl
}
tv_list_alloc_ret(rettv, end - start + 1);
while (start <= end) {
- tv_list_append_string(rettv->vval.v_list, ml_get_buf(buf, start++), -1);
+ tv_list_append_string(rettv->vval.v_list,
+ ml_get_buf(buf, start), (int)ml_get_buf_len(buf, start));
+ start++;
}
} else {
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count)
- ? xstrdup(ml_get_buf(buf, start)) : NULL);
+ rettv->vval.v_string =
+ start >= 1 && start <= buf->b_ml.ml_line_count
+ ? xstrnsave(ml_get_buf(buf, start), (size_t)ml_get_buf_len(buf, start))
+ : NULL;
}
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
@@ -2161,17 +2161,19 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
/// Convert from block_def to string
-static char *block_def2str(struct block_def *bd)
+static String block_def2str(struct block_def *bd)
{
size_t size = (size_t)bd->startspaces + (size_t)bd->endspaces + (size_t)bd->textlen;
- char *ret = xmalloc(size + 1);
- char *p = ret;
- memset(p, ' ', (size_t)bd->startspaces);
- p += bd->startspaces;
- memmove(p, bd->textstart, (size_t)bd->textlen);
- p += bd->textlen;
- memset(p, ' ', (size_t)bd->endspaces);
- *(p + bd->endspaces) = NUL;
+ String ret = { .data = xmalloc(size + 1) };
+
+ memset(ret.data, ' ', (size_t)bd->startspaces);
+ ret.size += (size_t)bd->startspaces;
+ memmove(ret.data + ret.size, bd->textstart, (size_t)bd->textlen);
+ ret.size += (size_t)bd->textlen;
+ memset(ret.data + ret.size, ' ', (size_t)bd->endspaces);
+ ret.size += (size_t)bd->endspaces;
+ ret.data[ret.size] = NUL;
+
return ret;
}
@@ -2326,24 +2328,22 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) {
- char *akt = NULL;
+ String akt = STRING_INIT;
- if (region_type == kMTLineWise) {
- akt = xstrdup(ml_get(lnum));
- } else if (region_type == kMTBlockWise) {
+ if (region_type == kMTBlockWise) {
struct block_def bd;
block_prep(&oa, &bd, lnum, false);
akt = block_def2str(&bd);
- } else if (p1.lnum < lnum && lnum < p2.lnum) {
- akt = xstrdup(ml_get(lnum));
+ } else if (region_type == kMTLineWise || (p1.lnum < lnum && lnum < p2.lnum)) {
+ akt = cbuf_to_string(ml_get(lnum), (size_t)ml_get_len(lnum));
} else {
struct block_def bd;
charwise_block_prep(p1, p2, &bd, lnum, inclusive);
akt = block_def2str(&bd);
}
- assert(akt != NULL);
- tv_list_append_allocated_string(rettv->vval.v_list, akt);
+ assert(akt.data != NULL);
+ tv_list_append_allocated_string(rettv->vval.v_list, akt.data);
}
// getregionpos() may change curbuf and virtual_op
@@ -6949,12 +6949,18 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
assert(len <= INT_MAX);
tv_list_alloc_ret(rettv, 2);
tv_list_append_string(rettv->vval.v_list, word, (ssize_t)len);
- tv_list_append_string(rettv->vval.v_list,
- (attr == HLF_SPB
- ? "bad" : (attr == HLF_SPR
- ? "rare" : (attr == HLF_SPL
- ? "local" : (attr == HLF_SPC
- ? "caps" : NULL)))), -1);
+ switch (attr) {
+ case HLF_SPB:
+ tv_list_append_string(rettv->vval.v_list, S_LEN("bad")); break;
+ case HLF_SPR:
+ tv_list_append_string(rettv->vval.v_list, S_LEN("rare")); break;
+ case HLF_SPL:
+ tv_list_append_string(rettv->vval.v_list, S_LEN("local")); break;
+ case HLF_SPC:
+ tv_list_append_string(rettv->vval.v_list, S_LEN("caps")); break;
+ default:
+ tv_list_append_string(rettv->vval.v_list, NULL, -1); break;
+ }
}
/// "spellsuggest()" function
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
@@ -238,11 +238,15 @@ static void get_framelayout(const frame_T *fr, list_T *l, bool outer)
if (fr->fr_layout == FR_LEAF) {
if (fr->fr_win != NULL) {
- tv_list_append_string(fr_list, "leaf", -1);
+ tv_list_append_string(fr_list, S_LEN("leaf"));
tv_list_append_number(fr_list, fr->fr_win->handle);
}
} else {
- tv_list_append_string(fr_list, fr->fr_layout == FR_ROW ? "row" : "col", -1);
+ if (fr->fr_layout == FR_ROW) {
+ tv_list_append_string(fr_list, S_LEN("row"));
+ } else {
+ tv_list_append_string(fr_list, S_LEN("col"));
+ }
list_T *const win_list = tv_list_alloc(kListLenUnknown);
tv_list_append_list(fr_list, win_list);
diff --git a/src/nvim/register.c b/src/nvim/register.c
@@ -1215,7 +1215,7 @@ void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
// The yanked text contents.
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
- tv_list_append_string(list, reg->y_array[i].data, -1);
+ tv_list_append_string(list, reg->y_array[i].data, (int)reg->y_array[i].size);
}
tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
@@ -2344,7 +2344,7 @@ void *get_reg_contents(int regname, int flags)
if (flags & kGRegList) {
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
- tv_list_append_string(list, reg->y_array[i].data, -1);
+ tv_list_append_string(list, reg->y_array[i].data, (int)reg->y_array[i].size);
}
return list;