mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Reflowing the grid in-place involved way too much memmove() for a big
performance cost with a large history. Instead change back to using a second grid and copying modified lines over which is much faster (this doesn't revert to the old code however which didn't support UTF-8 properly). GitHub issue 1249.
This commit is contained in:
		
							
								
								
									
										198
									
								
								grid.c
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								grid.c
									
									
									
									
									
								
							| @@ -226,7 +226,10 @@ grid_create(u_int sx, u_int sy, u_int hlimit) | ||||
| 	gd->hsize = 0; | ||||
| 	gd->hlimit = hlimit; | ||||
|  | ||||
| 	gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); | ||||
| 	if (gd->sy != 0) | ||||
| 		gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); | ||||
| 	else | ||||
| 		gd->linedata = NULL; | ||||
|  | ||||
| 	return (gd); | ||||
| } | ||||
| @@ -941,15 +944,66 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Mark line as dead. */ | ||||
| static void | ||||
| grid_reflow_dead(struct grid_line *gl) | ||||
| { | ||||
| 	memset(gl, 0, sizeof *gl); | ||||
| 	gl->flags = GRID_LINE_DEAD; | ||||
| } | ||||
|  | ||||
| /* Add lines, return the first new one. */ | ||||
| static struct grid_line * | ||||
| grid_reflow_add(struct grid *gd, u_int n) | ||||
| { | ||||
| 	struct grid_line	*gl; | ||||
| 	u_int			 sy = gd->sy + n; | ||||
|  | ||||
| 	gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata); | ||||
| 	gl = &gd->linedata[gd->sy]; | ||||
| 	memset(gl, 0, n * (sizeof *gl)); | ||||
| 	gd->sy = sy; | ||||
| 	return (gl); | ||||
| } | ||||
|  | ||||
| /* Move a line across. */ | ||||
| static struct grid_line * | ||||
| grid_reflow_move(struct grid *gd, struct grid_line *from) | ||||
| { | ||||
| 	struct grid_line	*to; | ||||
|  | ||||
| 	to = grid_reflow_add(gd, 1); | ||||
| 	memcpy(to, from, sizeof *to); | ||||
| 	grid_reflow_dead(from); | ||||
| 	return (to); | ||||
| } | ||||
|  | ||||
| /* Join line below onto this one. */ | ||||
| static void | ||||
| grid_reflow_join(struct grid *gd, u_int sx, u_int yy, u_int width, u_int *cy) | ||||
| grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, | ||||
|     u_int width, u_int *cy, int already) | ||||
| { | ||||
| 	struct grid_line	*gl = &gd->linedata[yy], *from; | ||||
| 	struct grid_line	*gl, *from; | ||||
| 	struct grid_cell	 gc; | ||||
| 	u_int			 lines, want, left, i, at = gl->cellused, line; | ||||
| 	u_int			 lines, want, left, i, to, line; | ||||
| 	u_int			 at; | ||||
| 	int			 wrapped = 1; | ||||
|  | ||||
| 	/* | ||||
| 	 * Add a new target line. | ||||
| 	 */ | ||||
| 	if (!already) { | ||||
| 		to = target->sy; | ||||
| 		gl = grid_reflow_move(target, &gd->linedata[yy]); | ||||
| 	} else { | ||||
| 		to = target->sy - 1; | ||||
| 		gl = &target->linedata[to]; | ||||
| 	} | ||||
| 	at = gl->cellused; | ||||
|  | ||||
| 	/* | ||||
| 	 * Loop until no more to consume or the target line is full. | ||||
| 	 */ | ||||
| 	lines = 0; | ||||
| 	for (;;) { | ||||
| 		/* | ||||
| @@ -978,7 +1032,7 @@ grid_reflow_join(struct grid *gd, u_int sx, u_int yy, u_int width, u_int *cy) | ||||
| 		if (width + gc.data.width > sx) | ||||
| 			break; | ||||
| 		width += gc.data.width; | ||||
| 		grid_set_cell(gd, at, yy, &gc); | ||||
| 		grid_set_cell(target, at, to, &gc); | ||||
| 		at++; | ||||
|  | ||||
| 		/* Join as much more as possible onto the current line. */ | ||||
| @@ -989,7 +1043,7 @@ grid_reflow_join(struct grid *gd, u_int sx, u_int yy, u_int width, u_int *cy) | ||||
| 				break; | ||||
| 			width += gc.data.width; | ||||
|  | ||||
| 			grid_set_cell(gd, at, yy, &gc); | ||||
| 			grid_set_cell(target, at, to, &gc); | ||||
| 			at++; | ||||
| 		} | ||||
| 		lines++; | ||||
| @@ -1018,48 +1072,39 @@ grid_reflow_join(struct grid *gd, u_int sx, u_int yy, u_int width, u_int *cy) | ||||
| 		gl->flags &= ~GRID_LINE_WRAPPED; | ||||
|  | ||||
| 	/* Remove the lines that were completely consumed. */ | ||||
| 	if (lines != 0) { | ||||
| 		if (yy + lines != gd->hsize + gd->sy) { | ||||
| 			memmove(&gd->linedata[yy + 1], | ||||
| 			    &gd->linedata[yy + lines + 1], | ||||
| 			    ((gd->hsize + gd->sy) - (yy + lines + 1)) * | ||||
| 			    (sizeof *gd->linedata)); | ||||
| 		} | ||||
| 		if (gd->hsize >= lines) | ||||
| 			gd->hsize -= lines; | ||||
| 		else { | ||||
| 			lines -= gd->hsize; | ||||
| 			gd->hsize = 0; | ||||
| 			for (i = 1; i < lines + 1; i++) | ||||
| 				grid_empty_line(gd, gd->hsize + gd->sy - i, 8); | ||||
| 		} | ||||
| 	for (i = yy + 1; i < yy + 1 + lines; i++) { | ||||
| 		free(gd->linedata[i].celldata); | ||||
| 		free(gd->linedata[i].extddata); | ||||
| 		grid_reflow_dead(&gd->linedata[i]); | ||||
| 	} | ||||
|  | ||||
| 	/* Adjust cursor and scroll positions. */ | ||||
| 	if (*cy > yy + lines) | ||||
| 	if (*cy > to + lines) | ||||
| 		*cy -= lines; | ||||
| 	else if (*cy > yy) | ||||
| 		*cy = yy; | ||||
| 	if (gd->hscrolled > yy + lines) | ||||
| 	else if (*cy > to) | ||||
| 		*cy = to; | ||||
| 	if (gd->hscrolled > to + lines) | ||||
| 		gd->hscrolled -= lines; | ||||
| 	else if (gd->hscrolled > yy) | ||||
| 		gd->hscrolled = yy; | ||||
| 	else if (gd->hscrolled > to) | ||||
| 		gd->hscrolled = to; | ||||
| } | ||||
|  | ||||
| /* Split this line into several new ones */ | ||||
| static void | ||||
| grid_reflow_split(struct grid *gd, u_int sx, u_int yy, u_int at, u_int *cy) | ||||
| grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, | ||||
|     u_int at, u_int *cy) | ||||
| { | ||||
| 	struct grid_line	*gl = &gd->linedata[yy]; | ||||
| 	struct grid_line	*gl = &gd->linedata[yy], *first; | ||||
| 	struct grid_cell	 gc; | ||||
| 	u_int			 line, lines, width, i, used = gl->cellused, xx; | ||||
| 	u_int			 line, lines, width, i, xx; | ||||
| 	u_int			 used = gl->cellused; | ||||
| 	int			 flags = gl->flags; | ||||
|  | ||||
| 	/* How many lines do we need to insert? We know we need at least one. */ | ||||
| 	/* How many lines do we need to insert? We know we need at least two. */ | ||||
| 	if (~gl->flags & GRID_LINE_EXTENDED) | ||||
| 		lines = (gl->cellused - 1) / sx; | ||||
| 		lines = 1 + (gl->cellused - 1) / sx; | ||||
| 	else { | ||||
| 		lines = 1; | ||||
| 		lines = 2; | ||||
| 		width = 0; | ||||
| 		for (i = at; i < used; i++) { | ||||
| 			grid_get_cell1(gl, i, &gc); | ||||
| @@ -1071,58 +1116,54 @@ grid_reflow_split(struct grid *gd, u_int sx, u_int yy, u_int at, u_int *cy) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Trim the original line size. */ | ||||
| 	gl->cellsize = gl->cellused = at; | ||||
| 	gl->flags |= GRID_LINE_WRAPPED; | ||||
|  | ||||
| 	/* Insert new lines. */ | ||||
| 	gd->linedata = xreallocarray(gd->linedata, gd->hsize + gd->sy + lines, | ||||
| 	    sizeof *gd->linedata); | ||||
| 	memmove(&gd->linedata[yy + lines + 1], &gd->linedata[yy + 1], | ||||
| 	    ((gd->hsize + gd->sy) - (yy + 1)) * (sizeof *gd->linedata)); | ||||
| 	gd->hsize += lines; | ||||
| 	for (i = 0; i < lines; i++) | ||||
| 		grid_empty_line(gd, yy + 1 + i, 8); | ||||
| 	gl = &gd->linedata[yy]; | ||||
| 	line = target->sy + 1; | ||||
| 	first = grid_reflow_add(target, lines); | ||||
|  | ||||
| 	/* Copy sections from the original line. */ | ||||
| 	line = yy + 1; | ||||
| 	width = 0; | ||||
| 	xx = 0; | ||||
| 	for (i = at; i < used; i++) { | ||||
| 		grid_get_cell1(gl, i, &gc); | ||||
| 		if (width + gc.data.width > sx) { | ||||
| 			gd->linedata[line].flags |= GRID_LINE_WRAPPED; | ||||
| 			target->linedata[line].flags |= GRID_LINE_WRAPPED; | ||||
|  | ||||
| 			line++; | ||||
| 			width = 0; | ||||
| 			xx = 0; | ||||
| 		} | ||||
| 		width += gc.data.width; | ||||
| 		grid_set_cell(gd, xx, line, &gc); | ||||
| 		grid_set_cell(target, xx, line, &gc); | ||||
| 		xx++; | ||||
| 	} | ||||
| 	if (flags & GRID_LINE_WRAPPED) | ||||
| 		gd->linedata[line].flags |= GRID_LINE_WRAPPED; | ||||
| 		target->linedata[line].flags |= GRID_LINE_WRAPPED; | ||||
|  | ||||
| 	/* Move the remainder of the original line. */ | ||||
| 	gl->cellsize = gl->cellused = at; | ||||
| 	gl->flags |= GRID_LINE_WRAPPED; | ||||
| 	memcpy(first, gl, sizeof *first); | ||||
| 	grid_reflow_dead(gl); | ||||
|  | ||||
| 	/* Adjust the cursor and scroll positions. */ | ||||
| 	if (yy <= *cy) | ||||
| 		(*cy) += lines; | ||||
| 		(*cy) += lines - 1; | ||||
| 	if (yy <= gd->hscrolled) | ||||
| 		gd->hscrolled += lines; | ||||
| 		gd->hscrolled += lines - 1; | ||||
|  | ||||
| 	/* | ||||
| 	 * If the original line had the wrapped flag and there is still space | ||||
| 	 * in the last new line, try to join with the next lines. | ||||
| 	 */ | ||||
| 	if (width < sx && (flags & GRID_LINE_WRAPPED)) | ||||
| 		grid_reflow_join(gd, sx, line, width, cy); | ||||
| 		grid_reflow_join(target, gd, sx, yy, width, cy, 1); | ||||
| } | ||||
|  | ||||
| /* Reflow lines on grid to new width. */ | ||||
| void | ||||
| grid_reflow(struct grid *gd, u_int sx, u_int *cursor) | ||||
| { | ||||
| 	struct grid		*target; | ||||
| 	struct grid_line	*gl; | ||||
| 	struct grid_cell	 gc; | ||||
| 	u_int			 yy, cy, width, i, at, first; | ||||
| @@ -1135,14 +1176,24 @@ grid_reflow(struct grid *gd, u_int sx, u_int *cursor) | ||||
| 	cy = gd->hsize + (*cursor); | ||||
|  | ||||
| 	/* | ||||
| 	 * Loop over lines from top to bottom. The size may change during the | ||||
| 	 * loop, but it is OK because we are always adding or removing lines | ||||
| 	 * below the current one. | ||||
| 	 * Create a destination grid. This is just used as a container for the | ||||
| 	 * line data and may not be fully valid. | ||||
| 	 */ | ||||
| 	target = grid_create(gd->sx, 0, 0); | ||||
|  | ||||
| 	/* | ||||
| 	 * Loop over each source line. | ||||
| 	 */ | ||||
| 	for (yy = 0; yy < gd->hsize + gd->sy; yy++) { | ||||
| 		gl = &gd->linedata[yy]; | ||||
| 		if (gl->flags & GRID_LINE_DEAD) | ||||
| 			continue; | ||||
|  | ||||
| 		/* Work out the width of this line. */ | ||||
| 		/* | ||||
| 		 * Work out the width of this line. first is the width of the | ||||
| 		 * first character, at is the point at which the available | ||||
| 		 * width is hit, and width is the full line width. | ||||
| 		 */ | ||||
| 		first = at = width = 0; | ||||
| 		if (~gl->flags & GRID_LINE_EXTENDED) { | ||||
| 			first = 1; | ||||
| @@ -1163,25 +1214,20 @@ grid_reflow(struct grid *gd, u_int sx, u_int *cursor) | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		 * If the line is exactly right, there is no need to do | ||||
| 		 * anything. | ||||
| 		 * If the line is exactly right or the first character is wider | ||||
| 		 * than the targe width, just move it across unchanged. | ||||
| 		 */ | ||||
| 		if (width == sx) | ||||
| 			continue; | ||||
|  | ||||
| 		/* | ||||
| 		 * If the first character is wider than the target width, there | ||||
| 		 * is no point in trying to do anything. | ||||
| 		 */ | ||||
| 		if (first > sx) | ||||
| 		if (width == sx || first > sx) { | ||||
| 			grid_reflow_move(target, gl); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		 * If the line is too big, it needs to be split, whether or not | ||||
| 		 * it was previously wrapped. | ||||
| 		 */ | ||||
| 		if (width > sx) { | ||||
| 			grid_reflow_split(gd, sx, yy, at, &cy); | ||||
| 			grid_reflow_split(target, gd, sx, yy, at, &cy); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| @@ -1190,10 +1236,24 @@ grid_reflow(struct grid *gd, u_int sx, u_int *cursor) | ||||
| 		 * of the next line. | ||||
| 		 */ | ||||
| 		if (gl->flags & GRID_LINE_WRAPPED) | ||||
| 			grid_reflow_join(gd, sx, yy, width, &cy); | ||||
|  | ||||
| 			grid_reflow_join(target, gd, sx, yy, width, &cy, 0); | ||||
| 		else | ||||
| 			grid_reflow_move(target, gl); | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Replace the old grid with the new. | ||||
| 	 */ | ||||
| 	if (target->sy < gd->sy) | ||||
| 		grid_reflow_add(target, gd->sy - target->sy); | ||||
| 	gd->hsize = target->sy - gd->sy; | ||||
| 	free(gd->linedata); | ||||
| 	gd->linedata = target->linedata; | ||||
| 	free(target); | ||||
|  | ||||
| 	/* | ||||
| 	 * Update scrolled and cursor positions. | ||||
| 	 */ | ||||
| 	if (gd->hscrolled > gd->hsize) | ||||
| 		gd->hscrolled = gd->hsize; | ||||
| 	if (cy < gd->hsize) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 nicm
					nicm