From 40ca86b2d30873ac13c73360a777cfa40b705a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 23 May 2021 21:15:06 +0200 Subject: [PATCH 01/18] grid: reflow: memcpy() chunks of cells, instead of single cell-by-cell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of walking the old grid cell-by-cell, and checking for tracking points, OSC-8 URIs etc on each cell, memcpy() sequences of cells. For each row, find the end column, by scanning backward, looking for the first non-empty cell. Chunk the row based on tracking point coordinates. If there aren’t any tracking coordinates, or OSC-8 URIs on the current row, the entire row is copied in one go. The chunk of cells is copied to the new grid. We may have to split it up into multiple copies, since not all cells may fit on the current “new” row. Care must also be taken to not line break in the middle of a multi-column character. --- grid.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 2787102f..da3a4710 100644 --- a/grid.c +++ b/grid.c @@ -13,7 +13,7 @@ #include "util.h" #include "xmalloc.h" -#define TIME_REFLOW 0 +#define TIME_REFLOW 1 struct grid * grid_snapshot(const struct grid *grid) @@ -527,6 +527,159 @@ grid_resize_and_reflow( new_row->cells[new_col_idx].attrs = old_cell->attrs; \ } while (0) + /* Find last non-empty cell */ + int col_count = 0; + for (int c = old_cols - 1; c > 0; c--) { + const struct cell *cell = &old_row->cells[c]; + if (!(cell->wc == 0 || cell->wc == CELL_SPACER)) { + col_count = c + 1; + break; + } + } + + xassert(col_count >= 0 && col_count <= old_cols); + + struct coord *tp = (*next_tp)->row == old_row_idx ? *next_tp : NULL; + struct row_uri_range *_range = + old_row->extra != NULL && tll_length(old_row->extra->uri_ranges) > 0 + ? &tll_front(old_row->extra->uri_ranges) + : NULL; + + if (tp != NULL) + col_count = max(col_count, tp->col + 1); + if (_range != NULL) + col_count = max(col_count, _range->start + 1); + + for (int start = 0, left = col_count; left > 0;) { + int tp_col = -1; + int uri_col = -1; + int end; + + if (tp != NULL && _range != NULL) { + tp_col = tp->col + 1; + uri_col = (_range->start >= start ? _range->start : _range->end) + 1; + end = min(tp_col, uri_col); + } else if (tp != NULL) + end = tp_col = tp->col + 1; + else if (_range != NULL) { + if (_range->start >= start) + uri_col = _range->start + 1; + else + uri_col = _range->end + 1; + end = uri_col; + } else + end = col_count; + + int cols = end - start; + xassert(cols > 0); + xassert(start + cols <= old_cols); + + bool tp_break = tp_col == end; + bool uri_break = uri_col == end; + + for (int count = cols, from = start; count > 0;) { + xassert(new_col_idx <= new_cols); + int new_row_cells_left = new_cols - new_col_idx; + + if (new_row_cells_left <= 0) { + line_wrap(); + new_row_cells_left = new_cols; + } + + int amount = min(count, new_row_cells_left); + xassert(amount > 0); + + int spacers = 0; + if (new_col_idx + amount >= new_cols) { + /* + * The cell *after* the last cell is a CELL_SPACER + * + * This means we have a multi-column character + * that doesn’t fit on the current row. We need to + * push it to the next row, and insert CELL_SPACER + * cells as padding. + */ + while ( + unlikely( + amount > 1 && + from + amount < old_cols && + old_row->cells[from + amount].wc >= CELL_SPACER + 1)) + { + amount--; + spacers++; + } + + xassert( + amount == 1 || + old_row->cells[from + amount - 1].wc <= CELL_SPACER + 1); + } + + xassert(new_col_idx + amount <= new_cols); + xassert(from + amount <= old_cols); + + memcpy( + &new_row->cells[new_col_idx], &old_row->cells[from], + amount * sizeof(struct cell)); + + count -= amount; + from += amount; + new_col_idx += amount; + + if (unlikely(spacers > 0)) { + xassert(new_col_idx + spacers == new_cols); + + const struct cell *cell = &old_row->cells[from + amount - 1]; + + for (int i = 0; i < spacers; i++, new_col_idx++) { + new_row->cells[new_col_idx].wc = CELL_SPACER; + new_row->cells[new_col_idx].attrs = cell->attrs; + } + } + } + + new_col_idx--; + + if (tp_break) { + do { + xassert(tp != NULL); + xassert(tp->row == old_row_idx); + xassert(tp->col == start + cols - 1); + + tp->row = new_row_idx; + tp->col = new_col_idx; + + next_tp++; + tp = *next_tp; + } while (tp->row == old_row_idx && tp->col == start + cols - 1); + + if (tp->row != old_row_idx) + tp = NULL; + } + + if (uri_break) { + if (_range->start == start + cols - 1) + reflow_uri_range_start(_range, new_row, new_col_idx); + + if (_range->end == start + cols - 1) { + reflow_uri_range_end(_range, new_row, new_col_idx); + + xassert(&tll_front(old_row->extra->uri_ranges) == _range); + grid_row_uri_range_destroy(_range); + tll_pop_front(old_row->extra->uri_ranges); + + _range = tll_length(old_row->extra->uri_ranges) > 0 + ? &tll_front(old_row->extra->uri_ranges) + : NULL; + } + } + + new_col_idx++; + + left -= cols; + start += cols; + } + +#if 0 /* * Keep track of empty cells. If the old line ends with a * string of empty cells, we don't need to, nor do we want to, @@ -653,6 +806,7 @@ grid_resize_and_reflow( new_col_idx++; } +#endif if (old_row->linebreak) { /* Erase the remaining cells */ @@ -673,6 +827,10 @@ grid_resize_and_reflow( memset(&new_row->cells[new_col_idx], 0, (new_cols - new_col_idx) * sizeof(new_row->cells[0])); + for (struct coord **tp = next_tp; *tp != &terminator; tp++) { + LOG_DBG("TP: row=%d, col=%d", + (*tp)->row, (*tp)->col); + } xassert(old_rows == 0 || *next_tp == &terminator); #if defined(_DEBUG) From 7c3a4b24d9358a65331a5c4e8b134e8df637ccdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:20:25 +0200 Subject: [PATCH 02/18] grid: reflow: remove dead code --- grid.c | 135 --------------------------------------------------------- 1 file changed, 135 deletions(-) diff --git a/grid.c b/grid.c index da3a4710..a0ed62e2 100644 --- a/grid.c +++ b/grid.c @@ -521,12 +521,6 @@ grid_resize_and_reflow( grid, new_grid, new_row, &new_row_idx, &new_col_idx, \ new_rows, new_cols) -#define print_spacer(remaining) \ - do { \ - new_row->cells[new_col_idx].wc = CELL_SPACER + (remaining); \ - new_row->cells[new_col_idx].attrs = old_cell->attrs; \ - } while (0) - /* Find last non-empty cell */ int col_count = 0; for (int c = old_cols - 1; c > 0; c--) { @@ -679,134 +673,6 @@ grid_resize_and_reflow( start += cols; } -#if 0 - /* - * Keep track of empty cells. If the old line ends with a - * string of empty cells, we don't need to, nor do we want to, - * add those to the new line. However, if there are non-empty - * cells *after* the string of empty cells, we need to emit - * the empty cells too. And that may trigger linebreaks - */ - int empty_count = 0; - - struct row_uri_range *uri_range = NULL; - - /* Walk current line of the old grid */ - for (int c = 0; c < old_cols; c++) { - const struct cell *old_cell = &old_row->cells[c]; - wchar_t wc = old_cell->wc; - - /* Check if this cell is one of the tracked cells */ - bool is_tracking_point = false; - - struct coord *tp = *next_tp; - if (unlikely(tp->row == old_row_idx && tp->col == c)) - is_tracking_point = true; - - /* If there’s an URI start/end point here, we need to make - * sure we handle it */ - bool on_uri = false; - if (old_row->extra != NULL) { - if (uri_range != NULL) - on_uri = uri_range->end == c; - - else if (tll_length(old_row->extra->uri_ranges) > 0) { - struct row_uri_range *range = &tll_front( - old_row->extra->uri_ranges); - - if (range->start == c) { - uri_range = range; - on_uri = true; - } - } - } - - if (wc == 0 && likely(!(is_tracking_point | on_uri))) { - empty_count++; - continue; - } - - /* Allow left-adjusted and right-adjusted text, with empty - * cells in between, to be "pushed together" */ - int old_cols_left = old_cols - c; - int cols_needed = empty_count + old_cols_left; - int new_cols_left = new_cols - new_col_idx; - if (new_cols_left < cols_needed && new_cols_left >= old_cols_left) - empty_count = max(0, empty_count - (cols_needed - new_cols_left)); - - for (int i = 0; i < empty_count; i++) { - if (new_col_idx + 1 > new_cols) - line_wrap(); - - size_t idx = c - empty_count + i; - - new_row->cells[new_col_idx].wc = 0; - new_row->cells[new_col_idx].attrs = old_row->cells[idx].attrs; - new_col_idx++; - } - - empty_count = 0; - - if (wc == CELL_SPACER) - continue; - - if (unlikely(wc < CELL_SPACER && - c + 1 < old_cols && - old_row->cells[c + 1].wc > CELL_SPACER)) - { - int width = old_row->cells[c + 1].wc - CELL_SPACER + 1; - assert(wcwidth(wc) == width); - - /* Out of columns on current row in new grid? */ - if (new_col_idx + width > new_cols) { - /* Pad to end-of-line with spacers, then line-wrap */ - for (;new_col_idx < new_cols; new_col_idx++) - print_spacer(0); - line_wrap(); - } - } - - if (new_col_idx + 1 > new_cols) - line_wrap(); - - xassert(new_row != NULL); - xassert(new_col_idx >= 0); - xassert(new_col_idx < new_cols); - - new_row->cells[new_col_idx] = *old_cell; - - /* Translate tracking point(s) */ - if (unlikely(is_tracking_point)) { - do { - xassert(tp != NULL); - xassert(tp->row == old_row_idx); - xassert(tp->col == c); - - tp->row = new_row_idx; - tp->col = new_col_idx; - - next_tp++; - tp = *next_tp; - } while (tp->row == old_row_idx && tp->col == c); - } - - if (unlikely(on_uri)) { - if (uri_range->start == c) - reflow_uri_range_start(uri_range, new_row, new_col_idx); - if (uri_range->end == c) { - reflow_uri_range_end(uri_range, new_row, new_col_idx); - - xassert(&tll_front(old_row->extra->uri_ranges) == uri_range); - grid_row_uri_range_destroy(uri_range); - tll_pop_front(old_row->extra->uri_ranges); - - uri_range = NULL; - } - } - - new_col_idx++; - } -#endif if (old_row->linebreak) { /* Erase the remaining cells */ @@ -819,7 +685,6 @@ grid_resize_and_reflow( grid_row_free(old_grid[old_row_idx]); grid->rows[old_row_idx] = NULL; -#undef print_spacer #undef line_wrap } From 315865f18ca5b12dbf93ee4cdebd1b4116dadd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:22:52 +0200 Subject: [PATCH 03/18] grid: reflow: rename _range -> range --- grid.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/grid.c b/grid.c index a0ed62e2..5a978e1c 100644 --- a/grid.c +++ b/grid.c @@ -534,32 +534,32 @@ grid_resize_and_reflow( xassert(col_count >= 0 && col_count <= old_cols); struct coord *tp = (*next_tp)->row == old_row_idx ? *next_tp : NULL; - struct row_uri_range *_range = + struct row_uri_range *range = old_row->extra != NULL && tll_length(old_row->extra->uri_ranges) > 0 ? &tll_front(old_row->extra->uri_ranges) : NULL; if (tp != NULL) col_count = max(col_count, tp->col + 1); - if (_range != NULL) - col_count = max(col_count, _range->start + 1); + if (range != NULL) + col_count = max(col_count, range->start + 1); for (int start = 0, left = col_count; left > 0;) { int tp_col = -1; int uri_col = -1; int end; - if (tp != NULL && _range != NULL) { + if (tp != NULL && range != NULL) { tp_col = tp->col + 1; - uri_col = (_range->start >= start ? _range->start : _range->end) + 1; + uri_col = (range->start >= start ? range->start : range->end) + 1; end = min(tp_col, uri_col); } else if (tp != NULL) end = tp_col = tp->col + 1; - else if (_range != NULL) { - if (_range->start >= start) - uri_col = _range->start + 1; + else if (range != NULL) { + if (range->start >= start) + uri_col = range->start + 1; else - uri_col = _range->end + 1; + uri_col = range->end + 1; end = uri_col; } else end = col_count; @@ -651,17 +651,17 @@ grid_resize_and_reflow( } if (uri_break) { - if (_range->start == start + cols - 1) - reflow_uri_range_start(_range, new_row, new_col_idx); + if (range->start == start + cols - 1) + reflow_uri_range_start(range, new_row, new_col_idx); - if (_range->end == start + cols - 1) { - reflow_uri_range_end(_range, new_row, new_col_idx); + if (range->end == start + cols - 1) { + reflow_uri_range_end(range, new_row, new_col_idx); - xassert(&tll_front(old_row->extra->uri_ranges) == _range); - grid_row_uri_range_destroy(_range); + xassert(&tll_front(old_row->extra->uri_ranges) == range); + grid_row_uri_range_destroy(range); tll_pop_front(old_row->extra->uri_ranges); - _range = tll_length(old_row->extra->uri_ranges) > 0 + range = tll_length(old_row->extra->uri_ranges) > 0 ? &tll_front(old_row->extra->uri_ranges) : NULL; } From 4b7e4fb885a3d9aa7fa2152ee9439964a420ea20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:28:11 +0200 Subject: [PATCH 04/18] grid: reflow: slightly simplified logic for end-coordinate calculation --- grid.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/grid.c b/grid.c index 5a978e1c..d318c541 100644 --- a/grid.c +++ b/grid.c @@ -549,19 +549,17 @@ grid_resize_and_reflow( int uri_col = -1; int end; - if (tp != NULL && range != NULL) { - tp_col = tp->col + 1; + if (range != NULL) { uri_col = (range->start >= start ? range->start : range->end) + 1; - end = min(tp_col, uri_col); + + if (tp != NULL) { + tp_col = tp->col + 1; + end = min(tp_col, uri_col); + } else + end = uri_col; } else if (tp != NULL) end = tp_col = tp->col + 1; - else if (range != NULL) { - if (range->start >= start) - uri_col = range->start + 1; - else - uri_col = range->end + 1; - end = uri_col; - } else + else end = col_count; int cols = end - start; From a56b54ad2f3cc0f5d43a1f32227d4f3867c07867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:31:38 +0200 Subject: [PATCH 05/18] grid: set tp/uri break flags explicitly when we know them to be true --- grid.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/grid.c b/grid.c index d318c541..4f1223dc 100644 --- a/grid.c +++ b/grid.c @@ -549,26 +549,32 @@ grid_resize_and_reflow( int uri_col = -1; int end; + bool tp_break = false; + bool uri_break = false; + if (range != NULL) { uri_col = (range->start >= start ? range->start : range->end) + 1; if (tp != NULL) { tp_col = tp->col + 1; end = min(tp_col, uri_col); - } else + + tp_break = end == tp_col; + uri_break = end == uri_col; + } else { end = uri_col; - } else if (tp != NULL) + uri_break = true; + } + } else if (tp != NULL) { end = tp_col = tp->col + 1; - else + tp_break = true; + } else end = col_count; int cols = end - start; xassert(cols > 0); xassert(start + cols <= old_cols); - bool tp_break = tp_col == end; - bool uri_break = uri_col == end; - for (int count = cols, from = start; count > 0;) { xassert(new_col_idx <= new_cols); int new_row_cells_left = new_cols - new_col_idx; From 3453f091a3349e6a977d69e07c1be6d3fe39c939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:32:25 +0200 Subject: [PATCH 06/18] grid: fix col max calculation when row contains URI ranges --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 4f1223dc..b1b8c57a 100644 --- a/grid.c +++ b/grid.c @@ -542,7 +542,7 @@ grid_resize_and_reflow( if (tp != NULL) col_count = max(col_count, tp->col + 1); if (range != NULL) - col_count = max(col_count, range->start + 1); + col_count = max(col_count, range->end + 1); for (int start = 0, left = col_count; left > 0;) { int tp_col = -1; From 5325ea042decae3d44d1cba54abb782a6fcd532c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:34:21 +0200 Subject: [PATCH 07/18] grid: no need to keep the tp_col/uri_col variables around --- grid.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/grid.c b/grid.c index b1b8c57a..1563fc10 100644 --- a/grid.c +++ b/grid.c @@ -545,18 +545,15 @@ grid_resize_and_reflow( col_count = max(col_count, range->end + 1); for (int start = 0, left = col_count; left > 0;) { - int tp_col = -1; - int uri_col = -1; int end; - bool tp_break = false; bool uri_break = false; if (range != NULL) { - uri_col = (range->start >= start ? range->start : range->end) + 1; + int uri_col = (range->start >= start ? range->start : range->end) + 1; if (tp != NULL) { - tp_col = tp->col + 1; + int tp_col = tp->col + 1; end = min(tp_col, uri_col); tp_break = end == tp_col; @@ -566,7 +563,7 @@ grid_resize_and_reflow( uri_break = true; } } else if (tp != NULL) { - end = tp_col = tp->col + 1; + end = tp->col + 1; tp_break = true; } else end = col_count; From ac97f20f997e300f87cb4c38ace331e529168d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 19:40:10 +0200 Subject: [PATCH 08/18] grid: reflow: comments --- grid.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 1563fc10..fbbe80e4 100644 --- a/grid.c +++ b/grid.c @@ -549,6 +549,13 @@ grid_resize_and_reflow( bool tp_break = false; bool uri_break = false; + /* + * Set end-coordinate for this chunk, by finding the next + * point-of-interrest on this row. + * + * If there are no more tracking points, or URI ranges, + * the end-coordinate will be at the end of the row, + */ if (range != NULL) { int uri_col = (range->start >= start ? range->start : range->end) + 1; @@ -572,22 +579,36 @@ grid_resize_and_reflow( xassert(cols > 0); xassert(start + cols <= old_cols); + /* + * Copy the row chunk to the new grid. Note that there may + * be fewer cells left on the new row than what we have in + * the chunk. I.e. the chunk may have to be split up into + * multiple memcpy:ies. + */ + for (int count = cols, from = start; count > 0;) { xassert(new_col_idx <= new_cols); int new_row_cells_left = new_cols - new_col_idx; + /* Row full, emit newline and get a new, fresh, row */ if (new_row_cells_left <= 0) { line_wrap(); new_row_cells_left = new_cols; } + /* Number of cells we can copy */ int amount = min(count, new_row_cells_left); xassert(amount > 0); + /* + * If we’re going to reach the end of the new row, we + * need to make sure we don’t end in the middle of a + * multi-column character. + */ int spacers = 0; if (new_col_idx + amount >= new_cols) { /* - * The cell *after* the last cell is a CELL_SPACER + * While the cell *after* the last cell is a CELL_SPACER * * This means we have a multi-column character * that doesn’t fit on the current row. We need to From 2029d201b571753585f5df1b0f7265adfca722a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 May 2021 12:22:28 +0200 Subject: [PATCH 09/18] grid: disable reflow timing by default --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index fbbe80e4..32107145 100644 --- a/grid.c +++ b/grid.c @@ -13,7 +13,7 @@ #include "util.h" #include "xmalloc.h" -#define TIME_REFLOW 1 +#define TIME_REFLOW 0 struct grid * grid_snapshot(const struct grid *grid) From c2314d689e9e36dc4e104051e2c2b15d0e71d99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 26 May 2021 12:27:12 +0200 Subject: [PATCH 10/18] grid: reflow: avoid unnecessary if-statements before chunking a row --- grid.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/grid.c b/grid.c index 32107145..6d8f93c3 100644 --- a/grid.c +++ b/grid.c @@ -533,16 +533,21 @@ grid_resize_and_reflow( xassert(col_count >= 0 && col_count <= old_cols); - struct coord *tp = (*next_tp)->row == old_row_idx ? *next_tp : NULL; - struct row_uri_range *range = - old_row->extra != NULL && tll_length(old_row->extra->uri_ranges) > 0 - ? &tll_front(old_row->extra->uri_ranges) - : NULL; - - if (tp != NULL) + /* Do we have a (at least one) tracking point on this row */ + struct coord *tp; + if ((*next_tp)->row == old_row_idx) { + tp = *next_tp; col_count = max(col_count, tp->col + 1); - if (range != NULL) + } else + tp = NULL; + + /* Does this row have any URIs? */ + struct row_uri_range *range; + if (old_row->extra != NULL && tll_length(old_row->extra->uri_ranges) > 0) { + range = &tll_front(old_row->extra->uri_ranges); col_count = max(col_count, range->end + 1); + } else + range = NULL; for (int start = 0, left = col_count; left > 0;) { int end; From ef1fdc40c8fd10411e7c40e0fe0d6982a702f505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 May 2021 17:41:13 +0200 Subject: [PATCH 11/18] grid: reflow: check the *entire* row for non-empty cells --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 6d8f93c3..a2b0c0fc 100644 --- a/grid.c +++ b/grid.c @@ -523,7 +523,7 @@ grid_resize_and_reflow( /* Find last non-empty cell */ int col_count = 0; - for (int c = old_cols - 1; c > 0; c--) { + for (int c = old_cols - 1; c >= 0; c--) { const struct cell *cell = &old_row->cells[c]; if (!(cell->wc == 0 || cell->wc == CELL_SPACER)) { col_count = c + 1; From 5a08ed641b39fcd84373ac120ecfb2d11f9987b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 May 2021 17:41:47 +0200 Subject: [PATCH 12/18] grid: reflow: when determining row end coord, check *last* tracking point --- grid.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/grid.c b/grid.c index a2b0c0fc..6d8434ab 100644 --- a/grid.c +++ b/grid.c @@ -535,9 +535,17 @@ grid_resize_and_reflow( /* Do we have a (at least one) tracking point on this row */ struct coord *tp; - if ((*next_tp)->row == old_row_idx) { + if (unlikely((*next_tp)->row == old_row_idx)) { tp = *next_tp; - col_count = max(col_count, tp->col + 1); + + /* Find the *last* tracking point on this row */ + struct coord *last_on_row = tp; + for (struct coord **iter = next_tp; (*iter)->row == old_row_idx; iter++) + last_on_row = *iter; + + /* And make sure its end point is included in the col range */ + xassert(last_on_row->row == old_row_idx); + col_count = max(col_count, last_on_row->col + 1); } else tp = NULL; From ceab9b93675f523f25b1eea410cd42d408d8c020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 May 2021 17:42:09 +0200 Subject: [PATCH 13/18] grid: reflow: when determining row end coord, check *last* URI range --- grid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 6d8434ab..5212e5b6 100644 --- a/grid.c +++ b/grid.c @@ -553,7 +553,11 @@ grid_resize_and_reflow( struct row_uri_range *range; if (old_row->extra != NULL && tll_length(old_row->extra->uri_ranges) > 0) { range = &tll_front(old_row->extra->uri_ranges); - col_count = max(col_count, range->end + 1); + + /* Make sure the *last* URI range's end point is included in the copy */ + const struct row_uri_range *last_on_row = + &tll_back(old_row->extra->uri_ranges); + col_count = max(col_count, last_on_row->end + 1); } else range = NULL; From 8a9643de67458e1958839e994350621e4588808f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 May 2021 17:42:25 +0200 Subject: [PATCH 14/18] grid: reflow: debug logging --- grid.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/grid.c b/grid.c index 5212e5b6..610ae89b 100644 --- a/grid.c +++ b/grid.c @@ -582,13 +582,16 @@ grid_resize_and_reflow( tp_break = end == tp_col; uri_break = end == uri_col; + LOG_DBG("tp+uri break at %d (%d, %d)", end, tp_col, uri_col); } else { end = uri_col; uri_break = true; + LOG_DBG("uri break at %d", end); } } else if (tp != NULL) { end = tp->col + 1; tp_break = true; + LOG_DBG("TP break at %d", end); } else end = col_count; @@ -658,6 +661,8 @@ grid_resize_and_reflow( from += amount; new_col_idx += amount; + xassert(new_col_idx <= new_cols); + if (unlikely(spacers > 0)) { xassert(new_col_idx + spacers == new_cols); @@ -670,31 +675,34 @@ grid_resize_and_reflow( } } - new_col_idx--; + xassert(new_col_idx > 0); if (tp_break) { do { xassert(tp != NULL); xassert(tp->row == old_row_idx); - xassert(tp->col == start + cols - 1); + xassert(tp->col == end - 1); tp->row = new_row_idx; - tp->col = new_col_idx; + tp->col = new_col_idx - 1; next_tp++; tp = *next_tp; - } while (tp->row == old_row_idx && tp->col == start + cols - 1); + } while (tp->row == old_row_idx && tp->col == end - 1); if (tp->row != old_row_idx) tp = NULL; + + LOG_DBG("next TP (tp=%p): %dx%d", + (void*)tp, (*next_tp)->row, (*next_tp)->col); } if (uri_break) { - if (range->start == start + cols - 1) - reflow_uri_range_start(range, new_row, new_col_idx); + if (range->start == end - 1) + reflow_uri_range_start(range, new_row, new_col_idx - 1); - if (range->end == start + cols - 1) { - reflow_uri_range_end(range, new_row, new_col_idx); + if (range->end == end - 1) { + reflow_uri_range_end(range, new_row, new_col_idx - 1); xassert(&tll_front(old_row->extra->uri_ranges) == range); grid_row_uri_range_destroy(range); @@ -706,8 +714,6 @@ grid_resize_and_reflow( } } - new_col_idx++; - left -= cols; start += cols; } @@ -732,8 +738,8 @@ grid_resize_and_reflow( (new_cols - new_col_idx) * sizeof(new_row->cells[0])); for (struct coord **tp = next_tp; *tp != &terminator; tp++) { - LOG_DBG("TP: row=%d, col=%d", - (*tp)->row, (*tp)->col); + LOG_DBG("TP: row=%d, col=%d (old cols: %d, new cols: %d)", + (*tp)->row, (*tp)->col, old_cols, new_cols); } xassert(old_rows == 0 || *next_tp == &terminator); From a003e56fdc2300818760bcdf020219604bb285ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 28 May 2021 18:19:21 +0200 Subject: [PATCH 15/18] grid: reflow: URI range start: take over ownership of URI string Instead of strdup:ing the URI, take over ownership. This is ok since the old URI range will be free:d anyway. --- grid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 610ae89b..d3edd2f0 100644 --- a/grid.c +++ b/grid.c @@ -300,8 +300,9 @@ reflow_uri_range_start(struct row_uri_range *range, struct row *new_row, .start = new_col_idx, .end = -1, .id = range->id, - .uri = xstrdup(range->uri), + .uri = range->uri, }; + range->uri = NULL; grid_row_add_uri_range(new_row, new_range); } From cb83d60089d9acad356d731323dca3f9fdfcc3b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 2 Jun 2021 19:29:06 +0200 Subject: [PATCH 16/18] selection: fix bad assertion When there are multiple multi-column characters back-to-back, the cell before the pivot end point may in fact be a SPACER+1 cell. --- selection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selection.c b/selection.c index 4862def2..2b312139 100644 --- a/selection.c +++ b/selection.c @@ -623,9 +623,9 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term, } xassert(term->grid->rows[pivot_start->row & (term->grid->num_rows - 1)]-> - cells[pivot_start->col].wc < CELL_SPACER); + cells[pivot_start->col].wc <= CELL_SPACER); xassert(term->grid->rows[pivot_end->row & (term->grid->num_rows - 1)]-> - cells[pivot_end->col].wc < CELL_SPACER); + cells[pivot_end->col].wc <= CELL_SPACER + 1); } void From 3292bb5b8eaddb8a0354387fcc2d8f9ffd0d1ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 2 Jun 2021 19:30:01 +0200 Subject: [PATCH 17/18] =?UTF-8?q?grid:=20reflow:=20=E2=80=98amount?= =?UTF-8?q?=E2=80=99=20has=20already=20been=20added=20to=20=E2=80=98from?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index d3edd2f0..4419434c 100644 --- a/grid.c +++ b/grid.c @@ -667,7 +667,7 @@ grid_resize_and_reflow( if (unlikely(spacers > 0)) { xassert(new_col_idx + spacers == new_cols); - const struct cell *cell = &old_row->cells[from + amount - 1]; + const struct cell *cell = &old_row->cells[from - 1]; for (int i = 0; i < spacers; i++, new_col_idx++) { new_row->cells[new_col_idx].wc = CELL_SPACER; From 354de2b8ee53972f5ded8c8f77af106bc24493b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 4 Jun 2021 07:42:53 +0200 Subject: [PATCH 18/18] changelog: text reflow performance --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f38db526..536dd96f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -152,6 +152,7 @@ * Multi-column characters being cut in half when resizing the alternate screen. * Restore `SIGHUP` in spawned processes. +* Text reflow performance (https://codeberg.org/dnkl/foot/issues/504). ### Security