From 1bbf53aa65770eeb26dbdbeb4de3871ad42cb9dd Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Sun, 27 Jul 2014 23:06:03 +0200 Subject: [PATCH] data: Added sdb_data_expr_eval(). This function may be used to evaluate simple operation expressions on data values. Supported operators are +, -, *, /, %, and (for string and binary values) concatenation. --- src/Makefile.am | 4 +- src/core/data.c | 147 +++++++++++++++++++++++++++++++ src/include/core/data.h | 41 +++++++++ t/unit/core/data_test.c | 187 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 377 insertions(+), 2 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 0130d42..2adc2e0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,7 +89,7 @@ libsysdb_la_SOURCES = \ utils/unixsock.c include/utils/unixsock.h libsysdb_la_CFLAGS = $(AM_CFLAGS) libsysdb_la_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) -libsysdb_la_LDFLAGS = $(AM_LDFLAGS) -version-info 0:0:0 -pthread +libsysdb_la_LDFLAGS = $(AM_LDFLAGS) -version-info 0:0:0 -pthread -lm libsysdb_la_LIBADD = libsysdb_fe_parser.la \ $(LIBLTDL) -lrt liboconfig/liboconfig.la libsysdb_la_DEPENDENCIES = libsysdb_fe_parser.la liboconfig/liboconfig.la @@ -124,7 +124,7 @@ sysdbd_SOURCES = tools/sysdbd/main.c include/sysdb.h \ $(libsysdb_la_SOURCES) sysdbd_CFLAGS = $(AM_CFLAGS) -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\"" sysdbd_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) -sysdbd_LDFLAGS = $(AM_LDFLAGS) -export-dynamic -pthread +sysdbd_LDFLAGS = $(AM_LDFLAGS) -export-dynamic -pthread -lm sysdbd_LDADD = libsysdb_fe_parser.la liboconfig/liboconfig.la \ $(LIBLTDL) -lrt sysdbd_DEPENDENCIES = libsysdb_fe_parser.la liboconfig/liboconfig.la diff --git a/src/core/data.c b/src/core/data.c index 2381636..cdafac3 100644 --- a/src/core/data.c +++ b/src/core/data.c @@ -42,6 +42,60 @@ #include #include +#include + +/* + * private helper functions + */ + +static int +data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res) +{ + unsigned char *new; + unsigned char *s1, *s2; + size_t len1, len2; + + if (d1->type == SDB_TYPE_STRING) { + s1 = (unsigned char *)d1->data.string; + s2 = (unsigned char *)d2->data.string; + len1 = s1 ? strlen((char *)s1) : 0; + len2 = s2 ? strlen((char *)s2) : 0; + } + else if (d1->type == SDB_TYPE_BINARY) { + s1 = d1->data.binary.datum; + s2 = d2->data.binary.datum; + len1 = d1->data.binary.length; + len2 = d2->data.binary.length; + } + else + return -1; + + if (s1 || s2) { + new = malloc(len1 + len2 + 1); + if (! new) + return -1; + } + else + new = NULL; + + if (len1) + memcpy(new, s1, len1); + if (len2) + memcpy(new + len1, s2, len2); + if (new) + new[len1 + len2] = '\0'; + + res->type = d1->type; + if (res->type == SDB_TYPE_STRING) { + res->data.string = (char *)new; + } + else { + res->data.binary.datum = new; + res->data.binary.length = len1 + len2; + } + return 0; +} /* data_concat */ + /* * public API */ @@ -150,6 +204,99 @@ sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2) #undef CMP_NULL } /* sdb_data_cmp */ +int +sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2, + sdb_data_t *res) +{ + if (d1->type != d2->type) + return -1; + + switch (op) { + case SDB_DATA_CONCAT: + return data_concat(d1, d2, res); + case SDB_DATA_ADD: + switch (d1->type) { + case SDB_TYPE_INTEGER: + res->data.integer = d1->data.integer + d2->data.integer; + break; + case SDB_TYPE_DECIMAL: + res->data.decimal = d1->data.decimal + d2->data.decimal; + break; + case SDB_TYPE_DATETIME: + res->data.datetime = d1->data.datetime + d2->data.datetime; + break; + default: + return -1; + } + break; + case SDB_DATA_SUB: + switch (d1->type) { + case SDB_TYPE_INTEGER: + res->data.integer = d1->data.integer - d2->data.integer; + break; + case SDB_TYPE_DECIMAL: + res->data.decimal = d1->data.decimal - d2->data.decimal; + break; + case SDB_TYPE_DATETIME: + res->data.datetime = d1->data.datetime - d2->data.datetime; + break; + default: + return -1; + } + break; + case SDB_DATA_MUL: + switch (d1->type) { + case SDB_TYPE_INTEGER: + res->data.integer = d1->data.integer * d2->data.integer; + break; + case SDB_TYPE_DECIMAL: + res->data.decimal = d1->data.decimal * d2->data.decimal; + break; + case SDB_TYPE_DATETIME: + res->data.datetime = d1->data.datetime * d2->data.datetime; + break; + default: + return -1; + } + break; + case SDB_DATA_DIV: + switch (d1->type) { + case SDB_TYPE_INTEGER: + res->data.integer = d1->data.integer / d2->data.integer; + break; + case SDB_TYPE_DECIMAL: + res->data.decimal = d1->data.decimal / d2->data.decimal; + break; + case SDB_TYPE_DATETIME: + res->data.datetime = d1->data.datetime / d2->data.datetime; + break; + default: + return -1; + } + break; + case SDB_DATA_MOD: + switch (d1->type) { + case SDB_TYPE_INTEGER: + res->data.integer = d1->data.integer % d2->data.integer; + break; + case SDB_TYPE_DECIMAL: + res->data.decimal = fmod(d1->data.decimal, d2->data.decimal); + break; + case SDB_TYPE_DATETIME: + res->data.datetime = d1->data.datetime % d2->data.datetime; + break; + default: + return -1; + } + break; + default: + return -1; + } + + res->type = d1->type; + return 0; +} /* sdb_data_expr_eval */ + size_t sdb_data_strlen(const sdb_data_t *datum) { diff --git a/src/include/core/data.h b/src/include/core/data.h index 4d7fda3..2e9b90b 100644 --- a/src/include/core/data.h +++ b/src/include/core/data.h @@ -75,6 +75,7 @@ typedef struct { } binary; /* SDB_TYPE_BINARY */ } data; } sdb_data_t; +#define SDB_DATA_INIT { 0, { .integer = 0 } } /* * sdb_data_copy: @@ -114,6 +115,46 @@ sdb_data_free_datum(sdb_data_t *datum); int sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2); +/* + * Operators supported by sdb_data_eval_expr. + */ +enum { + SDB_DATA_ADD = 1, /* addition */ + SDB_DATA_SUB, /* substraction */ + SDB_DATA_MUL, /* multiplication */ + SDB_DATA_DIV, /* division */ + SDB_DATA_MOD, /* modulo */ + SDB_DATA_CONCAT, /* string / binary data concatenation */ +}; + +#define SDB_DATA_OP_TO_STRING(op) \ + (((op) == SDB_DATA_ADD) \ + ? "+" \ + : ((op) == SDB_DATA_SUB) \ + ? "-" \ + : ((op) == SDB_DATA_MUL) \ + ? "*" \ + : ((op) == SDB_DATA_DIV) \ + ? "/" \ + : ((op) == SDB_DATA_MOD) \ + ? "%" \ + : ((op) == SDB_DATA_CONCAT) \ + ? "||" : "UNKNOWN") + +/* + * sdb_data_expr_eval: + * Evaluate a simple arithmetic expression on two data points. The data-type + * of d1 and d2 have to be the same. String and binary data only support + * concatenation and all other data types only support the other operators. + * + * Returns: + * - 0 on success + * - a negative value else + */ +int +sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2, + sdb_data_t *res); + /* * sdb_data_strlen: * Returns a (worst-case) estimate for the number of bytes required to format diff --git a/t/unit/core/data_test.c b/t/unit/core/data_test.c index e490a38..0fd61a5 100644 --- a/t/unit/core/data_test.c +++ b/t/unit/core/data_test.c @@ -290,6 +290,192 @@ START_TEST(test_cmp) } END_TEST +START_TEST(test_expr_eval) +{ + struct { + sdb_data_t d1; + sdb_data_t d2; + sdb_data_t expected_add; + sdb_data_t expected_sub; + sdb_data_t expected_mul; + sdb_data_t expected_div; + sdb_data_t expected_mod; + sdb_data_t expected_concat; + } golden_data[] = { + { + { SDB_TYPE_INTEGER, { .integer = 4711 } }, + { SDB_TYPE_INTEGER, { .integer = 47 } }, + { SDB_TYPE_INTEGER, { .integer = 4758 } }, + { SDB_TYPE_INTEGER, { .integer = 4664 } }, + { SDB_TYPE_INTEGER, { .integer = 221417 } }, + { SDB_TYPE_INTEGER, { .integer = 100 } }, + { SDB_TYPE_INTEGER, { .integer = 11 } }, + SDB_DATA_INIT, + }, + { + { SDB_TYPE_DECIMAL, { .decimal = 35.0 } }, + { SDB_TYPE_DECIMAL, { .decimal = 17.5 } }, + { SDB_TYPE_DECIMAL, { .decimal = 52.5 } }, + { SDB_TYPE_DECIMAL, { .decimal = 17.5 } }, + { SDB_TYPE_DECIMAL, { .decimal = 612.5 } }, + { SDB_TYPE_DECIMAL, { .decimal = 2.0 } }, + { SDB_TYPE_DECIMAL, { .decimal = 0.0 } }, + SDB_DATA_INIT, + }, + { + { SDB_TYPE_STRING, { .string = NULL } }, + { SDB_TYPE_STRING, { .string = "" } }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { SDB_TYPE_STRING, { .string = "" } }, + }, + { + { SDB_TYPE_STRING, { .string = NULL } }, + { SDB_TYPE_STRING, { .string = NULL } }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { SDB_TYPE_STRING, { .string = NULL } }, + }, + { + { SDB_TYPE_STRING, { .string = "" } }, + { SDB_TYPE_STRING, { .string = NULL } }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { SDB_TYPE_STRING, { .string = "" } }, + }, + { + { SDB_TYPE_STRING, { .string = "a" } }, + { SDB_TYPE_STRING, { .string = "b" } }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { SDB_TYPE_STRING, { .string = "ab" } }, + }, + { + { SDB_TYPE_DATETIME, { .datetime = 47114711 } }, + { SDB_TYPE_DATETIME, { .datetime = 4711 } }, + { SDB_TYPE_DATETIME, { .datetime = 47119422 } }, + { SDB_TYPE_DATETIME, { .datetime = 47110000 } }, + { SDB_TYPE_DATETIME, { .datetime = 221957403521 } }, + { SDB_TYPE_DATETIME, { .datetime = 10001 } }, + { SDB_TYPE_DATETIME, { .datetime = 0 } }, + SDB_DATA_INIT, + }, + { + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } }, + }, + { + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + }, + { + { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } }, + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } }, + }, + { + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0a" } }, + }, + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"b\0b" } }, + }, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + SDB_DATA_INIT, + { + SDB_TYPE_BINARY, + { .binary = { 6, (unsigned char *)"a\0ab\0b" } }, + }, + }, + }; + + size_t i; + + for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) { + struct { + int op; + sdb_data_t expected; + } tests[] = { + { SDB_DATA_ADD, golden_data[i].expected_add }, + { SDB_DATA_SUB, golden_data[i].expected_sub }, + { SDB_DATA_MUL, golden_data[i].expected_mul }, + { SDB_DATA_DIV, golden_data[i].expected_div }, + { SDB_DATA_MOD, golden_data[i].expected_mod }, + { SDB_DATA_CONCAT, golden_data[i].expected_concat }, + }; + + size_t j; + for (j = 0; j < SDB_STATIC_ARRAY_LEN(tests); ++j) { + sdb_data_t res; + int check; + + char d1_str[64] = "", d2_str[64] = ""; + sdb_data_format(&golden_data[i].d1, d1_str, sizeof(d1_str), + SDB_DOUBLE_QUOTED); + sdb_data_format(&golden_data[i].d2, d2_str, sizeof(d2_str), + SDB_DOUBLE_QUOTED); + + check = sdb_data_expr_eval(tests[j].op, + &golden_data[i].d1, &golden_data[i].d2, &res); + fail_unless((check == 0) == (tests[j].expected.type != 0), + "sdb_data_expr_eval(%s, %s, %s) = %d; expected: %d", + SDB_DATA_OP_TO_STRING(tests[j].op), d1_str, d2_str, check, + tests[j].expected.type == 0 ? -1 : 0); + if (tests[j].expected.type == 0) + continue; + + check = sdb_data_cmp(&res, &tests[j].expected); + if (check != 0) { + char res_str[64] = "", expected_str[64] = ""; + sdb_data_format(&res, res_str, sizeof(res_str), + SDB_DOUBLE_QUOTED); + sdb_data_format(&tests[j].expected, expected_str, + sizeof(expected_str), SDB_DOUBLE_QUOTED); + fail("sdb_data_expr_eval(%s, %s, %s) evaluated to %s; " + "expected: %s", SDB_DATA_OP_TO_STRING(tests[j].op), + d1_str, d2_str, res_str, expected_str); + } + + sdb_data_free_datum(&res); + } + } +} +END_TEST + START_TEST(test_format) { struct { @@ -428,6 +614,7 @@ core_data_suite(void) tc = tcase_create("core"); tcase_add_test(tc, test_data); tcase_add_test(tc, test_cmp); + tcase_add_test(tc, test_expr_eval); tcase_add_test(tc, test_format); tcase_add_test(tc, test_parse); suite_add_tcase(s, tc); -- 2.30.2