From b3a3028fd9bb32ba398680673fee0770564dcd05 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 21 Feb 2026 06:32:20 +0800 Subject: [PATCH] vim-patch:9.2.0031: Inefficient use of ga_concat() Problem: Inefficient use of ga_concat() Solution: Use ga_concat_len() when the length is already known to avoid use of strlen() (John Marriott). closes: vim/vim#19422 https://github.com/vim/vim/commit/ed202035b1904c84d92adb76d7e1a95b7540e048 Co-authored-by: John Marriott Co-authored-by: Hirohito Higashi --- src/nvim/errors.h | 2 +- src/nvim/eval/encode.c | 72 ++++++++++++++------------- src/nvim/eval/fs.c | 2 +- src/nvim/eval/typval.c | 29 ++++++----- src/nvim/ex_cmds.c | 2 +- src/nvim/garray.h | 3 ++ src/nvim/os/shell.c | 4 +- src/nvim/testing.c | 108 +++++++++++++++++++++-------------------- 8 files changed, 118 insertions(+), 104 deletions(-) diff --git a/src/nvim/errors.h b/src/nvim/errors.h index be2b8ca800..5fd5bacd0e 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -74,7 +74,7 @@ EXTERN const char e_noroom[] INIT(= N_("E36: Not enough room")); EXTERN const char e_notmp[] INIT(= N_("E483: Can't get temp file name")); EXTERN const char e_notopen[] INIT(= N_("E484: Can't open file %s")); EXTERN const char e_notopen_2[] INIT(= N_("E484: Can't open file %s: %s")); -EXTERN const char e_notread[] INIT(= N_("E485: Can't read file %s")); +EXTERN const char e_cant_read_file_str[] INIT(= N_("E485: Can't read file %s")); EXTERN const char e_null[] INIT(= N_("E38: Null argument")); EXTERN const char e_number_exp[] INIT(= N_("E39: Number expected")); EXTERN const char e_openerrf[] INIT(= N_("E40: Can't open errorfile %s")); diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 76edb22d01..2ebbb9cdc5 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -124,7 +124,7 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, const char *const partial_self_msg = _("partial self dictionary"); for (size_t i = 0; i < kv_size(*mpstack); i++) { if (i != 0) { - ga_concat_len(&msg_ga, S_LEN(", ")); + GA_CONCAT_LITERAL(&msg_ga, ", "); } MPConvStackVal v = kv_A(*mpstack, i); switch (v.type) { @@ -296,7 +296,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s do { \ const char *const buf_ = (buf); \ if (buf_ == NULL) { \ - ga_concat_len(gap, S_LEN("''")); \ + GA_CONCAT_LITERAL(gap, "''"); \ } else { \ const size_t len_ = (len); \ ga_grow(gap, (int)(2 + len_ + memcnt(buf_, '\'', len_))); \ @@ -321,13 +321,13 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s const blob_T *const blob_ = (blob); \ const int len_ = (len); \ if (len_ == 0) { \ - ga_concat_len(gap, S_LEN("0z")); \ + GA_CONCAT_LITERAL(gap, "0z"); \ } else { \ /* Allocate space for "0z", the two hex chars per byte, and a */ \ /* "." separator after every eight hex chars. */ \ /* Example: "0z00112233.44556677.8899" */ \ ga_grow(gap, 2 + 2 * len_ + (len_ - 1) / 4); \ - ga_concat_len(gap, S_LEN("0z")); \ + GA_CONCAT_LITERAL(gap, "0z"); \ char numbuf[NUMBUFLEN]; \ for (int i_ = 0; i_ < len_; i_++) { \ if (i_ > 0 && (i_ & 3) == 0) { \ @@ -343,8 +343,9 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ do { \ char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t)(num)); \ - ga_concat(gap, numbuf); \ + size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \ + "%" PRId64, (int64_t)(num)); \ + ga_concat_len(gap, numbuf, numbuflen); \ } while (0) #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ @@ -352,20 +353,21 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s const float_T flt_ = (flt); \ switch (xfpclassify(flt_)) { \ case FP_NAN: { \ - ga_concat_len(gap, S_LEN("str2float('nan')")); \ + GA_CONCAT_LITERAL(gap, "str2float('nan')"); \ break; \ } \ case FP_INFINITE: { \ if (flt_ < 0) { \ ga_append(gap, '-'); \ } \ - ga_concat_len(gap, S_LEN("str2float('inf')")); \ + GA_CONCAT_LITERAL(gap, "str2float('inf')"); \ break; \ } \ default: { \ char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ - ga_concat(gap, numbuf); \ + size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \ + "%g", flt_); \ + ga_concat_len(gap, numbuf, numbuflen); \ } \ } \ } while (0) @@ -375,10 +377,10 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s const char *const fun_ = (fun); \ if (fun_ == NULL) { \ internal_error("string(): NULL function name"); \ - ga_concat_len(gap, S_LEN("function(NULL")); \ + GA_CONCAT_LITERAL(gap, "function(NULL"); \ } else { \ const char *const prefix_ = (prefix); \ - ga_concat_len(gap, S_LEN("function(")); \ + GA_CONCAT_LITERAL(gap, "function("); \ const int name_off = gap->ga_len; \ ga_concat(gap, prefix_); \ TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \ @@ -391,14 +393,14 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \ do { \ if ((len) != 0) { \ - ga_concat_len(gap, S_LEN(", ")); \ + GA_CONCAT_LITERAL(gap, ", "); \ } \ } while (0) #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \ do { \ if ((ptrdiff_t)(len) != -1) { \ - ga_concat_len(gap, S_LEN(", ")); \ + GA_CONCAT_LITERAL(gap, ", "); \ } \ } while (0) @@ -406,7 +408,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s ga_append(gap, ')') #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - ga_concat_len(gap, S_LEN("[]")) + GA_CONCAT_LITERAL(gap, "[]") #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ ga_append(gap, '[') @@ -414,19 +416,19 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s #define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - ga_concat_len(gap, S_LEN("{}")) + GA_CONCAT_LITERAL(gap, "{}") #define TYPVAL_ENCODE_CHECK_BEFORE #define TYPVAL_ENCODE_CONV_NIL(tv) \ - ga_concat_len(gap, S_LEN("v:null")) + GA_CONCAT_LITERAL(gap, "v:null") #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ do { \ if (num) { \ - ga_concat_len(gap, S_LEN("v:true")); \ + GA_CONCAT_LITERAL(gap, "v:true"); \ } else { \ - ga_concat_len(gap, S_LEN("v:false")); \ + GA_CONCAT_LITERAL(gap, "v:false"); \ } \ } while (0) @@ -441,10 +443,10 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s ga_append(gap, '}') #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \ - ga_concat_len(gap, S_LEN(": ")) + GA_CONCAT_LITERAL(gap, ": ") #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \ - ga_concat_len(gap, S_LEN(", ")) + GA_CONCAT_LITERAL(gap, ", ") #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) @@ -552,15 +554,15 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s #undef TYPVAL_ENCODE_CONV_NIL #define TYPVAL_ENCODE_CONV_NIL(tv) \ - ga_concat_len(gap, S_LEN("null")) + GA_CONCAT_LITERAL(gap, "null") #undef TYPVAL_ENCODE_CONV_BOOL #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ do { \ if (num) { \ - ga_concat_len(gap, S_LEN("true")); \ + GA_CONCAT_LITERAL(gap, "true"); \ } else { \ - ga_concat_len(gap, S_LEN("false")); \ + GA_CONCAT_LITERAL(gap, "false"); \ } \ } while (0) @@ -568,8 +570,9 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \ do { \ char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \ - ga_concat(gap, numbuf); \ + size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \ + "%" PRIu64, (num)); \ + ga_concat_len(gap, numbuf, numbuflen); \ } while (0) #undef TYPVAL_ENCODE_CONV_FLOAT @@ -587,8 +590,9 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s } \ default: { \ char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ - ga_concat(gap, numbuf); \ + size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \ + "%g", flt_); \ + ga_concat_len(gap, numbuf, numbuflen); \ break; \ } \ } \ @@ -620,7 +624,7 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const { const char *utf_buf = buf; if (utf_buf == NULL) { - ga_concat_len(gap, S_LEN("\"\"")); + GA_CONCAT_LITERAL(gap, "\"\""); } else { size_t utf_len = len; char *tofree = NULL; @@ -749,17 +753,17 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const const blob_T *const blob_ = (blob); \ const int len_ = (len); \ if (len_ == 0) { \ - ga_concat_len(gap, S_LEN("[]")); \ + GA_CONCAT_LITERAL(gap, "[]"); \ } else { \ ga_append(gap, '['); \ char numbuf[NUMBUFLEN]; \ for (int i_ = 0; i_ < len_; i_++) { \ if (i_ > 0) { \ - ga_concat_len(gap, S_LEN(", ")); \ + GA_CONCAT_LITERAL(gap, ", "); \ } \ - vim_snprintf((char *)numbuf, ARRAY_SIZE(numbuf), "%d", \ - (int)tv_blob_get(blob_, i_)); \ - ga_concat(gap, numbuf); \ + size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \ + "%d", (int)tv_blob_get(blob_, i_)); \ + ga_concat_len(gap, numbuf, numbuflen); \ } \ ga_append(gap, ']'); \ } \ diff --git a/src/nvim/eval/fs.c b/src/nvim/eval/fs.c index 65e27b0620..b4e6671c6e 100644 --- a/src/nvim/eval/fs.c +++ b/src/nvim/eval/fs.c @@ -1307,7 +1307,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl if (blob) { if (read_blob(fd, rettv, offset, size) == FAIL) { - semsg(_(e_notread), fname); + semsg(_(e_cant_read_file_str), fname); } fclose(fd); return; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 84d94f2643..732d972bda 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -971,7 +971,7 @@ int tv_list_slice_or_index(list_T *list, bool range, varnumber_T n1_arg, varnumb } typedef struct { - char *s; + String s; char *tofree; } Join; @@ -995,25 +995,26 @@ static int list_join_inner(garray_T *const gap, list_T *const l, const char *con if (got_int) { break; } - char *s; - size_t len; - s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len); - if (s == NULL) { + String s; + s.data = encode_tv2echo(TV_LIST_ITEM_TV(item), &s.size); + if (s.data == NULL) { return FAIL; } - sumlen += len; + sumlen += s.size; Join *const p = GA_APPEND_VIA_PTR(Join, join_gap); - p->tofree = p->s = s; + p->s = s; + p->tofree = s.data; line_breakcheck(); }); // Allocate result buffer with its total size, avoid re-allocation and // multiple copy operations. Add 2 for a tailing ']' and NUL. + size_t seplen = strlen(sep); if (join_gap->ga_len >= 2) { - sumlen += strlen(sep) * (size_t)(join_gap->ga_len - 1); + sumlen += seplen * (size_t)(join_gap->ga_len - 1); } ga_grow(gap, (int)sumlen + 2); @@ -1021,12 +1022,12 @@ static int list_join_inner(garray_T *const gap, list_T *const l, const char *con if (first) { first = false; } else { - ga_concat(gap, sep); + ga_concat_len(gap, sep, seplen); } const Join *const p = ((const Join *)join_gap->ga_data) + i; - if (p->s != NULL) { - ga_concat(gap, p->s); + if (p->s.data != NULL) { + ga_concat_len(gap, p->s.data, p->s.size); } line_breakcheck(); } @@ -1106,8 +1107,10 @@ void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char buf[MB_MAXBYTES + 1]; TV_LIST_ITER_CONST(l, li, { - buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), buf)] = NUL; - ga_concat(&ga, buf); + const varnumber_T n = tv_get_number(TV_LIST_ITEM_TV(li)); + const size_t buflen = (size_t)utf_char2bytes((int)n, buf); + buf[buflen] = NUL; + ga_concat_len(&ga, buf, buflen); }); ga_append(&ga, NUL); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 4315da03ee..6d9daf7ee4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1373,7 +1373,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b READ_FILTER, false) != OK) { if (!aborting()) { msg_putchar('\n'); - semsg(_(e_notread), otmp); + semsg(_(e_cant_read_file_str), otmp); } goto error; } diff --git a/src/nvim/garray.h b/src/nvim/garray.h index 41e77f315f..8f364e2b2c 100644 --- a/src/nvim/garray.h +++ b/src/nvim/garray.h @@ -4,6 +4,7 @@ #include #include "nvim/garray_defs.h" // IWYU pragma: keep +#include "nvim/macros_defs.h" // IWYU pragma: keep #include "nvim/memory.h" #define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0) @@ -45,3 +46,5 @@ /// /// @param gap the garray to be freed #define GA_DEEP_CLEAR_PTR(gap) GA_DEEP_CLEAR(gap, void *, FREE_PTR_PTR) + +#define GA_CONCAT_LITERAL(gap, s) ga_concat_len((gap), S_LEN(s)) diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index acda634814..04e072908b 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -415,7 +415,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in os_remove(tempname); if (readlen != len) { // unexpected read error - semsg(_(e_notread), tempname); + semsg(_(e_cant_read_file_str), tempname); xfree(tempname); xfree(buffer); return FAIL; @@ -809,7 +809,7 @@ char *get_cmd_output(char *cmd, char *infile, int flags, size_t *ret_len) fclose(fd); os_remove(tempname); if (i != len) { - semsg(_(e_notread), tempname); + semsg(_(e_cant_read_file_str), tempname); XFREE_CLEAR(buffer); } else if (ret_len == NULL) { // Change NUL into SOH, otherwise the string is truncated. diff --git a/src/nvim/testing.c b/src/nvim/testing.c index f57a7b6b41..6aabd81c81 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -57,7 +57,6 @@ static const char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[] /// Prepare "gap" for an assert error and add the sourcing position. static void prepare_assert_error(garray_T *gap) { - char buf[NUMBUFLEN]; char *sname = estack_sfile(ESTACK_NONE); ga_init(gap, 1, 100); @@ -68,11 +67,13 @@ static void prepare_assert_error(garray_T *gap) } } if (SOURCING_LNUM > 0) { - vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)SOURCING_LNUM); - ga_concat(gap, buf); + char buf[NUMBUFLEN]; + size_t buflen = vim_snprintf_safelen(buf, ARRAY_SIZE(buf), + "line %" PRId64, (int64_t)SOURCING_LNUM); + ga_concat_len(gap, buf, buflen); } if (sname != NULL || SOURCING_LNUM > 0) { - ga_concat(gap, ": "); + GA_CONCAT_LITERAL(gap, ": "); } xfree(sname); } @@ -87,29 +88,29 @@ static void ga_concat_esc(garray_T *gap, const char *p, int clen) if (clen > 1) { memmove(buf, p, (size_t)clen); buf[clen] = NUL; - ga_concat(gap, buf); + ga_concat_len(gap, buf, (size_t)clen); return; } switch (*p) { case BS: - ga_concat(gap, "\\b"); break; + GA_CONCAT_LITERAL(gap, "\\b"); break; case ESC: - ga_concat(gap, "\\e"); break; + GA_CONCAT_LITERAL(gap, "\\e"); break; case FF: - ga_concat(gap, "\\f"); break; + GA_CONCAT_LITERAL(gap, "\\f"); break; case NL: - ga_concat(gap, "\\n"); break; + GA_CONCAT_LITERAL(gap, "\\n"); break; case TAB: - ga_concat(gap, "\\t"); break; + GA_CONCAT_LITERAL(gap, "\\t"); break; case CAR: - ga_concat(gap, "\\r"); break; + GA_CONCAT_LITERAL(gap, "\\r"); break; case '\\': - ga_concat(gap, "\\\\"); break; + GA_CONCAT_LITERAL(gap, "\\\\"); break; default: if ((uint8_t)(*p) < ' ' || *p == 0x7f) { - vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p); - ga_concat(gap, buf); + size_t buflen = vim_snprintf_safelen(buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat_len(gap, buf, buflen); } else { ga_append(gap, (uint8_t)(*p)); } @@ -125,7 +126,7 @@ static void ga_concat_shorten_esc(garray_T *gap, const char *str) char buf[NUMBUFLEN]; if (str == NULL) { - ga_concat(gap, "NULL"); + GA_CONCAT_LITERAL(gap, "NULL"); return; } @@ -139,12 +140,12 @@ static void ga_concat_shorten_esc(garray_T *gap, const char *str) s += clen; } if (same_len > 20) { - ga_concat(gap, "\\["); + GA_CONCAT_LITERAL(gap, "\\["); ga_concat_esc(gap, p, clen); - ga_concat(gap, " occurs "); - vim_snprintf(buf, NUMBUFLEN, "%d", same_len); - ga_concat(gap, buf); - ga_concat(gap, " times]"); + GA_CONCAT_LITERAL(gap, " occurs "); + size_t buflen = vim_snprintf_safelen(buf, NUMBUFLEN, "%d", same_len); + ga_concat_len(gap, buf, buflen); + GA_CONCAT_LITERAL(gap, " times]"); p = s; } else { ga_concat_esc(gap, p, clen); @@ -169,15 +170,15 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, const char *e char *tofree = encode_tv2echo(opt_msg_tv, NULL); ga_concat(gap, tofree); xfree(tofree); - ga_concat(gap, ": "); + GA_CONCAT_LITERAL(gap, ": "); } if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { - ga_concat(gap, "Pattern "); + GA_CONCAT_LITERAL(gap, "Pattern "); } else if (atype == ASSERT_NOTEQUAL) { - ga_concat(gap, "Expected not equal to "); + GA_CONCAT_LITERAL(gap, "Expected not equal to "); } else { - ga_concat(gap, "Expected "); + GA_CONCAT_LITERAL(gap, "Expected "); } if (exp_str == NULL) { @@ -232,21 +233,21 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, const char *e xfree(tofree); } else { if (atype == ASSERT_FAILS) { - ga_concat(gap, "'"); + GA_CONCAT_LITERAL(gap, "'"); } ga_concat_shorten_esc(gap, exp_str); if (atype == ASSERT_FAILS) { - ga_concat(gap, "'"); + GA_CONCAT_LITERAL(gap, "'"); } } if (atype != ASSERT_NOTEQUAL) { if (atype == ASSERT_MATCH) { - ga_concat(gap, " does not match "); + GA_CONCAT_LITERAL(gap, " does not match "); } else if (atype == ASSERT_NOTMATCH) { - ga_concat(gap, " does match "); + GA_CONCAT_LITERAL(gap, " does match "); } else { - ga_concat(gap, " but got "); + GA_CONCAT_LITERAL(gap, " but got "); } char *tofree = encode_tv2string(got_tv, NULL); ga_concat_shorten_esc(gap, tofree); @@ -254,9 +255,10 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, const char *e if (omitted != 0) { char buf[100]; - vim_snprintf(buf, sizeof(buf), " - %d equal item%s omitted", omitted, - omitted == 1 ? "" : "s"); - ga_concat(gap, buf); + size_t buflen = vim_snprintf_safelen(buf, sizeof(buf), + " - %d equal item%s omitted", + omitted, omitted == 1 ? "" : "s"); + ga_concat_len(gap, buf, buflen); } } @@ -354,9 +356,9 @@ static int assert_beeps(typval_T *argvars, bool no_beep) garray_T ga; prepare_assert_error(&ga); if (no_beep) { - ga_concat(&ga, "command did beep: "); + GA_CONCAT_LITERAL(&ga, "command did beep: "); } else { - ga_concat(&ga, "command did not beep: "); + GA_CONCAT_LITERAL(&ga, "command did not beep: "); } ga_concat(&ga, cmd); assert_error(&ga); @@ -400,17 +402,18 @@ static int assert_equalfile(typval_T *argvars) } IObuff[0] = NUL; + size_t IObufflen = 0; FILE *const fd1 = os_fopen(fname1, READBIN); char line1[200]; char line2[200]; ptrdiff_t lineidx = 0; if (fd1 == NULL) { - snprintf(IObuff, IOSIZE, e_notread, fname1); + IObufflen = vim_snprintf_safelen(IObuff, IOSIZE, e_cant_read_file_str, fname1); } else { FILE *const fd2 = os_fopen(fname2, READBIN); if (fd2 == NULL) { fclose(fd1); - snprintf(IObuff, IOSIZE, e_notread, fname2); + IObufflen = vim_snprintf_safelen(IObuff, IOSIZE, e_cant_read_file_str, fname2); } else { int64_t linecount = 1; for (int64_t count = 0;; count++) { @@ -418,20 +421,21 @@ static int assert_equalfile(typval_T *argvars) const int c2 = fgetc(fd2); if (c1 == EOF) { if (c2 != EOF) { - xstrlcpy(IObuff, "first file is shorter", IOSIZE); + IObufflen = xstrlcpy(IObuff, "first file is shorter", IOSIZE); } break; } else if (c2 == EOF) { - xstrlcpy(IObuff, "second file is shorter", IOSIZE); + IObufflen = xstrlcpy(IObuff, "second file is shorter", IOSIZE); break; } else { line1[lineidx] = (char)c1; line2[lineidx] = (char)c2; lineidx++; if (c1 != c2) { - snprintf(IObuff, IOSIZE, - "difference at byte %" PRId64 ", line %" PRId64, - count, linecount); + IObufflen + = vim_snprintf_safelen(IObuff, IOSIZE, + "difference at byte %" PRId64 ", line %" PRId64, + count, linecount); break; } } @@ -449,26 +453,26 @@ static int assert_equalfile(typval_T *argvars) } } - if (IObuff[0] != NUL) { + if (IObufflen > 0) { garray_T ga; prepare_assert_error(&ga); if (argvars[2].v_type != VAR_UNKNOWN) { char *const tofree = encode_tv2echo(&argvars[2], NULL); ga_concat(&ga, tofree); xfree(tofree); - ga_concat(&ga, ": "); + GA_CONCAT_LITERAL(&ga, ": "); } - ga_concat(&ga, IObuff); + ga_concat_len(&ga, IObuff, IObufflen); if (lineidx > 0) { line1[lineidx] = NUL; line2[lineidx] = NUL; - ga_concat(&ga, " after \""); - ga_concat(&ga, line1); + GA_CONCAT_LITERAL(&ga, " after \""); + ga_concat_len(&ga, line1, (size_t)lineidx); if (strcmp(line1, line2) != 0) { - ga_concat(&ga, "\" vs \""); - ga_concat(&ga, line2); + GA_CONCAT_LITERAL(&ga, "\" vs \""); + ga_concat_len(&ga, line2, (size_t)lineidx); } - ga_concat(&ga, "\""); + GA_CONCAT_LITERAL(&ga, "\""); } assert_error(&ga); ga_clear(&ga); @@ -498,7 +502,7 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const char *const error = tv_get_string_chk(&argvars[0]); if (*get_vim_var_str(VV_EXCEPTION) == NUL) { prepare_assert_error(&ga); - ga_concat(&ga, "v:exception is not set"); + GA_CONCAT_LITERAL(&ga, "v:exception is not set"); assert_error(&ga); ga_clear(&ga); rettv->vval.v_number = 1; @@ -547,7 +551,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (called_emsg == called_emsg_before) { prepare_assert_error(&ga); - ga_concat(&ga, "command did not fail: "); + GA_CONCAT_LITERAL(&ga, "command did not fail: "); assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); ga_clear(&ga); @@ -633,7 +637,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } fill_assert_error(&ga, &argvars[2], expected_str, &argvars[error_found_index], &actual_tv, ASSERT_FAILS); - ga_concat(&ga, ": "); + GA_CONCAT_LITERAL(&ga, ": "); assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); ga_clear(&ga);