patch 9.1.0851: too many strlen() calls in getchar.c

Problem:  too many strlen() calls in getchar.c
Solution: refactor code and reduce strlen() calls
          (John Marriott)

closes: #16017

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/getchar.c b/src/getchar.c
index 96e180f..c1628ee 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -36,9 +36,9 @@
 
 #define MINIMAL_SIZE 20			// minimal size for b_str
 
-static buffheader_T redobuff = {{NULL, {NUL}}, NULL, 0, 0};
-static buffheader_T old_redobuff = {{NULL, {NUL}}, NULL, 0, 0};
-static buffheader_T recordbuff = {{NULL, {NUL}}, NULL, 0, 0};
+static buffheader_T redobuff = {{NULL, 0, {NUL}}, NULL, 0, 0, FALSE};
+static buffheader_T old_redobuff = {{NULL, 0, {NUL}}, NULL, 0, 0, FALSE};
+static buffheader_T recordbuff = {{NULL, 0, {NUL}}, NULL, 0, 0, FALSE};
 
 static int typeahead_char = 0;		// typeahead char that's not flushed
 
@@ -129,17 +129,19 @@
     static char_u *
 get_buffcont(
     buffheader_T	*buffer,
-    int			dozero)	    // count == zero is not an error
+    int			dozero,	    // count == zero is not an error
+    size_t		*len)	    // the length of the returned buffer
 {
     long_u	    count = 0;
     char_u	    *p = NULL;
     char_u	    *p2;
     char_u	    *str;
     buffblock_T *bp;
+    size_t	    i = 0;
 
     // compute the total length of the string
     for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
-	count += (long_u)STRLEN(bp->b_str);
+	count += (long_u)bp->b_strlen;
 
     if ((count > 0 || dozero) && (p = alloc(count + 1)) != NULL)
     {
@@ -148,7 +150,12 @@
 	    for (str = bp->b_str; *str; )
 		*p2++ = *str++;
 	*p2 = NUL;
+	i = (size_t)(p2 - p);
     }
+
+    if (len != NULL)
+	*len = i;
+
     return p;
 }
 
@@ -163,14 +170,13 @@
     char_u	*p;
     size_t	len;
 
-    p = get_buffcont(&recordbuff, TRUE);
+    p = get_buffcont(&recordbuff, TRUE, &len);
     free_buff(&recordbuff);
 
     /*
      * Remove the characters that were added the last time, these must be the
      * (possibly mapped) characters that stopped the recording.
      */
-    len = STRLEN(p);
     if (len >= last_recorded_len)
     {
 	len -= last_recorded_len;
@@ -194,7 +200,7 @@
     char_u *
 get_inserted(void)
 {
-    return get_buffcont(&redobuff, FALSE);
+    return get_buffcont(&redobuff, FALSE, NULL);
 }
 
 /*
@@ -207,9 +213,6 @@
     char_u		*s,
     long		slen)	// length of "s" or -1
 {
-    buffblock_T *p;
-    long_u	    len;
-
     if (slen < 0)
 	slen = (long)STRLEN(s);
     if (slen == 0)				// don't add empty strings
@@ -217,8 +220,8 @@
 
     if (buf->bh_first.b_next == NULL)	// first add to list
     {
-	buf->bh_space = 0;
 	buf->bh_curr = &(buf->bh_first);
+	buf->bh_create_newblock = TRUE;
     }
     else if (buf->bh_curr == NULL)	// buffer has already been read
     {
@@ -226,19 +229,26 @@
 	return;
     }
     else if (buf->bh_index != 0)
+    {
 	mch_memmove(buf->bh_first.b_next->b_str,
 		    buf->bh_first.b_next->b_str + buf->bh_index,
-		    STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
+		    (buf->bh_first.b_next->b_strlen - buf->bh_index) + 1);
+	buf->bh_first.b_next->b_strlen -= buf->bh_index;
+	buf->bh_space += buf->bh_index;
+    }
     buf->bh_index = 0;
 
-    if (buf->bh_space >= (int)slen)
+    if (!buf->bh_create_newblock && buf->bh_space >= (int)slen)
     {
-	len = (long_u)STRLEN(buf->bh_curr->b_str);
-	vim_strncpy(buf->bh_curr->b_str + len, s, (size_t)slen);
+	vim_strncpy(buf->bh_curr->b_str + buf->bh_curr->b_strlen, s, (size_t)slen);
+	buf->bh_curr->b_strlen += slen;
 	buf->bh_space -= slen;
     }
     else
     {
+	long_u	    len;
+	buffblock_T *p;
+
 	if (slen < MINIMAL_SIZE)
 	    len = MINIMAL_SIZE;
 	else
@@ -246,8 +256,10 @@
 	p = alloc(offsetof(buffblock_T, b_str) + len + 1);
 	if (p == NULL)
 	    return; // no space, just forget it
-	buf->bh_space = (int)(len - slen);
 	vim_strncpy(p->b_str, s, (size_t)slen);
+	p->b_strlen = slen;
+	buf->bh_space = (int)(len - slen);
+	buf->bh_create_newblock = FALSE;
 
 	p->b_next = buf->bh_curr->b_next;
 	buf->bh_curr->b_next = p;
@@ -262,15 +274,13 @@
     static void
 delete_buff_tail(buffheader_T *buf, int slen)
 {
-    int len;
-
     if (buf->bh_curr == NULL)
 	return;  // nothing to delete
-    len = (int)STRLEN(buf->bh_curr->b_str);
-    if (len < slen)
+    if (buf->bh_curr->b_strlen < (size_t)slen)
 	return;
 
-    buf->bh_curr->b_str[len - slen] = NUL;
+    buf->bh_curr->b_str[buf->bh_curr->b_strlen - (size_t)slen] = NUL;
+    buf->bh_curr->b_strlen -= slen;
     buf->bh_space += slen;
 }
 
@@ -281,9 +291,10 @@
 add_num_buff(buffheader_T *buf, long n)
 {
     char_u	number[32];
+    int		numberlen;
 
-    sprintf((char *)number, "%ld", n);
-    add_buff(buf, number, -1L);
+    numberlen = vim_snprintf((char *)number, sizeof(number), "%ld", n);
+    add_buff(buf, number, (long)numberlen);
 }
 
 /*
@@ -297,6 +308,7 @@
     int		len;
     int		i;
     char_u	temp[4];
+    long	templen;
 
     if (IS_SPECIAL(c))
 	len = 1;
@@ -314,6 +326,7 @@
 	    temp[1] = K_SECOND(c);
 	    temp[2] = K_THIRD(c);
 	    temp[3] = NUL;
+	    templen = 3;
 	}
 #ifdef FEAT_GUI
 	else if (c == CSI)
@@ -323,22 +336,24 @@
 	    temp[1] = KS_EXTRA;
 	    temp[2] = (int)KE_CSI;
 	    temp[3] = NUL;
+	    templen = 3;
 	}
 #endif
 	else
 	{
 	    temp[0] = c;
 	    temp[1] = NUL;
+	    templen = 1;
 	}
-	add_buff(buf, temp, -1L);
+	add_buff(buf, temp, templen);
     }
 }
 
 // First read ahead buffer. Used for translated commands.
-static buffheader_T readbuf1 = {{NULL, {NUL}}, NULL, 0, 0};
+static buffheader_T readbuf1 = {{NULL, 0, {NUL}}, NULL, 0, 0, FALSE};
 
 // Second read ahead buffer. Used for redo.
-static buffheader_T readbuf2 = {{NULL, {NUL}}, NULL, 0, 0};
+static buffheader_T readbuf2 = {{NULL, 0, {NUL}}, NULL, 0, 0, FALSE};
 
 /*
  * Get one byte from the read buffers.  Use readbuf1 one first, use readbuf2
@@ -390,12 +405,12 @@
     if (readbuf1.bh_first.b_next != NULL)
     {
 	readbuf1.bh_curr = &(readbuf1.bh_first);
-	readbuf1.bh_space = 0;
+	readbuf1.bh_create_newblock = TRUE;		// force a new block to be created (see add_buff())
     }
     if (readbuf2.bh_first.b_next != NULL)
     {
 	readbuf2.bh_curr = &(readbuf2.bh_first);
-	readbuf2.bh_space = 0;
+	readbuf2.bh_create_newblock = TRUE;		// force a new block to be created (see add_buff())
     }
 }
 
@@ -529,6 +544,7 @@
 saveRedobuff(save_redo_T *save_redo)
 {
     char_u	*s;
+    size_t	slen;
 
     save_redo->sr_redobuff = redobuff;
     redobuff.bh_first.b_next = NULL;
@@ -536,11 +552,11 @@
     old_redobuff.bh_first.b_next = NULL;
 
     // Make a copy, so that ":normal ." in a function works.
-    s = get_buffcont(&save_redo->sr_redobuff, FALSE);
+    s = get_buffcont(&save_redo->sr_redobuff, FALSE, &slen);
     if (s == NULL)
 	return;
 
-    add_buff(&redobuff, s, -1L);
+    add_buff(&redobuff, s, (long)slen);
     vim_free(s);
 }
 
@@ -2219,13 +2235,15 @@
 	// character.  Only use it when changed, otherwise continue with the
 	// original character.
 	char_u *v_char;
+	size_t	v_charlen;
 
 	v_char = get_vim_var_str(VV_CHAR);
+	v_charlen = STRLEN(v_char);
 
 	// Convert special bytes when it is special string.
-	if (STRLEN(v_char) >= 3 && v_char[0] == K_SPECIAL)
+	if (v_charlen >= 3 && v_char[0] == K_SPECIAL)
 	    res = TERMCAP2KEY(v_char[1], v_char[2]);
-	else if (STRLEN(v_char) > 0)
+	else if (v_charlen > 0)
 	    res = PTR2CHAR(v_char);
     }
 
@@ -2438,9 +2456,9 @@
 	    i += (*mb_char2bytes)(n, temp + i);
 	else
 	    temp[i++] = n;
-	temp[i++] = NUL;
+	temp[i] = NUL;
 	rettv->v_type = VAR_STRING;
-	rettv->vval.v_string = vim_strsave(temp);
+	rettv->vval.v_string = vim_strnsave(temp, i);
 
 	if (is_mouse_key(n))
 	{
@@ -2507,9 +2525,9 @@
 	else
 	    temp[i++] = n;
     }
-    temp[i++] = NUL;
+    temp[i] = NUL;
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = vim_strsave(temp);
+    rettv->vval.v_string = vim_strnsave(temp, i);
 }
 
 /*
@@ -3256,7 +3274,7 @@
 		buf[1] = KS_EXTRA;
 		buf[2] = KE_IGNORE;
 		buf[3] = NUL;
-		map_str = vim_strsave(buf);
+		map_str = vim_strnsave(buf, 3);
 		if (State & MODE_CMDLINE)
 		{
 		    // redraw the command below the error
@@ -4265,6 +4283,7 @@
 may_add_last_used_map_to_redobuff(void)
 {
     char_u  buf[3 + 20];
+    int	    buflen;
     int	    sid = -1;
 
     if (last_used_map != NULL)
@@ -4279,8 +4298,10 @@
     buf[0] = K_SPECIAL;
     buf[1] = KS_EXTRA;
     buf[2] = KE_SID;
-    vim_snprintf((char *)buf + 3, 20, "%d;", sid);
-    add_buff(&redobuff, buf, -1L);
+    buflen = 3;
+
+    buflen += vim_snprintf((char *)buf + 3, 20, "%d;", sid);
+    add_buff(&redobuff, buf, (long)buflen);
 }
 #endif