daemon daemon-ctrl \
signames \
opt opt-help opt-conf \
- table
+ table table-types
LIBUCW_MAIN_INCLUDES= \
lib.h log.h tbf.h threads.h time.h \
signames.h \
sighandler.h \
opt.h \
- table.h
+ table.h table-types.h
ifdef CONFIG_UCW_THREADS
# Some modules require threading
$(o)/ucw/trie-test: $(o)/ucw/trie-test.o $(LIBUCW)
$(o)/ucw/opt-test: $(o)/ucw/opt-test.o $(LIBUCW)
$(o)/ucw/table-test: $(o)/ucw/table-test.o $(LIBUCW)
+$(o)/ucw/table-test-2: $(o)/ucw/table-test-2.o $(LIBUCW)
$(o)/ucw/table-test-align: $(o)/ucw/table-test-align.o $(LIBUCW)
TESTS+=$(addprefix $(o)/ucw/,regex.test unicode.test hash-test.test mempool.test stkstring.test \
fb-mem.test fb-buffer.test fb-mmap.test fb-multi.test fb-null.test \
redblack-test.test url.test strtonum-test.test \
gary.test time.test crc.test signames.test md5.test bitops.test opt.test \
- table.test table-test.test table-test-align.test)
+ table.test table-test.test table-test-2.test table-test-align.test)
$(o)/ucw/varint.test: $(o)/ucw/varint-t
$(o)/ucw/regex.test: $(o)/ucw/regex-t
$(o)/ucw/opt.test: $(o)/ucw/opt-test
$(o)/ucw/table.test: $(o)/ucw/table-t
$(o)/ucw/table-test.test: $(o)/ucw/table-test
+$(o)/ucw/table-test-2.test: $(o)/ucw/table-test-2
$(o)/ucw/table-test-align.test: $(o)/ucw/table-test-align
ifdef CONFIG_UCW_THREADS
--- /dev/null
+/*
+ * Unit tests of table printer
+ *
+ * (c) 2014 Robert Kessl <robert.kessl@economia.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/table.h>
+#include <ucw/table-types.h>
+#include <ucw/opt.h>
+#include <stdio.h>
+
+enum test_table_cols {
+ TEST_COL0_SIZE, TEST_COL1_TS
+};
+
+static struct table test_tbl = {
+ TBL_COLUMNS {
+ [TEST_COL0_SIZE] = TBL_COL_SIZE_FMT("size", 10, UNIT_BYTE),
+ [TEST_COL1_TS] = TBL_COL_TIMESTAMP("ts", 20),
+ TBL_COL_END
+ },
+ TBL_OUTPUT_HUMAN_READABLE,
+};
+
+static void do_test(void)
+{
+ struct fastbuf *out;
+ out = bfdopen_shared(1, 4096);
+ table_init(&test_tbl);
+ table_start(&test_tbl, out);
+
+ u64 test_time = 1403685533;
+ s64 test_size = 4LU*(1024LU * 1024LU * 1024LU);
+
+ table_col_size(&test_tbl, TEST_COL0_SIZE, test_size);
+ table_col_timestamp(&test_tbl, TEST_COL1_TS, test_time);
+ table_end_row(&test_tbl);
+
+ test_tbl.column_order[TEST_COL0_SIZE].output_type = UNIT_KILOBYTE;
+ table_col_size(&test_tbl, TEST_COL0_SIZE, test_size);
+ table_col_timestamp(&test_tbl, TEST_COL1_TS, test_time);
+ table_end_row(&test_tbl);
+
+ test_tbl.column_order[TEST_COL0_SIZE].output_type = UNIT_MEGABYTE;
+ table_col_size(&test_tbl, TEST_COL0_SIZE, test_size);
+ table_col_timestamp(&test_tbl, TEST_COL1_TS, test_time);
+ table_end_row(&test_tbl);
+
+ test_tbl.column_order[TEST_COL0_SIZE].output_type = UNIT_GIGABYTE;
+ test_tbl.column_order[TEST_COL1_TS].output_type = TIMESTAMP_DATETIME;
+ table_col_size(&test_tbl, TEST_COL0_SIZE, test_size);
+ table_col_timestamp(&test_tbl, TEST_COL1_TS, test_time);
+ table_end_row(&test_tbl);
+
+ table_end(&test_tbl);
+ table_cleanup(&test_tbl);
+
+ bclose(out);
+}
+
+int main(int argc UNUSED, char **argv UNUSED)
+{
+ do_test();
+ return 0;
+}
+
--- /dev/null
+Run: ../obj/ucw/table-test-2
+Out <<EOF
+ size ts
+4294967296 1403685533
+ 4194304KB 1403685533
+ 4096MB 1403685533
+ 4GB 2014-06-25 08:38:53
+EOF
test_col0_str, test_col1_int, test_col2_uint, test_col3_bool, test_col4_double
};
-static uint test_column_order[] = { test_col3_bool, test_col4_double, test_col2_uint,test_col1_int, test_col0_str };
+static struct table_col_info test_column_order[] = { TBL_COL(test_col3_bool), TBL_COL(test_col4_double), TBL_COL(test_col2_uint), TBL_COL(test_col1_int), TBL_COL(test_col0_str) };
static struct table test_tbl = {
TBL_COLUMNS {
--- /dev/null
+#include <ucw/lib.h>
+#include <ucw/config.h>
+#include <ucw/table-types.h>
+#include <ucw/fastbuf.h>
+#include <ucw/config.h>
+#include <ucw/table.h>
+#include <time.h>
+#include <stdio.h>
+
+void table_col_name(struct table *tbl, const char *col_name, u64 val)
+{
+}
+
+void table_col_size(struct table *tbl, int col, u64 val)
+{
+ ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);
+ ASSERT(tbl->columns[col].type == COL_TYPE_ANY || COL_TYPE_SIZE == tbl->columns[col].type);
+
+ tbl->last_printed_col = col;
+ tbl->row_printing_started = 1;
+
+ static u64 unit_div[] = {
+ [UNIT_BYTE] = (u64) 1,
+ [UNIT_KILOBYTE] = (u64) 1024LU,
+ [UNIT_MEGABYTE] = (u64) (1024LU * 1024LU),
+ [UNIT_GIGABYTE] = (u64) (1024LU * 1024LU * 1024LU),
+ [UNIT_TERABYTE] = (u64) (1024LU * 1024LU * 1024LU * 1024LU)
+ };
+
+ static const char *unit_suffix[] = {
+ [UNIT_BYTE] = "",
+ [UNIT_KILOBYTE] = "KB",
+ [UNIT_MEGABYTE] = "MB",
+ [UNIT_GIGABYTE] = "GB",
+ [UNIT_TERABYTE] = "TB"
+ };
+
+ // FIXME: do some rounding?
+ val = val / unit_div[tbl->column_order[col].output_type];
+
+ tbl->col_str_ptrs[col] = mp_printf(tbl->pool, "%lu%s", val, unit_suffix[tbl->column_order[col].output_type]);
+}
+
+#define FORMAT_TIME_SIZE 20 // Minimum buffer size
+
+void table_col_timestamp(struct table *tbl, int col, u64 val)
+{
+ ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);
+ ASSERT(tbl->columns[col].type == COL_TYPE_ANY || COL_TYPE_TIMESTAMP == tbl->columns[col].type);
+ //ASSERT(fmt != NULL);
+
+ char formatted_time_buf[FORMAT_TIME_SIZE];
+
+ time_t tmp_time = (time_t)val;
+ struct tm t = *gmtime(&tmp_time);
+
+ switch (tbl->column_order[col].output_type) {
+ case TIMESTAMP_EPOCH:
+ sprintf(formatted_time_buf, "%u", (uint) val);
+ break;
+ case TIMESTAMP_DATETIME:
+ strftime(formatted_time_buf, FORMAT_TIME_SIZE, "%F %T", &t);
+ default:
+ break;
+ }
+
+ tbl->col_str_ptrs[col] = mp_printf(tbl->pool, "%s", formatted_time_buf);
+}
+
--- /dev/null
+#ifndef _UCW_TABLE_TYPES_H
+#define _UCW_TABLE_TYPES_H
+
+#include <ucw/table.h>
+
+enum size_units {
+ UNIT_BYTE,
+ UNIT_KILOBYTE,
+ UNIT_MEGABYTE,
+ UNIT_GIGABYTE,
+ UNIT_TERABYTE,
+ UNIT_AUTO
+};
+
+enum timestamp_format {
+ TIMESTAMP_EPOCH,
+ TIMESTAMP_DATETIME
+};
+
+
+#define COL_TYPE_SIZE COL_TYPE_CUSTOM
+#define COL_TYPE_TIMESTAMP (COL_TYPE_CUSTOM+1)
+
+#define TBL_COL_SIZE(_name, _width) { .name = _name, .width = _width, .fmt = "%llu", .type = COL_TYPE_SIZE }
+#define TBL_COL_TIMESTAMP(_name, _width) { .name = _name, .width = _width, .fmt = "%lld", .type = COL_TYPE_TIMESTAMP }
+
+#define TBL_COL_SIZE_FMT(_name, _width, _units) { .name = _name, .width = _width, .fmt = "%llu", .type = COL_TYPE_SIZE }
+#define TBL_COL_TIMESTAMP_FMT(_name, _width, _fmt) { .name = _name, .width = _width, .fmt = "%lld", .type = COL_TYPE_TIMESTAMP }
+
+/*
+ union {
+ enum size_units units;
+ enum timestamp_format ts_fmt;
+ };
+*/
+/*
+#define TBL_COL_SIZE(_name, _width) { .name = _name, .width = _width, .fmt = "%llu", .type = COL_TYPE_SIZE }
+#define TBL_COL_TIMESTAMP(_name, _width) { .name = _name, .width = _width, .fmt = "%lld", .type = COL_TYPE_TIMESTAMP }
+
+#define TBL_COL_SIZE_FMT(_name, _width, _units) { .name = _name, .width = _width, .fmt = "%llu", .type = COL_TYPE_SIZE }
+#define TBL_COL_TIMESTAMP_FMT(_name, _width, _fmt) { .name = _name, .width = _width, .fmt = "%lld", .type = COL_TYPE_TIMESTAMP }
+
+#define TABLE_COL_PROTO(_name_, _type_) void table_col_##_name_(struct table *tbl, int col, _type_ val);\
+ void table_col_##_name_##_name(struct table *tbl, const char *col_name, _type_ val);\
+ void table_col_##_name_##_fmt(struct table *tbl, int col, _type_ val) FORMAT_CHECK(printf, 3, 0);
+//TABLE_COL_PROTO(size, u64);
+//TABLE_COL_PROTO(timestamp, u64);
+#undef TABLE_COL_PROTO
+*/
+
+void table_col_size(struct table *tbl, int col, u64 val);
+void table_col_timestamp(struct table *tbl, int col, u64 val);
+
+//TABLE_COL(size, u64, COL_TYPE_SIZE)
+//TABLE_COL_STR(size, u64, COL_TYPE_SIZE)
+
+//TABLE_COL(timestamp, u64, COL_TYPE_TIMESTAMP)
+//TABLE_COL_STR(timestamp, u64, COL_TYPE_TIMESTAMP)
+
+
+#endif
ASSERT(tbl->columns[col_count].name != NULL);
ASSERT(tbl->columns[col_count].type == COL_TYPE_ANY || tbl->columns[col_count].fmt != NULL);
ASSERT(tbl->columns[col_count].width != 0);
- ASSERT(tbl->columns[col_count].type < COL_TYPE_LAST);
col_count++;
}
tbl->pool = mp_new(4096);
ASSERT_MSG(col_order[i] >= 0 && col_order[i] < tbl->column_count, "Column %d does not exist (column number should be between 0 and %d)", col_order[i], tbl->column_count - 1);
}
- tbl->column_order = col_order;
tbl->cols_to_output = cols_to_output;
+ tbl->column_order = mp_alloc_zero(tbl->pool, sizeof(struct table_col_info) * cols_to_output);
+ for(int i = 0; i < cols_to_output; i++) {
+ tbl->column_order[i].idx = col_order[i];
+ }
}
bool table_col_is_printed(struct table *tbl, uint col_idx)
{
for(uint i = 0; i < tbl->cols_to_output; i++) {
- if(tbl->column_order[i] == col_idx) return 1;
+ if(tbl->column_order[i].idx == col_idx) return 1;
}
return 0;
const char * table_set_col_order_by_name(struct table *tbl, const char *col_order_str)
{
if(col_order_str[0] == '*') {
- tbl->column_order = mp_alloc(tbl->pool, sizeof(int) * tbl->column_count);
+ tbl->column_order = mp_alloc(tbl->pool, sizeof(struct table_col_info) * tbl->column_count);
tbl->cols_to_output = tbl->column_count;
- for(uint i = 0; i < tbl->cols_to_output; i++) tbl->column_order[i] = i;
+ for(uint i = 0; i < tbl->cols_to_output; i++) {
+ tbl->column_order[i].idx = i;
+ tbl->column_order[i].output_type = 0;
+ }
return NULL;
}
}
}
- int *col_order_int = mp_alloc_zero(tbl->pool, sizeof(int) * col_count);
+ int *col_order_int = alloca(sizeof(int) * col_count); //mp_alloc_zero(tbl->pool, sizeof(int) * col_count);
int curr_col_order_int = 0;
const char *name_start = tmp_col_order;
while(name_start) {
name_start = next;
}
- tbl->column_order = col_order_int;
- tbl->cols_to_output = curr_col_order_int;
+ //tbl->column_order = col_order_int;
+ //tbl->cols_to_output = curr_col_order_int;
+ table_set_col_order(tbl, col_order_int, curr_col_order_int);
return NULL;
}
static void table_row_human_readable(struct table *tbl)
{
for(uint i = 0; i < tbl->cols_to_output; i++) {
- int col_idx = tbl->column_order[i];
+ int col_idx = tbl->column_order[i].idx;
if(i) {
bputs(tbl->out, tbl->col_delimiter);
}
static void table_write_header(struct table *tbl)
{
for(uint i = 0; i < tbl->cols_to_output; i++) {
- int col_idx = tbl->column_order[i];
+ int col_idx = tbl->column_order[i].idx;
if(i) {
bputs(tbl->out, tbl->col_delimiter);
}
static void table_row_machine_readable(struct table *tbl)
{
for(uint i = 0; i < tbl->cols_to_output; i++) {
- int col_idx = tbl->column_order[i];
+ int col_idx = tbl->column_order[i].idx;
if(i) {
bputs(tbl->out, tbl->col_delimiter);
}
}
if(tbl->print_header != 0) {
- uint col_idx = tbl->column_order[0];
+ uint col_idx = tbl->column_order[0].idx;
bputs(tbl->out, tbl->columns[col_idx].name);
for(uint i = 1; i < tbl->cols_to_output; i++) {
- col_idx = tbl->column_order[i];
+ col_idx = tbl->column_order[i].idx;
bputs(tbl->out, tbl->col_delimiter);
bputs(tbl->out, tbl->columns[col_idx].name);
}
static void table_row_blockline_output(struct table *tbl)
{
for(uint i = 0; i < tbl->cols_to_output; i++) {
- int col_idx = tbl->column_order[i];
+ int col_idx = tbl->column_order[i].idx;
bprintf(tbl->out, "%s: %s\n", tbl->columns[col_idx].name, tbl->col_str_ptrs[col_idx]);
}
bputc(tbl->out, '\n');
test_col0_str, test_col1_int, test_col2_uint, test_col3_bool, test_col4_double
};
-static uint test_column_order[] = {test_col3_bool, test_col4_double, test_col2_uint,test_col1_int, test_col0_str};
+static struct table_col_info test_column_order[] = { TBL_COL(test_col3_bool), TBL_COL(test_col4_double), TBL_COL(test_col2_uint), TBL_COL(test_col1_int), TBL_COL(test_col0_str) };
static struct table test_tbl = {
TBL_COLUMNS {
test_any_col0_int, test_any_col1_any
};
-static uint test_any_column_order[] = { test_any_col0_int, test_any_col1_any };
+static struct table_col_info test_any_column_order[] = { TBL_COL(test_any_col0_int), TBL_COL(test_any_col1_any) };
static struct table test_any_tbl = {
TBL_COLUMNS {
COL_TYPE_LAST
};
+#define COL_TYPE_UCW 0x100
+#define COL_TYPE_CUSTOM 0x1000
+
/** Justify cell contents to the left. **/
#define CELL_ALIGN_LEFT (1U << 31)
enum column_type type; // [*] Type of the cells in the column
};
+struct table_col_info {
+ uint idx;
+ uint output_type;
+};
+
/**
* Definition of a table. Contains column definitions, per-table settings
* and internal data. Please use only fields marked with `[*]`.
char **col_str_ptrs; // Values of cells in the current row (allocated from the pool)
- uint *column_order; // [*] Order of the columns in the print-out of the table
+ struct table_col_info *column_order; // [*] Order of the columns in the print-out of the table
uint cols_to_output; // [*] Number of columns that are printed
const char *col_delimiter; // [*] Delimiter that is placed between columns
const char *append_delimiter; // [*] Separator of multiple values in a single cell (see table_append_...())
#define TBL_COL_END { .name = 0, .width = 0, .fmt = 0, .type = COL_TYPE_LAST }
#define TBL_COLUMNS .columns = (struct table_column [])
-#define TBL_COL_ORDER(order) .column_order = (int *) order, .cols_to_output = ARRAY_SIZE(order)
+#define TBL_COL_ORDER(order) .column_order = (struct table_col_info *) order, .cols_to_output = ARRAY_SIZE(order)
#define TBL_COL_DELIMITER(_delimiter_) .col_delimiter = _delimiter_
#define TBL_APPEND_DELIMITER(_delimiter_) .append_delimiter = _delimiter_
+#define TBL_COL(_idx) { .idx = _idx, .output_type = 0 }
#define TBL_OUTPUT_HUMAN_READABLE .formatter = &table_fmt_human_readable
#define TBL_OUTPUT_BLOCKLINE .formatter = &table_fmt_blockline