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

ed202035b1

Co-authored-by: John Marriott <basilisk@internode.on.net>
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
This commit is contained in:
zeertzjq
2026-02-21 06:32:20 +08:00
parent 9aa936d892
commit b3a3028fd9
8 changed files with 118 additions and 104 deletions

View File

@@ -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_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[] 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_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_null[] INIT(= N_("E38: Null argument"));
EXTERN const char e_number_exp[] INIT(= N_("E39: Number expected")); EXTERN const char e_number_exp[] INIT(= N_("E39: Number expected"));
EXTERN const char e_openerrf[] INIT(= N_("E40: Can't open errorfile %s")); EXTERN const char e_openerrf[] INIT(= N_("E40: Can't open errorfile %s"));

View File

@@ -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"); const char *const partial_self_msg = _("partial self dictionary");
for (size_t i = 0; i < kv_size(*mpstack); i++) { for (size_t i = 0; i < kv_size(*mpstack); i++) {
if (i != 0) { if (i != 0) {
ga_concat_len(&msg_ga, S_LEN(", ")); GA_CONCAT_LITERAL(&msg_ga, ", ");
} }
MPConvStackVal v = kv_A(*mpstack, i); MPConvStackVal v = kv_A(*mpstack, i);
switch (v.type) { switch (v.type) {
@@ -296,7 +296,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
do { \ do { \
const char *const buf_ = (buf); \ const char *const buf_ = (buf); \
if (buf_ == NULL) { \ if (buf_ == NULL) { \
ga_concat_len(gap, S_LEN("''")); \ GA_CONCAT_LITERAL(gap, "''"); \
} else { \ } else { \
const size_t len_ = (len); \ const size_t len_ = (len); \
ga_grow(gap, (int)(2 + len_ + memcnt(buf_, '\'', 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 blob_T *const blob_ = (blob); \
const int len_ = (len); \ const int len_ = (len); \
if (len_ == 0) { \ if (len_ == 0) { \
ga_concat_len(gap, S_LEN("0z")); \ GA_CONCAT_LITERAL(gap, "0z"); \
} else { \ } else { \
/* Allocate space for "0z", the two hex chars per byte, and a */ \ /* Allocate space for "0z", the two hex chars per byte, and a */ \
/* "." separator after every eight hex chars. */ \ /* "." separator after every eight hex chars. */ \
/* Example: "0z00112233.44556677.8899" */ \ /* Example: "0z00112233.44556677.8899" */ \
ga_grow(gap, 2 + 2 * len_ + (len_ - 1) / 4); \ ga_grow(gap, 2 + 2 * len_ + (len_ - 1) / 4); \
ga_concat_len(gap, S_LEN("0z")); \ GA_CONCAT_LITERAL(gap, "0z"); \
char numbuf[NUMBUFLEN]; \ char numbuf[NUMBUFLEN]; \
for (int i_ = 0; i_ < len_; i_++) { \ for (int i_ = 0; i_ < len_; i_++) { \
if (i_ > 0 && (i_ & 3) == 0) { \ 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) \ #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \ do { \
char numbuf[NUMBUFLEN]; \ char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t)(num)); \ size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
ga_concat(gap, numbuf); \ "%" PRId64, (int64_t)(num)); \
ga_concat_len(gap, numbuf, numbuflen); \
} while (0) } while (0)
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ #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); \ const float_T flt_ = (flt); \
switch (xfpclassify(flt_)) { \ switch (xfpclassify(flt_)) { \
case FP_NAN: { \ case FP_NAN: { \
ga_concat_len(gap, S_LEN("str2float('nan')")); \ GA_CONCAT_LITERAL(gap, "str2float('nan')"); \
break; \ break; \
} \ } \
case FP_INFINITE: { \ case FP_INFINITE: { \
if (flt_ < 0) { \ if (flt_ < 0) { \
ga_append(gap, '-'); \ ga_append(gap, '-'); \
} \ } \
ga_concat_len(gap, S_LEN("str2float('inf')")); \ GA_CONCAT_LITERAL(gap, "str2float('inf')"); \
break; \ break; \
} \ } \
default: { \ default: { \
char numbuf[NUMBUFLEN]; \ char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
ga_concat(gap, numbuf); \ "%g", flt_); \
ga_concat_len(gap, numbuf, numbuflen); \
} \ } \
} \ } \
} while (0) } while (0)
@@ -375,10 +377,10 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
const char *const fun_ = (fun); \ const char *const fun_ = (fun); \
if (fun_ == NULL) { \ if (fun_ == NULL) { \
internal_error("string(): NULL function name"); \ internal_error("string(): NULL function name"); \
ga_concat_len(gap, S_LEN("function(NULL")); \ GA_CONCAT_LITERAL(gap, "function(NULL"); \
} else { \ } else { \
const char *const prefix_ = (prefix); \ const char *const prefix_ = (prefix); \
ga_concat_len(gap, S_LEN("function(")); \ GA_CONCAT_LITERAL(gap, "function("); \
const int name_off = gap->ga_len; \ const int name_off = gap->ga_len; \
ga_concat(gap, prefix_); \ ga_concat(gap, prefix_); \
TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \ 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) \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \
do { \ do { \
if ((len) != 0) { \ if ((len) != 0) { \
ga_concat_len(gap, S_LEN(", ")); \ GA_CONCAT_LITERAL(gap, ", "); \
} \ } \
} while (0) } while (0)
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \
do { \ do { \
if ((ptrdiff_t)(len) != -1) { \ if ((ptrdiff_t)(len) != -1) { \
ga_concat_len(gap, S_LEN(", ")); \ GA_CONCAT_LITERAL(gap, ", "); \
} \ } \
} while (0) } while (0)
@@ -406,7 +408,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
ga_append(gap, ')') ga_append(gap, ')')
#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ #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) \ #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
ga_append(gap, '[') 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_REAL_LIST_AFTER_START(tv, mpsv)
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ #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_CHECK_BEFORE
#define TYPVAL_ENCODE_CONV_NIL(tv) \ #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) \ #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
do { \ do { \
if (num) { \ if (num) { \
ga_concat_len(gap, S_LEN("v:true")); \ GA_CONCAT_LITERAL(gap, "v:true"); \
} else { \ } else { \
ga_concat_len(gap, S_LEN("v:false")); \ GA_CONCAT_LITERAL(gap, "v:false"); \
} \ } \
} while (0) } while (0)
@@ -441,10 +443,10 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
ga_append(gap, '}') ga_append(gap, '}')
#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \ #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) \ #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) #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 #undef TYPVAL_ENCODE_CONV_NIL
#define TYPVAL_ENCODE_CONV_NIL(tv) \ #define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat_len(gap, S_LEN("null")) GA_CONCAT_LITERAL(gap, "null")
#undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_BOOL
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
do { \ do { \
if (num) { \ if (num) { \
ga_concat_len(gap, S_LEN("true")); \ GA_CONCAT_LITERAL(gap, "true"); \
} else { \ } else { \
ga_concat_len(gap, S_LEN("false")); \ GA_CONCAT_LITERAL(gap, "false"); \
} \ } \
} while (0) } 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) \ #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
do { \ do { \
char numbuf[NUMBUFLEN]; \ char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \ size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
ga_concat(gap, numbuf); \ "%" PRIu64, (num)); \
ga_concat_len(gap, numbuf, numbuflen); \
} while (0) } while (0)
#undef TYPVAL_ENCODE_CONV_FLOAT #undef TYPVAL_ENCODE_CONV_FLOAT
@@ -587,8 +590,9 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
} \ } \
default: { \ default: { \
char numbuf[NUMBUFLEN]; \ char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
ga_concat(gap, numbuf); \ "%g", flt_); \
ga_concat_len(gap, numbuf, numbuflen); \
break; \ break; \
} \ } \
} \ } \
@@ -620,7 +624,7 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const
{ {
const char *utf_buf = buf; const char *utf_buf = buf;
if (utf_buf == NULL) { if (utf_buf == NULL) {
ga_concat_len(gap, S_LEN("\"\"")); GA_CONCAT_LITERAL(gap, "\"\"");
} else { } else {
size_t utf_len = len; size_t utf_len = len;
char *tofree = NULL; 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 blob_T *const blob_ = (blob); \
const int len_ = (len); \ const int len_ = (len); \
if (len_ == 0) { \ if (len_ == 0) { \
ga_concat_len(gap, S_LEN("[]")); \ GA_CONCAT_LITERAL(gap, "[]"); \
} else { \ } else { \
ga_append(gap, '['); \ ga_append(gap, '['); \
char numbuf[NUMBUFLEN]; \ char numbuf[NUMBUFLEN]; \
for (int i_ = 0; i_ < len_; i_++) { \ for (int i_ = 0; i_ < len_; i_++) { \
if (i_ > 0) { \ if (i_ > 0) { \
ga_concat_len(gap, S_LEN(", ")); \ GA_CONCAT_LITERAL(gap, ", "); \
} \ } \
vim_snprintf((char *)numbuf, ARRAY_SIZE(numbuf), "%d", \ size_t numbuflen = vim_snprintf_safelen(numbuf, ARRAY_SIZE(numbuf), \
(int)tv_blob_get(blob_, i_)); \ "%d", (int)tv_blob_get(blob_, i_)); \
ga_concat(gap, numbuf); \ ga_concat_len(gap, numbuf, numbuflen); \
} \ } \
ga_append(gap, ']'); \ ga_append(gap, ']'); \
} \ } \

View File

@@ -1307,7 +1307,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl
if (blob) { if (blob) {
if (read_blob(fd, rettv, offset, size) == FAIL) { if (read_blob(fd, rettv, offset, size) == FAIL) {
semsg(_(e_notread), fname); semsg(_(e_cant_read_file_str), fname);
} }
fclose(fd); fclose(fd);
return; return;

View File

@@ -971,7 +971,7 @@ int tv_list_slice_or_index(list_T *list, bool range, varnumber_T n1_arg, varnumb
} }
typedef struct { typedef struct {
char *s; String s;
char *tofree; char *tofree;
} Join; } Join;
@@ -995,25 +995,26 @@ static int list_join_inner(garray_T *const gap, list_T *const l, const char *con
if (got_int) { if (got_int) {
break; break;
} }
char *s; String s;
size_t len; s.data = encode_tv2echo(TV_LIST_ITEM_TV(item), &s.size);
s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len); if (s.data == NULL) {
if (s == NULL) {
return FAIL; return FAIL;
} }
sumlen += len; sumlen += s.size;
Join *const p = GA_APPEND_VIA_PTR(Join, join_gap); Join *const p = GA_APPEND_VIA_PTR(Join, join_gap);
p->tofree = p->s = s; p->s = s;
p->tofree = s.data;
line_breakcheck(); line_breakcheck();
}); });
// Allocate result buffer with its total size, avoid re-allocation and // Allocate result buffer with its total size, avoid re-allocation and
// multiple copy operations. Add 2 for a tailing ']' and NUL. // multiple copy operations. Add 2 for a tailing ']' and NUL.
size_t seplen = strlen(sep);
if (join_gap->ga_len >= 2) { 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); 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) { if (first) {
first = false; first = false;
} else { } else {
ga_concat(gap, sep); ga_concat_len(gap, sep, seplen);
} }
const Join *const p = ((const Join *)join_gap->ga_data) + i; const Join *const p = ((const Join *)join_gap->ga_data) + i;
if (p->s != NULL) { if (p->s.data != NULL) {
ga_concat(gap, p->s); ga_concat_len(gap, p->s.data, p->s.size);
} }
line_breakcheck(); line_breakcheck();
} }
@@ -1106,8 +1107,10 @@ void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char buf[MB_MAXBYTES + 1]; char buf[MB_MAXBYTES + 1];
TV_LIST_ITER_CONST(l, li, { TV_LIST_ITER_CONST(l, li, {
buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), buf)] = NUL; const varnumber_T n = tv_get_number(TV_LIST_ITEM_TV(li));
ga_concat(&ga, buf); const size_t buflen = (size_t)utf_char2bytes((int)n, buf);
buf[buflen] = NUL;
ga_concat_len(&ga, buf, buflen);
}); });
ga_append(&ga, NUL); ga_append(&ga, NUL);

View File

@@ -1373,7 +1373,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
READ_FILTER, false) != OK) { READ_FILTER, false) != OK) {
if (!aborting()) { if (!aborting()) {
msg_putchar('\n'); msg_putchar('\n');
semsg(_(e_notread), otmp); semsg(_(e_cant_read_file_str), otmp);
} }
goto error; goto error;
} }

View File

@@ -4,6 +4,7 @@
#include <stddef.h> #include <stddef.h>
#include "nvim/garray_defs.h" // IWYU pragma: keep #include "nvim/garray_defs.h" // IWYU pragma: keep
#include "nvim/macros_defs.h" // IWYU pragma: keep
#include "nvim/memory.h" #include "nvim/memory.h"
#define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0) #define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0)
@@ -45,3 +46,5 @@
/// ///
/// @param gap the garray to be freed /// @param gap the garray to be freed
#define GA_DEEP_CLEAR_PTR(gap) GA_DEEP_CLEAR(gap, void *, FREE_PTR_PTR) #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))

View File

@@ -415,7 +415,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
os_remove(tempname); os_remove(tempname);
if (readlen != len) { if (readlen != len) {
// unexpected read error // unexpected read error
semsg(_(e_notread), tempname); semsg(_(e_cant_read_file_str), tempname);
xfree(tempname); xfree(tempname);
xfree(buffer); xfree(buffer);
return FAIL; return FAIL;
@@ -809,7 +809,7 @@ char *get_cmd_output(char *cmd, char *infile, int flags, size_t *ret_len)
fclose(fd); fclose(fd);
os_remove(tempname); os_remove(tempname);
if (i != len) { if (i != len) {
semsg(_(e_notread), tempname); semsg(_(e_cant_read_file_str), tempname);
XFREE_CLEAR(buffer); XFREE_CLEAR(buffer);
} else if (ret_len == NULL) { } else if (ret_len == NULL) {
// Change NUL into SOH, otherwise the string is truncated. // Change NUL into SOH, otherwise the string is truncated.

View File

@@ -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. /// Prepare "gap" for an assert error and add the sourcing position.
static void prepare_assert_error(garray_T *gap) static void prepare_assert_error(garray_T *gap)
{ {
char buf[NUMBUFLEN];
char *sname = estack_sfile(ESTACK_NONE); char *sname = estack_sfile(ESTACK_NONE);
ga_init(gap, 1, 100); ga_init(gap, 1, 100);
@@ -68,11 +67,13 @@ static void prepare_assert_error(garray_T *gap)
} }
} }
if (SOURCING_LNUM > 0) { if (SOURCING_LNUM > 0) {
vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)SOURCING_LNUM); char buf[NUMBUFLEN];
ga_concat(gap, buf); 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) { if (sname != NULL || SOURCING_LNUM > 0) {
ga_concat(gap, ": "); GA_CONCAT_LITERAL(gap, ": ");
} }
xfree(sname); xfree(sname);
} }
@@ -87,29 +88,29 @@ static void ga_concat_esc(garray_T *gap, const char *p, int clen)
if (clen > 1) { if (clen > 1) {
memmove(buf, p, (size_t)clen); memmove(buf, p, (size_t)clen);
buf[clen] = NUL; buf[clen] = NUL;
ga_concat(gap, buf); ga_concat_len(gap, buf, (size_t)clen);
return; return;
} }
switch (*p) { switch (*p) {
case BS: case BS:
ga_concat(gap, "\\b"); break; GA_CONCAT_LITERAL(gap, "\\b"); break;
case ESC: case ESC:
ga_concat(gap, "\\e"); break; GA_CONCAT_LITERAL(gap, "\\e"); break;
case FF: case FF:
ga_concat(gap, "\\f"); break; GA_CONCAT_LITERAL(gap, "\\f"); break;
case NL: case NL:
ga_concat(gap, "\\n"); break; GA_CONCAT_LITERAL(gap, "\\n"); break;
case TAB: case TAB:
ga_concat(gap, "\\t"); break; GA_CONCAT_LITERAL(gap, "\\t"); break;
case CAR: case CAR:
ga_concat(gap, "\\r"); break; GA_CONCAT_LITERAL(gap, "\\r"); break;
case '\\': case '\\':
ga_concat(gap, "\\\\"); break; GA_CONCAT_LITERAL(gap, "\\\\"); break;
default: default:
if ((uint8_t)(*p) < ' ' || *p == 0x7f) { if ((uint8_t)(*p) < ' ' || *p == 0x7f) {
vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p); size_t buflen = vim_snprintf_safelen(buf, NUMBUFLEN, "\\x%02x", *p);
ga_concat(gap, buf); ga_concat_len(gap, buf, buflen);
} else { } else {
ga_append(gap, (uint8_t)(*p)); 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]; char buf[NUMBUFLEN];
if (str == NULL) { if (str == NULL) {
ga_concat(gap, "NULL"); GA_CONCAT_LITERAL(gap, "NULL");
return; return;
} }
@@ -139,12 +140,12 @@ static void ga_concat_shorten_esc(garray_T *gap, const char *str)
s += clen; s += clen;
} }
if (same_len > 20) { if (same_len > 20) {
ga_concat(gap, "\\["); GA_CONCAT_LITERAL(gap, "\\[");
ga_concat_esc(gap, p, clen); ga_concat_esc(gap, p, clen);
ga_concat(gap, " occurs "); GA_CONCAT_LITERAL(gap, " occurs ");
vim_snprintf(buf, NUMBUFLEN, "%d", same_len); size_t buflen = vim_snprintf_safelen(buf, NUMBUFLEN, "%d", same_len);
ga_concat(gap, buf); ga_concat_len(gap, buf, buflen);
ga_concat(gap, " times]"); GA_CONCAT_LITERAL(gap, " times]");
p = s; p = s;
} else { } else {
ga_concat_esc(gap, p, clen); 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); char *tofree = encode_tv2echo(opt_msg_tv, NULL);
ga_concat(gap, tofree); ga_concat(gap, tofree);
xfree(tofree); xfree(tofree);
ga_concat(gap, ": "); GA_CONCAT_LITERAL(gap, ": ");
} }
if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
ga_concat(gap, "Pattern "); GA_CONCAT_LITERAL(gap, "Pattern ");
} else if (atype == ASSERT_NOTEQUAL) { } else if (atype == ASSERT_NOTEQUAL) {
ga_concat(gap, "Expected not equal to "); GA_CONCAT_LITERAL(gap, "Expected not equal to ");
} else { } else {
ga_concat(gap, "Expected "); GA_CONCAT_LITERAL(gap, "Expected ");
} }
if (exp_str == NULL) { 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); xfree(tofree);
} else { } else {
if (atype == ASSERT_FAILS) { if (atype == ASSERT_FAILS) {
ga_concat(gap, "'"); GA_CONCAT_LITERAL(gap, "'");
} }
ga_concat_shorten_esc(gap, exp_str); ga_concat_shorten_esc(gap, exp_str);
if (atype == ASSERT_FAILS) { if (atype == ASSERT_FAILS) {
ga_concat(gap, "'"); GA_CONCAT_LITERAL(gap, "'");
} }
} }
if (atype != ASSERT_NOTEQUAL) { if (atype != ASSERT_NOTEQUAL) {
if (atype == ASSERT_MATCH) { if (atype == ASSERT_MATCH) {
ga_concat(gap, " does not match "); GA_CONCAT_LITERAL(gap, " does not match ");
} else if (atype == ASSERT_NOTMATCH) { } else if (atype == ASSERT_NOTMATCH) {
ga_concat(gap, " does match "); GA_CONCAT_LITERAL(gap, " does match ");
} else { } else {
ga_concat(gap, " but got "); GA_CONCAT_LITERAL(gap, " but got ");
} }
char *tofree = encode_tv2string(got_tv, NULL); char *tofree = encode_tv2string(got_tv, NULL);
ga_concat_shorten_esc(gap, tofree); 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) { if (omitted != 0) {
char buf[100]; char buf[100];
vim_snprintf(buf, sizeof(buf), " - %d equal item%s omitted", omitted, size_t buflen = vim_snprintf_safelen(buf, sizeof(buf),
omitted == 1 ? "" : "s"); " - %d equal item%s omitted",
ga_concat(gap, buf); 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; garray_T ga;
prepare_assert_error(&ga); prepare_assert_error(&ga);
if (no_beep) { if (no_beep) {
ga_concat(&ga, "command did beep: "); GA_CONCAT_LITERAL(&ga, "command did beep: ");
} else { } else {
ga_concat(&ga, "command did not beep: "); GA_CONCAT_LITERAL(&ga, "command did not beep: ");
} }
ga_concat(&ga, cmd); ga_concat(&ga, cmd);
assert_error(&ga); assert_error(&ga);
@@ -400,17 +402,18 @@ static int assert_equalfile(typval_T *argvars)
} }
IObuff[0] = NUL; IObuff[0] = NUL;
size_t IObufflen = 0;
FILE *const fd1 = os_fopen(fname1, READBIN); FILE *const fd1 = os_fopen(fname1, READBIN);
char line1[200]; char line1[200];
char line2[200]; char line2[200];
ptrdiff_t lineidx = 0; ptrdiff_t lineidx = 0;
if (fd1 == NULL) { if (fd1 == NULL) {
snprintf(IObuff, IOSIZE, e_notread, fname1); IObufflen = vim_snprintf_safelen(IObuff, IOSIZE, e_cant_read_file_str, fname1);
} else { } else {
FILE *const fd2 = os_fopen(fname2, READBIN); FILE *const fd2 = os_fopen(fname2, READBIN);
if (fd2 == NULL) { if (fd2 == NULL) {
fclose(fd1); fclose(fd1);
snprintf(IObuff, IOSIZE, e_notread, fname2); IObufflen = vim_snprintf_safelen(IObuff, IOSIZE, e_cant_read_file_str, fname2);
} else { } else {
int64_t linecount = 1; int64_t linecount = 1;
for (int64_t count = 0;; count++) { for (int64_t count = 0;; count++) {
@@ -418,20 +421,21 @@ static int assert_equalfile(typval_T *argvars)
const int c2 = fgetc(fd2); const int c2 = fgetc(fd2);
if (c1 == EOF) { if (c1 == EOF) {
if (c2 != EOF) { if (c2 != EOF) {
xstrlcpy(IObuff, "first file is shorter", IOSIZE); IObufflen = xstrlcpy(IObuff, "first file is shorter", IOSIZE);
} }
break; break;
} else if (c2 == EOF) { } else if (c2 == EOF) {
xstrlcpy(IObuff, "second file is shorter", IOSIZE); IObufflen = xstrlcpy(IObuff, "second file is shorter", IOSIZE);
break; break;
} else { } else {
line1[lineidx] = (char)c1; line1[lineidx] = (char)c1;
line2[lineidx] = (char)c2; line2[lineidx] = (char)c2;
lineidx++; lineidx++;
if (c1 != c2) { if (c1 != c2) {
snprintf(IObuff, IOSIZE, IObufflen
"difference at byte %" PRId64 ", line %" PRId64, = vim_snprintf_safelen(IObuff, IOSIZE,
count, linecount); "difference at byte %" PRId64 ", line %" PRId64,
count, linecount);
break; break;
} }
} }
@@ -449,26 +453,26 @@ static int assert_equalfile(typval_T *argvars)
} }
} }
if (IObuff[0] != NUL) { if (IObufflen > 0) {
garray_T ga; garray_T ga;
prepare_assert_error(&ga); prepare_assert_error(&ga);
if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) {
char *const tofree = encode_tv2echo(&argvars[2], NULL); char *const tofree = encode_tv2echo(&argvars[2], NULL);
ga_concat(&ga, tofree); ga_concat(&ga, tofree);
xfree(tofree); xfree(tofree);
ga_concat(&ga, ": "); GA_CONCAT_LITERAL(&ga, ": ");
} }
ga_concat(&ga, IObuff); ga_concat_len(&ga, IObuff, IObufflen);
if (lineidx > 0) { if (lineidx > 0) {
line1[lineidx] = NUL; line1[lineidx] = NUL;
line2[lineidx] = NUL; line2[lineidx] = NUL;
ga_concat(&ga, " after \""); GA_CONCAT_LITERAL(&ga, " after \"");
ga_concat(&ga, line1); ga_concat_len(&ga, line1, (size_t)lineidx);
if (strcmp(line1, line2) != 0) { if (strcmp(line1, line2) != 0) {
ga_concat(&ga, "\" vs \""); GA_CONCAT_LITERAL(&ga, "\" vs \"");
ga_concat(&ga, line2); ga_concat_len(&ga, line2, (size_t)lineidx);
} }
ga_concat(&ga, "\""); GA_CONCAT_LITERAL(&ga, "\"");
} }
assert_error(&ga); assert_error(&ga);
ga_clear(&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]); const char *const error = tv_get_string_chk(&argvars[0]);
if (*get_vim_var_str(VV_EXCEPTION) == NUL) { if (*get_vim_var_str(VV_EXCEPTION) == NUL) {
prepare_assert_error(&ga); prepare_assert_error(&ga);
ga_concat(&ga, "v:exception is not set"); GA_CONCAT_LITERAL(&ga, "v:exception is not set");
assert_error(&ga); assert_error(&ga);
ga_clear(&ga); ga_clear(&ga);
rettv->vval.v_number = 1; 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) { if (called_emsg == called_emsg_before) {
prepare_assert_error(&ga); 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_append_cmd_or_arg(&ga, argvars, cmd);
assert_error(&ga); assert_error(&ga);
ga_clear(&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, fill_assert_error(&ga, &argvars[2], expected_str,
&argvars[error_found_index], &actual_tv, ASSERT_FAILS); &argvars[error_found_index], &actual_tv, ASSERT_FAILS);
ga_concat(&ga, ": "); GA_CONCAT_LITERAL(&ga, ": ");
assert_append_cmd_or_arg(&ga, argvars, cmd); assert_append_cmd_or_arg(&ga, argvars, cmd);
assert_error(&ga); assert_error(&ga);
ga_clear(&ga); ga_clear(&ga);