Refactor stats tables around descriptor-driven row emission

This commit is contained in:
Slobodan Predolac 2026-07-17 09:02:53 -07:00
parent 02f8e972fe
commit e1b5edcc62
3 changed files with 802 additions and 358 deletions

View file

@ -590,6 +590,96 @@ emitter_sparse_row(emitter_t *emitter, emitter_row_t *row, bool is_gap) {
}
}
/*
* Descriptor-driven column tables. A descriptor array defines table order;
* callers provide a separate index array when JSON requires a different
* subset or ordering. The getter lets one engine serve different row types
* while keeping derived values (rates, utilization, and so on) with the
* table-specific code. A column is active when all its required_flags are
* present in the active_flags supplied by the caller; the emitter assigns no
* domain-specific meaning to those bits.
*/
typedef struct emitter_col_desc_s emitter_col_desc_t;
struct emitter_col_desc_s {
const char *json_key;
const char *table_label;
emitter_justify_t justify;
int width;
emitter_type_t type;
unsigned required_flags;
void (*get)(const void *row, emitter_col_t *col);
};
static inline bool
emitter_col_desc_active(const emitter_col_desc_t *desc,
unsigned active_flags) {
return (desc->required_flags & ~active_flags) == 0;
}
static inline void
emitter_col_table_build(const emitter_col_desc_t *descs, size_t ndescs,
unsigned active_flags, emitter_row_t *row, emitter_col_t *cols,
emitter_row_t *header_row, emitter_col_t *header_cols) {
emitter_row_init(row);
emitter_row_init(header_row);
for (size_t i = 0; i < ndescs; i++) {
if (!emitter_col_desc_active(&descs[i], active_flags)) {
continue;
}
emitter_col_init(&cols[i], row);
cols[i].justify = descs[i].justify;
cols[i].width = descs[i].width;
cols[i].type = descs[i].type;
emitter_col_init(&header_cols[i], header_row);
header_cols[i].justify = descs[i].justify;
header_cols[i].width = descs[i].width;
header_cols[i].type = emitter_type_title;
header_cols[i].str_val = descs[i].table_label != NULL
? descs[i].table_label : descs[i].json_key;
}
}
static inline void
emitter_col_table_header(emitter_t *emitter, emitter_row_t *header_row,
emitter_col_t *first_header_col, const char *table_prefix,
const char *json_key) {
first_header_col->width -= (int)strlen(table_prefix);
emitter_table_printf(emitter, "%s", table_prefix);
emitter_table_row(emitter, header_row);
emitter_json_array_kv_begin(emitter, json_key);
}
static inline void
emitter_col_table_fill(const emitter_col_desc_t *descs, size_t ndescs,
unsigned active_flags, emitter_col_t *cols, const void *row) {
for (size_t i = 0; i < ndescs; i++) {
if (!emitter_col_desc_active(&descs[i], active_flags)) {
continue;
}
cols[i].type = descs[i].type;
descs[i].get(row, &cols[i]);
}
}
static inline void
emitter_col_table_emit_json(emitter_t *emitter,
const emitter_col_desc_t *descs, size_t ndescs, unsigned active_flags,
emitter_col_t *cols, const unsigned *json_order, size_t json_order_len) {
for (size_t i = 0; i < json_order_len; i++) {
unsigned col_ind = json_order[i];
assert(col_ind < ndescs);
const emitter_col_desc_t *desc = &descs[col_ind];
if (!emitter_col_desc_active(desc, active_flags)) {
continue;
}
assert(desc->json_key != NULL);
emitter_json_kv(emitter, desc->json_key, cols[col_ind].type,
(const void *)&cols[col_ind].bool_val);
}
}
static inline void
emitter_begin(emitter_t *emitter) {
if (emitter_outputs_json(emitter)) {

File diff suppressed because it is too large Load diff

View file

@ -668,6 +668,111 @@ static const char *sparse_row_table =
" 2\n"
" ---\n";
typedef struct {
size_t size;
uint64_t count;
uint64_t prof_count;
uint64_t requests;
} col_table_test_row_t;
#define COL_TABLE_TEST_GETTER(name, member, value_member) \
static void \
col_table_test_get_##name(const void *vrow, emitter_col_t *col) { \
const col_table_test_row_t *row = vrow; \
col->value_member = row->member; \
}
COL_TABLE_TEST_GETTER(size, size, size_val)
COL_TABLE_TEST_GETTER(count, count, uint64_val)
COL_TABLE_TEST_GETTER(prof_count, prof_count, uint64_val)
COL_TABLE_TEST_GETTER(requests, requests, uint64_val)
#undef COL_TABLE_TEST_GETTER
enum {
COL_TABLE_TEST_SIZE,
COL_TABLE_TEST_COUNT,
COL_TABLE_TEST_PROF_COUNT,
COL_TABLE_TEST_REQUESTS,
COL_TABLE_TEST_NCOLS
};
#define COL_TABLE_TEST_FLAG_PROF (1U << 0)
static const emitter_col_desc_t col_table_test_descs[] = {
{NULL, "size", emitter_justify_right, 8, emitter_type_size, 0,
col_table_test_get_size},
{"count", "count", emitter_justify_right, 8, emitter_type_uint64,
0, col_table_test_get_count},
{"prof_count", "prof", emitter_justify_right, 6, emitter_type_uint64,
COL_TABLE_TEST_FLAG_PROF, col_table_test_get_prof_count},
{"requests", "requests", emitter_justify_right, 10,
emitter_type_uint64, 0, col_table_test_get_requests},
};
_Static_assert(
sizeof(col_table_test_descs) / sizeof(col_table_test_descs[0]) ==
COL_TABLE_TEST_NCOLS,
"col_table_test_descs must match COL_TABLE_TEST_NCOLS");
TEST_BEGIN(test_col_desc_flags) {
expect_true(emitter_col_desc_active(
&col_table_test_descs[COL_TABLE_TEST_COUNT], 0),
"A column without requirements should always be active");
expect_false(emitter_col_desc_active(
&col_table_test_descs[COL_TABLE_TEST_PROF_COUNT], 0),
"A column with an unmet requirement should be inactive");
expect_true(emitter_col_desc_active(
&col_table_test_descs[COL_TABLE_TEST_PROF_COUNT],
COL_TABLE_TEST_FLAG_PROF),
"A column with a satisfied requirement should be active");
}
TEST_END
static const unsigned col_table_test_json_order[] = {
COL_TABLE_TEST_REQUESTS,
COL_TABLE_TEST_PROF_COUNT,
COL_TABLE_TEST_COUNT,
};
static void
emit_col_table(emitter_t *emitter) {
col_table_test_row_t value = {42, 7, 100, 9};
emitter_row_t row, header_row;
emitter_col_t cols[COL_TABLE_TEST_NCOLS];
emitter_col_t header_cols[COL_TABLE_TEST_NCOLS];
emitter_begin(emitter);
emitter_col_table_build(col_table_test_descs, COL_TABLE_TEST_NCOLS,
COL_TABLE_TEST_FLAG_PROF, &row, cols, &header_row, header_cols);
emitter_col_table_header(
emitter, &header_row, &header_cols[0], "mock:", "rows");
emitter_col_table_fill(col_table_test_descs, COL_TABLE_TEST_NCOLS,
COL_TABLE_TEST_FLAG_PROF, cols, &value);
emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, col_table_test_descs,
COL_TABLE_TEST_NCOLS, COL_TABLE_TEST_FLAG_PROF, cols,
col_table_test_json_order, sizeof(col_table_test_json_order) /
sizeof(col_table_test_json_order[0]));
emitter_json_object_end(emitter);
emitter_table_row(emitter, &row);
emitter_json_array_end(emitter);
emitter_end(emitter);
}
static const char *col_table_json =
"{\n"
"\t\"rows\": [\n"
"\t\t{\n"
"\t\t\t\"requests\": 9,\n"
"\t\t\t\"prof_count\": 100,\n"
"\t\t\t\"count\": 7\n"
"\t\t}\n"
"\t]\n"
"}\n";
static const char *col_table_json_compact =
"{\"rows\":[{\"requests\":9,\"prof_count\":100,\"count\":7}]}";
static const char *col_table_table =
"mock:size count prof requests\n"
" 42 7 100 9\n";
#define GENERATE_TEST(feature) \
TEST_BEGIN(test_##feature) { \
expect_emit_output(emit_##feature, feature##_json, \
@ -685,10 +790,12 @@ GENERATE_TEST(json_nested_array)
GENERATE_TEST(table_row)
GENERATE_TEST(row)
GENERATE_TEST(sparse_row)
GENERATE_TEST(col_table)
int
main(void) {
return test_no_reentrancy(test_dict, test_table_printf,
test_nested_dict, test_types, test_modal, test_json_array,
test_json_nested_array, test_table_row, test_row, test_sparse_row);
test_json_nested_array, test_table_row, test_row, test_sparse_row,
test_col_table, test_col_desc_flags);
}