From cadb192c7ec1abe2c2f2bfac1b4cc3f0000c3809 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Thu, 12 Jun 2014 18:57:29 +0200 Subject: [PATCH] data: Added sdb_data_cmp() comparing two data points. --- src/core/data.c | 56 ++++++++++++ src/include/core/data.h | 15 +++- t/unit/core/data_test.c | 183 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+), 1 deletion(-) diff --git a/src/core/data.c b/src/core/data.c index 4e0fd77..0e8cfcb 100644 --- a/src/core/data.c +++ b/src/core/data.c @@ -90,6 +90,62 @@ sdb_data_free_datum(sdb_data_t *datum) } } /* sdb_data_free_datum */ +int +sdb_data_cmp(sdb_data_t *d1, sdb_data_t *d2) +{ +#define CMP_NULL(a, b) \ + do { \ + if (!(a) && !(b)) return 0; \ + if (!(a)) return -1; \ + if (!(b)) return 1; \ + } while (0) + + CMP_NULL(d1, d2); + + if (d1->type != d2->type) + return -1; + +#define CMP(a, b) ((a) < (b) ? -1 : (a) > (b) ? 1 : 0) + switch (d1->type) { + case SDB_TYPE_INTEGER: + return CMP(d1->data.integer, d2->data.integer); + case SDB_TYPE_DECIMAL: + return CMP(d1->data.decimal, d2->data.decimal); + case SDB_TYPE_STRING: + CMP_NULL(d1->data.string, d2->data.string); + return strcasecmp(d1->data.string, d2->data.string); + case SDB_TYPE_DATETIME: + return CMP(d1->data.datetime, d2->data.datetime); + case SDB_TYPE_BINARY: + { + int diff; + + CMP_NULL(d1->data.binary.datum, d2->data.binary.datum); + + /* on a common prefix, the shorter datum sorts less */ + if (d1->data.binary.length < d2->data.binary.length) { + diff = memcmp(d1->data.binary.datum, d2->data.binary.datum, + d1->data.binary.length); + diff = diff ? diff : -1; + } + else if (d1->data.binary.length > d2->data.binary.length) { + diff = memcmp(d1->data.binary.datum, d2->data.binary.datum, + d2->data.binary.length); + diff = diff ? diff : 1; + } + else + diff = memcmp(d1->data.binary.datum, d2->data.binary.datum, + d1->data.binary.length); + + return diff; + } + default: + return -1; + } +#undef CMP +#undef CMP_NULL +} /* sdb_data_cmp */ + size_t sdb_data_strlen(sdb_data_t *datum) { diff --git a/src/include/core/data.h b/src/include/core/data.h index 0b4b2e7..bd10266 100644 --- a/src/include/core/data.h +++ b/src/include/core/data.h @@ -1,6 +1,6 @@ /* * SysDB - src/include/core/data.h - * Copyright (C) 2012 Sebastian 'tokkee' Harl + * Copyright (C) 2012-2014 Sebastian 'tokkee' Harl * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -100,6 +100,19 @@ sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src); void sdb_data_free_datum(sdb_data_t *datum); +/* + * sdb_data_cmp: + * Compare two data points. A NULL datum is considered less than any non-NULL + * datum. On data-type mismatch, the function always returns a negative value. + * + * Returns: + * - a value less than zero if d1 compares less than d2 + * - zero if d1 compares equal to d2 + * - a value greater than zero if d1 compares greater than d2 + */ +int +sdb_data_cmp(sdb_data_t *d1, sdb_data_t *d2); + /* * 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 432bd75..e0ff3a8 100644 --- a/t/unit/core/data_test.c +++ b/t/unit/core/data_test.c @@ -107,6 +107,188 @@ START_TEST(test_data) } END_TEST +START_TEST(test_cmp) +{ + struct { + sdb_data_t d1; + sdb_data_t d2; + int expected; + } golden_data[] = { + { + { SDB_TYPE_INTEGER, { .integer = 47 } }, + { SDB_TYPE_INTEGER, { .integer = 4711 } }, + -1, + }, + { + { SDB_TYPE_INTEGER, { .integer = 4711 } }, + { SDB_TYPE_INTEGER, { .integer = 4711 } }, + 0, + }, + { + { SDB_TYPE_INTEGER, { .integer = 4711 } }, + { SDB_TYPE_INTEGER, { .integer = 47 } }, + 1, + }, + { + { SDB_TYPE_DECIMAL, { .decimal = 65535.9 } }, + { SDB_TYPE_DECIMAL, { .decimal = 65536.0 } }, + -1, + }, + { + { SDB_TYPE_DECIMAL, { .decimal = 65536.0 } }, + { SDB_TYPE_DECIMAL, { .decimal = 65536.0 } }, + 0, + }, + { + { SDB_TYPE_DECIMAL, { .decimal = 65536.0 } }, + { SDB_TYPE_DECIMAL, { .decimal = 65535.9 } }, + 1, + }, + { + { SDB_TYPE_STRING, { .string = NULL } }, + { SDB_TYPE_STRING, { .string = "" } }, + -1, + }, + { + { SDB_TYPE_STRING, { .string = NULL } }, + { SDB_TYPE_STRING, { .string = NULL } }, + 0, + }, + { + { SDB_TYPE_STRING, { .string = "" } }, + { SDB_TYPE_STRING, { .string = NULL } }, + 1, + }, + { + { SDB_TYPE_STRING, { .string = "a" } }, + { SDB_TYPE_STRING, { .string = "b" } }, + -1, + }, + { + { SDB_TYPE_STRING, { .string = "a" } }, + { SDB_TYPE_STRING, { .string = "ab" } }, + -1, + }, + { + { SDB_TYPE_STRING, { .string = "a" } }, + { SDB_TYPE_STRING, { .string = "a" } }, + 0, + }, + { + { SDB_TYPE_STRING, { .string = "b" } }, + { SDB_TYPE_STRING, { .string = "a" } }, + 1, + }, + { + { SDB_TYPE_STRING, { .string = "ab" } }, + { SDB_TYPE_STRING, { .string = "a" } }, + 1, + }, + { + { SDB_TYPE_DATETIME, { .datetime = 471147114711471000 } }, + { SDB_TYPE_DATETIME, { .datetime = 471147114711471100 } }, + -1, + }, + { + { SDB_TYPE_DATETIME, { .datetime = 471147114711471100 } }, + { SDB_TYPE_DATETIME, { .datetime = 471147114711471100 } }, + 0, + }, + { + { SDB_TYPE_DATETIME, { .datetime = 471147114711471100 } }, + { SDB_TYPE_DATETIME, { .datetime = 471147114711471000 } }, + 1, + }, + { + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } }, + -1, + }, + { + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + 0, + }, + { + { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } }, + { SDB_TYPE_BINARY, { .binary = { 0, NULL } } }, + 1, + }, + { + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0a" } }, + }, + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0b" } }, + }, + -1, + }, + { + { + SDB_TYPE_BINARY, + { .binary = { 1, (unsigned char *)"a" } }, + }, + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0\0" } }, + }, + -1, + }, + { + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0a" } }, + }, + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0a" } }, + }, + 0, + }, + { + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0b" } }, + }, + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0a" } }, + }, + 1, + }, + { + { + SDB_TYPE_BINARY, + { .binary = { 3, (unsigned char *)"a\0\0" } }, + }, + { + SDB_TYPE_BINARY, + { .binary = { 1, (unsigned char *)"a" } }, + }, + 1, + }, + }; + + size_t i; + + for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) { + int check = sdb_data_cmp(&golden_data[i].d1, &golden_data[i].d2); + check = check < 0 ? -1 : check > 0 ? 1 : 0; + if (check != golden_data[i].expected) { + 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); + fail("sdb_data_cmp(%s, %s) = %d; expected: %d", + d1_str, d2_str, check, golden_data[i].expected); + } + } +} +END_TEST + START_TEST(test_format) { struct { @@ -191,6 +373,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_format); suite_add_tcase(s, tc); -- 2.30.2