mirror of
https://github.com/tmux/tmux.git
synced 2024-12-13 01:48:47 +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:
parent
7f4513ec34
commit
320abba341
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->hsize = 0;
|
||||||
gd->hlimit = hlimit;
|
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);
|
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. */
|
/* Join line below onto this one. */
|
||||||
static void
|
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;
|
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;
|
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;
|
lines = 0;
|
||||||
for (;;) {
|
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)
|
if (width + gc.data.width > sx)
|
||||||
break;
|
break;
|
||||||
width += gc.data.width;
|
width += gc.data.width;
|
||||||
grid_set_cell(gd, at, yy, &gc);
|
grid_set_cell(target, at, to, &gc);
|
||||||
at++;
|
at++;
|
||||||
|
|
||||||
/* Join as much more as possible onto the current line. */
|
/* 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;
|
break;
|
||||||
width += gc.data.width;
|
width += gc.data.width;
|
||||||
|
|
||||||
grid_set_cell(gd, at, yy, &gc);
|
grid_set_cell(target, at, to, &gc);
|
||||||
at++;
|
at++;
|
||||||
}
|
}
|
||||||
lines++;
|
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;
|
gl->flags &= ~GRID_LINE_WRAPPED;
|
||||||
|
|
||||||
/* Remove the lines that were completely consumed. */
|
/* Remove the lines that were completely consumed. */
|
||||||
if (lines != 0) {
|
for (i = yy + 1; i < yy + 1 + lines; i++) {
|
||||||
if (yy + lines != gd->hsize + gd->sy) {
|
free(gd->linedata[i].celldata);
|
||||||
memmove(&gd->linedata[yy + 1],
|
free(gd->linedata[i].extddata);
|
||||||
&gd->linedata[yy + lines + 1],
|
grid_reflow_dead(&gd->linedata[i]);
|
||||||
((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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust cursor and scroll positions. */
|
/* Adjust cursor and scroll positions. */
|
||||||
if (*cy > yy + lines)
|
if (*cy > to + lines)
|
||||||
*cy -= lines;
|
*cy -= lines;
|
||||||
else if (*cy > yy)
|
else if (*cy > to)
|
||||||
*cy = yy;
|
*cy = to;
|
||||||
if (gd->hscrolled > yy + lines)
|
if (gd->hscrolled > to + lines)
|
||||||
gd->hscrolled -= lines;
|
gd->hscrolled -= lines;
|
||||||
else if (gd->hscrolled > yy)
|
else if (gd->hscrolled > to)
|
||||||
gd->hscrolled = yy;
|
gd->hscrolled = to;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Split this line into several new ones */
|
/* Split this line into several new ones */
|
||||||
static void
|
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;
|
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;
|
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)
|
if (~gl->flags & GRID_LINE_EXTENDED)
|
||||||
lines = (gl->cellused - 1) / sx;
|
lines = 1 + (gl->cellused - 1) / sx;
|
||||||
else {
|
else {
|
||||||
lines = 1;
|
lines = 2;
|
||||||
width = 0;
|
width = 0;
|
||||||
for (i = at; i < used; i++) {
|
for (i = at; i < used; i++) {
|
||||||
grid_get_cell1(gl, i, &gc);
|
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. */
|
/* Insert new lines. */
|
||||||
gd->linedata = xreallocarray(gd->linedata, gd->hsize + gd->sy + lines,
|
line = target->sy + 1;
|
||||||
sizeof *gd->linedata);
|
first = grid_reflow_add(target, lines);
|
||||||
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];
|
|
||||||
|
|
||||||
/* Copy sections from the original line. */
|
/* Copy sections from the original line. */
|
||||||
line = yy + 1;
|
|
||||||
width = 0;
|
width = 0;
|
||||||
xx = 0;
|
xx = 0;
|
||||||
for (i = at; i < used; i++) {
|
for (i = at; i < used; i++) {
|
||||||
grid_get_cell1(gl, i, &gc);
|
grid_get_cell1(gl, i, &gc);
|
||||||
if (width + gc.data.width > sx) {
|
if (width + gc.data.width > sx) {
|
||||||
gd->linedata[line].flags |= GRID_LINE_WRAPPED;
|
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
||||||
|
|
||||||
line++;
|
line++;
|
||||||
width = 0;
|
width = 0;
|
||||||
xx = 0;
|
xx = 0;
|
||||||
}
|
}
|
||||||
width += gc.data.width;
|
width += gc.data.width;
|
||||||
grid_set_cell(gd, xx, line, &gc);
|
grid_set_cell(target, xx, line, &gc);
|
||||||
xx++;
|
xx++;
|
||||||
}
|
}
|
||||||
if (flags & GRID_LINE_WRAPPED)
|
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. */
|
/* Adjust the cursor and scroll positions. */
|
||||||
if (yy <= *cy)
|
if (yy <= *cy)
|
||||||
(*cy) += lines;
|
(*cy) += lines - 1;
|
||||||
if (yy <= gd->hscrolled)
|
if (yy <= gd->hscrolled)
|
||||||
gd->hscrolled += lines;
|
gd->hscrolled += lines - 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the original line had the wrapped flag and there is still space
|
* 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.
|
* in the last new line, try to join with the next lines.
|
||||||
*/
|
*/
|
||||||
if (width < sx && (flags & GRID_LINE_WRAPPED))
|
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. */
|
/* Reflow lines on grid to new width. */
|
||||||
void
|
void
|
||||||
grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
|
grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
|
||||||
{
|
{
|
||||||
|
struct grid *target;
|
||||||
struct grid_line *gl;
|
struct grid_line *gl;
|
||||||
struct grid_cell gc;
|
struct grid_cell gc;
|
||||||
u_int yy, cy, width, i, at, first;
|
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);
|
cy = gd->hsize + (*cursor);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop over lines from top to bottom. The size may change during the
|
* Create a destination grid. This is just used as a container for the
|
||||||
* loop, but it is OK because we are always adding or removing lines
|
* line data and may not be fully valid.
|
||||||
* below the current one.
|
*/
|
||||||
|
target = grid_create(gd->sx, 0, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop over each source line.
|
||||||
*/
|
*/
|
||||||
for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
|
for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
|
||||||
gl = &gd->linedata[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;
|
first = at = width = 0;
|
||||||
if (~gl->flags & GRID_LINE_EXTENDED) {
|
if (~gl->flags & GRID_LINE_EXTENDED) {
|
||||||
first = 1;
|
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
|
* If the line is exactly right or the first character is wider
|
||||||
* anything.
|
* than the targe width, just move it across unchanged.
|
||||||
*/
|
*/
|
||||||
if (width == sx)
|
if (width == sx || first > sx) {
|
||||||
continue;
|
grid_reflow_move(target, gl);
|
||||||
|
|
||||||
/*
|
|
||||||
* If the first character is wider than the target width, there
|
|
||||||
* is no point in trying to do anything.
|
|
||||||
*/
|
|
||||||
if (first > sx)
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the line is too big, it needs to be split, whether or not
|
* If the line is too big, it needs to be split, whether or not
|
||||||
* it was previously wrapped.
|
* it was previously wrapped.
|
||||||
*/
|
*/
|
||||||
if (width > sx) {
|
if (width > sx) {
|
||||||
grid_reflow_split(gd, sx, yy, at, &cy);
|
grid_reflow_split(target, gd, sx, yy, at, &cy);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1190,10 +1236,24 @@ grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
|
|||||||
* of the next line.
|
* of the next line.
|
||||||
*/
|
*/
|
||||||
if (gl->flags & GRID_LINE_WRAPPED)
|
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)
|
if (gd->hscrolled > gd->hsize)
|
||||||
gd->hscrolled = gd->hsize;
|
gd->hscrolled = gd->hsize;
|
||||||
if (cy < gd->hsize)
|
if (cy < gd->hsize)
|
||||||
|
1
tmux.h
1
tmux.h
@ -551,6 +551,7 @@ enum utf8_state {
|
|||||||
/* Grid line flags. */
|
/* Grid line flags. */
|
||||||
#define GRID_LINE_WRAPPED 0x1
|
#define GRID_LINE_WRAPPED 0x1
|
||||||
#define GRID_LINE_EXTENDED 0x2
|
#define GRID_LINE_EXTENDED 0x2
|
||||||
|
#define GRID_LINE_DEAD 0x4
|
||||||
|
|
||||||
/* Grid cell data. */
|
/* Grid cell data. */
|
||||||
struct grid_cell {
|
struct grid_cell {
|
||||||
|
Loading…
Reference in New Issue
Block a user