From 2f85566fbc79498eb7abfaf1cbd279f71f7ea8d1 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Mon, 20 Oct 2014 09:44:16 +0200 Subject: [PATCH] data: Added support for comparing integer, decimal, and string arrays. Array comparison works element-by-element, returning how the first non-equal elements compare to each other. Added tests for array comparison and concatenation. --- src/core/data.c | 51 ++++++++++++++++- t/unit/core/data_test.c | 121 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 3 deletions(-) diff --git a/src/core/data.c b/src/core/data.c index 1baeada..22d9ba3 100644 --- a/src/core/data.c +++ b/src/core/data.c @@ -97,6 +97,52 @@ free_array_values(sdb_data_t *datum) } } /* free_array_values */ +/* compare two arrays element-by-element returning how the first non-equal + * elements compare to each other */ +static int +array_cmp(const sdb_data_t *a1, const sdb_data_t *a2) +{ + int type = a1->type & 0xff; + size_t len, i; + + assert((a1->type == a2->type) && (a1->type & SDB_TYPE_ARRAY)); + + len = SDB_MIN(a1->data.array.length, a2->data.array.length); + + if (type == SDB_TYPE_INTEGER) { + int64_t *v1 = a1->data.array.values; + int64_t *v2 = a2->data.array.values; + + for (i = 0; i < len; ++i) + if (v1[i] != v2[i]) + return SDB_CMP(v1[i], v2[i]); + } + else if (type == SDB_TYPE_DECIMAL) { + double *v1 = a1->data.array.values; + double *v2 = a2->data.array.values; + + for (i = 0; i < len; ++i) + if (v1[i] != v2[i]) + return SDB_CMP(v1[i], v2[i]); + } + else if (type == SDB_TYPE_STRING) { + char **v1 = a1->data.array.values; + char **v2 = a2->data.array.values; + + for (i = 0; i < len; ++i) { + int diff = strcasecmp(v1[i], v2[i]); + if (diff) + return diff; + } + } + else { + /* TODO */ + errno = ENOTSUP; + /* but fall through to ensure stable sorting: */ + } + return SDB_CMP(a1->data.array.length, a2->data.array.length); +} /* array_cmp */ + /* Calculate the linear function 'd1 + n * d2'. */ static int data_lin(const sdb_data_t *d1, int n, const sdb_data_t *d2, sdb_data_t *res) @@ -443,9 +489,8 @@ sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2) return strcmp(d1->data.re.raw, d2->data.re.raw); } else if (d1->type & SDB_TYPE_ARRAY) { - /* TODO */ - errno = ENOTSUP; - return -1; + CMP_NULL(d1->data.array.values, d2->data.array.values); + return array_cmp(d1, d2); } return -1; } /* sdb_data_cmp */ diff --git a/t/unit/core/data_test.c b/t/unit/core/data_test.c index 56c6649..a41a3c2 100644 --- a/t/unit/core/data_test.c +++ b/t/unit/core/data_test.c @@ -225,6 +225,11 @@ END_TEST START_TEST(test_cmp) { + int64_t int_values1[] = { 1, 2, 3 }; + int64_t int_values2[] = { 1, 3, 2 }; + char *string_values1[] = { "a", "b", "c" }; + char *string_values2[] = { "a", "c", "b" }; + struct { sdb_data_t d1; sdb_data_t d2; @@ -400,6 +405,72 @@ START_TEST(test_cmp) { SDB_TYPE_REGEX, { .re = { "a", empty_re } } }, 1, }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values1), int_values1 } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values1), int_values1 } }, + }, + 0, + }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values1), int_values1 } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values2), int_values2 } }, + }, + -1, + }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values2), int_values2 } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values1), int_values1 } }, + }, + 1, + }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values1), string_values1 } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values1), string_values1 } }, + }, + 0, + }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values1), string_values1 } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values2), string_values2 } }, + }, + -1, + }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values2), string_values2 } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values1), string_values1 } }, + }, + 1, + }, }; size_t i; @@ -697,6 +768,12 @@ START_TEST(test_expr_eval) { sdb_data_t err = { -1, { .integer = 0 } }; + int64_t int_values[] = { 47, 11, 23 }; + int64_t expected_int_concat[] = { 47, 11, 23, 47, 11, 23 }; + char *string_values[] = { "foo", "bar", "qux" "baz" }; + char *expected_string_concat[] = + { "foo", "bar", "qux" "baz", "foo", "bar", "qux" "baz" }; + struct { sdb_data_t d1; sdb_data_t d2; @@ -836,6 +913,50 @@ START_TEST(test_expr_eval) err, err, }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values), int_values } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { SDB_STATIC_ARRAY_LEN(int_values), int_values } }, + }, + err, + err, + err, + err, + err, + { + SDB_TYPE_ARRAY | SDB_TYPE_INTEGER, + { .array = { + SDB_STATIC_ARRAY_LEN(expected_int_concat), + expected_int_concat + } }, + }, + }, + { + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values), string_values } }, + }, + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { SDB_STATIC_ARRAY_LEN(string_values), string_values } }, + }, + err, + err, + err, + err, + err, + { + SDB_TYPE_ARRAY | SDB_TYPE_STRING, + { .array = { + SDB_STATIC_ARRAY_LEN(expected_string_concat), + expected_string_concat + } }, + }, + }, { { SDB_TYPE_NULL, { .integer = 0 } }, { SDB_TYPE_NULL, { .integer = 0 } }, -- 2.30.2