view release on metacpan or search on metacpan
bundled/CMakeLists.txt view on Meta::CPAN
else()
target_compile_options(histo_compiler_flags INTERFACE -Wall -Wextra -Wpedantic)
if(LIBHISTO_WARNINGS_AS_ERRORS)
target_compile_options(histo_compiler_flags INTERFACE -Werror)
endif()
endif()
# Sanitizers configuration
if(NOT MSVC)
if(LIBHISTO_ENABLE_ASAN)
target_compile_options(histo_compiler_flags INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_options(histo_compiler_flags INTERFACE -fsanitize=address,undefined)
elseif(LIBHISTO_ENABLE_TSAN)
target_compile_options(histo_compiler_flags INTERFACE -fsanitize=thread -fno-omit-frame-pointer)
target_link_options(histo_compiler_flags INTERFACE -fsanitize=thread)
elseif(LIBHISTO_ENABLE_MSAN)
target_compile_options(histo_compiler_flags INTERFACE -fsanitize=memory -fno-omit-frame-pointer)
target_link_options(histo_compiler_flags INTERFACE -fsanitize=memory)
endif()
endif()
# Valgrind integration
find_program(VALGRIND_EXECUTABLE valgrind)
if(VALGRIND_EXECUTABLE)
set(MEMORYCHECK_COMMAND ${VALGRIND_EXECUTABLE})
set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --error-exitcode=1")
set(CTEST_MEMORYCHECK_COMMAND ${VALGRIND_EXECUTABLE})
bundled/tools/include/tui_term.h view on Meta::CPAN
} tui_key_type_t;
typedef struct {
tui_key_type_t type;
int ch;
int mouse_x;
int mouse_y;
int mouse_btn;
} tui_key_event_t;
/* Double-buffered frame buffer */
typedef struct {
char *buf;
size_t len;
size_t cap;
} tui_frame_t;
/* Terminal state lifecycle */
bool tui_term_init(void);
void tui_term_restore(void);
bool tui_term_raw_enter(void);
void tui_term_raw_leave(void);
void tui_term_get_size(int *out_cols, int *out_rows);
int tui_term_get_tty_fd(void);
/* Non-blocking key event reader from tty */
tui_key_event_t tui_term_read_key(int tty_fd, int timeout_ms);
/* Frame buffer methods */
void tui_frame_init(tui_frame_t *f, size_t initial_cap);
void tui_frame_clear(tui_frame_t *f);
void tui_frame_append(tui_frame_t *f, const char *str, size_t len);
void tui_frame_puts(tui_frame_t *f, const char *str);
void tui_frame_printf(tui_frame_t *f, const char *fmt, ...);
void tui_frame_flush(tui_frame_t *f, FILE *out_fp);
void tui_frame_free(tui_frame_t *f);
/* Color & style helpers */
void tui_term_get_color(double fraction, bool monochrome, char *out_ansi, size_t max_len);
int tui_visual_width(const char *str);
void tui_render_row(tui_frame_t *f, const char *content, int width, bool newline);
#ifdef __cplusplus
}
#endif
#endif /* HISTO_TUI_TERM_H */
bundled/tools/src/cmd_top.c view on Meta::CPAN
char status_msg[128];
double status_msg_time;
histo_t *frozen_snapshot;
histo2d_t *frozen_snapshot_2d;
} tui_state_t;
static const char *const BLOCKS_UTF8[9] = { " ", "â", "â", "â", "â", "â", "â", "â", "â" };
static void render_top_border(tui_frame_t *f, const char *title, const char *badge, int width) {
tui_frame_printf(f, "\033[1mââ %s ", title);
int title_cols = 3 + (int)strlen(title) + 1; // "ââ " (3) + title + " " (1)
int badge_cols = (badge && badge[0]) ? (tui_visual_width(badge) + 4) : 2; // " " + badge + " ââ"
int dashes = width - title_cols - badge_cols;
if (dashes < 0) dashes = 0;
for (int i = 0; i < dashes; ++i) tui_frame_puts(f, "â");
if (badge && badge[0]) {
tui_frame_printf(f, " %s ââ\033[0m\r\n", badge);
} else {
tui_frame_puts(f, "ââ\033[0m\r\n");
}
}
static void render_divider(tui_frame_t *f, int width) {
tui_frame_puts(f, "â");
for (int i = 0; i < width - 2; ++i) tui_frame_puts(f, "â");
tui_frame_puts(f, "â¤\r\n");
}
static void render_bottom_border(tui_frame_t *f, int width, bool newline) {
tui_frame_puts(f, "â");
for (int i = 0; i < width - 2; ++i) tui_frame_puts(f, "â");
tui_frame_puts(f, "â");
if (newline) tui_frame_puts(f, "\r\n");
}
static void render_2d_heatmap_viewport(tui_frame_t *f, const tui_state_t *st, const histo2d_t *h, int width, int max_rows) {
if (!h || max_rows <= 0) {
for (int r = 0; r < max_rows; ++r) tui_render_row(f, "", width, true);
return;
}
uint32_t nx = histo2d_nbins_x(h);
uint32_t ny = histo2d_nbins_y(h);
if (nx == 0 || ny == 0) {
for (int r = 0; r < max_rows; ++r) tui_render_row(f, "", width, true);
return;
bundled/tools/src/cmd_top.c view on Meta::CPAN
snprintf(buf, sz, "%8.2e", val);
} else if (fabs(val) >= 10000.0) {
snprintf(buf, sz, "%8.1f", val);
} else if (fabs(val) >= 100.0) {
snprintf(buf, sz, "%7.2f", val);
} else {
snprintf(buf, sz, "%6.2f", val);
}
}
static void render_1d_bars_viewport(tui_frame_t *f, const tui_state_t *st, const histo_t *h, int width, int max_rows) {
if (!h || max_rows <= 0) {
for (int r = 0; r < max_rows; ++r) tui_render_row(f, "", width, true);
return;
}
uint32_t nbins = histo_nbins(h);
if (nbins == 0) {
for (int r = 0; r < max_rows; ++r) tui_render_row(f, "", width, true);
return;
}
bundled/tools/src/cmd_top.c view on Meta::CPAN
if (kde) histo_kde_destroy(kde);
if (fit_res) histo_fit_result_destroy(fit_res);
while (rows_drawn < max_rows) {
tui_render_row(f, "", width, true);
rows_drawn++;
}
}
static void render_help_viewport(tui_frame_t *f, const tui_state_t *st, int width, int max_rows) {
if (st && st->view_mode == VIEW_2D_HEATMAP) {
const char *help_lines_2d[] = {
"ââ Interactive Help Cheatsheet (2D Heatmap Mode) âââââââââââââââââââââââââââ",
"",
"DISPLAY & INTENSITY SCALING",
" l Toggle Log-Z intensity color scale (LIN <-> LOG-Z)",
" p / P Cycle color palettes (viridis, plasma, inferno, magma, turbo, cividis, gray, rainbow)",
" C Toggle TrueColor / Monochrome ASCII density",
" g Toggle Color Range Legend reference bar",
" [Space] Freeze / Unfreeze live streaming display",
bundled/tools/src/cmd_top.c view on Meta::CPAN
tui_state_t st;
memset(&st, 0, sizeof(st));
st.monochrome = monochrome;
st.palette = palette;
st.view_mode = is_2d ? VIEW_2D_HEATMAP : VIEW_1D_BARS;
tui_engine_start(&eng);
int tty_fd = tui_term_get_tty_fd();
tui_frame_t frame;
tui_frame_init(&frame, 65536);
while (eng.running) {
tui_key_event_t ev = tui_term_read_key(tty_fd, 33);
if (ev.type != TUI_KEY_NONE) {
if (ev.type == TUI_KEY_CTRL_C || (ev.type == TUI_KEY_CHAR && ev.ch == 3)) {
eng.running = false;
break;
}
if (st.cmd_active) {
if (ev.type == TUI_KEY_ESC) {
bundled/tools/src/cmd_top.c view on Meta::CPAN
if (eng.is_2d) {
snap_2d = tui_engine_get_snapshot_2d(&eng);
n_entries = snap_2d ? histo2d_num_entries(snap_2d) : 0;
tot_w = snap_2d ? histo2d_total_weight(snap_2d) : 0.0;
} else {
snap = st.paused ? st.frozen_snapshot : tui_engine_get_snapshot_1d(&eng);
n_entries = snap ? histo_num_entries(snap) : 0;
tot_w = snap ? histo_total_weight(snap) : 0.0;
}
tui_frame_clear(&frame);
tui_frame_puts(&frame, "\033[H"); // Cursor home
// Row 1: Top border with badge
char badge[96] = "";
if (st.paused) {
if (eng.has_weights) {
snprintf(badge, sizeof(badge), "[PAUSED | N=%lu | W=%.2f]", (unsigned long)n_entries, tot_w);
} else {
snprintf(badge, sizeof(badge), "[PAUSED | N=%lu]", (unsigned long)n_entries);
}
} else if (tui_engine_is_finished(&eng)) {
bundled/tools/src/cmd_top.c view on Meta::CPAN
if (rate >= 1e6) snprintf(rate_str, sizeof(rate_str), "%.1fM/s", rate / 1e6);
else if (rate >= 1e3) snprintf(rate_str, sizeof(rate_str), "%.1fk/s", rate / 1e3);
else snprintf(rate_str, sizeof(rate_str), "%.0f/s", rate);
if (eng.has_weights) {
snprintf(badge, sizeof(badge), "[LIVE: %s | N=%lu | W=%.2f]", rate_str, (unsigned long)n_entries, tot_w);
} else {
snprintf(badge, sizeof(badge), "[LIVE: %s | N=%lu]", rate_str, (unsigned long)n_entries);
}
}
render_top_border(&frame, eng.is_2d ? "libhisto top (2D)" : "libhisto top", badge, cols);
if (st.compact_header) {
char stats_buf[256];
if (eng.is_2d) {
if (snap_2d && n_entries > 0) {
histo2d_axis_t ax, ay;
histo2d_axis_x(snap_2d, &ax);
histo2d_axis_y(snap_2d, &ay);
snprintf(stats_buf, sizeof(stats_buf), "[2D] X:[%.1f,%.1f] Y:[%.1f,%.1f] â N=%lu â %s â Pal:%s",
ax.min, ax.max, ay.min, ay.max, (unsigned long)n_entries,
bundled/tools/src/cmd_top.c view on Meta::CPAN
histo_mean(snap, &mean);
histo_std_dev(snap, &sdev);
histo_median(snap, &med);
histo_quantile(snap, 0.95, &p95);
snprintf(stats_buf, sizeof(stats_buf), "[Col %d] Mean: %.2f ± %.2f â Med: %.2f â P95: %.2f â N=%lu â Pal:%s",
eng.val_col, mean, sdev, med, p95, (unsigned long)n_entries, histo_palette_name(st.palette));
} else {
snprintf(stats_buf, sizeof(stats_buf), "Waiting for stream...");
}
}
tui_render_row(&frame, stats_buf, cols, true);
render_divider(&frame, cols);
} else {
// Row 2: Stats summary
char stats_buf[256];
if (eng.is_2d) {
if (snap_2d && n_entries > 0) {
histo2d_axis_t ax, ay;
histo2d_axis_x(snap_2d, &ax);
histo2d_axis_y(snap_2d, &ay);
snprintf(stats_buf, sizeof(stats_buf),
"X: [%.2f, %.2f] %ux â Y: [%.2f, %.2f] %uy â Entries: %lu â Weight: %.2f",
bundled/tools/src/cmd_top.c view on Meta::CPAN
histo_median(snap, &med);
histo_iqr(snap, &iqr);
histo_quantile(snap, 0.95, &p95);
histo_quantile(snap, 0.99, &p99);
snprintf(stats_buf, sizeof(stats_buf), "Mean: %.2f ± %.2f â Med: %.2f â IQR: %.2f â P95: %.2f â P99: %.2f",
mean, sdev, med, iqr, p95, p99);
} else {
snprintf(stats_buf, sizeof(stats_buf), "Waiting for streaming samples...");
}
}
tui_render_row(&frame, stats_buf, cols, true);
// Row 3: Divider
render_divider(&frame, cols);
// Row 4: Subheader
char subhdr[384];
if (eng.is_2d) {
if (snap_2d) {
histo2d_axis_t ax, ay;
histo2d_axis_x(snap_2d, &ax);
histo2d_axis_y(snap_2d, &ay);
snprintf(subhdr, sizeof(subhdr), "2D Heatmap â X: %u bins [%.1f, %.1f] â Y: %u bins [%.1f, %.1f] â Pal: %s â %s %s",
histo2d_nbins_x(snap_2d), ax.min, ax.max,
bundled/tools/src/cmd_top.c view on Meta::CPAN
if (eng.window_size > 0) snprintf(win_tag, sizeof(win_tag), "Win:%zu â ", eng.window_size);
snprintf(subhdr, sizeof(subhdr), "%s%sRange: [%.2f, %.2f] â Bins: %u â Scale: %s â Pal: %s â Auto: %s %s%s%s%s",
col_tag, win_tag, r_min, r_max, nb, sc,
histo_palette_name(st.palette),
eng.auto_range ? "ON" : "OFF",
kde_tag, fit_tag, err_tag, yaxis_tag);
} else {
snprintf(subhdr, sizeof(subhdr), "Initializing...");
}
tui_render_row(&frame, subhdr, cols, true);
}
if (st.modal == MODAL_HELP) {
render_help_viewport(&frame, &st, cols, viewport_rows);
} else if (eng.is_2d) {
render_2d_heatmap_viewport(&frame, &st, snap_2d, cols, viewport_rows);
} else {
render_1d_bars_viewport(&frame, &st, snap, cols, viewport_rows);
}
// Row rows - 2: Divider
render_divider(&frame, cols);
// Row rows - 1: Footer / Command
if (st.cmd_active) {
char cmd_line[300];
snprintf(cmd_line, sizeof(cmd_line), ":%s\033[7m \033[0m", st.cmd_buf);
tui_render_row(&frame, cmd_line, cols, true);
} else if (st.status_msg[0] != '\0' && (cli_get_time_sec() - st.status_msg_time < 3.0)) {
char status_row[256];
snprintf(status_row, sizeof(status_row), ">> %s", st.status_msg);
tui_render_row(&frame, status_row, cols, true);
} else {
const char *hints = eng.is_2d ?
"[Space] Freeze [+/-] Zoom [ââââ] Pan [l] Log-Z [p] Pal [g] Legend [:] Cmd [?] Help [q] Quit" :
"[Space] Freeze [+/-] Zoom [Tab] Col [w] Win [s] Save [H] Compact [B] Auto-Bins [:] Cmd [?] Help";
tui_render_row(&frame, hints, cols, true);
}
// Row rows: Bottom border (no trailing newline to avoid scrolling on final cell)
render_bottom_border(&frame, cols, false);
tui_frame_puts(&frame, "\033[J"); // Erase below in case window grew or shrank
tui_frame_flush(&frame, stdout);
if (!st.paused && snap) {
histo_destroy(snap);
}
if (!st.paused && snap_2d) {
histo2d_destroy(snap_2d);
}
}
if (st.frozen_snapshot) {
histo_destroy(st.frozen_snapshot);
}
if (st.frozen_snapshot_2d) {
histo2d_destroy(st.frozen_snapshot_2d);
}
tui_frame_free(&frame);
tui_term_restore();
tui_engine_free(&eng);
if (in_fp != stdin) fclose(in_fp);
return 0;
}
bundled/tools/src/tui_term.c view on Meta::CPAN
return ev;
}
ev.type = TUI_KEY_CHAR;
ev.ch = (unsigned char)c;
#endif
return ev;
}
/* Frame buffer methods */
void tui_frame_init(tui_frame_t *f, size_t initial_cap) {
if (!f) return;
if (initial_cap < 4096) initial_cap = 65536;
f->buf = (char *)malloc(initial_cap);
f->len = 0;
f->cap = initial_cap;
if (f->buf) f->buf[0] = '\0';
}
void tui_frame_clear(tui_frame_t *f) {
if (!f) return;
f->len = 0;
if (f->buf) f->buf[0] = '\0';
}
void tui_frame_append(tui_frame_t *f, const char *str, size_t len) {
if (!f || !str || len == 0) return;
if (f->len + len + 1 > f->cap) {
size_t new_cap = (f->cap * 2 > f->len + len + 1) ? f->cap * 2 : f->len + len + 4096;
char *new_buf = (char *)realloc(f->buf, new_cap);
if (!new_buf) return;
f->buf = new_buf;
f->cap = new_cap;
}
memcpy(f->buf + f->len, str, len);
f->len += len;
f->buf[f->len] = '\0';
}
void tui_frame_puts(tui_frame_t *f, const char *str) {
if (!f || !str) return;
tui_frame_append(f, str, strlen(str));
}
void tui_frame_printf(tui_frame_t *f, const char *fmt, ...) {
if (!f || !fmt) return;
char stack_buf[1024];
va_list args;
va_start(args, fmt);
int written = vsnprintf(stack_buf, sizeof(stack_buf), fmt, args);
va_end(args);
if (written < 0) return;
if ((size_t)written < sizeof(stack_buf)) {
tui_frame_append(f, stack_buf, (size_t)written);
} else {
char *heap_buf = (char *)malloc((size_t)written + 1);
if (heap_buf) {
va_start(args, fmt);
vsnprintf(heap_buf, (size_t)written + 1, fmt, args);
va_end(args);
tui_frame_append(f, heap_buf, (size_t)written);
free(heap_buf);
}
}
}
void tui_frame_flush(tui_frame_t *f, FILE *out_fp) {
if (!f || !f->buf || f->len == 0) return;
if (!out_fp) out_fp = stdout;
fwrite(f->buf, 1, f->len, out_fp);
fflush(out_fp);
tui_frame_clear(f);
}
void tui_frame_free(tui_frame_t *f) {
if (!f) return;
if (f->buf) {
free(f->buf);
f->buf = NULL;
}
f->len = 0;
f->cap = 0;
}
void tui_term_get_color(double fraction, bool monochrome, char *out_ansi, size_t max_len) {
bundled/tools/src/tui_term.c view on Meta::CPAN
} else if ((*p & 0xF8) == 0xF0) {
cols += 2;
p += (*(p + 1) && *(p + 2) && *(p + 3)) ? 4 : 1;
} else {
p++;
}
}
return cols;
}
void tui_render_row(tui_frame_t *f, const char *content, int width, bool newline) {
if (!f) return;
tui_frame_puts(f, "â ");
int vis = tui_visual_width(content);
int inner_width = width - 4;
if (inner_width < 1) inner_width = 1;
if (vis <= inner_width) {
tui_frame_puts(f, content ? content : "");
int pad = inner_width - vis;
for (int i = 0; i < pad; ++i) tui_frame_puts(f, " ");
} else {
const unsigned char *p = (const unsigned char *)(content ? content : "");
int cur_cols = 0;
while (*p && cur_cols < inner_width) {
if (*p == '\033') {
const unsigned char *start = p++;
if (*p == '[') {
p++;
while (*p && (*p < 0x40 || *p > 0x7E)) p++;
if (*p) p++;
}
tui_frame_append(f, (const char *)start, (size_t)(p - start));
} else if (*p < 0x80) {
if (*p >= 32 && *p <= 126) cur_cols++;
tui_frame_append(f, (const char *)p, 1);
p++;
} else if ((*p & 0xE0) == 0xC0) {
cur_cols++;
size_t len = (*(p + 1)) ? 2 : 1;
tui_frame_append(f, (const char *)p, len);
p += len;
} else if ((*p & 0xF0) == 0xE0) {
cur_cols++;
size_t len = (*(p + 1) && *(p + 2)) ? 3 : 1;
tui_frame_append(f, (const char *)p, len);
p += len;
} else if ((*p & 0xF8) == 0xF0) {
if (cur_cols + 2 <= inner_width) {
cur_cols += 2;
size_t len = (*(p + 1) && *(p + 2) && *(p + 3)) ? 4 : 1;
tui_frame_append(f, (const char *)p, len);
p += len;
} else break;
} else {
p++;
}
}
int pad = inner_width - cur_cols;
for (int i = 0; i < pad; ++i) tui_frame_puts(f, " ");
}
tui_frame_puts(f, "\033[0m â");
if (newline) tui_frame_puts(f, "\r\n");
}