grid: refactor: first step towards a more generic range handling

This commit is contained in:
Daniel Eklöf 2024-06-23 13:29:12 +02:00
parent cbe399ecd9
commit 20923bb2e8
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
4 changed files with 135 additions and 81 deletions

163
grid.c
View file

@ -85,22 +85,27 @@ ensure_row_has_extra_data(struct row *row)
} }
static void static void
verify_no_overlapping_uris(const struct row_data *extra) verify_no_overlapping_ranges_of_type(const struct row_ranges *ranges,
enum row_range_type type)
{ {
#if defined(_DEBUG) #if defined(_DEBUG)
for (size_t i = 0; i < extra->uri_ranges.count; i++) { for (size_t i = 0; i < ranges->count; i++) {
const struct row_uri_range *r1 = &extra->uri_ranges.v[i]; const struct row_range *r1 = &ranges->v[i];
for (size_t j = i + 1; j < extra->uri_ranges.count; j++) { for (size_t j = i + 1; j < ranges->count; j++) {
const struct row_uri_range *r2 = &extra->uri_ranges.v[j]; const struct row_range *r2 = &ranges->v[j];
xassert(r1 != r2); xassert(r1 != r2);
if ((r1->start <= r2->start && r1->end >= r2->start) || if ((r1->start <= r2->start && r1->end >= r2->start) ||
(r1->start <= r2->end && r1->end >= r2->end)) (r1->start <= r2->end && r1->end >= r2->end))
{ {
BUG("OSC-8 URI overlap: %s: %d-%d: %s: %d-%d", switch (type) {
r1->uri, r1->start, r1->end, case ROW_RANGE_URI:
r2->uri, r2->start, r2->end); BUG("OSC-8 URI overlap: %s: %d-%d: %s: %d-%d",
r1->uri.uri, r1->start, r1->end,
r2->uri.uri, r2->start, r2->end);
break;
}
} }
} }
} }
@ -108,20 +113,31 @@ verify_no_overlapping_uris(const struct row_data *extra)
} }
static void static void
verify_uris_are_sorted(const struct row_data *extra) verify_no_overlapping_ranges(const struct row_data *extra)
{
verify_no_overlapping_ranges_of_type(&extra->uri_ranges, ROW_RANGE_URI);
}
static void
verify_ranges_of_type_are_sorted(const struct row_ranges *ranges,
enum row_range_type type)
{ {
#if defined(_DEBUG) #if defined(_DEBUG)
const struct row_uri_range *last = NULL; const struct row_range *last = NULL;
for (size_t i = 0; i < extra->uri_ranges.count; i++) { for (size_t i = 0; i < ranges->count; i++) {
const struct row_uri_range *r = &extra->uri_ranges.v[i]; const struct row_range *r = &ranges->v[i];
if (last != NULL) { if (last != NULL) {
if (last->start >= r->start || last->end >= r->end) { if (last->start >= r->start || last->end >= r->end) {
BUG("OSC-8 URI not sorted correctly: " switch (type) {
"%s: %d-%d came before %s: %d-%d", case ROW_RANGE_URI:
last->uri, last->start, last->end, BUG("OSC-8 URI not sorted correctly: "
r->uri, r->start, r->end); "%s: %d-%d came before %s: %d-%d",
last->uri.uri, last->start, last->end,
r->uri.uri, r->start, r->end);
break;
}
} }
} }
@ -130,6 +146,12 @@ verify_uris_are_sorted(const struct row_data *extra)
#endif #endif
} }
static void
verify_ranges_are_sorted(const struct row_data *extra)
{
verify_ranges_of_type_are_sorted(&extra->uri_ranges, ROW_RANGE_URI);
}
static void static void
uri_range_ensure_size(struct row_data *extra, uint32_t count_to_add) uri_range_ensure_size(struct row_data *extra, uint32_t count_to_add)
{ {
@ -161,11 +183,13 @@ uri_range_insert(struct row_data *extra, size_t idx, int start, int end,
move_count * sizeof(extra->uri_ranges.v[0])); move_count * sizeof(extra->uri_ranges.v[0]));
extra->uri_ranges.count++; extra->uri_ranges.count++;
extra->uri_ranges.v[idx] = (struct row_uri_range){ extra->uri_ranges.v[idx] = (struct row_range){
.start = start, .start = start,
.end = end, .end = end,
.id = id, .uri = {
.uri = xstrdup(uri), .id = id,
.uri = xstrdup(uri),
},
}; };
} }
@ -174,11 +198,13 @@ uri_range_append_no_strdup(struct row_data *extra, int start, int end,
uint64_t id, char *uri) uint64_t id, char *uri)
{ {
uri_range_ensure_size(extra, 1); uri_range_ensure_size(extra, 1);
extra->uri_ranges.v[extra->uri_ranges.count++] = (struct row_uri_range){ extra->uri_ranges.v[extra->uri_ranges.count++] = (struct row_range){
.start = start, .start = start,
.end = end, .end = end,
.id = id, .uri = {
.uri = uri, .id = id,
.uri = uri,
},
}; };
} }
@ -246,10 +272,10 @@ grid_snapshot(const struct grid *grid)
uri_range_ensure_size(clone_extra, extra->uri_ranges.count); uri_range_ensure_size(clone_extra, extra->uri_ranges.count);
for (size_t i = 0; i < extra->uri_ranges.count; i++) { for (size_t i = 0; i < extra->uri_ranges.count; i++) {
const struct row_uri_range *range = &extra->uri_ranges.v[i]; const struct row_range *range = &extra->uri_ranges.v[i];
uri_range_append( uri_range_append(
clone_extra, clone_extra,
range->start, range->end, range->id, range->uri); range->start, range->end, range->uri.id, range->uri.uri);
} }
} else } else
clone_row->extra = NULL; clone_row->extra = NULL;
@ -467,7 +493,7 @@ grid_resize_without_reflow(
uri_range_ensure_size(new_extra, old_extra->uri_ranges.count); uri_range_ensure_size(new_extra, old_extra->uri_ranges.count);
for (size_t i = 0; i < old_extra->uri_ranges.count; i++) { for (size_t i = 0; i < old_extra->uri_ranges.count; i++) {
const struct row_uri_range *range = &old_extra->uri_ranges.v[i]; const struct row_range *range = &old_extra->uri_ranges.v[i];
if (range->start >= new_cols) { if (range->start >= new_cols) {
/* The whole range is truncated */ /* The whole range is truncated */
@ -476,7 +502,7 @@ grid_resize_without_reflow(
const int start = range->start; const int start = range->start;
const int end = min(range->end, new_cols - 1); const int end = min(range->end, new_cols - 1);
uri_range_append(new_extra, start, end, range->id, range->uri); uri_range_append(new_extra, start, end, range->uri.id, range->uri.uri);
} }
} }
@ -498,8 +524,8 @@ grid_resize_without_reflow(
if (row->extra == NULL) if (row->extra == NULL)
continue; continue;
verify_no_overlapping_uris(row->extra); verify_no_overlapping_ranges(row->extra);
verify_uris_are_sorted(row->extra); verify_ranges_are_sorted(row->extra);
} }
#endif #endif
@ -549,26 +575,26 @@ grid_resize_without_reflow(
} }
static void static void
reflow_uri_range_start(struct row_uri_range *range, struct row *new_row, reflow_uri_range_start(struct row_range *range, struct row *new_row,
int new_col_idx) int new_col_idx)
{ {
ensure_row_has_extra_data(new_row); ensure_row_has_extra_data(new_row);
uri_range_append_no_strdup uri_range_append_no_strdup
(new_row->extra, new_col_idx, -1, range->id, range->uri); (new_row->extra, new_col_idx, -1, range->uri.id, range->uri.uri);
range->uri = NULL; range->uri.uri = NULL;
} }
static void static void
reflow_uri_range_end(struct row_uri_range *range, struct row *new_row, reflow_uri_range_end(struct row_range *range, struct row *new_row,
int new_col_idx) int new_col_idx)
{ {
struct row_data *extra = new_row->extra; struct row_data *extra = new_row->extra;
xassert(extra->uri_ranges.count > 0); xassert(extra->uri_ranges.count > 0);
struct row_uri_range *new_range = struct row_range *new_range =
&extra->uri_ranges.v[extra->uri_ranges.count - 1]; &extra->uri_ranges.v[extra->uri_ranges.count - 1];
xassert(new_range->id == range->id); xassert(new_range->uri.id == range->uri.id);
xassert(new_range->end < 0); xassert(new_range->end < 0);
new_range->end = new_col_idx; new_range->end = new_col_idx;
} }
@ -619,7 +645,7 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row,
* next/current row. * next/current row.
*/ */
if (extra->uri_ranges.count > 0) { if (extra->uri_ranges.count > 0) {
struct row_uri_range *range = struct row_range *range =
&extra->uri_ranges.v[extra->uri_ranges.count - 1]; &extra->uri_ranges.v[extra->uri_ranges.count - 1];
if (range->end < 0) { if (range->end < 0) {
@ -629,7 +655,7 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row,
/* Open a new range on the new/current row */ /* Open a new range on the new/current row */
ensure_row_has_extra_data(new_row); ensure_row_has_extra_data(new_row);
uri_range_append(new_row->extra, 0, -1, range->id, range->uri); uri_range_append(new_row->extra, 0, -1, range->uri.id, range->uri.uri);
} }
} }
@ -817,7 +843,7 @@ grid_resize_and_reflow(
tp = NULL; tp = NULL;
/* Does this row have any URIs? */ /* Does this row have any URIs? */
struct row_uri_range *range, *range_terminator; struct row_range *range, *range_terminator;
struct row_data *extra = old_row->extra; struct row_data *extra = old_row->extra;
if (extra != NULL && extra->uri_ranges.count > 0) { if (extra != NULL && extra->uri_ranges.count > 0) {
@ -826,7 +852,7 @@ grid_resize_and_reflow(
/* Make sure the *last* URI range's end point is included /* Make sure the *last* URI range's end point is included
* in the copy */ * in the copy */
const struct row_uri_range *last_on_row = const struct row_range *last_on_row =
&extra->uri_ranges.v[extra->uri_ranges.count - 1]; &extra->uri_ranges.v[extra->uri_ranges.count - 1];
col_count = max(col_count, last_on_row->end + 1); col_count = max(col_count, last_on_row->end + 1);
} else } else
@ -1043,8 +1069,8 @@ grid_resize_and_reflow(
for (size_t i = 0; i < row->extra->uri_ranges.count; i++) for (size_t i = 0; i < row->extra->uri_ranges.count; i++)
xassert(row->extra->uri_ranges.v[i].end >= 0); xassert(row->extra->uri_ranges.v[i].end >= 0);
verify_no_overlapping_uris(row->extra); verify_no_overlapping_ranges(row->extra);
verify_uris_are_sorted(row->extra); verify_ranges_are_sorted(row->extra);
} }
/* Verify all old rows have been free:d */ /* Verify all old rows have been free:d */
@ -1136,9 +1162,9 @@ grid_row_uri_range_put(struct row *row, int col, const char *uri, uint64_t id)
struct row_data *extra = row->extra; struct row_data *extra = row->extra;
for (ssize_t i = (ssize_t)extra->uri_ranges.count - 1; i >= 0; i--) { for (ssize_t i = (ssize_t)extra->uri_ranges.count - 1; i >= 0; i--) {
struct row_uri_range *r = &extra->uri_ranges.v[i]; struct row_range *r = &extra->uri_ranges.v[i];
const bool matching_id = r->id == id; const bool matching_id = r->uri.id == id;
if (matching_id && r->end + 1 == col) { if (matching_id && r->end + 1 == col) {
/* Extend existing URI's tail */ /* Extend existing URI's tail */
@ -1177,7 +1203,7 @@ grid_row_uri_range_put(struct row *row, int col, const char *uri, uint64_t id)
xassert(r->start < col); xassert(r->start < col);
xassert(r->end > col); xassert(r->end > col);
uri_range_insert(extra, i + 1, col + 1, r->end, r->id, r->uri); uri_range_insert(extra, i + 1, col + 1, r->end, r->uri.id, r->uri.uri);
/* The insertion may xrealloc() the vector, making our /* The insertion may xrealloc() the vector, making our
* 'old' pointer invalid */ * 'old' pointer invalid */
@ -1196,21 +1222,23 @@ grid_row_uri_range_put(struct row *row, int col, const char *uri, uint64_t id)
if (replace) { if (replace) {
grid_row_uri_range_destroy(&extra->uri_ranges.v[insert_idx]); grid_row_uri_range_destroy(&extra->uri_ranges.v[insert_idx]);
extra->uri_ranges.v[insert_idx] = (struct row_uri_range){ extra->uri_ranges.v[insert_idx] = (struct row_range){
.start = col, .start = col,
.end = col, .end = col,
.id = id, .uri = {
.uri = xstrdup(uri), .id = id,
.uri = xstrdup(uri),
},
}; };
} else } else
uri_range_insert(extra, insert_idx, col, col, id, uri); uri_range_insert(extra, insert_idx, col, col, id, uri);
if (run_merge_pass) { if (run_merge_pass) {
for (size_t i = 1; i < extra->uri_ranges.count; i++) { for (size_t i = 1; i < extra->uri_ranges.count; i++) {
struct row_uri_range *r1 = &extra->uri_ranges.v[i - 1]; struct row_range *r1 = &extra->uri_ranges.v[i - 1];
struct row_uri_range *r2 = &extra->uri_ranges.v[i]; struct row_range *r2 = &extra->uri_ranges.v[i];
if (r1->id == r2->id && r1->end + 1 == r2->start) { if (r1->uri.id == r2->uri.id && r1->end + 1 == r2->start) {
r1->end = r2->end; r1->end = r2->end;
uri_range_delete(extra, i); uri_range_delete(extra, i);
i--; i--;
@ -1219,8 +1247,8 @@ grid_row_uri_range_put(struct row *row, int col, const char *uri, uint64_t id)
} }
out: out:
verify_no_overlapping_uris(extra); verify_no_overlapping_ranges(extra);
verify_uris_are_sorted(extra); verify_ranges_are_sorted(extra);
} }
UNITTEST UNITTEST
@ -1233,7 +1261,7 @@ UNITTEST
xassert(idx < row_data.uri_ranges.count); \ xassert(idx < row_data.uri_ranges.count); \
xassert(row_data.uri_ranges.v[idx].start == _start); \ xassert(row_data.uri_ranges.v[idx].start == _start); \
xassert(row_data.uri_ranges.v[idx].end == _end); \ xassert(row_data.uri_ranges.v[idx].end == _end); \
xassert(row_data.uri_ranges.v[idx].id == _id); \ xassert(row_data.uri_ranges.v[idx].uri.id == _id); \
} while (0) } while (0)
grid_row_uri_range_put(&row, 0, "http://foo.bar", 123); grid_row_uri_range_put(&row, 0, "http://foo.bar", 123);
@ -1298,7 +1326,7 @@ grid_row_uri_range_erase(struct row *row, int start, int end)
/* Split up, or remove, URI ranges affected by the erase */ /* Split up, or remove, URI ranges affected by the erase */
for (ssize_t i = (ssize_t)extra->uri_ranges.count - 1; i >= 0; i--) { for (ssize_t i = (ssize_t)extra->uri_ranges.count - 1; i >= 0; i--) {
struct row_uri_range *old = &extra->uri_ranges.v[i]; struct row_range *old = &extra->uri_ranges.v[i];
if (old->end < start) if (old->end < start)
return; return;
@ -1314,7 +1342,7 @@ grid_row_uri_range_erase(struct row *row, int start, int end)
else if (start > old->start && end < old->end) { else if (start > old->start && end < old->end) {
/* Erase range erases a part in the middle of the URI */ /* Erase range erases a part in the middle of the URI */
uri_range_insert( uri_range_insert(
extra, i + 1, end + 1, old->end, old->id, old->uri); extra, i + 1, end + 1, old->end, old->uri.id, old->uri.uri);
/* The insertion may xrealloc() the vector, making our /* The insertion may xrealloc() the vector, making our
* 'old' pointer invalid */ * 'old' pointer invalid */
@ -1352,14 +1380,14 @@ UNITTEST
xassert(row_data.uri_ranges.count == 2); xassert(row_data.uri_ranges.count == 2);
xassert(row_data.uri_ranges.v[1].start == 11); xassert(row_data.uri_ranges.v[1].start == 11);
xassert(row_data.uri_ranges.v[1].end == 20); xassert(row_data.uri_ranges.v[1].end == 20);
verify_no_overlapping_uris(&row_data); verify_no_overlapping_ranges(&row_data);
verify_uris_are_sorted(&row_data); verify_ranges_are_sorted(&row_data);
/* Erase both URis */ /* Erase both URis */
grid_row_uri_range_erase(&row, 1, 20); grid_row_uri_range_erase(&row, 1, 20);
xassert(row_data.uri_ranges.count == 0); xassert(row_data.uri_ranges.count == 0);
verify_no_overlapping_uris(&row_data); verify_no_overlapping_ranges(&row_data);
verify_uris_are_sorted(&row_data); verify_ranges_are_sorted(&row_data);
/* Two URIs, then erase second half of the first, first half of /* Two URIs, then erase second half of the first, first half of
the second */ the second */
@ -1371,11 +1399,11 @@ UNITTEST
xassert(row_data.uri_ranges.v[0].end == 4); xassert(row_data.uri_ranges.v[0].end == 4);
xassert(row_data.uri_ranges.v[1].start == 16); xassert(row_data.uri_ranges.v[1].start == 16);
xassert(row_data.uri_ranges.v[1].end == 20); xassert(row_data.uri_ranges.v[1].end == 20);
verify_no_overlapping_uris(&row_data); verify_no_overlapping_ranges(&row_data);
verify_uris_are_sorted(&row_data); verify_ranges_are_sorted(&row_data);
grid_row_uri_range_destroy(&row_data.uri_ranges.v[0]); grid_row_range_destroy(&row_data.uri_ranges.v[0], ROW_RANGE_URI);
grid_row_uri_range_destroy(&row_data.uri_ranges.v[1]); grid_row_range_destroy(&row_data.uri_ranges.v[1], ROW_RANGE_URI);
row_data.uri_ranges.count = 0; row_data.uri_ranges.count = 0;
/* One URI, erase middle part of it */ /* One URI, erase middle part of it */
@ -1386,11 +1414,11 @@ UNITTEST
xassert(row_data.uri_ranges.v[0].end == 4); xassert(row_data.uri_ranges.v[0].end == 4);
xassert(row_data.uri_ranges.v[1].start == 7); xassert(row_data.uri_ranges.v[1].start == 7);
xassert(row_data.uri_ranges.v[1].end == 10); xassert(row_data.uri_ranges.v[1].end == 10);
verify_no_overlapping_uris(&row_data); verify_no_overlapping_ranges(&row_data);
verify_uris_are_sorted(&row_data); verify_ranges_are_sorted(&row_data);
grid_row_uri_range_destroy(&row_data.uri_ranges.v[0]); grid_row_range_destroy(&row_data.uri_ranges.v[0], ROW_RANGE_URI);
grid_row_uri_range_destroy(&row_data.uri_ranges.v[1]); grid_row_range_destroy(&row_data.uri_ranges.v[1], ROW_RANGE_URI);
row_data.uri_ranges.count = 0; row_data.uri_ranges.count = 0;
/* /*
@ -1416,7 +1444,6 @@ UNITTEST
grid_row_uri_range_erase(&row, 5, 7); grid_row_uri_range_erase(&row, 5, 7);
xassert(row_data.uri_ranges.count == 2); xassert(row_data.uri_ranges.count == 2);
for (size_t i = 0; i < row_data.uri_ranges.count; i++) grid_row_ranges_destroy(&row_data.uri_ranges, ROW_RANGE_URI);
grid_row_uri_range_destroy(&row_data.uri_ranges.v[i]);
free(row_data.uri_ranges.v); free(row_data.uri_ranges.v);
} }

20
grid.h
View file

@ -89,9 +89,25 @@ void grid_row_uri_range_put(
void grid_row_uri_range_erase(struct row *row, int start, int end); void grid_row_uri_range_erase(struct row *row, int start, int end);
static inline void static inline void
grid_row_uri_range_destroy(struct row_uri_range *range) grid_row_uri_range_destroy(struct row_range *range)
{ {
free(range->uri); free(range->uri.uri);
}
static inline void
grid_row_range_destroy(struct row_range *range, enum row_range_type type)
{
switch (type) {
case ROW_RANGE_URI: grid_row_uri_range_destroy(range); break;
}
}
static inline void
grid_row_ranges_destroy(struct row_ranges *ranges, enum row_range_type type)
{
for (int i = 0; i < ranges->count; i++) {
grid_row_range_destroy(&ranges->v[i], type);
}
} }
static inline void static inline void

View file

@ -99,19 +99,30 @@ struct damage {
uint16_t lines; uint16_t lines;
}; };
struct row_uri_range { struct uri_range_data {
int start;
int end;
uint64_t id; uint64_t id;
char *uri; char *uri;
}; };
struct row_range {
int start;
int end;
union {
struct uri_range_data uri;
};
};
struct row_ranges {
struct row_range *v;
int size;
int count;
};
enum row_range_type {ROW_RANGE_URI};
struct row_data { struct row_data {
struct { struct row_ranges uri_ranges;
struct row_uri_range *v;
uint32_t size;
uint32_t count;
} uri_ranges;
}; };
struct row { struct row {

View file

@ -509,7 +509,7 @@ osc8_uris(const struct terminal *term, enum url_action action, url_list_t *urls)
continue; continue;
for (size_t i = 0; i < extra->uri_ranges.count; i++) { for (size_t i = 0; i < extra->uri_ranges.count; i++) {
const struct row_uri_range *range = &extra->uri_ranges.v[i]; const struct row_range *range = &extra->uri_ranges.v[i];
struct coord start = { struct coord start = {
.col = range->start, .col = range->start,
@ -522,8 +522,8 @@ osc8_uris(const struct terminal *term, enum url_action action, url_list_t *urls)
tll_push_back( tll_push_back(
*urls, *urls,
((struct url){ ((struct url){
.id = range->id, .id = range->uri.id,
.url = xstrdup(range->uri), .url = xstrdup(range->uri.uri),
.range = { .range = {
.start = start, .start = start,
.end = end, .end = end,