patch 9.1.1212: too many strlen() calls in edit.c

Problem:  too many strlen() calls in edit.c
Solution: refactor edit.c and remove strlen() calls
          (John Marriott)

This commit attempts to make edit.c more efficient by:

- in truncate_spaces() pass in the length of the string.
- return a string_T from get_last_insert(), so that the length of the
  string is available to the caller.
- refactor stuff_insert():

  - replace calls to stuffReadbuff() (which calls STRLEN() on it's
    string argument) with stuffReadbuffLen() (which gets the length of
    it's string argument passed in).
  - replace call to vim_strrchr() which searches from the start of the
    string with a loop which searches from end of the string to find the
    last ESC character.

- change get_last_insert_save() to call get_last_insert() to get the
  last_insert string (the logic is in one place).

closes: #16863

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/edit.c b/src/edit.c
index f15a19d..a23f66b 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -78,7 +78,7 @@
 static colnr_T	Insstart_blank_vcol;	// vcol for first inserted blank
 static int	update_Insstart_orig = TRUE; // set Insstart_orig to Insstart
 
-static char_u	*last_insert = NULL;	// the text of the previous insert,
+static string_T	last_insert = {NULL, 0};    // the text of the previous insert,
 					// K_SPECIAL and CSI are escaped
 static int	last_insert_skip; // nr of chars in front of previous insert
 static int	new_insert_skip;  // nr of chars in front of current insert
@@ -1809,12 +1809,12 @@
  * MODE_VREPLACE modes.
  */
     void
-truncate_spaces(char_u *line)
+truncate_spaces(char_u *line, size_t len)
 {
     int	    i;
 
     // find start of trailing white space
-    for (i = (int)STRLEN(line) - 1; i >= 0 && VIM_ISWHITE(line[i]); i--)
+    for (i = (int)len - 1; i >= 0 && VIM_ISWHITE(line[i]); i--)
     {
 	if (State & REPLACE_FLAG)
 	    replace_join(0);	    // remove a NUL from the replace stack
@@ -2455,8 +2455,8 @@
     int added = inserted.string == NULL ? 0 : (int)inserted.length - new_insert_skip;
     if (did_restart_edit == 0 || added > 0)
     {
-	vim_free(last_insert);
-	last_insert = inserted.string;
+	vim_free(last_insert.string);
+	last_insert = inserted;			    // structure copy
 	last_insert_skip = added < 0 ? 0 : new_insert_skip;
     }
     else
@@ -2566,18 +2566,22 @@
 {
     char_u	*s;
 
-    vim_free(last_insert);
-    last_insert = alloc(MB_MAXBYTES * 3 + 5);
-    if (last_insert == NULL)
+    vim_free(last_insert.string);
+    last_insert.string = alloc(MB_MAXBYTES * 3 + 5);
+    if (last_insert.string == NULL)
+    {
+	last_insert.length = 0;
 	return;
+    }
 
-    s = last_insert;
+    s = last_insert.string;
     // Use the CTRL-V only when entering a special char
     if (c < ' ' || c == DEL)
 	*s++ = Ctrl_V;
     s = add_char2buf(c, s);
     *s++ = ESC;
-    *s++ = NUL;
+    *s = NUL;
+    last_insert.length = (size_t)(s - last_insert.string);
     last_insert_skip = 0;
 }
 
@@ -2585,7 +2589,7 @@
     void
 free_last_insert(void)
 {
-    VIM_CLEAR(last_insert);
+    VIM_CLEAR_STRING(last_insert);
 }
 #endif
 
@@ -2911,13 +2915,11 @@
     long    count,	// Repeat this many times
     int	    no_esc)	// Don't add an ESC at the end
 {
-    char_u	*esc_ptr;
-    char_u	*ptr;
-    char_u	*last_ptr;
-    char_u	last = NUL;
+    string_T	*insert;				    // text to be inserted
+    char_u	last = ' ';
 
-    ptr = get_last_insert();
-    if (ptr == NULL)
+    insert = get_last_insert();
+    if (insert->string == NULL)
     {
 	emsg(_(e_no_inserted_text_yet));
 	return FAIL;
@@ -2926,36 +2928,57 @@
     // may want to stuff the command character, to start Insert mode
     if (c != NUL)
 	stuffcharReadbuff(c);
-    if ((esc_ptr = vim_strrchr(ptr, ESC)) != NULL)
-	*esc_ptr = NUL;	    // remove the ESC
 
-    // when the last char is either "0" or "^" it will be quoted if no ESC
-    // comes after it OR if it will inserted more than once and "ptr"
-    // starts with ^D.	-- Acevedo
-    last_ptr = (esc_ptr ? esc_ptr : ptr + STRLEN(ptr)) - 1;
-    if (last_ptr >= ptr && (*last_ptr == '0' || *last_ptr == '^')
-	    && (no_esc || (*ptr == Ctrl_D && count > 1)))
+    if (insert->length > 0)
     {
-	last = *last_ptr;
-	*last_ptr = NUL;
+	char_u	*p;
+
+	// look for the last ESC in 'insert'
+	for (p = insert->string + (insert->length - 1); p >= insert->string; --p)
+	{
+	    if (*p == ESC)
+	    {
+		insert->length = (size_t)(p - insert->string);
+		break;
+	    }
+	}
+
+	// when the last char is either "0" or "^" it will be quoted if no ESC
+	// comes after it OR if it will insert more than once and "ptr"
+	// starts with ^D.	-- Acevedo
+	if (p >= insert->string
+	    && (*p == '0' || *p == '^')
+	    && (no_esc || (*insert->string == Ctrl_D && count > 1)))
+	{
+	    last = *p;
+	    --insert->length;
+	}
     }
 
     do
     {
-	stuffReadbuff(ptr);
+	stuffReadbuffLen(insert->string, insert->length);
 	// a trailing "0" is inserted as "<C-V>048", "^" as "<C-V>^"
-	if (last)
-	    stuffReadbuff(
-		       (char_u *)(last == '0' ? "\026\060\064\070" : "\026^"));
+	switch (last)
+	{
+	case '0':
+#define TEXT_TO_INSERT "\026\060\064\070"
+	    stuffReadbuffLen((char_u *)TEXT_TO_INSERT, STRLEN_LITERAL(TEXT_TO_INSERT));
+#undef TEXT_TO_INSERT
+	    break;
+
+	case '^':
+#define TEXT_TO_INSERT "\026^"
+	    stuffReadbuffLen((char_u *)TEXT_TO_INSERT, STRLEN_LITERAL(TEXT_TO_INSERT));
+#undef TEXT_TO_INSERT
+	    break;
+
+	default:
+	    break;
+	}
     }
     while (--count > 0);
 
-    if (last)
-	*last_ptr = last;
-
-    if (esc_ptr != NULL)
-	*esc_ptr = ESC;	    // put the ESC back
-
     // may want to stuff a trailing ESC, to get out of Insert mode
     if (!no_esc)
 	stuffcharReadbuff(ESC);
@@ -2963,12 +2986,23 @@
     return OK;
 }
 
-    char_u *
+    string_T *
 get_last_insert(void)
 {
-    if (last_insert == NULL)
-	return NULL;
-    return last_insert + last_insert_skip;
+    static string_T insert = {NULL, 0};
+
+    if (last_insert.string == NULL)
+    {
+	insert.string = NULL;
+	insert.length = 0;
+    }
+    else
+    {
+	insert.string = last_insert.string + last_insert_skip;
+	insert.length = (size_t)(last_insert.length - last_insert_skip);
+    }
+
+    return &insert;
 }
 
 /*
@@ -2978,18 +3012,22 @@
     char_u *
 get_last_insert_save(void)
 {
+    string_T	*insert = get_last_insert();
     char_u	*s;
-    int		len;
 
-    if (last_insert == NULL)
+    if (insert->string == NULL)
 	return NULL;
-    len = (int)STRLEN(last_insert + last_insert_skip);
-    s = vim_strnsave(last_insert + last_insert_skip, len);
+    s = vim_strnsave(insert->string, insert->length);
     if (s == NULL)
 	return NULL;
 
-    if (len > 0 && s[len - 1] == ESC)	// remove trailing ESC
-	s[len - 1] = NUL;
+    if (insert->length > 0)
+    {
+	// remove trailing ESC
+	--insert->length;
+	if (s[insert->length] == ESC)
+	    s[insert->length] = NUL;
+    }
     return s;
 }
 
@@ -3837,7 +3875,7 @@
 		buf[1] = KS_MODIFIER;
 		buf[2] = mod_mask;
 		buf[3] = NUL;
-		stuffReadbuff(buf);
+		stuffReadbuffLen(buf, 3L);
 	    }
 	    stuffcharReadbuff(c);
 	    return TRUE;
@@ -5414,6 +5452,7 @@
 {
     char_u	*res;
     char_u	buf[MB_MAXBYTES + 1];
+    size_t	buflen;
     int		save_State = State;
 
     // Return quickly when there is nothing to do.
@@ -5424,16 +5463,17 @@
 	return NULL;
 
     if (has_mbyte)
-	buf[(*mb_char2bytes)(c, buf)] = NUL;
+	buflen = (*mb_char2bytes)(c, buf);
     else
     {
 	buf[0] = c;
-	buf[1] = NUL;
+	buflen = 1;
     }
+    buf[buflen] = NUL;
 
     // Lock the text to avoid weird things from happening.
     ++textlock;
-    set_vim_var_string(VV_CHAR, buf, -1);  // set v:char
+    set_vim_var_string(VV_CHAR, buf, buflen);  // set v:char
 
     res = NULL;
     if (ins_apply_autocmds(EVENT_INSERTCHARPRE))