diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core.c | 2 | ||||
| -rw-r--r-- | src/core.h | 36 | ||||
| -rw-r--r-- | src/fastcsi.c | 4 | ||||
| -rw-r--r-- | src/hist.c | 172 | ||||
| -rw-r--r-- | src/hist.h | 39 | ||||
| -rw-r--r-- | src/hist_view.c | 10 | ||||
| -rw-r--r-- | src/hist_view.h | 8 | ||||
| -rw-r--r-- | src/internal.h | 9 | ||||
| -rw-r--r-- | src/pen.h | 41 | ||||
| -rw-r--r-- | src/screen.c | 32 | ||||
| -rw-r--r-- | src/screen.h | 29 | ||||
| -rw-r--r-- | src/selection.c | 16 | ||||
| -rw-r--r-- | src/text.c | 8 |
13 files changed, 226 insertions, 180 deletions
@@ -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; @@ -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; } @@ -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; } @@ -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); @@ -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; } @@ -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; |
