patch 9.1.0843: too many strlen() calls in undo.c

Commit: 
https://github.com/vim/vim/commit/fd1a838d364b62a9211af23c47e0eab8d54ea452
Author: John Marriott <basil...@internode.on.net>
Date:   Wed Nov 6 21:21:50 2024 +0100

    patch 9.1.0843: too many strlen() calls in undo.c
    
    Problem:  too many strlen() calls in undo.c
    Solution: refactor code and remove strlen() calls, update test_undo.vim
              and close remaining open swap files (John Marriott)
    
    closes: #15995
    
    Signed-off-by: John Marriott <basil...@internode.on.net>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/structs.h b/src/structs.h
index 47999a953..66d57e02d 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -397,6 +397,8 @@ typedef struct {
     char_u     *ul_line;       // text of the line
     long       ul_len;         // length of the line including NUL, plus text
                                // properties
+    colnr_T    ul_textlen;     // length of the line excluding NUL and any text
+                               // properties
 } undoline_T;
 
 typedef struct u_entry u_entry_T;
diff --git a/src/testdir/test_undo.vim b/src/testdir/test_undo.vim
index eec0e0bc0..2e598d35c 100644
--- a/src/testdir/test_undo.vim
+++ b/src/testdir/test_undo.vim
@@ -149,7 +149,7 @@ func Test_undotree_bufnr()
   " Drop created windows
   set ul&
   new
-  only!
+  bw!
 endfunc
 
 func Test_global_local_undolevels()
@@ -193,6 +193,7 @@ func Test_global_local_undolevels()
   " Drop created windows
   set ul&
   new
+  bw! one two
   only!
 endfunc
 
@@ -253,7 +254,7 @@ func Test_undo_del_chars()
   later 1h
   call assert_equal('123-abc', getline(1))
 
-  close!
+  bw!
 endfunc
 
 func Test_undolist()
@@ -274,7 +275,16 @@ func Test_undolist()
   call feedkeys('achange3\<Esc>', 'xt')
   let a = execute('undolist')
   call assert_match("^
number changes  when  *saved
 *2  *2  *.*
 *3  *2 .*$", a)
-  close!
+
+  " 3 save number
+  if has("persistent_undo")
+    setl undofile
+    w Xundolist.txt
+    defer delete('Xundolist.txt')
+    let lastline = execute('undolist')->split("
")[-1]
+    call assert_match("ago        1", lastline)
+  endif
+  bw!
 endfunc
 
 func Test_U_command()
@@ -286,7 +296,7 @@ func Test_U_command()
   call assert_equal('', getline(1))
   norm! U
   call assert_equal('change1change2', getline(1))
-  close!
+  bw!
 endfunc
 
 func Test_undojoin()
@@ -393,7 +403,7 @@ func Test_insert_expr()
   call feedkeys("u", 'x')
   call assert_equal(['a', 'b', 'c', '12', 'd'], getline(2, '$'))
 
-  close!
+  bw!
 endfunc
 
 func Test_undofile_earlier()
diff --git a/src/undo.c b/src/undo.c
index 8c2783acd..52df9394f 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -361,6 +361,7 @@ u_save_line(undoline_T *ul, linenr_T lnum)
 {
     char_u *line = ml_get(lnum);
 
+    ul->ul_textlen = ml_get_len(lnum);
     if (curbuf->b_ml.ml_line_len == 0)
     {
        ul->ul_len = 1;
@@ -793,14 +794,10 @@ u_compute_hash(char_u *hash)
 {
     context_sha256_T   ctx;
     linenr_T           lnum;
-    char_u             *p;
 
     sha256_start(&ctx);
     for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
-    {
-       p = ml_get(lnum);
-       sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
-    }
+       sha256_update(&ctx, ml_get(lnum), (UINT32_T)(ml_get_len(lnum) + 1));
     sha256_finish(&ctx, hash);
 }
 
@@ -820,8 +817,10 @@ u_get_undo_file_name(char_u *buf_ffname, int reading)
     char_u     *undo_file_name = NULL;
     int                dir_len;
     char_u     *p;
+    size_t     plen;
     stat_T     st;
     char_u     *ffname = buf_ffname;
+    size_t     ffnamelen;
 #ifdef HAVE_READLINK
     char_u     fname_buf[MAXPATHL];
 #endif
@@ -836,6 +835,7 @@ u_get_undo_file_name(char_u *buf_ffname, int reading)
        ffname = fname_buf;
 #endif
 
+    ffnamelen = STRLEN(ffname);
     // Loop over 'undodir'.  When reading find the first file that exists.
     // When not reading use the first directory that exists or ".".
     dirp = p_udir;
@@ -846,23 +846,24 @@ u_get_undo_file_name(char_u *buf_ffname, int reading)
        {
            // Use same directory as the ffname,
            // "dir/name" -> "dir/.name.un~"
-           undo_file_name = vim_strnsave(ffname, STRLEN(ffname) + 5);
+           undo_file_name = vim_strnsave(ffname, ffnamelen + 5);
            if (undo_file_name == NULL)
                break;
            p = gettail(undo_file_name);
+           plen = (size_t)(ffnamelen - (p - undo_file_name));
 #ifdef VMS
            // VMS can not handle more than one dot in the filenames
            // use "dir/name" -> "dir/_un_name" - add _un_
            // at the beginning to keep the extension
-           mch_memmove(p + 4,  p, STRLEN(p) + 1);
+           mch_memmove(p + 4,  p, plen + 1);
            mch_memmove(p, "_un_", 4);
 
 #else
            // Use same directory as the ffname,
            // "dir/name" -> "dir/.name.un~"
-           mch_memmove(p + 1, p, STRLEN(p) + 1);
+           mch_memmove(p + 1, p, plen + 1);
            *p = '.';
-           STRCAT(p, ".un~");
+           STRCPY(p + plen + 1, ".un~");
 #endif
        }
        else
@@ -872,7 +873,7 @@ u_get_undo_file_name(char_u *buf_ffname, int reading)
            {
                if (munged_name == NULL)
                {
-                   munged_name = vim_strsave(ffname);
+                   munged_name = vim_strnsave(ffname, ffnamelen);
                    if (munged_name == NULL)
                        return NULL;
                    for (p = munged_name; *p != NUL; MB_PTR_ADV(p))
@@ -1189,7 +1190,6 @@ read_string_decrypt(bufinfo_T *bi, int len)
     static int
 serialize_header(bufinfo_T *bi, char_u *hash)
 {
-    long       len;
     buf_T      *buf = bi->bi_buf;
     FILE       *fp = bi->bi_fp;
     char_u     time_buf[8];
@@ -1205,6 +1205,7 @@ serialize_header(bufinfo_T *bi, char_u *hash)
     {
        char_u *header;
        int    header_len;
+       long    len;
 
        undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2);
        bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf),
@@ -1244,11 +1245,9 @@ serialize_header(bufinfo_T *bi, char_u *hash)
 
     // buffer-specific data
     undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
-    len = buf->b_u_line_ptr.ul_line == NULL
-                               ? 0L : (long)STRLEN(buf->b_u_line_ptr.ul_line);
-    undo_write_bytes(bi, (long_u)len, 4);
-    if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr.ul_line, (size_t)len)
-                                                                      == FAIL)
+    undo_write_bytes(bi, (long_u)buf->b_u_line_ptr.ul_textlen, 4);
+    if (buf->b_u_line_ptr.ul_textlen > 0 && fwrite_crypt(bi, 
buf->b_u_line_ptr.ul_line,
+                       (size_t)buf->b_u_line_ptr.ul_textlen) == FAIL)
        return FAIL;
     undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
     undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);
@@ -1415,7 +1414,6 @@ serialize_uep(
     u_entry_T  *uep)
 {
     int                i;
-    size_t     len;
 
     undo_write_bytes(bi, (long_u)uep->ue_top, 4);
     undo_write_bytes(bi, (long_u)uep->ue_bot, 4);
@@ -1425,10 +1423,10 @@ serialize_uep(
     {
        // Text is written without the text properties, since we cannot restore
        // the text property types.
-       len = STRLEN(uep->ue_array[i].ul_line);
-       if (undo_write_bytes(bi, (long_u)len, 4) == FAIL)
+       if (undo_write_bytes(bi, (long_u)uep->ue_array[i].ul_textlen, 4) == 
FAIL)
            return FAIL;
-       if (len > 0 && fwrite_crypt(bi, uep->ue_array[i].ul_line, len) == FAIL)
+       if (uep->ue_array[i].ul_textlen > 0
+               && fwrite_crypt(bi, uep->ue_array[i].ul_line, 
uep->ue_array[i].ul_textlen) == FAIL)
            return FAIL;
     }
     return OK;
@@ -1484,6 +1482,7 @@ unserialize_uep(bufinfo_T *bi, int *error, char_u 
*file_name)
        }
        array[i].ul_line = line;
        array[i].ul_len = line_len + 1;
+       array[i].ul_textlen = line_len;
     }
     return uep;
 }
@@ -1862,6 +1861,7 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name 
UNUSED)
 
     CLEAR_FIELD(bi);
     line_ptr.ul_len = 0;
+    line_ptr.ul_textlen = 0;
     line_ptr.ul_line = NULL;
 
     if (name == NULL)
@@ -1986,6 +1986,7 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name 
UNUSED)
     {
        line_ptr.ul_line = read_string_decrypt(&bi, str_len);
        line_ptr.ul_len = str_len + 1;
+       line_ptr.ul_textlen = str_len;
     }
     line_lnum = (linenr_T)undo_read_4c(&bi);
     line_colnr = (colnr_T)undo_read_4c(&bi);
@@ -3098,7 +3099,7 @@ ex_undolist(exarg_T *eap UNUSED)
     int                mark;
     int                nomark;
     int                changes = 1;
-    int                i;
+    int                len;
 
     /*
      * 1: walk the tree to find all leafs, put the info in "ga".
@@ -3117,18 +3118,20 @@ ex_undolist(exarg_T *eap UNUSED)
        {
            if (ga_grow(&ga, 1) == FAIL)
                break;
-           vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d  ",
+           len = vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d  ",
                                                        uhp->uh_seq, changes);
-           add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
-                                                               uhp->uh_time);
+           add_time(IObuff + len, IOSIZE - len, uhp->uh_time);
+
+           // we have to call STRLEN() here because add_time() does not report
+           // the number of characters added.
+           len += STRLEN(IObuff + len);
            if (uhp->uh_save_nr > 0)
            {
-               while (STRLEN(IObuff) < 33)
-                   STRCAT(IObuff, " ");
-               vim_snprintf_add((char *)IObuff, IOSIZE,
-                                                  "  %3ld", uhp->uh_save_nr);
+               int n = (len >= 33) ? 0 : 33 - len;
+
+               len += vim_snprintf((char *)IObuff + len, IOSIZE - len, "%*.*s  
%3ld", n, n, " ", uhp->uh_save_nr);
            }
-           ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
+           ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strnsave(IObuff, len);
        }
 
        uhp->uh_walk = mark;
@@ -3175,6 +3178,8 @@ ex_undolist(exarg_T *eap UNUSED)
        msg(_("Nothing to undo"));
     else
     {
+       int             i;
+
        sort_strings((char_u **)ga.ga_data, ga.ga_len);
 
        msg_start();
@@ -3481,6 +3486,7 @@ u_clearall(buf_T *buf)
     buf->b_u_numhead = 0;
     buf->b_u_line_ptr.ul_line = NULL;
     buf->b_u_line_ptr.ul_len = 0;
+    buf->b_u_line_ptr.ul_textlen = 0;
     buf->b_u_line_lnum = 0;
 }
 
@@ -3540,6 +3546,7 @@ u_clearline(void)
 
     VIM_CLEAR(curbuf->b_u_line_ptr.ul_line);
     curbuf->b_u_line_ptr.ul_len = 0;
+    curbuf->b_u_line_ptr.ul_textlen = 0;
     curbuf->b_u_line_lnum = 0;
 }
 
diff --git a/src/version.c b/src/version.c
index 7d02e6cf7..7e95a46a7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    843,
 /**/
     842,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1t8mtj-0025Od-2k%40256bit.org.

Raspunde prin e-mail lui