summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 14:46:44 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 14:46:44 +0300
commit1e69599900a753e2d7275d7ba714202b7d9c843e (patch)
tree13416f651319145aa5dbdd85b9947bec4858b366
parent5ab6b560e8528c27ba934b90f94b1828b7771e27 (diff)
Eliminate pen style and history stream parameter smuggling
- Introduce struct text_pen bundling (fg, bg, sl) across screen and core - Reduce text_append from 9 to 7 parameters - Reduce text_commit from 8 to 6 parameters - Reduce hist_runs_add_pen from 5 to 3 parameters - Reduce sc_text_ascii and sc_ascii_fast from 6 to 4 parameters - Remove unused fg, bg parameters from mark_row_blank - Introduce struct hist_stream_state in src/hist.c, reducing stream_lines from 9 to 5 params and lf_rows from 10 to 6 params - Introduce struct hist_view_pos in src/hist_view.h, reducing term_hist_view_row_pos from 9 to 7 params
-rw-r--r--src/core.c2
-rw-r--r--src/core.h36
-rw-r--r--src/fastcsi.c4
-rw-r--r--src/hist.c172
-rw-r--r--src/hist.h39
-rw-r--r--src/hist_view.c10
-rw-r--r--src/hist_view.h8
-rw-r--r--src/internal.h9
-rw-r--r--src/pen.h41
-rw-r--r--src/screen.c32
-rw-r--r--src/screen.h29
-rw-r--r--src/selection.c16
-rw-r--r--src/text.c8
-rw-r--r--tests/hist_test.c18
14 files changed, 233 insertions, 191 deletions
diff --git a/src/core.c b/src/core.c
index fcd73d0..12daa72 100644
--- a/src/core.c
+++ b/src/core.c
@@ -1009,7 +1009,7 @@ static void stream_queue_finish(struct stream_emit_ctx *ctx)
dst->text.text = dst->ascii;
text_append(dst, (const uint8_t *)(p_ptr + off_in_line),
chunk, chunk, chunk, HLINE_NARROW,
- ctx->attr->fg, ctx->attr->bg, pen_sl(ctx->attr));
+ text_pen_from_attr(ctx->attr));
prev_wrapped = row_wraps;
off_in_line += chunk;
diff --git a/src/core.h b/src/core.h
index beec4e0..79e6eb9 100644
--- a/src/core.h
+++ b/src/core.h
@@ -205,10 +205,9 @@ static inline void sc_text_advance(struct term *t, int x, int cols, int avail)
}
}
-/* Append ASCII at the cursor, with the pen fg, bg, sl (flags | link << 16),
+/* Append ASCII at the cursor, with the pen,
* when the cursor row takes text. Return the bytes taken. */
-static inline int sc_text_ascii(struct term *t, const char *s, int n, uint32_t fg, uint32_t bg,
- uint32_t sl)
+static inline int sc_text_ascii(struct term *t, const char *s, int n, struct text_pen pen)
{
if (TERM_EXPECT(t->insert_mode || t->lr_margin_mode, 0))
return 0;
@@ -225,7 +224,7 @@ static inline int sc_text_ascii(struct term *t, const char *s, int n, uint32_t f
int take = n < avail ? n : avail;
if (take <= 0)
return 0;
- text_append(row, (const uint8_t *)s, take, take, take, HLINE_NARROW, fg, bg, sl);
+ text_append(row, (const uint8_t *)s, take, take, take, HLINE_NARROW, pen);
t->last_char = (unsigned char)s[take - 1];
sc_text_advance(t, x, take, avail);
return take;
@@ -233,11 +232,9 @@ static inline int sc_text_ascii(struct term *t, const char *s, int n, uint32_t f
static inline void sc_print_ascii(struct term *t, const char *s, int n)
{
- uint32_t fg = TERM_EXPECT(t->inverse, 0) ? t->cur_bg : t->cur_fg;
- uint32_t bg = TERM_EXPECT(t->inverse, 0) ? t->cur_fg : t->cur_bg;
- uint32_t flags = term_flags_pad(t);
+ struct text_pen pen = term_text_pen(t);
- for (int k; n > 0 && (k = sc_text_ascii(t, s, n, fg, bg, flags)) > 0; s += k, n -= k) {}
+ for (int k; n > 0 && (k = sc_text_ascii(t, s, n, pen)) > 0; s += k, n -= k) {}
while (n > 0) {
struct span span = sc_span_begin(t, n);
@@ -251,7 +248,7 @@ static inline void sc_print_ascii(struct term *t, const char *s, int n)
if (take <= 0)
break;
}
- put_ascii_chunk(span.cells, s, take, fg, bg, flags);
+ put_ascii_chunk(span.cells, s, take, pen.fg, pen.bg, pen.sl);
sc_span_end(t, &span, take, 0, (unsigned char)s[take - 1]);
if (TERM_EXPECT(n <= take, 1)) break;
s += take;
@@ -261,7 +258,7 @@ static inline void sc_print_ascii(struct term *t, const char *s, int n)
/* Write a short ASCII span without settling an already writable row. */
static inline bool sc_ascii_fast(struct term *t, const char *s, int n,
- uint32_t fg, uint32_t bg, uint32_t flags_pad)
+ struct text_pen pen)
{
int cx = t->cursor_x;
if (TERM_EXPECT(t->pending_wrap || t->insert_mode ||
@@ -270,7 +267,7 @@ static inline bool sc_ascii_fast(struct term *t, const char *s, int n,
return false;
struct srow *row = term_row(t, t->cursor_y);
if (row->kind == ROW_TEXT || (row->len == 0 && cx == 0)) {
- if (sc_text_ascii(t, s, n, fg, bg, flags_pad) == n)
+ if (sc_text_ascii(t, s, n, pen) == n)
return true;
if (row->kind == ROW_TEXT)
return false;
@@ -282,7 +279,7 @@ static inline bool sc_ascii_fast(struct term *t, const char *s, int n,
if (TERM_EXPECT((cx < len && (cells[cx].flags & CELL_TRAIL)) ||
(cx + n < len && (cells[cx + n].flags & CELL_TRAIL)), 0))
return false;
- put_ascii_chunk(cells + cx, s, n, fg, bg, flags_pad);
+ put_ascii_chunk(cells + cx, s, n, pen.fg, pen.bg, pen.sl);
cx += n;
if (cx > len)
row->len = (uint16_t)cx;
@@ -299,8 +296,7 @@ static inline bool sc_ascii_fast(struct term *t, const char *s, int n,
}
/* Fast inline check for 1-character write (fg-char fast path). */
-static inline bool sc_put1_fast(struct term *t, char ch, uint32_t fg, uint32_t bg,
- uint32_t flags_pad)
+static inline bool sc_put1_fast(struct term *t, char ch, struct text_pen pen)
{
int cx = t->cursor_x;
int cols = t->cols;
@@ -312,12 +308,12 @@ static inline bool sc_put1_fast(struct term *t, char ch, uint32_t fg, uint32_t b
if (row->kind == ROW_TEXT) {
struct text_row *tr = &row->text;
if (TERM_EXPECT(cx != tr->cells || !tr->cells || cx + 1 == cols, 0))
- return sc_text_ascii(t, &ch, 1, fg, bg, flags_pad) == 1;
+ return sc_text_ascii(t, &ch, 1, pen) == 1;
/* One character in the middle of a text row. */
tr->text[tr->text_len++] = (uint8_t)ch;
tr->cells++;
- hist_runs_add_pen(&tr->runs, fg, bg, flags_pad, 1);
- if (flags_pad >> 16)
+ hist_runs_add_pen(&tr->runs, pen, 1);
+ if (pen.sl >> 16)
tr->flags |= HLINE_HAS_LINK;
if (tr->cells > row->len)
row->len = tr->cells;
@@ -327,7 +323,7 @@ static inline bool sc_put1_fast(struct term *t, char ch, uint32_t fg, uint32_t b
return true;
}
if (row->len == 0 && cx == 0) {
- if (sc_text_ascii(t, &ch, 1, fg, bg, flags_pad) == 1)
+ if (sc_text_ascii(t, &ch, 1, pen) == 1)
return true;
}
int len = row->len;
@@ -338,8 +334,8 @@ static inline bool sc_put1_fast(struct term *t, char ch, uint32_t fg, uint32_t b
(cx + 1 < len && (cells[cx + 1].flags & CELL_TRAIL)), 0))
return false;
- cells[cx] = (struct cell){ (uint8_t)ch, fg, bg,
- (uint16_t)flags_pad, (uint16_t)(flags_pad >> 16) };
+ cells[cx] = (struct cell){ (uint8_t)ch, pen.fg, pen.bg,
+ (uint16_t)pen.sl, (uint16_t)(pen.sl >> 16) };
t->last_char = (uint8_t)ch;
cx++;
if (cx > len)
diff --git a/src/fastcsi.c b/src/fastcsi.c
index 74d4800..416e3dc 100644
--- a/src/fastcsi.c
+++ b/src/fastcsi.c
@@ -144,7 +144,7 @@ int term_try_fast_csi(struct term *t, const char *buf, size_t rem)
flags_pad = term_flags_pad(t);
flags_ok = true;
}
- if (TERM_EXPECT(!sc_put1_fast(t, ch, fg, bg, flags_pad), 0)) {
+ if (TERM_EXPECT(!sc_put1_fast(t, ch, (struct text_pen){ fg, bg, flags_pad }), 0)) {
t->cur_fg = fg;
t->cur_bg = bg;
sc_print_ascii(t, &ch, 1);
@@ -343,7 +343,7 @@ tail:
flags_pad = term_flags_pad(t);
flags_ok = true;
}
- if (!sc_ascii_fast(t, p, run, fg, bg, flags_pad))
+ if (!sc_ascii_fast(t, p, run, (struct text_pen){ fg, bg, flags_pad }))
sc_print_ascii(t, p, run);
p += run;
}
diff --git a/src/hist.c b/src/hist.c
index 817b0f8..7bd9d04 100644
--- a/src/hist.c
+++ b/src/hist.c
@@ -758,7 +758,7 @@ void term_hist_runs_emit_slow(struct hist_runs *r)
static inline void runs_repeat(struct hist_runs *r)
{
- hist_runs_switch(r, r->pen.fg, r->pen.bg, pen_sl(&r->pen));
+ hist_runs_switch(r, text_pen_from_attr(&r->pen));
}
/* Emit the pending run unless it is a default tail, which readers imply. */
@@ -1144,8 +1144,8 @@ static int encode_cells(struct hist_runs *r, const struct cell *cells, int n, ui
const struct cell *cu = &cells[i];
int k = uniform_utf8_prefix(cu, n - i, dst + o->bytes, &u, &runcells);
if (k > 0) {
- hist_runs_add_pen(r, cu->fg, cu->bg,
- (uint32_t)(cu->flags & STYLE_FLAGS) | ((uint32_t)cu->pad << 16), runcells);
+ hist_runs_add_pen(r, (struct text_pen){ cu->fg, cu->bg,
+ (uint32_t)(cu->flags & STYLE_FLAGS) | ((uint32_t)cu->pad << 16) }, runcells);
o->bytes += u.bytes;
o->cells += u.cells;
o->wide += u.wide;
@@ -1760,19 +1760,28 @@ void term_hist_push_text(struct hist *h, const struct text_row *row, bool wrappe
walk_advance(h);
}
+struct hist_stream_state {
+ uint64_t head;
+ uint64_t rows;
+ uint32_t text_count;
+ uint32_t base;
+ uint32_t pg;
+ const char *first;
+};
+
/* Write the narrow descriptors of ends[i..stop) in blocks, while a block
* fits before the ring wraps. Return the first line left for the caller. */
#if defined(TERM_KERNEL_AVX512)
TERM_TARGET_AVX512
static int lf_rows_avx512(struct hist *restrict h, const uint16_t *restrict ends, int i, int stop,
- uint32_t pg, uint32_t base, uint32_t cols, uint64_t *head_io,
- uint64_t *rows_io, uint32_t *text_count_io)
+ uint32_t cols, struct hist_stream_state *state)
{
struct hline *restrict ring = h->ring;
struct hist_block *restrict blocks = h->blocks;
uint32_t mask = h->ring_mask;
- uint64_t head = *head_io, rows = *rows_io;
- uint32_t text_count = *text_count_io;
+ uint64_t head = state->head, rows = state->rows;
+ uint32_t text_count = state->text_count;
+ uint32_t pg = state->pg, base = state->base;
const __m512i recip = _mm512_set1_epi32((int)(UINT64_C(0x100000000) / cols + 1));
for (; i + 16 <= stop && (head & mask) + 16 <= mask + 1; i += 16, head += 16) {
__m512i start = _mm512_cvtepu16_epi32(_mm256_loadu_si256((const __m256i *)(ends + i)));
@@ -1822,9 +1831,9 @@ static int lf_rows_avx512(struct hist *restrict h, const uint16_t *restrict ends
_mm512_storeu_si512(dst + 8, _mm512_shuffle_i32x4(c, d, 0x88));
_mm512_storeu_si512(dst + 12, _mm512_shuffle_i32x4(c, d, 0xdd));
}
- *head_io = head;
- *rows_io = rows;
- *text_count_io = text_count;
+ state->head = head;
+ state->rows = rows;
+ state->text_count = text_count;
return i;
}
#endif
@@ -1832,14 +1841,14 @@ static int lf_rows_avx512(struct hist *restrict h, const uint16_t *restrict ends
#if defined(TERM_KERNEL_AVX2)
TERM_TARGET_AVX2
static int lf_rows_avx2(struct hist *restrict h, const uint16_t *restrict ends, int i, int stop,
- uint32_t pg, uint32_t base, uint32_t cols, uint64_t *head_io,
- uint64_t *rows_io, uint32_t *text_count_io)
+ uint32_t cols, struct hist_stream_state *state)
{
struct hline *restrict ring = h->ring;
struct hist_block *restrict blocks = h->blocks;
uint32_t mask = h->ring_mask;
- uint64_t head = *head_io, rows = *rows_io;
- uint32_t text_count = *text_count_io;
+ uint64_t head = state->head, rows = state->rows;
+ uint32_t text_count = state->text_count;
+ uint32_t pg = state->pg, base = state->base;
uint32_t recip = (uint32_t)(UINT64_C(0x100000000) / cols + 1);
uint32_t block_rows = 0, block_refs = 0;
for (; i + 4 <= stop && (head & mask) + 4 <= mask + 1; i += 4, head += 4) {
@@ -1881,31 +1890,31 @@ static int lf_rows_avx2(struct hist *restrict h, const uint16_t *restrict ends,
block_rows = block_refs = 0;
}
}
- *head_io = head;
- *rows_io = rows;
- *text_count_io = text_count;
+ state->head = head;
+ state->rows = rows;
+ state->text_count = text_count;
return i;
}
#endif
static inline int lf_rows(struct hist *restrict h, const uint16_t *restrict ends, int i, int stop,
- uint32_t pg, uint32_t base, uint32_t cols, uint64_t *head_io,
- uint64_t *rows_io, uint32_t *text_count_io)
+ uint32_t cols, struct hist_stream_state *state)
{
#if defined(TERM_KERNEL_AVX512)
if (term_use_avx512())
- return lf_rows_avx512(h, ends, i, stop, pg, base, cols, head_io, rows_io, text_count_io);
+ return lf_rows_avx512(h, ends, i, stop, cols, state);
#endif
#if defined(TERM_KERNEL_AVX2)
if (term_use_avx2())
- return lf_rows_avx2(h, ends, i, stop, pg, base, cols, head_io, rows_io, text_count_io);
+ return lf_rows_avx2(h, ends, i, stop, cols, state);
#endif
#if defined(__aarch64__)
struct hline *restrict ring = h->ring;
struct hist_block *restrict blocks = h->blocks;
uint32_t mask = h->ring_mask;
- uint64_t head = *head_io, rows = *rows_io;
- uint32_t text_count = *text_count_io;
+ uint64_t head = state->head, rows = state->rows;
+ uint32_t text_count = state->text_count;
+ uint32_t pg = state->pg, base = state->base;
uint32_t recip = (uint32_t)(UINT64_C(0x100000000) / cols + 1);
uint32_t block_rows = 0, block_refs = 0;
for (; i + 4 <= stop && (head & mask) + 4 <= mask + 1; i += 4, head += 4) {
@@ -1948,12 +1957,11 @@ static inline int lf_rows(struct hist *restrict h, const uint16_t *restrict ends
block_rows = block_refs = 0;
}
}
- *head_io = head;
- *rows_io = rows;
- *text_count_io = text_count;
+ state->head = head;
+ state->rows = rows;
+ state->text_count = text_count;
#else
- (void)h, (void)ends, (void)stop, (void)pg, (void)base, (void)cols;
- (void)head_io, (void)rows_io, (void)text_count_io;
+ (void)h, (void)ends, (void)stop, (void)cols, (void)state;
#endif
return i;
}
@@ -1991,42 +1999,47 @@ void term_hist_push_ascii_lf(struct hist *restrict h, const char *restrict bytes
uint32_t base = (pg << 16) + off - ends[i];
uint32_t span = ends[stop] - ends[i];
memcpy(h->pages[pg] + off, bytes + ends[i], span);
- uint64_t head = h->head, rows = n;
+ struct hist_stream_state state = {
+ .head = h->head,
+ .rows = n,
+ .text_count = 0,
+ .base = base,
+ .pg = pg,
+ };
struct hline *restrict ring = h->ring;
uint32_t mask = h->ring_mask;
- uint32_t text_count = 0;
- while (i < stop && (head & 15)) {
+ while (i < stop && (state.head & 15)) {
uint32_t len = ends[i + 1] - ends[i] - 1;
- struct hline *dst = ring + (head & mask);
+ struct hline *dst = ring + (state.head & mask);
if (!len) *dst = HLINE_BLANK;
else {
uint32_t r = rows_narrow(len, (int)cols);
part_put_narrow_rows(dst, base + ends[i], len, r);
- rows += r - 1;
- text_count++;
+ state.rows += r - 1;
+ state.text_count++;
}
i++;
- head++;
+ state.head++;
}
- i = lf_rows(h, ends, i, stop, pg, base, cols, &head, &rows, &text_count);
- for (; i < stop; i++, head++) {
+ i = lf_rows(h, ends, i, stop, cols, &state);
+ for (; i < stop; i++, state.head++) {
uint32_t len = ends[i + 1] - ends[i] - 1;
- struct hline *dst = ring + (head & mask);
+ struct hline *dst = ring + (state.head & mask);
if (!len) *dst = HLINE_BLANK;
else {
uint32_t r = rows_narrow(len, (int)cols);
part_put_narrow_rows(dst, base + ends[i], len, r);
- rows += r - 1;
- text_count++;
+ state.rows += r - 1;
+ state.text_count++;
}
}
- h->head = head;
- h->page_used[pg] = off + (text_count ? span : 0);
- h->page_parts[pg] += text_count;
+ h->head = state.head;
+ h->page_used[pg] = off + (state.text_count ? span : 0);
+ h->page_parts[pg] += state.text_count;
h->lines += n;
- if (text_count) h->all_blank = false;
+ if (state.text_count) h->all_blank = false;
if (count_rows) {
- h->counted_rows += rows;
+ h->counted_rows += state.rows;
h->rows_id += n;
}
}
@@ -2041,11 +2054,12 @@ static_assert(offsetof(struct hist_ascii_line, len) == 8, "len is in the second
#if defined(TERM_KERNEL_AVX512)
TERM_TARGET_AVX512
static int stream_lines_avx512(struct hist *restrict h, const struct hist_ascii_line *lines,
- int i, int stream_end, const char *first, uint32_t base,
- uint32_t pg, uint64_t *head_io, uint32_t *text_count_io)
+ int i, int stream_end, struct hist_stream_state *state)
{
- uint64_t head = *head_io;
- uint32_t text_count = *text_count_io;
+ uint64_t head = state->head;
+ uint32_t text_count = state->text_count;
+ uint32_t base = state->base, pg = state->pg;
+ const char *first = state->first;
const __m512i even = _mm512_setr_epi64(0, 2, 4, 6, 8, 10, 12, 14);
const __m512i odd = _mm512_setr_epi64(1, 3, 5, 7, 9, 11, 13, 15);
for (; i + 16 <= stream_end &&
@@ -2096,8 +2110,8 @@ static int stream_lines_avx512(struct hist *restrict h, const struct hist_ascii_
};
}
}
- *head_io = head;
- *text_count_io = text_count;
+ state->head = head;
+ state->text_count = text_count;
return i;
}
#endif
@@ -2105,12 +2119,12 @@ static int stream_lines_avx512(struct hist *restrict h, const struct hist_ascii_
#if defined(TERM_KERNEL_AVX2)
TERM_TARGET_AVX2
static int stream_lines_avx2(struct hist *restrict h, const struct hist_ascii_line *lines,
- int i, int stream_end, const char *first, uint32_t base,
- uint32_t pg, uint64_t *head_io, uint32_t *text_count_io)
+ int i, int stream_end, struct hist_stream_state *state)
{
- (void)pg;
- uint64_t head = *head_io;
- uint32_t text_count = *text_count_io;
+ uint64_t head = state->head;
+ uint32_t text_count = state->text_count;
+ uint32_t base = state->base;
+ const char *first = state->first;
for (; i + 4 <= stream_end &&
(head & h->ring_mask) + 4 <= h->ring_mask + 1; i += 4, head += 4) {
struct hline *dst = h->ring + (head & h->ring_mask);
@@ -2136,28 +2150,28 @@ static int stream_lines_avx2(struct hist *restrict h, const struct hist_ascii_li
_mm256_castsi128_si256(_mm_unpacklo_epi64(b, d)), _mm_unpackhi_epi64(b, d), 1));
text_count += refs;
}
- *head_io = head;
- *text_count_io = text_count;
+ state->head = head;
+ state->text_count = text_count;
return i;
}
#endif
static inline int stream_lines(struct hist *restrict h, const struct hist_ascii_line *lines,
- int i, int stream_end, const char *first, uint32_t base,
- uint32_t pg, uint64_t *head_io, uint32_t *text_count_io)
+ int i, int stream_end, struct hist_stream_state *state)
{
#if defined(TERM_KERNEL_AVX512)
if (term_use_avx512())
- return stream_lines_avx512(h, lines, i, stream_end, first, base, pg, head_io, text_count_io);
+ return stream_lines_avx512(h, lines, i, stream_end, state);
#endif
#if defined(TERM_KERNEL_AVX2)
if (term_use_avx2())
- return stream_lines_avx2(h, lines, i, stream_end, first, base, pg, head_io, text_count_io);
+ return stream_lines_avx2(h, lines, i, stream_end, state);
#endif
#if defined(__aarch64__)
- (void)pg;
- uint64_t head = *head_io;
- uint32_t text_count = *text_count_io;
+ uint64_t head = state->head;
+ uint32_t text_count = state->text_count;
+ uint32_t base = state->base;
+ const char *first = state->first;
for (; i + 4 <= stream_end &&
(head & h->ring_mask) + 4 <= h->ring_mask + 1; i += 4, head += 4) {
struct hline *dst = h->ring + (head & h->ring_mask);
@@ -2174,11 +2188,10 @@ static inline int stream_lines(struct hist *restrict h, const struct hist_ascii_
vst4q_u32((uint32_t *)dst, parts);
text_count += refs;
}
- *head_io = head;
- *text_count_io = text_count;
+ state->head = head;
+ state->text_count = text_count;
#else
- (void)h, (void)lines, (void)stream_end, (void)first, (void)base, (void)pg;
- (void)head_io, (void)text_count_io;
+ (void)h, (void)lines, (void)stream_end, (void)state;
#endif
return i;
}
@@ -2249,13 +2262,18 @@ void term_hist_push_ascii_lines(struct hist *h, const struct hist_ascii_line *li
uint32_t off = h->page_used[pg];
memcpy(h->pages[pg] + off, l->ptr, span);
uint32_t base = (pg << 16) | off;
- uint32_t text_count = 0;
- uint64_t head = h->head;
+ struct hist_stream_state state = {
+ .head = h->head,
+ .text_count = 0,
+ .base = base,
+ .pg = pg,
+ .first = l->ptr,
+ };
if (!extra_rows)
- i = stream_lines(h, lines, i, stream_end, l->ptr, base, pg, &head, &text_count);
+ i = stream_lines(h, lines, i, stream_end, &state);
for (; i < stream_end; i++) {
uint32_t len = lines[i].len;
- struct hline *dst = &h->ring[head++ & h->ring_mask];
+ struct hline *dst = &h->ring[state.head++ & h->ring_mask];
if (len) {
uint32_t line_off = base + (uint32_t)(lines[i].ptr - l->ptr);
if (TERM_EXPECT(len <= cols, 1)) {
@@ -2263,7 +2281,7 @@ void term_hist_push_ascii_lines(struct hist *h, const struct hist_ascii_line *li
} else {
part_put_narrow_rows(dst, line_off, len, rows_narrow(len, cols));
}
- text_count++;
+ state.text_count++;
} else {
*dst = HLINE_BLANK;
}
@@ -2272,11 +2290,11 @@ void term_hist_push_ascii_lines(struct hist *h, const struct hist_ascii_line *li
h->counted_rows += stream_rows;
h->rows_id += n;
}
- h->head = head;
+ h->head = state.head;
h->page_used[pg] = off + span;
- h->page_parts[pg] += text_count;
+ h->page_parts[pg] += state.text_count;
h->lines += n;
- if (text_count)
+ if (state.text_count)
h->all_blank = false;
continue;
}
diff --git a/src/hist.h b/src/hist.h
index 1aceae7..74ccb09 100644
--- a/src/hist.h
+++ b/src/hist.h
@@ -91,31 +91,14 @@ struct text_row {
#define TEXT_ROW_BYTES(cols) ((size_t)(cols) * 4 + 16)
#define TEXT_ROW_RUNS(cols) (((size_t)(cols) + 2) * 22)
-/* A pen as three words: fg, bg, and flags | link << 16 (the layout of
- * pen_attr.flags and .link). The hot path reads and writes whole words, so
- * a store is never read back through a wider load. */
-static inline uint32_t pen_sl(const struct pen_attr *p)
-{
- uint32_t sl;
- memcpy(&sl, &p->flags, 4);
- return sl;
-}
-
-static inline void pen_set(struct pen_attr *p, uint32_t fg, uint32_t bg, uint32_t sl)
-{
- p->fg = fg;
- p->bg = bg;
- memcpy(&p->flags, &sl, 4);
-}
-
/* Emit the pending run in the general form. */
void term_hist_runs_emit_slow(struct hist_runs *r);
-/* Close the pending run and start one with the pen fg, bg, sl. The caller
+/* Close the pending run and start one with the given pen. The caller
* sized the buffer. A run that changes only colors and covers fewer than
* 128 cells is a header byte (bit 0 fg, bit 1 bg), a count byte, and the
* changed colors. */
-static inline void hist_runs_switch(struct hist_runs *r, uint32_t fg, uint32_t bg, uint32_t sl)
+static inline void hist_runs_switch(struct hist_runs *r, struct text_pen pen)
{
if (r->count) {
uint32_t psl = pen_sl(&r->pen);
@@ -133,21 +116,21 @@ static inline void hist_runs_switch(struct hist_runs *r, uint32_t fg, uint32_t b
}
pen_set(&r->base, r->pen.fg, r->pen.bg, psl);
}
- pen_set(&r->pen, fg, bg, sl);
+ pen_set_text(&r->pen, pen);
r->count = 0;
}
-static inline void hist_runs_add_pen(struct hist_runs *r, uint32_t fg, uint32_t bg, uint32_t sl,
+static inline void hist_runs_add_pen(struct hist_runs *r, struct text_pen pen,
uint32_t n)
{
- if (fg != r->pen.fg || bg != r->pen.bg || sl != pen_sl(&r->pen))
- hist_runs_switch(r, fg, bg, sl);
+ if (pen.fg != r->pen.fg || pen.bg != r->pen.bg || pen.sl != pen_sl(&r->pen))
+ hist_runs_switch(r, pen);
r->count += n;
}
static inline void hist_runs_add(struct hist_runs *r, const struct pen_attr *pen, uint32_t n)
{
- hist_runs_add_pen(r, pen->fg, pen->bg, pen_sl(pen), n);
+ hist_runs_add_pen(r, text_pen_from_attr(pen), n);
}
/* Start a text row. Each field gets its own store of its own width, because
@@ -155,17 +138,17 @@ static inline void hist_runs_add(struct hist_runs *r, const struct pen_attr *pen
static inline void text_row_start(struct text_row *row, const struct pen_attr *start)
{
if (row->storage) row->text = row->storage;
- uint32_t fg = start->fg, bg = start->bg, sl = pen_sl(start);
+ struct text_pen pen = text_pen_from_attr(start);
row->text_len = 0;
row->cells = 0;
row->wide = 0;
row->fill_to = 0;
row->flags = HLINE_NARROW;
- pen_set(&row->start, fg, bg, sl);
+ pen_set_text(&row->start, pen);
row->runs.len = 0;
row->runs.count = 0;
- pen_set(&row->runs.base, fg, bg, sl);
- pen_set(&row->runs.pen, fg, bg, sl);
+ pen_set_text(&row->runs.base, pen);
+ pen_set_text(&row->runs.pen, pen);
}
static inline void text_row_unshare(struct text_row *row)
diff --git a/src/hist_view.c b/src/hist_view.c
index 29eac35..f5a287b 100644
--- a/src/hist_view.c
+++ b/src/hist_view.c
@@ -320,7 +320,7 @@ void term_hist_view_scroll(struct hist_view *v, const struct hist *h, int cols,
/* A viewport walks exact segments from its anchor, never an estimated row. */
bool term_hist_view_row_pos(const struct hist_view *v, const struct hist *h, int cols,
int screen_rows, struct line_pos screen_top, int y,
- uint64_t *out_id, int *out_seg, int *out_screen_y)
+ struct hist_view_pos *pos)
{
(void)screen_top;
if (v->anchor.active && h->lines) {
@@ -335,17 +335,17 @@ bool term_hist_view_row_pos(const struct hist_view *v, const struct hist *h, int
while (id < hist_end_id(h)) {
int count = term_hist_line_rows(h, id, cols);
if (target < count) {
- *out_id = id;
- *out_seg = (int)target;
+ pos->id = id;
+ pos->seg = (int)target;
return true;
}
target -= count;
id++;
}
- *out_screen_y = target < screen_rows ? (int)target : screen_rows - 1;
+ pos->screen_y = target < screen_rows ? (int)target : screen_rows - 1;
return false;
}
- *out_screen_y = y;
+ pos->screen_y = y;
return false;
}
diff --git a/src/hist_view.h b/src/hist_view.h
index 0214c0b..d09b5d0 100644
--- a/src/hist_view.h
+++ b/src/hist_view.h
@@ -49,12 +49,18 @@ void term_hist_view_scroll_to(struct hist_view *v, const struct hist *h, int col
struct line_pos screen_top, int offset);
void term_hist_view_reset_bottom(struct hist_view *v);
+struct hist_view_pos {
+ uint64_t id;
+ int seg;
+ int screen_y;
+};
+
/* Clamp an evicted anchor. This does not update the index on the write path. */
void term_hist_view_note_push(struct hist_view *v, const struct hist *h, int cols);
void term_hist_view_rewrap(struct hist_view *v, const struct hist *h, int cols);
bool term_hist_view_row_pos(const struct hist_view *v, const struct hist *h, int cols,
int screen_rows, struct line_pos screen_top, int y,
- uint64_t *out_id, int *out_seg, int *out_screen_y);
+ struct hist_view_pos *pos);
#endif
diff --git a/src/internal.h b/src/internal.h
index dd4649c..979883e 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -335,6 +335,15 @@ static inline uint32_t term_flags_pad(const struct term *t)
return term_current_flags(t) | ((uint32_t)t->cur_link << 16);
}
+static inline struct text_pen term_text_pen(const struct term *t)
+{
+ return (struct text_pen){
+ .fg = TERM_EXPECT(t->inverse, 0) ? t->cur_bg : t->cur_fg,
+ .bg = TERM_EXPECT(t->inverse, 0) ? t->cur_fg : t->cur_bg,
+ .sl = term_flags_pad(t),
+ };
+}
+
static inline void term_tabs_clear(struct term *t)
{
memset(t->tabs, 0, (size_t)t->tabs_cap);
diff --git a/src/pen.h b/src/pen.h
index 8349566..f84d6e3 100644
--- a/src/pen.h
+++ b/src/pen.h
@@ -5,6 +5,7 @@
#include "term.h"
#include <stdbool.h>
#include <stdint.h>
+#include <string.h>
struct term_pen {
uint32_t fg;
@@ -28,6 +29,46 @@ struct pen_attr {
uint16_t link;
};
+/* A pen as three words: fg, bg, and flags | link << 16 (the layout of
+ * pen_attr.flags and .link). The hot path reads and writes whole words, so
+ * a store is never read back through a wider load. */
+struct text_pen {
+ uint32_t fg;
+ uint32_t bg;
+ uint32_t sl;
+};
+
+static inline struct text_pen text_pen(uint32_t fg, uint32_t bg, uint32_t sl)
+{
+ return (struct text_pen){ fg, bg, sl };
+}
+
+static inline uint32_t pen_sl(const struct pen_attr *p)
+{
+ uint32_t sl;
+ memcpy(&sl, &p->flags, 4);
+ return sl;
+}
+
+static inline struct text_pen text_pen_from_attr(const struct pen_attr *p)
+{
+ return (struct text_pen){ p->fg, p->bg, pen_sl(p) };
+}
+
+static inline void pen_set(struct pen_attr *p, uint32_t fg, uint32_t bg, uint32_t sl)
+{
+ p->fg = fg;
+ p->bg = bg;
+ memcpy(&p->flags, &sl, 4);
+}
+
+static inline void pen_set_text(struct pen_attr *p, struct text_pen pen)
+{
+ p->fg = pen.fg;
+ p->bg = pen.bg;
+ memcpy(&p->flags, &pen.sl, 4);
+}
+
static inline struct pen_attr pen_attr_default(void)
{
return (struct pen_attr){ .fg = TERM_COLOR_FG, .bg = TERM_COLOR_BG };
diff --git a/src/screen.c b/src/screen.c
index d0f573c..fbfebfc 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -91,7 +91,7 @@ static void screen_push_row_inner(struct term *t, struct srow *row)
if (fill > 0) {
text_row_unshare(tr);
memset(tr->text + tr->text_len, ' ', (size_t)fill);
- text_commit(row, fill, fill, fill, HLINE_NARROW, tr->fill.fg, tr->fill.bg, 0);
+ text_commit(row, fill, fill, fill, HLINE_NARROW, (struct text_pen){ tr->fill.fg, tr->fill.bg, 0 });
}
term_hist_push_text(&t->hist, tr, wrapped);
return;
@@ -193,22 +193,20 @@ const struct cell *term_screen_view_row(const struct term *t, int y)
{
if (!t || y < 0 || y >= t->rows)
return NULL;
- uint64_t id;
- int seg, scr_y;
- if (term_hist_view_row_pos(&t->view, &t->hist, t->cols, t->rows, screen_top_line(t), y,
- &id, &seg, &scr_y)) {
+ struct hist_view_pos pos;
+ if (term_hist_view_row_pos(&t->view, &t->hist, t->cols, t->rows, screen_top_line(t), y, &pos)) {
struct cell *out = t->view_row_scratch;
- int got = term_hist_decode_segment(&t->hist, id, seg, t->cols, out, t->gstore);
+ int got = term_hist_decode_segment(&t->hist, pos.id, pos.seg, t->cols, out, t->gstore);
if (got < t->cols) {
fill_cells(out + got, t->cols - got, TERM_COLOR_FG, TERM_COLOR_BG);
- int line_rows = term_hist_line_rows(&t->hist, id, t->cols);
- if (seg + 1 < line_rows || (id + 1 == hist_end_id(&t->hist) && hist_is_open(&t->hist)))
+ int line_rows = term_hist_line_rows(&t->hist, pos.id, t->cols);
+ if (pos.seg + 1 < line_rows || (pos.id + 1 == hist_end_id(&t->hist) && hist_is_open(&t->hist)))
set_cells_wrap_pad(out + got, t->cols - got);
}
return out;
}
const struct screen *s = term_active_screen(t);
- struct srow *srow = screen_row_at(s, scr_y);
+ struct srow *srow = screen_row_at(s, pos.screen_y);
if (!srow)
return NULL;
struct cell *out = t->view_row_scratch;
@@ -237,18 +235,16 @@ uint64_t term_screen_view_row_key(const struct term *t, int y)
{
if (!t || y < 0 || y >= t->rows)
return 0;
- uint64_t id;
- int seg, scr_y;
- if (term_hist_view_row_pos(&t->view, &t->hist, t->cols, t->rows, screen_top_line(t), y,
- &id, &seg, &scr_y)) {
- uint64_t line_ver = (id + 1 == hist_end_id(&t->hist) && hist_is_open(&t->hist))
- ? (term_hist_line_cells(&t->hist, id) << 32) : 0;
- if (id == t->hist.base_cell_id)
+ struct hist_view_pos pos;
+ if (term_hist_view_row_pos(&t->view, &t->hist, t->cols, t->rows, screen_top_line(t), y, &pos)) {
+ uint64_t line_ver = (pos.id + 1 == hist_end_id(&t->hist) && hist_is_open(&t->hist))
+ ? (term_hist_line_cells(&t->hist, pos.id) << 32) : 0;
+ if (pos.id == t->hist.base_cell_id)
line_ver ^= mix64(t->hist.base_cell);
- return mix64(((uint64_t)id << 16) ^ (uint64_t)seg ^ line_ver ^ 0x517cc1b727220a95ull);
+ return mix64(((uint64_t)pos.id << 16) ^ (uint64_t)pos.seg ^ line_ver ^ 0x517cc1b727220a95ull);
}
const struct screen *s = term_active_screen(t);
- const struct srow *row = screen_row_at(s, scr_y);
+ const struct srow *row = screen_row_at(s, pos.screen_y);
if (!row)
return 0;
return mix64(row->uid ^ ((uint64_t)row->version << 32));
diff --git a/src/screen.h b/src/screen.h
index 8498e9d..5f92929 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -230,10 +230,10 @@ static inline struct srow *text_append_row(struct term *t)
start = text_row_last_pen(&prev->text);
}
if (row->kind == ROW_TEXT) {
- uint32_t sl = pen_sl(&start);
- pen_set(&row->text.start, start.fg, start.bg, sl);
- pen_set(&row->text.runs.base, start.fg, start.bg, sl);
- pen_set(&row->text.runs.pen, start.fg, start.bg, sl);
+ struct text_pen p = text_pen_from_attr(&start);
+ pen_set_text(&row->text.start, p);
+ pen_set_text(&row->text.runs.base, p);
+ pen_set_text(&row->text.runs.pen, p);
return row;
}
row->kind = ROW_TEXT;
@@ -242,10 +242,9 @@ static inline struct srow *text_append_row(struct term *t)
}
/* Take nbytes bytes that a writer put at the end of the row text: chars
- * characters in cells columns, with the pen fg, bg, sl (flags | link << 16).
- * Runs count characters. */
+ * characters in cells columns, with the pen. Runs count characters. */
static inline void text_commit(struct srow *row, int nbytes, int chars, int cells, uint8_t hflags,
- uint32_t fg, uint32_t bg, uint32_t sl)
+ struct text_pen pen)
{
struct text_row *tr = &row->text;
tr->text_len = (uint16_t)(tr->text_len + nbytes);
@@ -253,8 +252,8 @@ static inline void text_commit(struct srow *row, int nbytes, int chars, int cell
tr->wide = (uint16_t)(tr->wide + cells - chars);
tr->flags = (uint8_t)((tr->flags & hflags & HLINE_NARROW) |
((tr->flags | hflags) & ~HLINE_NARROW) |
- ((sl >> 16) ? HLINE_HAS_LINK : 0));
- hist_runs_add_pen(&tr->runs, fg, bg, sl, (uint32_t)chars);
+ ((pen.sl >> 16) ? HLINE_HAS_LINK : 0));
+ hist_runs_add_pen(&tr->runs, pen, (uint32_t)chars);
if (tr->cells > row->len)
row->len = tr->cells;
row->version++;
@@ -263,10 +262,10 @@ static inline void text_commit(struct srow *row, int nbytes, int chars, int cell
}
static inline void text_append(struct srow *row, const uint8_t *bytes, int nbytes, int chars,
- int cells, uint8_t hflags, uint32_t fg, uint32_t bg, uint32_t sl)
+ int cells, uint8_t hflags, struct text_pen pen)
{
bytes_copy_small(row->text.text + row->text.text_len, (const char *)bytes, nbytes);
- text_commit(row, nbytes, chars, cells, hflags, fg, bg, sl);
+ text_commit(row, nbytes, chars, cells, hflags, pen);
}
static inline struct cell *row_cells(const struct term *t, int p)
@@ -340,7 +339,7 @@ static inline bool using_default_colors(const struct term *t)
return t->cur_fg == TERM_COLOR_FG && t->cur_bg == TERM_COLOR_BG;
}
-static inline void mark_row_blank(struct term *t, int p, uint32_t fg, uint32_t bg)
+static inline void mark_row_blank(struct term *t, int p)
{
struct srow *row = &t->scr.slot[p];
uint8_t tail_padded = ((row->kind == ROW_CELLS && row->len == 0) || row->kind == ROW_TEXT)
@@ -349,8 +348,6 @@ static inline void mark_row_blank(struct term *t, int p, uint32_t fg, uint32_t b
row->len = 0;
row->flags = tail_padded;
row->version++;
- (void)fg;
- (void)bg;
}
static inline void term_row_fill_gap(struct srow *row, int x)
@@ -384,7 +381,7 @@ static inline void erase_span(struct term *t, int p, int from, int to)
if (from >= to) return;
if (pristine && from == 0 && to >= len) {
- mark_row_blank(t, p, t->cur_fg, t->cur_bg);
+ mark_row_blank(t, p);
return;
}
struct cell *cells = row_cells(t, p);
@@ -410,7 +407,7 @@ static inline void erase_span(struct term *t, int p, int from, int to)
return;
}
if (from == 0) {
- mark_row_blank(t, p, t->cur_fg, t->cur_bg);
+ mark_row_blank(t, p);
} else {
row->len = (uint16_t)from;
row->flags &= ~RF_TAIL_PADDED;
diff --git a/src/selection.c b/src/selection.c
index b05a43c..4602d8f 100644
--- a/src/selection.c
+++ b/src/selection.c
@@ -281,14 +281,16 @@ static uint32_t line_seg_start_off(const struct lines *L, uint64_t id, int seg)
static bool view_row_line(const struct lines *L, int y, uint64_t *id, int *seg)
{
const struct term *t = L->t;
- int y_scr;
- if (term_hist_view_row_pos(&t->view, &t->hist, L->cols, t->rows, screen_top_line(t), y,
- id, seg, &y_scr))
+ struct hist_view_pos pos;
+ if (term_hist_view_row_pos(&t->view, &t->hist, L->cols, t->rows, screen_top_line(t), y, &pos)) {
+ *id = pos.id;
+ *seg = pos.seg;
return true;
- if (y_scr >= lines_rows(L))
- y_scr = lines_rows(L) - 1;
- *id = L->pos[y_scr].id;
- *seg = (int)L->pos[y_scr].seg;
+ }
+ if (pos.screen_y >= lines_rows(L))
+ pos.screen_y = lines_rows(L) - 1;
+ *id = L->pos[pos.screen_y].id;
+ *seg = (int)L->pos[pos.screen_y].seg;
return true;
}
diff --git a/src/text.c b/src/text.c
index ad3e7f2..c68011f 100644
--- a/src/text.c
+++ b/src/text.c
@@ -140,9 +140,7 @@ static inline bool try_text_run(struct term *t, const char *buf, size_t len,
if (t->cursor_x != 0 &&
t->cursor_x != term_row(t, t->cursor_y)->len)
return false;
- uint32_t fg = t->inverse ? t->cur_bg : t->cur_fg;
- uint32_t bg = t->inverse ? t->cur_fg : t->cur_bg;
- uint32_t flags = term_flags_pad(t);
+ struct text_pen pen = term_text_pen(t);
bool wrote = false;
do {
struct srow *text = t->insert_mode || t->lr_margin_mode ? NULL : text_append_row(t);
@@ -156,7 +154,7 @@ static inline bool try_text_run(struct term *t, const char *buf, size_t len,
wrote = true;
text_commit(text, r.out, r.chars, r.cols,
(uint8_t)((r.narrow ? HLINE_NARROW : 0) | (r.wide ? HLINE_HAS_WIDE : 0)),
- fg, bg, flags);
+ pen);
t->last_char = r.last;
sc_text_advance(t, x, r.cols, avail);
i += (size_t)r.bytes;
@@ -165,7 +163,7 @@ static inline bool try_text_run(struct term *t, const char *buf, size_t len,
if (span.avail < 2)
break;
struct text_run_result r = term_text_run(span.cells, buf + i, (int)(len - i),
- span.avail, fg, bg, flags, t->last_char,
+ span.avail, pen.fg, pen.bg, pen.sl, t->last_char,
&t->text_width);
if (!r.bytes)
break;
diff --git a/tests/hist_test.c b/tests/hist_test.c
index be9fd3f..f05c279 100644
--- a/tests/hist_test.c
+++ b/tests/hist_test.c
@@ -678,27 +678,23 @@ static void test_view_after_prefix_eviction(void)
term_hist_push_ascii(&h, bytes, sizeof(bytes), NULL, true);
}
term_hist_view_scroll_to(&view, &h, 1000, (struct line_pos){0}, 10);
- uint64_t id;
- int seg, screen_y;
+ struct hist_view_pos pos;
struct cell before[1000], after[1000];
- assert(term_hist_view_row_pos(&view, &h, 1000, 3, (struct line_pos){0},
- 0, &id, &seg, &screen_y));
- assert(term_hist_decode_segment(&h, id, seg, 1000, before, NULL) == 1000);
+ assert(term_hist_view_row_pos(&view, &h, 1000, 3, (struct line_pos){0}, 0, &pos));
+ assert(term_hist_decode_segment(&h, pos.id, pos.seg, 1000, before, NULL) == 1000);
for (int i = 100; i < 140; i++) {
memset(bytes, 'A' + i % 26, sizeof(bytes));
term_hist_push_ascii(&h, bytes, sizeof(bytes), NULL, true);
term_hist_view_note_push(&view, &h, 1000);
}
assert(term_hist_line_cells(&h, 0) < 140000);
- assert(term_hist_view_row_pos(&view, &h, 1000, 3, (struct line_pos){0},
- 0, &id, &seg, &screen_y));
- assert(term_hist_decode_segment(&h, id, seg, 1000, after, NULL) == 1000);
+ assert(term_hist_view_row_pos(&view, &h, 1000, 3, (struct line_pos){0}, 0, &pos));
+ assert(term_hist_decode_segment(&h, pos.id, pos.seg, 1000, after, NULL) == 1000);
for (int i = 0; i < 1000; i++)
assert(before[i].c == after[i].c);
term_hist_view_rewrap(&view, &h, 500);
- assert(term_hist_view_row_pos(&view, &h, 500, 3, (struct line_pos){0},
- 0, &id, &seg, &screen_y));
- assert(term_hist_decode_segment(&h, id, seg, 500, after, NULL) == 500);
+ assert(term_hist_view_row_pos(&view, &h, 500, 3, (struct line_pos){0}, 0, &pos));
+ assert(term_hist_decode_segment(&h, pos.id, pos.seg, 500, after, NULL) == 500);
for (int i = 0; i < 500; i++)
assert(before[i].c == after[i].c);
term_hist_view_free(&view);