From 08e3266ac3b607522a5cf7f4132ccb2c0ae18472 Mon Sep 17 00:00:00 2001
From: basilisk0315 <basilisk@internode.on.net>
Date: Mon, 28 Oct 2024 10:46:14 +1100
Subject: [PATCH] Refactor register.c to remove calls to STRLEN()

---
 src/clipboard.c             |   8 +-
 src/ops.c                   |   4 +
 src/register.c              | 289 +++++++++++++++++++++---------------
 src/structs.h               |   9 +-
 src/testdir/test_visual.vim |  10 +-
 src/viminfo.c               |  52 ++++---
 6 files changed, 227 insertions(+), 145 deletions(-)

diff --git a/src/clipboard.c b/src/clipboard.c
index 8b9850e44e..6c8b60c2e7 100644
--- a/src/clipboard.c
+++ b/src/clipboard.c
@@ -2129,7 +2129,7 @@ clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
 	return -1;
 
     for (i = 0; i < y_ptr->y_size; i++)
-	*len += (long_u)STRLEN(y_ptr->y_array[i]) + eolsize;
+	*len += (long_u)y_ptr->y_array[i].length + eolsize;
 
     // Don't want newline character at end of last line if we're in MCHAR mode.
     if (y_ptr->y_type == MCHAR && *len >= eolsize)
@@ -2141,9 +2141,9 @@ clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
     lnum = 0;
     for (i = 0, j = 0; i < (int)*len; i++, j++)
     {
-	if (y_ptr->y_array[lnum][j] == '\n')
+	if (y_ptr->y_array[lnum].string[j] == '\n')
 	    p[i] = NUL;
-	else if (y_ptr->y_array[lnum][j] == NUL)
+	else if (y_ptr->y_array[lnum].string[j] == NUL)
 	{
 # ifdef USE_CRNL
 	    p[i++] = '\r';
@@ -2153,7 +2153,7 @@ clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
 	    j = -1;
 	}
 	else
-	    p[i] = y_ptr->y_array[lnum][j];
+	    p[i] = y_ptr->y_array[lnum].string[j];
     }
     return y_ptr->y_type;
 }
diff --git a/src/ops.c b/src/ops.c
index eb8f64c1fb..a544ff59ea 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -2504,7 +2504,11 @@ charwise_block_prep(
 	}
     }
     if (endcol == MAXCOL)
+    {
+	// the selection ends at the end of the line
 	endcol = ml_get_len(lnum);
+	inclusive = 0;		// make sure we don't go over the end of the line
+    }
     if (startcol > endcol || bdp->is_oneChar)
 	bdp->textlen = 0;
     else
diff --git a/src/register.c b/src/register.c
index 47ed218462..1986837180 100644
--- a/src/register.c
+++ b/src/register.c
@@ -30,8 +30,7 @@ static yankreg_T	*y_previous = NULL; // ptr to last written yankreg
 
 static int	stuff_yank(int, char_u *);
 static void	put_reedit_in_typebuf(int silent);
-static int	put_in_typebuf(char_u *s, int esc, int colon,
-								 int silent);
+static int	put_in_typebuf(char_u *s, int esc, int colon, int silent);
 static int	yank_copy_line(struct block_def *bd, long y_idx, int exclude_trailing_space);
 #ifdef FEAT_CLIPBOARD
 static void	copy_yank_reg(yankreg_T *reg);
@@ -260,6 +259,7 @@ get_yank_register(int regname, int writing)
     y_current = &(y_regs[i]);
     if (writing)	// remember the register we write into for do_put()
 	y_previous = y_current;
+
     return ret;
 }
 
@@ -304,11 +304,14 @@ get_register(
 	if (reg->y_size == 0 || y_current->y_array == NULL)
 	    reg->y_array = NULL;
 	else
-	    reg->y_array = ALLOC_MULT(char_u *, reg->y_size);
+	    reg->y_array = ALLOC_MULT(string_T, reg->y_size);
 	if (reg->y_array != NULL)
 	{
 	    for (i = 0; i < reg->y_size; ++i)
-		reg->y_array[i] = vim_strsave(y_current->y_array[i]);
+	    {
+		reg->y_array[i].string = vim_strnsave(y_current->y_array[i].string, y_current->y_array[i].length);
+		reg->y_array[i].length = y_current->y_array[i].length;
+	    }
 	}
     }
     else
@@ -425,8 +428,7 @@ do_record(int c)
     static int
 stuff_yank(int regname, char_u *p)
 {
-    char_u	*lp;
-    char_u	**pp;
+    size_t	plen;
 
     // check for read-only register
     if (regname != 0 && !valid_yank_reg(regname, TRUE))
@@ -439,31 +441,40 @@ stuff_yank(int regname, char_u *p)
 	vim_free(p);
 	return OK;
     }
+
+    plen = STRLEN(p);
     get_yank_register(regname, TRUE);
     if (y_append && y_current->y_array != NULL)
     {
+	string_T    *pp;
+	char_u	    *tmp;
+	size_t	    tmplen;
+
 	pp = &(y_current->y_array[y_current->y_size - 1]);
-	lp = alloc(STRLEN(*pp) + STRLEN(p) + 1);
-	if (lp == NULL)
+	tmplen = pp->length + plen;
+	tmp = alloc(tmplen + 1);
+	if (tmp == NULL)
 	{
 	    vim_free(p);
 	    return FAIL;
 	}
-	STRCPY(lp, *pp);
-	STRCAT(lp, p);
+	STRCPY(tmp, pp->string);
+	STRCPY(tmp + pp->length, p);
 	vim_free(p);
-	vim_free(*pp);
-	*pp = lp;
+	vim_free(pp->string);
+	pp->string = tmp;
+	pp->length = tmplen;
     }
     else
     {
 	free_yank_all();
-	if ((y_current->y_array = ALLOC_ONE(char_u *)) == NULL)
+	if ((y_current->y_array = ALLOC_ONE(string_T)) == NULL)
 	{
 	    vim_free(p);
 	    return FAIL;
 	}
-	y_current->y_array[0] = p;
+	y_current->y_array[0].string = p;
+	y_current->y_array[0].length = plen;
 	y_current->y_size = 1;
 	y_current->y_type = MCHAR;  // used to be MLINE, why?
 #ifdef FEAT_VIMINFO
@@ -509,7 +520,7 @@ set_execreg_lastc(int lastc)
  * processed next is returned in idx.
  */
     static char_u *
-execreg_line_continuation(char_u **lines, long *idx)
+execreg_line_continuation(string_T *lines, long *idx)
 {
     garray_T	ga;
     long	i = *idx;
@@ -526,17 +537,17 @@ execreg_line_continuation(char_u **lines, long *idx)
     // command.
     while (--i > 0)
     {
-	p = skipwhite(lines[i]);
+	p = skipwhite(lines[i].string);
 	if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' '))
 	    break;
     }
     cmd_start = i;
 
     // join all the lines
-    ga_concat(&ga, lines[cmd_start]);
+    ga_concat(&ga, lines[cmd_start].string);
     for (j = cmd_start + 1; j <= cmd_end; j++)
     {
-	p = skipwhite(lines[j]);
+	p = skipwhite(lines[j].string);
 	if (*p == '\\')
 	{
 	    // Adjust the growsize to the current length to
@@ -552,7 +563,7 @@ execreg_line_continuation(char_u **lines, long *idx)
 	}
     }
     ga_append(&ga, NUL);
-    str = vim_strsave(ga.ga_data);
+    str = vim_strnsave(ga.ga_data, ga.ga_len);
     ga_clear(&ga);
 
     *idx = i;
@@ -677,7 +688,7 @@ do_execreg(
 	    }
 
 	    // Handle line-continuation for :@<register>
-	    str = y_current->y_array[i];
+	    str = y_current->y_array[i].string;
 	    if (colon && i > 0)
 	    {
 		p = skipwhite(str);
@@ -834,7 +845,7 @@ insert_reg(
 			pos_T curpos;
 			if (u_save_cursor() == FAIL)
 			    return FAIL;
-			del_chars((long)mb_charlen(y_current->y_array[0]), TRUE);
+			del_chars((long)mb_charlen(y_current->y_array[0].string), TRUE);
 			curpos = curwin->w_cursor;
 			if (oneright() == FAIL)
 			    // hit end of line, need to put forward (after the current position)
@@ -847,7 +858,7 @@ insert_reg(
 		    do_put(regname, NULL, dir, 1L, PUT_CURSEND);
 		}
 		else
-		    stuffescaped(y_current->y_array[i], literally);
+		    stuffescaped(y_current->y_array[i].string, literally);
 		// Insert a newline between lines and after last line if
 		// y_type is MLINE.
 		if (y_current->y_type == MLINE || i < y_current->y_size - 1)
@@ -972,7 +983,7 @@ cmdline_paste_reg(
 
     for (i = 0; i < y_current->y_size; ++i)
     {
-	cmdline_paste_str(y_current->y_array[i], literally);
+	cmdline_paste_str(y_current->y_array[i].string, literally);
 
 	// Insert ^M between lines and after last line if type is MLINE.
 	// Don't do this when "remcr" is TRUE.
@@ -1029,7 +1040,7 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
 
     // yanked text contents
     for (n = 0; n < reg->y_size; n++)
-	list_append_string(list, reg->y_array[n], -1);
+	list_append_string(list, reg->y_array[n].string, -1);
     list->lv_lock = VAR_FIXED;
     (void)dict_add_list(v_event, "regcontents", list);
 
@@ -1118,7 +1129,10 @@ free_yank(long n)
     long	    i;
 
     for (i = n; --i >= 0; )
-	vim_free(y_current->y_array[i]);
+    {
+	vim_free(y_current->y_array[i].string);
+	y_current->y_array[i].length = 0;
+    }
     VIM_CLEAR(y_current->y_array);
 }
 
@@ -1142,9 +1156,7 @@ op_yank(oparg_T *oap, int deleting, int mess)
     long		y_idx;		// index in y_array[]
     yankreg_T		*curr;		// copy of y_current
     yankreg_T		newreg;		// new yank register when appending
-    char_u		**new_ptr;
     linenr_T		lnum;		// current line number
-    long		j;
     int			yanktype = oap->motion_type;
     long		yanklines = oap->line_count;
     linenr_T		yankendlnum = oap->end.lnum;
@@ -1153,8 +1165,9 @@ op_yank(oparg_T *oap, int deleting, int mess)
 #if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
     int			did_star = FALSE;
 #endif
+    colnr_T		len;
+    int			i;
 
-				    // check for read-only register
     if (oap->regname != 0 && !valid_yank_reg(oap->regname, TRUE))
     {
 	beep_flush();
@@ -1198,12 +1211,14 @@ op_yank(oparg_T *oap, int deleting, int mess)
     y_current->y_size = yanklines;
     y_current->y_type = yanktype;   // set the yank register type
     y_current->y_width = 0;
-    y_current->y_array = lalloc_clear(sizeof(char_u *) * yanklines, TRUE);
+    y_current->y_array = lalloc_clear(sizeof(string_T) * yanklines, TRUE);
     if (y_current->y_array == NULL)
     {
 	y_current = curr;
 	return FAIL;
     }
+    for (i = 0; i < y_current->y_size; ++i)
+	y_current->y_array[i].length = 0;
 #ifdef FEAT_VIMINFO
     y_current->y_time_set = vim_time();
 #endif
@@ -1232,9 +1247,11 @@ op_yank(oparg_T *oap, int deleting, int mess)
 		break;
 
 	    case MLINE:
-		if ((y_current->y_array[y_idx] =
-					    vim_strsave(ml_get(lnum))) == NULL)
+		len = ml_get_len(lnum);
+		if ((y_current->y_array[y_idx].string =
+					    vim_strnsave(ml_get(lnum), len)) == NULL)
 		    goto fail;
+		y_current->y_array[y_idx].length = len;
 		break;
 
 	    case MCHAR:
@@ -1250,7 +1267,10 @@ op_yank(oparg_T *oap, int deleting, int mess)
 
     if (curr != y_current)	// append the new block to the old block
     {
-	new_ptr = ALLOC_MULT(char_u *, curr->y_size + y_current->y_size);
+	string_T *new_ptr;
+	long j;
+
+	new_ptr = ALLOC_MULT(string_T, curr->y_size + y_current->y_size);
 	if (new_ptr == NULL)
 	    goto fail;
 	for (j = 0; j < curr->y_size; ++j)
@@ -1268,18 +1288,22 @@ op_yank(oparg_T *oap, int deleting, int mess)
 	// the new block, unless being Vi compatible.
 	if (curr->y_type == MCHAR && vim_strchr(p_cpo, CPO_REGAPPEND) == NULL)
 	{
-	    pnew = alloc(STRLEN(curr->y_array[curr->y_size - 1])
-					  + STRLEN(y_current->y_array[0]) + 1);
+	    pnew = alloc(curr->y_array[curr->y_size - 1].length + y_current->y_array[0].length + 1);
 	    if (pnew == NULL)
 	    {
 		y_idx = y_current->y_size - 1;
 		goto fail;
 	    }
-	    STRCPY(pnew, curr->y_array[--j]);
-	    STRCAT(pnew, y_current->y_array[0]);
-	    vim_free(curr->y_array[j]);
-	    vim_free(y_current->y_array[0]);
-	    curr->y_array[j++] = pnew;
+
+	    --j;
+	    STRCPY(pnew, curr->y_array[j].string);
+	    STRCPY(pnew + curr->y_array[j].length, y_current->y_array[0].string);
+	    vim_free(curr->y_array[j].string);
+	    curr->y_array[j].string = pnew;
+	    curr->y_array[j].length = curr->y_array[j].length + y_current->y_array[0].length;
+	    ++j;
+	    vim_free(y_current->y_array[0].string);
+	    y_current->y_array[0].length = 0;
 	    y_idx = 1;
 	}
 	else
@@ -1417,7 +1441,8 @@ yank_copy_line(struct block_def *bd, long y_idx, int exclude_trailing_space)
     if ((pnew = alloc(bd->startspaces + bd->endspaces + bd->textlen + 1))
 								      == NULL)
 	return FAIL;
-    y_current->y_array[y_idx] = pnew;
+
+    y_current->y_array[y_idx].string = pnew;
     vim_memset(pnew, ' ', (size_t)bd->startspaces);
     pnew += bd->startspaces;
     mch_memmove(pnew, bd->textstart, (size_t)bd->textlen);
@@ -1431,10 +1456,13 @@ yank_copy_line(struct block_def *bd, long y_idx, int exclude_trailing_space)
 	while (s > 0 && VIM_ISWHITE(*(bd->textstart + s - 1)))
 	{
 	    s = s - (*mb_head_off)(bd->textstart, bd->textstart + s - 1) - 1;
-	    pnew--;
+	    --pnew;
 	}
     }
     *pnew = NUL;
+
+    y_current->y_array[y_idx].length = (size_t)(pnew - y_current->y_array[y_idx].string);
+
     return OK;
 }
 
@@ -1452,17 +1480,20 @@ copy_yank_reg(yankreg_T *reg)
     free_yank_all();
     *y_current = *curr;
     y_current->y_array = lalloc_clear(
-				    sizeof(char_u *) * y_current->y_size, TRUE);
+				    sizeof(string_T) * y_current->y_size, TRUE);
     if (y_current->y_array == NULL)
 	y_current->y_size = 0;
     else
 	for (j = 0; j < y_current->y_size; ++j)
-	    if ((y_current->y_array[j] = vim_strsave(curr->y_array[j])) == NULL)
+	{
+	    if ((y_current->y_array[j].string = vim_strnsave(curr->y_array[j].string, curr->y_array[j].length)) == NULL)
 	    {
 		free_yank(j);
 		y_current->y_size = 0;
 		break;
 	    }
+	    y_current->y_array[j].length = curr->y_array[j].length;
+	}
     y_current = curr;
 }
 #endif
@@ -1484,7 +1515,8 @@ do_put(
     int		flags)
 {
     char_u	*ptr;
-    char_u	*newp, *oldp;
+    char_u	*newp;
+    char_u	*oldp;
     int		yanklen;
     int		totlen = 0;		// init for gcc
     linenr_T	lnum;
@@ -1495,23 +1527,11 @@ do_put(
     int		oldlen;
     long	y_width = 0;
     colnr_T	vcol;
-    int		delcount;
-    int		incr = 0;
-    long	j;
-    struct block_def bd;
-    char_u	**y_array = NULL;
+    string_T	*y_array = NULL;
     yankreg_T	*y_current_used = NULL;
     long	nr_lines = 0;
-    pos_T	new_cursor;
-    int		indent;
-    int		orig_indent = 0;	// init for gcc
-    int		indent_diff = 0;	// init for gcc
-    int		first_indent = TRUE;
-    int		lendiff = 0;
-    pos_T	old_pos;
-    char_u	*insert_string = NULL;
+    string_T	insert_string;
     int		allocated = FALSE;
-    long	cnt;
     pos_T	orig_start = curbuf->b_op_start;
     pos_T	orig_end = curbuf->b_op_end;
     unsigned int cur_ve_flags = get_ve_flags();
@@ -1522,9 +1542,6 @@ do_put(
     (void)may_get_selection(regname);
 #endif
 
-    if (flags & PUT_FIXINDENT)
-	orig_indent = get_indent();
-
     curbuf->b_op_start = curwin->w_cursor;	// default for '[ mark
     curbuf->b_op_end = curwin->w_cursor;	// default for '] mark
 
@@ -1546,10 +1563,11 @@ do_put(
     // For special registers '%' (file name), '#' (alternate file name) and
     // ':' (last command line), etc. we have to create a fake yank register.
     // For compiled code "expr_result" holds the expression result.
+    insert_string.string = NULL;
     if (regname == '=' && expr_result != NULL)
-	insert_string = expr_result;
-    else if (get_spec_reg(regname, &insert_string, &allocated, TRUE)
-		&& insert_string == NULL)
+	insert_string.string = expr_result;
+    else if (get_spec_reg(regname, &insert_string.string, &allocated, TRUE)
+		&& insert_string.string == NULL)
 	return;
 
     // Autocommands may be executed when saving lines for undo.  This might
@@ -1557,41 +1575,57 @@ do_put(
     if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL)
 	goto end;
 
-    if (insert_string != NULL)
+    if (insert_string.string != NULL)
     {
+	insert_string.length = STRLEN(insert_string.string);
+
 	y_type = MCHAR;
 #ifdef FEAT_EVAL
 	if (regname == '=')
 	{
+	    size_t  ptrlen;
+	    char_u  *tmp;
+
 	    // For the = register we need to split the string at NL
 	    // characters.
 	    // Loop twice: count the number of lines and save them.
 	    for (;;)
 	    {
 		y_size = 0;
-		ptr = insert_string;
+		ptr = insert_string.string;
+		ptrlen = insert_string.length;
 		while (ptr != NULL)
 		{
 		    if (y_array != NULL)
-			y_array[y_size] = ptr;
+			y_array[y_size].string = ptr;
 		    ++y_size;
-		    ptr = vim_strchr(ptr, '\n');
-		    if (ptr != NULL)
+		    tmp = vim_strchr(ptr, '\n');
+		    if (tmp == NULL)
 		    {
 			if (y_array != NULL)
-			    *ptr = NUL;
-			++ptr;
+			    y_array[y_size - 1].length = ptrlen;
+		    }
+		    else
+		    {
+			if (y_array != NULL)
+			{
+			    *tmp = NUL;
+			    y_array[y_size - 1].length = (size_t)(tmp - ptr);
+			    ptrlen -= y_array[y_size - 1].length + 1;
+			}
+			++tmp;
 			// A trailing '\n' makes the register linewise.
-			if (*ptr == NUL)
+			if (*tmp == NUL)
 			{
 			    y_type = MLINE;
 			    break;
 			}
 		    }
+		    ptr = tmp;
 		}
 		if (y_array != NULL)
 		    break;
-		y_array = ALLOC_MULT(char_u *, y_size);
+		y_array = ALLOC_MULT(string_T, y_size);
 		if (y_array == NULL)
 		    goto end;
 	    }
@@ -1618,16 +1652,19 @@ do_put(
     {
 	if (flags & PUT_LINE_SPLIT)
 	{
+	    char_u *p_orig;
 	    char_u *p;
+	    size_t plen;
 
 	    // "p" or "P" in Visual mode: split the lines to put the text in
 	    // between.
 	    if (u_save_cursor() == FAIL)
 		goto end;
-	    p = ml_get_cursor();
+	    p_orig = p = ml_get_cursor();
+	    plen = ml_get_cursor_len();
 	    if (dir == FORWARD && *p != NUL)
 		MB_PTR_ADV(p);
-	    ptr = vim_strsave(p);
+	    ptr = vim_strnsave(p, plen - (p - p_orig));
 	    if (ptr == NULL)
 		goto end;
 	    ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, FALSE);
@@ -1700,8 +1737,6 @@ do_put(
     else if (u_save_cursor() == FAIL)
 	goto end;
 
-    yanklen = (int)STRLEN(y_array[0]);
-
     if (cur_ve_flags == VE_ALL && y_type == MCHAR)
     {
 	if (gchar_cursor() == TAB)
@@ -1732,6 +1767,10 @@ do_put(
     // Block mode
     if (y_type == MBLOCK)
     {
+	int	delcount;
+	int	incr = 0;
+	struct block_def bd;
+	long	j;
 	int	c = gchar_cursor();
 	colnr_T	endcol2 = 0;
 
@@ -1830,7 +1869,7 @@ do_put(
 		}
 	    }
 
-	    yanklen = (int)STRLEN(y_array[i]);
+	    yanklen = (int)y_array[i].length;
 
 	    if ((flags & PUT_BLOCK_INNER) == 0)
 	    {
@@ -1838,7 +1877,7 @@ do_put(
 		// block
 		spaces = y_width + 1;
 		init_chartabsize_arg(&cts, curwin, 0, 0,
-						      y_array[i], y_array[i]);
+						      y_array[i].string, y_array[i].string);
 
 		while (*cts.cts_ptr != NUL)
 		{
@@ -1877,7 +1916,7 @@ do_put(
 	    // insert the new text
 	    for (j = 0; j < count; ++j)
 	    {
-		mch_memmove(ptr, y_array[i], (size_t)yanklen);
+		mch_memmove(ptr, y_array[i].string, (size_t)yanklen);
 		ptr += yanklen;
 
 		// insert block's trailing spaces only if there's text behind
@@ -1933,6 +1972,10 @@ do_put(
     }
     else
     {
+	pos_T	new_cursor;
+
+	yanklen = (int)y_array[0].length;
+
 	// Character or Line mode
 	if (y_type == MCHAR)
 	{
@@ -2031,10 +2074,10 @@ do_put(
 		    ptr = newp + col;
 		    for (i = 0; i < count; ++i)
 		    {
-			mch_memmove(ptr, y_array[0], (size_t)yanklen);
+			mch_memmove(ptr, y_array[0].string, (size_t)yanklen);
 			ptr += yanklen;
 		    }
-		    STRMOVE(ptr, oldp + col);
+		    mch_memmove(ptr, oldp + col, (size_t)(oldlen - col) + 1);	    // +1 for NUL
 
 		    // compute the byte offset for the last character
 		    first_byte_off = mb_head_off(newp, ptr - 1);
@@ -2073,7 +2116,17 @@ do_put(
 	else
 	{
 	    linenr_T	new_lnum = new_cursor.lnum;
-	    size_t	len;
+	    int		indent;
+	    int		orig_indent;
+	    int		indent_diff = 0;	// init for gcc
+	    int		first_indent = TRUE;
+	    int		lendiff = 0;
+	    long	cnt;
+
+	    if (flags & PUT_FIXINDENT)
+		orig_indent = get_indent();
+	    else
+		orig_indent = 0;
 
 	    // Insert at least one line.  When y_type is MCHAR, break the first
 	    // line in two.
@@ -2087,12 +2140,12 @@ do_put(
 		    // Then append y_array[0] to first line.
 		    lnum = new_cursor.lnum;
 		    ptr = ml_get(lnum) + col;
-		    totlen = (int)STRLEN(y_array[y_size - 1]);
+		    totlen = (int)y_array[y_size - 1].length;
 		    newp = alloc(ml_get_len(lnum) - col + totlen + 1);
 		    if (newp == NULL)
 			goto error;
-		    STRCPY(newp, y_array[y_size - 1]);
-		    STRCAT(newp, ptr);
+		    STRCPY(newp, y_array[y_size - 1].string);
+		    STRCPY(newp + totlen, ptr);
 		    // insert second line
 		    ml_append(lnum, newp, (colnr_T)0, FALSE);
 		    ++new_lnum;
@@ -2105,7 +2158,7 @@ do_put(
 					    // copy first part of line
 		    mch_memmove(newp, oldp, (size_t)col);
 					    // append to first line
-		    mch_memmove(newp + col, y_array[0], (size_t)(yanklen + 1));
+		    mch_memmove(newp + col, y_array[0].string, (size_t)(yanklen + 1));
 		    ml_replace(lnum, newp, FALSE);
 
 		    curwin->w_cursor.lnum = lnum;
@@ -2116,16 +2169,17 @@ do_put(
 		{
 		    if (y_type != MCHAR || i < y_size - 1)
 		    {
-			if (ml_append(lnum, y_array[i], (colnr_T)0, FALSE)
+			if (ml_append(lnum, y_array[i].string, (colnr_T)0, FALSE)
 								      == FAIL)
 			    goto error;
-			new_lnum++;
+			++new_lnum;
 		    }
-		    lnum++;
+		    ++lnum;
 		    ++nr_lines;
 		    if (flags & PUT_FIXINDENT)
 		    {
-			old_pos = curwin->w_cursor;
+			pos_T	old_pos = curwin->w_cursor;
+
 			curwin->w_cursor.lnum = lnum;
 			ptr = ml_get(lnum);
 			if (cnt == count && i == y_size - 1)
@@ -2133,7 +2187,7 @@ do_put(
 			if (*ptr == '#' && preprocs_left())
 			    indent = 0;     // Leave # lines at start
 			else
-			     if (*ptr == NUL)
+			if (*ptr == NUL)
 			    indent = 0;     // Ignore empty lines
 			else if (first_indent)
 			{
@@ -2184,14 +2238,13 @@ error:
 	    // Put the '] mark on the first byte of the last inserted character.
 	    // Correct the length for change in indent.
 	    curbuf->b_op_end.lnum = new_lnum;
-	    len = STRLEN(y_array[y_size - 1]);
-	    col = (colnr_T)len - lendiff;
+	    col = (colnr_T)y_array[y_size - 1].length - lendiff;
 	    if (col > 1)
 	    {
 		curbuf->b_op_end.col = col - 1;
-		if (len > 0)
-		    curbuf->b_op_end.col -= mb_head_off(y_array[y_size - 1],
-						y_array[y_size - 1] + len - 1);
+		if (y_array[y_size - 1].length > 0)
+		    curbuf->b_op_end.col -= mb_head_off(y_array[y_size - 1].string,
+						y_array[y_size - 1].string + y_array[y_size - 1].length - 1);
 	    }
 	    else
 		curbuf->b_op_end.col = 0;
@@ -2254,7 +2307,7 @@ end:
 	curbuf->b_op_end = orig_end;
     }
     if (allocated)
-	vim_free(insert_string);
+	vim_free(insert_string.string);
     if (regname == '=')
 	vim_free(y_array);
 
@@ -2366,7 +2419,7 @@ ex_display(exarg_T *eap)
 	    int do_show = FALSE;
 
 	    for (j = 0; !do_show && j < yb->y_size; ++j)
-		do_show = !message_filtered(yb->y_array[j]);
+		do_show = !message_filtered(yb->y_array[j].string);
 
 	    if (do_show || yb->y_size == 0)
 	    {
@@ -2386,7 +2439,7 @@ ex_display(exarg_T *eap)
 			msg_puts_attr("^J", attr);
 			n -= 2;
 		    }
-		    for (p = yb->y_array[j];
+		    for (p = yb->y_array[j].string;
 				    *p != NUL && (n -= ptr2cells(p)) >= 0; ++p)
 		    {
 			clen = (*mb_ptr2len)(p);
@@ -2500,7 +2553,7 @@ dnd_yank_drag_data(char_u *str, long len)
     yankreg_T *curr;
 
     curr = y_current;
-    y_current = &y_regs[TILDE_REGISTER];
+    y_current = &y_regs[TILDE_REGISTER].string;
     free_yank_all();
     str_to_reg(y_current, MCHAR, str, len, 0L, FALSE);
     y_current = curr;
@@ -2589,7 +2642,7 @@ getreg_wrap_one_line(char_u *s, int flags)
     char_u *
 get_reg_contents(int regname, int flags)
 {
-    long	i;
+    linenr_T	i;
     char_u	*retval;
     int		allocated;
     long	len;
@@ -2636,7 +2689,7 @@ get_reg_contents(int regname, int flags)
 	if (list == NULL)
 	    return NULL;
 	for (i = 0; i < y_current->y_size; ++i)
-	    if (list_append_string(list, y_current->y_array[i], -1) == FAIL)
+	    if (list_append_string(list, y_current->y_array[i].string, -1) == FAIL)
 		error = TRUE;
 	if (error)
 	{
@@ -2650,9 +2703,10 @@ get_reg_contents(int regname, int flags)
     len = 0;
     for (i = 0; i < y_current->y_size; ++i)
     {
-	len += (long)STRLEN(y_current->y_array[i]);
-	// Insert a newline between lines and after last line if
-	// y_type is MLINE.
+	len += y_current->y_array[i].length;
+
+	// Insert a NL between lines and after the last line if y_type is
+	// MLINE.
 	if (y_current->y_type == MLINE || i < y_current->y_size - 1)
 	    ++len;
     }
@@ -2665,8 +2719,8 @@ get_reg_contents(int regname, int flags)
     len = 0;
     for (i = 0; i < y_current->y_size; ++i)
     {
-	STRCPY(retval + len, y_current->y_array[i]);
-	len += (long)STRLEN(retval + len);
+	STRCPY(retval + len, y_current->y_array[i].string);
+	len += y_current->y_array[i].length;
 
 	// Insert a NL between lines and after the last line if y_type is
 	// MLINE.
@@ -2815,7 +2869,7 @@ write_reg_contents_ex(
 		semsg(_(e_buffer_nr_does_not_exist), (long)num);
 	}
 	else
-	    buf = buflist_findnr(buflist_findpat(str, str + STRLEN(str),
+	    buf = buflist_findnr(buflist_findpat(str, str + len,
 							 TRUE, FALSE, FALSE));
 	if (buf == NULL)
 	    return;
@@ -2877,7 +2931,7 @@ str_to_reg(
     int		append = FALSE;		// append to last line in register
     char_u	*s;
     char_u	**ss;
-    char_u	**pp;
+    string_T	*pp;
     long	maxlen;
 
     if (y_ptr->y_array == NULL)		// NULL means empty register
@@ -2923,7 +2977,7 @@ str_to_reg(
 
     // Allocate an array to hold the pointers to the new register lines.
     // If the register was not empty, move the existing lines to the new array.
-    pp = lalloc_clear((y_ptr->y_size + newlines) * sizeof(char_u *), TRUE);
+    pp = lalloc_clear((y_ptr->y_size + newlines) * sizeof(string_T), TRUE);
     if (pp == NULL)	// out of memory
 	return;
     for (lnum = 0; lnum < y_ptr->y_size; ++lnum)
@@ -2937,7 +2991,8 @@ str_to_reg(
     {
 	for (ss = (char_u **) str; *ss != NULL; ++ss, ++lnum)
 	{
-	    pp[lnum] = vim_strsave(*ss);
+	    pp[lnum].length = STRLEN(*ss);
+	    pp[lnum].string = vim_strnsave(*ss, pp[lnum].length);
 	    if (type == MBLOCK)
 	    {
 		int charlen = mb_string2cells(*ss, -1);
@@ -2966,7 +3021,7 @@ str_to_reg(
 	    if (append)
 	    {
 		--lnum;
-		extra = (int)STRLEN(y_ptr->y_array[lnum]);
+		extra = (int)y_ptr->y_array[lnum].length;
 	    }
 	    else
 		extra = 0;
@@ -2974,14 +3029,16 @@ str_to_reg(
 	    if (s == NULL)
 		break;
 	    if (extra)
-		mch_memmove(s, y_ptr->y_array[lnum], (size_t)extra);
+		mch_memmove(s, y_ptr->y_array[lnum].string, (size_t)extra);
 	    if (append)
-		vim_free(y_ptr->y_array[lnum]);
+		vim_free(y_ptr->y_array[lnum].string);
 	    if (i > 0)
 		mch_memmove(s + extra, str + start, (size_t)i);
 	    extra += i;
 	    s[extra] = NUL;
-	    y_ptr->y_array[lnum++] = s;
+	    y_ptr->y_array[lnum].string = s;
+	    y_ptr->y_array[lnum].length = extra;
+	    lnum++;
 	    while (--extra >= 0)
 	    {
 		if (*s == NUL)
diff --git a/src/structs.h b/src/structs.h
index 39e60a42ea..d11279645d 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -20,6 +20,13 @@ typedef int		colnr_T;
 typedef unsigned short	short_u;
 #endif
 
+// structure to store a string (including it's length)
+typedef struct
+{
+    char_u  *string;		// the string
+    size_t  length;		// length of the string (excluding the NUL)
+} string_T;
+
 /*
  * Position in file or buffer.
  */
@@ -4784,7 +4791,7 @@ struct block_def
 // Each yank register has an array of pointers to lines.
 typedef struct
 {
-    char_u	**y_array;	// pointer to array of line pointers
+    string_T	*y_array;	// pointer to array of string_T structs
     linenr_T	y_size;		// number of lines in y_array
     char_u	y_type;		// MLINE, MCHAR or MBLOCK
     colnr_T	y_width;	// only set if y_type == MBLOCK
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index 3750ebf6b9..3000d1ae9b 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -1882,8 +1882,8 @@ func Test_visual_getregion()
           \ ],
           \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
     call assert_equal([
-          \   [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 4, 0]],
-          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
+          \   [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
           \   [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 2]],
           \ ],
           \ getregionpos(getpos('v'), getpos('.'),
@@ -1894,14 +1894,14 @@ func Test_visual_getregion()
     call assert_equal(['', 'two', 'three  '],
           \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
     call assert_equal([
-          \   [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]],
+          \   [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 3, 0]],
           \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
           \   [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
           \ ],
           \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
     call assert_equal([
-          \   [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 4, 0]],
-          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
+          \   [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
           \   [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 2]],
           \ ],
           \ getregionpos(getpos('v'), getpos('.'),
diff --git a/src/viminfo.c b/src/viminfo.c
index 2196d3c426..6736cd5fc1 100644
--- a/src/viminfo.c
+++ b/src/viminfo.c
@@ -1606,7 +1606,7 @@ finish_viminfo_registers(void)
 	if (y_read_regs[i].y_array != NULL)
 	{
 	    for (j = 0; j < y_read_regs[i].y_size; j++)
-		vim_free(y_read_regs[i].y_array[j]);
+		vim_free(y_read_regs[i].y_array[j].string);
 	    vim_free(y_read_regs[i].y_array);
 	}
     VIM_CLEAR(y_read_regs);
@@ -1622,7 +1622,7 @@ read_viminfo_register(vir_T *virp, int force)
     int		i;
     int		set_prev = FALSE;
     char_u	*str;
-    char_u	**array = NULL;
+    string_T	*array = NULL;
     int		new_type = MCHAR; // init to shut up compiler
     colnr_T	new_width = 0; // init to shut up compiler
     yankreg_T	*y_current_p;
@@ -1665,7 +1665,7 @@ read_viminfo_register(vir_T *virp, int force)
 	// array[] needs to be freed.
 	if (set_prev)
 	    set_y_previous(y_current_p);
-	array = ALLOC_MULT(char_u *, limit);
+	array = ALLOC_MULT(string_T, limit);
 	str = skipwhite(skiptowhite(str));
 	if (STRNCMP(str, "CHAR", 4) == 0)
 	    new_type = MCHAR;
@@ -1685,8 +1685,8 @@ read_viminfo_register(vir_T *virp, int force)
 	{
 	    if (size == limit)
 	    {
-		char_u **new_array = (char_u **)
-					   alloc(limit * 2 * sizeof(char_u *));
+		string_T *new_array = (string_T *)
+					   alloc(limit * 2 * sizeof(string_T));
 
 		if (new_array == NULL)
 		{
@@ -1701,7 +1701,11 @@ read_viminfo_register(vir_T *virp, int force)
 	    }
 	    str = viminfo_readstring(virp, 1, TRUE);
 	    if (str != NULL)
-		array[size++] = str;
+	    {
+		array[size].string = str;
+		array[size].length = STRLEN(str);
+		++size;
+	    }
 	    else
 		// error, don't store the result
 		do_it = FALSE;
@@ -1712,7 +1716,7 @@ read_viminfo_register(vir_T *virp, int force)
     {
 	// free y_array[]
 	for (i = 0; i < y_current_p->y_size; i++)
-	    vim_free(y_current_p->y_array[i]);
+	    vim_free(y_current_p->y_array[i].string);
 	vim_free(y_current_p->y_array);
 
 	y_current_p->y_type = new_type;
@@ -1726,13 +1730,19 @@ read_viminfo_register(vir_T *virp, int force)
 	else
 	{
 	    // Move the lines from array[] to y_array[].
-	    y_current_p->y_array = ALLOC_MULT(char_u *, size);
+	    y_current_p->y_array = ALLOC_MULT(string_T, size);
 	    for (i = 0; i < size; i++)
 	    {
 		if (y_current_p->y_array == NULL)
-		    vim_free(array[i]);
+		{
+		    vim_free(array[i].string);
+		    array[i].length = 0;
+		}
 		else
-		    y_current_p->y_array[i] = array[i];
+		{
+		    y_current_p->y_array[i].string = array[i].string;
+		    y_current_p->y_array[i].length = array[i].length;
+		}
 	    }
 	}
     }
@@ -1740,7 +1750,7 @@ read_viminfo_register(vir_T *virp, int force)
     {
 	// Free array[] if it was filled.
 	for (i = 0; i < size; i++)
-	    vim_free(array[i]);
+	    vim_free(array[i].string);
     }
     vim_free(array);
 
@@ -1804,7 +1814,7 @@ handle_viminfo_register(garray_T *values, int force)
 
     if (y_ptr->y_array != NULL)
 	for (i = 0; i < y_ptr->y_size; i++)
-	    vim_free(y_ptr->y_array[i]);
+	    vim_free(y_ptr->y_array[i].string);
     vim_free(y_ptr->y_array);
 
     if (y_read_regs == NULL)
@@ -1823,7 +1833,7 @@ handle_viminfo_register(garray_T *values, int force)
 	y_ptr->y_array = NULL;
 	return;
     }
-    y_ptr->y_array = ALLOC_MULT(char_u *, linecount);
+    y_ptr->y_array = ALLOC_MULT(string_T, linecount);
     if (y_ptr->y_array == NULL)
     {
 	y_ptr->y_size = 0; // ensure object state is consistent
@@ -1833,7 +1843,8 @@ handle_viminfo_register(garray_T *values, int force)
     {
 	if (vp[i + 6].bv_allocated)
 	{
-	    y_ptr->y_array[i] = vp[i + 6].bv_string;
+	    y_ptr->y_array[i].string = vp[i + 6].bv_string;
+	    y_ptr->y_array[i].length = vp[i + 6].bv_len;
 	    vp[i + 6].bv_string = NULL;
 	}
 	else if (vp[i + 6].bv_type != BVAL_STRING)
@@ -1842,7 +1853,10 @@ handle_viminfo_register(garray_T *values, int force)
 	    y_ptr->y_array = NULL;
 	}
 	else
-	    y_ptr->y_array[i] = vim_strsave(vp[i + 6].bv_string);
+	{
+	    y_ptr->y_array[i].string = vim_strnsave(vp[i + 6].bv_string, vp[i + 6].bv_len);
+	    y_ptr->y_array[i].length = vp[i + 6].bv_len;
+	}
     }
 }
 
@@ -1899,7 +1913,7 @@ write_viminfo_registers(FILE *fp)
 	num_lines = y_ptr->y_size;
 	if (num_lines == 0
 		|| (num_lines == 1 && y_ptr->y_type == MCHAR
-					&& *y_ptr->y_array[0] == NUL))
+					&& *y_ptr->y_array[0].string == NUL))
 	    continue;
 
 	if (max_kbyte > 0)
@@ -1907,7 +1921,7 @@ write_viminfo_registers(FILE *fp)
 	    // Skip register if there is more text than the maximum size.
 	    len = 0;
 	    for (j = 0; j < num_lines; j++)
-		len += (long)STRLEN(y_ptr->y_array[j]) + 1L;
+		len += (long)y_ptr->y_array[j].length + 1L;
 	    if (len > (long)max_kbyte * 1024L)
 		continue;
 	}
@@ -1942,7 +1956,7 @@ write_viminfo_registers(FILE *fp)
 	for (j = 0; j < num_lines; j++)
 	{
 	    putc('\t', fp);
-	    viminfo_writestring(fp, y_ptr->y_array[j]);
+	    viminfo_writestring(fp, y_ptr->y_array[j].string);
 	}
 
 	{
@@ -1967,7 +1981,7 @@ write_viminfo_registers(FILE *fp)
 	    {
 		putc(',', fp);
 		--remaining;
-		remaining = barline_writestring(fp, y_ptr->y_array[j],
+		remaining = barline_writestring(fp, y_ptr->y_array[j].string,
 								   remaining);
 	    }
 	    putc('\n', fp);