summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 812d8a3)
raw | patch | inline | side by side (parent: 812d8a3)
author | Sebastian Harl <sh@tokkee.org> | |
Sun, 27 Jul 2014 21:06:03 +0000 (23:06 +0200) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Sun, 27 Jul 2014 21:06:03 +0000 (23:06 +0200) |
This function may be used to evaluate simple operation expressions on data
values. Supported operators are +, -, *, /, %, and (for string and binary
values) concatenation.
values. Supported operators are +, -, *, /, %, and (for string and binary
values) concatenation.
src/Makefile.am | patch | blob | history | |
src/core/data.c | patch | blob | history | |
src/include/core/data.h | patch | blob | history | |
t/unit/core/data_test.c | patch | blob | history |
diff --git a/src/Makefile.am b/src/Makefile.am
index 0130d4269abaee37b5a177d9b98779c85fe4b088..2adc2e0c2a1e8290638ac7f5f7f4b260ebe40ca6 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
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
$(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 2381636092ceb03b7d4ef25ae8c9935ec3300583..cdafac30f284701482b583bd2f14f85e508b6526 100644 (file)
--- a/src/core/data.c
+++ b/src/core/data.c
#include <stdlib.h>
#include <string.h>
+#include <math.h>
+
+/*
+ * 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
*/
#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)
{
index 4d7fda31c11d590a865fc6aff0665292249f0014..2e9b90bcfd55af660d3cb576b1c5e3e21427730e 100644 (file)
--- a/src/include/core/data.h
+++ b/src/include/core/data.h
} binary; /* SDB_TYPE_BINARY */
} data;
} sdb_data_t;
+#define SDB_DATA_INIT { 0, { .integer = 0 } }
/*
* sdb_data_copy:
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
index e490a383e5b740f9875a0080abf4fd100da54132..0fd61a57539ff7e322be0356666a5375add663ee 100644 (file)
--- a/t/unit/core/data_test.c
+++ b/t/unit/core/data_test.c
}
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 {
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);