From 5287a04be4ed1736ae64ca990f10ffa1d430a0c0 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 10 Aug 2026 04:27:49 -0400 Subject: [PATCH] perf(mbyte): annotate pure functions #41251 --- src/nvim/mbyte.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 886bd1a7ee..b1ff9a92ae 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -405,6 +405,7 @@ int bomb_size(void) // Remove all BOM from "s" by moving remaining text. void remove_bom(char *s) + FUNC_ATTR_NONNULL_ALL { char *p = s; @@ -444,6 +445,7 @@ int mb_get_class_tab(const char *p, const uint64_t *const chartab) } static bool prop_is_emojilike(const utf8proc_property_t *prop) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { return prop->boundclass == UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC || prop->boundclass == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR; @@ -455,6 +457,7 @@ static bool prop_is_emojilike(const utf8proc_property_t *prop) /// When p_ambw is "double", return 2 for a character with East Asian Width /// class 'A'(mbiguous). int utf_char2cells(int c) + FUNC_ATTR_PURE { if (c < 0x80) { return 1; @@ -492,6 +495,7 @@ int utf_char2cells(int c) /// Return the number of display cells character at "*p" occupies. /// This doesn't take care of unprintable characters, use ptr2cells() for that. int utf_ptr2cells(const char *p_in) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { const uint8_t *p = (const uint8_t *)p_in; // Need to convert to a character number. @@ -592,6 +596,7 @@ ret: /// Like utf_ptr2cells(), but limit string length to "size". /// For an empty string or truncated character returns 1. int utf_ptr2cells_len(const char *p, int size) + FUNC_ATTR_PURE { // Need to convert to a wide character. if (size > 0 && (uint8_t)(*p) >= 0x80) { @@ -628,6 +633,7 @@ int utf_ptr2cells_len(const char *p, int size) /// string. /// @return The number of cells occupied by string `str` size_t mb_string2cells(const char *str) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { size_t clen = 0; @@ -645,7 +651,7 @@ size_t mb_string2cells(const char *str) /// @param size maximum length of string. It will terminate on earlier NUL. /// @return The number of cells occupied by string `str` size_t mb_string2cells_len(const char *str, size_t size) - FUNC_ATTR_NONNULL_ARG(1) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ARG(1) { size_t clen = 0; @@ -741,6 +747,7 @@ int utf_ptr2char(const char *const p_in) // If byte sequence is illegal or incomplete, returns -1 and does not advance // "s". static int utf_safe_read_char_adv(const char **s, size_t *n) + FUNC_ATTR_NONNULL_ARG(2) { if (*n == 0) { // end of buffer return 0; @@ -780,6 +787,7 @@ static int utf_safe_read_char_adv(const char **s, size_t *n) // Get character at **pp and advance *pp to the next character. // Note: composing characters are skipped! int mb_ptr2char_adv(const char **const pp) + FUNC_ATTR_NONNULL_ALL { int c = utf_ptr2char(*pp); *pp += utfc_ptr2len(*pp); @@ -789,6 +797,7 @@ int mb_ptr2char_adv(const char **const pp) // Get character at **pp and advance *pp to the next character. // Note: composing characters are returned as separate characters. int mb_cptr2char_adv(const char **pp) + FUNC_ATTR_NONNULL_ALL { int c = utf_ptr2char(*pp); *pp += utf_ptr2len(*pp); @@ -799,6 +808,7 @@ int mb_cptr2char_adv(const char **pp) /// by a space byte to be drawn correctly, and not merge with the space left of /// the string. bool utf_iscomposing_first(int c) + FUNC_ATTR_PURE { return c >= 128 && !utf8proc_grapheme_break(' ', c); } @@ -898,6 +908,7 @@ schar_T utfc_ptrlen2schar(const char *p, int len, int *firstc) /// Caller must ensure there is space for `first_compose` static schar_T schar_from_buf_first(const char *buf, size_t len, bool first_compose) + FUNC_ATTR_NONNULL_ALL { if (first_compose) { char cbuf[MAX_SCHAR_SIZE]; @@ -935,6 +946,7 @@ int utf_ptr2len(const char *const p_in) // "b" must be between 0 and 255! // Returns 1 for an invalid first byte value. int utf_byte2len(int b) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return utf8len_tab[b]; } @@ -946,6 +958,7 @@ int utf_byte2len(int b) // Returns number > "size" for an incomplete byte sequence. // Never returns zero. int utf_ptr2len_len(const char *p, int size) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int m; @@ -1008,6 +1021,7 @@ int utfc_ptr2len(const char *const p) /// Returns 0 for an empty string. /// Returns 1 for an illegal char or an incomplete byte sequence. int utfc_ptr2len_len(const char *p, int size) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (size < 1 || *p == NUL) { return 0; @@ -1053,6 +1067,7 @@ int utfc_ptr2len_len(const char *p, int size) /// Determine how many bytes certain unicode codepoint will occupy int utf_char2len(const int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { if (c < 0x80) { return 1; @@ -1076,6 +1091,7 @@ int utf_char2len(const int c) /// must have room for at least 6 bytes /// @return Number of bytes (1-6). int utf_char2bytes(const int c, char *const buf) + FUNC_ATTR_NONNULL_ALL { if (c < 0x80) { // 7 bits buf[0] = (char)c; @@ -1125,6 +1141,7 @@ int utf_char2bytes(const int c, char *const buf) /// Based on code from Markus Kuhn. /// Returns false for negative values. bool utf_iscomposing_legacy(int c) + FUNC_ATTR_PURE { const utf8proc_property_t *prop = utf8proc_get_property(c); return prop->category == UTF8PROC_CATEGORY_MN || prop->category == UTF8PROC_CATEGORY_ME; @@ -1222,6 +1239,7 @@ bool utf_printable(int c) // 1: punctuation // 2 or bigger: some class of word character. int utf_class(const int c) + FUNC_ATTR_PURE { return utf_class_tab(c, curbuf->b_chartab); } @@ -1344,6 +1362,7 @@ int utf_class_tab(const int c, const uint64_t *const chartab) } bool utf_ambiguous_width(const char *p) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { // be quick if there is nothing to print or ASCII-only if (p[0] == NUL || p[1] == NUL) { @@ -1366,6 +1385,7 @@ bool utf_ambiguous_width(const char *p) // Return the folded-case equivalent of "a", which is a UCS-4 character. Uses // full case folding. int utf_fold(int a) + FUNC_ATTR_PURE { if (a < 0x80) { // be fast for ASCII @@ -1454,6 +1474,7 @@ bool mb_isalpha(int a) } int utf_strnicmp(const char *s1, const char *s2, size_t n1, size_t n2) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { int c1, c2; char buffer[6]; @@ -1649,7 +1670,7 @@ void mb_utflen(const char *s, size_t len, size_t *codepoints, size_t *codeunits) } ssize_t mb_utf_index_to_bytes(const char *s, size_t len, size_t index, bool use_utf16_units) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { size_t count = 0; size_t clen; @@ -1680,6 +1701,7 @@ ssize_t mb_utf_index_to_bytes(const char *s, size_t len, size_t index, bool use_ /// @return zero if s1 and s2 are equal (ignoring case), the difference between /// two characters otherwise. int mb_strnicmp(const char *s1, const char *s2, const size_t nn) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return utf_strnicmp(s1, s2, nn, nn); } @@ -1697,6 +1719,7 @@ int mb_strnicmp(const char *s1, const char *s2, const size_t nn) /// /// @return 0 if strings are equal, <0 if s1 < s2, >0 if s1 > s2. int mb_stricmp(const char *s1, const char *s2) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { return mb_strnicmp(s1, s2, MAXCOL); } @@ -1741,6 +1764,7 @@ void show_utf8(void) /// @return true if boundclass bc always starts a new cluster regardless of what's before /// false negatives are allowed (perf cost, not correctness) static bool always_break(int bc) + FUNC_ATTR_CONST { return (bc == UTF8PROC_BOUNDCLASS_CONTROL); } @@ -1748,6 +1772,7 @@ static bool always_break(int bc) /// @return true if bc2 always starts a cluster after bc1 /// false negatives are allowed (perf cost, not correctness) static bool always_break_two(int bc1, int bc2) + FUNC_ATTR_CONST { // don't check for UTF8PROC_BOUNDCLASS_CONTROL for bc2 as it either has been checked by // "always_break" on first iteration or when it was bc1 in the previous iteration @@ -1763,6 +1788,7 @@ static bool always_break_two(int bc1, int bc2) /// If "p" points to the NUL at the end of the string return 0. /// Returns 0 when already at the first byte of a character. int utf_head_off(const char *base_in, const char *p_in) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ARG(2) { if ((uint8_t)(*p_in) < 0x80) { // be quick for ASCII return 0; @@ -1850,6 +1876,7 @@ int utf_head_off(const char *base_in, const char *p_in) /// Assumes caller already handles ascii. see `utfc_next` StrCharInfo utfc_next_impl(StrCharInfo cur) + FUNC_ATTR_PURE { int32_t prev_code = cur.chr.value; uint8_t *next = (uint8_t *)(cur.ptr + cur.chr.len); @@ -2022,6 +2049,7 @@ bool utf_allow_break(int cc, int ncc) /// @param[in,out] fp Source of the character to copy. /// @param[in,out] tp Destination to copy to. void mb_copy_char(const char **const fp, char **const tp) + FUNC_ATTR_NONNULL_ALL { const size_t l = (size_t)utfc_ptr2len(*fp); @@ -2034,6 +2062,7 @@ void mb_copy_char(const char **const fp, char **const tp) /// at the start of a character 0 is returned, otherwise the offset to the next /// character. Can start anywhere in a stream of bytes. int mb_off_next(const char *base, const char *p) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ARG(2) { int head_off = utf_head_off(base, p); @@ -2159,6 +2188,7 @@ theend: /// @return true if string "s" is a valid utf-8 string. /// When "end" is NULL stop at the first NUL. Otherwise stop at "end". bool utf_valid_string(const char *s, const char *end) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ARG(1) { const uint8_t *p = (uint8_t *)s; @@ -2227,6 +2257,7 @@ void mb_check_adjust_col(void *win_) /// /// @return a pointer to the character before "*p", if there is one. char *mb_prevptr(char *line, char *p) + FUNC_ATTR_PURE { if (p > line) { MB_PTR_BACK(line, p); @@ -2237,6 +2268,7 @@ char *mb_prevptr(char *line, char *p) /// Return the character length of "str". Each multi-byte character (with /// following composing characters) counts as one. int mb_charlen(const char *str) + FUNC_ATTR_PURE { const char *p = str; int count; @@ -2254,6 +2286,7 @@ int mb_charlen(const char *str) /// Like mb_charlen() but for a string with specified length. int mb_charlen_len(const char *str, int len) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { const char *p = str; int count; @@ -2314,6 +2347,7 @@ const char *mb_unescape(const char **const pp) /// Skip the Vim specific head of a 'encoding' name. char *enc_skip(char *p) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { if (strncmp(p, "2byte-", 6) == 0) { return p + 6; @@ -2330,7 +2364,7 @@ char *enc_skip(char *p) /// /// @return an allocated string. char *enc_canonize(char *enc) - FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { if (strcmp(enc, "default") == 0) { // Use the default encoding as found by set_init_1(). @@ -2392,6 +2426,7 @@ char *enc_canonize(char *enc) /// Search for an encoding alias of "name". /// Returns -1 when not found. static int enc_alias_search(const char *name) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { for (int i = 0; enc_alias_table[i].name != NULL; i++) { if (strcmp(name, enc_alias_table[i].name) == 0) { @@ -2511,6 +2546,7 @@ void *my_iconv_open(char *to, char *from) // If resultlenp is not NULL, sets it to the result length in bytes. static char *iconv_string(const vimconv_T *const vcp, const char *str, size_t slen, size_t *unconvlenp, size_t *resultlenp) + FUNC_ATTR_NONNULL_ARG(1) { char *to; size_t len = 0; @@ -2618,6 +2654,7 @@ void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @return FAIL when conversion is not supported, OK otherwise. int convert_setup(vimconv_T *vcp, char *from, char *to) + FUNC_ATTR_NONNULL_ARG(1) { return convert_setup_ext(vcp, from, true, to, true); } @@ -2626,6 +2663,7 @@ int convert_setup(vimconv_T *vcp, char *from, char *to) /// "from" unicode charsets be considered utf-8. Same for "to". int convert_setup_ext(vimconv_T *vcp, char *from, bool from_unicode_is_utf8, char *to, bool to_unicode_is_utf8) + FUNC_ATTR_NONNULL_ARG(1) { int from_is_utf8; int to_is_utf8; @@ -2862,6 +2900,7 @@ static size_t cw_table_size = 0; /// @param c The source character. /// @return 1 or 2 when `c` is in the cellwidth table, 0 if not. static int cw_value(int c) + FUNC_ATTR_PURE { if (cw_table == NULL) { return 0; @@ -2889,6 +2928,7 @@ static int cw_value(int c) } static int tv_nr_compare(const void *a1, const void *a2) + FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { const listitem_T *const li1 = tv_list_first(*(const list_T **)a1); const listitem_T *const li2 = tv_list_first(*(const list_T **)a2); @@ -3041,6 +3081,7 @@ void f_charclass(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// Function given to ExpandGeneric() to obtain the possible arguments of the /// encoding options. char *get_encoding_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) + FUNC_ATTR_PURE { if (idx >= (int)ARRAY_SIZE(enc_canon_table)) { return NULL;