==> src/sparkline.c <==1/* ratatui-widgets 0.3.2's Sparkline (every bar present: no absent values). */
2#include "tui/sparkline.h"
3
4static const char *const NINE_LEVELS[9] = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};
5
6/* floor(a * b / c) for b < 2^20, in 128 bits made of two u64 (ratatui computes in u128). */
7static uint64_t mul_div(uint64_t a, uint64_t b, uint64_t c) {
8 uint64_t lo_part = (a & 0xFFFFFFFFu) * b, hi_part = (a >> 32) * b;
9 uint64_t lo = lo_part + (hi_part << 32);
10 uint64_t hi = (hi_part >> 32) + (lo < lo_part);
11 uint64_t q = 0, r = 0;
12 for (int i = 127; i >= 0; i--) {
13 uint64_t bit = i >= 64 ? (hi >> (i - 64)) & 1 : (lo >> i) & 1;
14 uint64_t top = r >> 63;
15 r = r << 1 | bit;
16 if (top || r >= c) {
17 r -= c;
18 if (i < 64) q |= (uint64_t)1 << i;
19 }
20 }
21 return q; /* the quotient's high half is 0 for our inputs (value * ticks / max <= ticks when value <= max) */
22}
23
24static uint64_t scale_height(uint64_t value, uint64_t max, uint16_t max_height) {
25 if (max == 0) return 0;
26 uint64_t max_ticks = (uint64_t)max_height * 8;
27 uint64_t ticks = mul_div(value, max_ticks, max);
28 /* A quotient past 2^64 (value far above max) still clamps to max_ticks. */
29 if (value > max && value / max >= max_ticks) return max_ticks;
30 return ticks < max_ticks ? ticks : max_ticks;
31}
32
33void tui_sparkline_render(tui_arena *a, const tui_sparkline *s, tui_rect area, tui_buffer *buf) {
34 if (s->block) tui_block_render(a, s->block, area, buf);
35 tui_rect sa = tui_block_inner_opt(s->block, area);
36 if (tui_rect_empty(sa)) return;
37 uint64_t max = s->max;
38 if (!s->has_max) {
39 max = 1;
40 if (s->n) {
41 max = 0;
42 for (size_t i = 0; i < s->n; i++)
43 if (s->data[i] > max) max = s->data[i];
44 }
45 }
46 size_t count = s->n < sa.width ? s->n : sa.width;
47 for (size_t i = 0; i < count; i++) {
48 uint16_t x = s->right_to_left ? (uint16_t)(tui_rect_right(sa) - i - 1) : (uint16_t)(sa.x + i);
49 uint64_t h = scale_height(s->data[i], max, sa.height);
50 for (uint16_t j = sa.height; j-- > 0;) {
51 const char *sym = NINE_LEVELS[h > 8 ? 8 : h];
52 h = h > 8 ? h - 8 : 0;
53 tui_cell *c = tui_buffer_cell(buf, x, (uint16_t)(sa.y + j));
54 if (!c) continue;
55 tui_cell_set_str(c, sym);
56 tui_cell_set_style(c, s->style);
57 }
58 }
59}