From c5e7e465a3ec8eccfd881411f08244d8c5265660 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Mon, 20 Oct 2014 08:03:13 +0200 Subject: [PATCH] data: Added basic support for arrays. An array may contain elements of one type and it's stored in compact raw form (as in: not an array of union values but an array of the actual values). 'copy', 'free', and 'concat' operations are currently supported but limited to integer, decimal, or string element types. ENOTSUP is returned for any other operations. --- src/core/data.c | 116 +++++++++++++++++++++++++++++++++++++++- src/include/core/data.h | 45 ++++++++++------ t/unit/core/data_test.c | 46 ++++++++++++++++ 3 files changed, 190 insertions(+), 17 deletions(-) diff --git a/src/core/data.c b/src/core/data.c index c1e99a8..0e40474 100644 --- a/src/core/data.c +++ b/src/core/data.c @@ -50,6 +50,53 @@ * private helper functions */ +/* this function supports in-place copies */ +static int +copy_array_values(sdb_data_t *dst, const sdb_data_t *src, size_t elem_size) +{ + int type = src->type & 0xff; + + if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)) { + if (dst != src) + memcpy(dst->data.array.values, src->data.array.values, + src->data.array.length * elem_size); + } + else if (type == SDB_TYPE_STRING) { + char **s = src->data.array.values; + char **d = dst->data.array.values; + size_t i; + + for (i = 0; i < src->data.array.length; ++i) { + d[i] = strdup(s[i]); + if (! d[i]) + return -1; + } + } + else { + /* TODO */ + errno = ENOTSUP; + return -1; + } + return 0; +} /* copy_array_values */ + +static void +free_array_values(sdb_data_t *datum) +{ + int type = datum->type & 0xff; + + if (type == SDB_TYPE_STRING) { + char **v = datum->data.array.values; + size_t i; + + for (i = 0; i < datum->data.array.length; ++i) { + if (v[i]) + free(v[i]); + v[i] = NULL; + } + } +} /* free_array_values */ + /* 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) @@ -184,6 +231,7 @@ data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res) unsigned char *s1, *s2; size_t len1, len2; + /* TODO: support array plus element */ if (d1->type != d2->type) return -1; @@ -199,6 +247,13 @@ data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res) len1 = d1->data.binary.length; len2 = d2->data.binary.length; } + else if (d1->type & SDB_TYPE_ARRAY) { + size_t elem_size = sdb_data_sizeof(d1->type & 0xff); + s1 = (unsigned char *)d1->data.array.values; + s2 = (unsigned char *)d2->data.array.values; + len1 = d1->data.array.length * elem_size; + len2 = d2->data.array.length * elem_size; + } else return -1; @@ -218,10 +273,22 @@ data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res) if (res->type == SDB_TYPE_STRING) { res->data.string = (char *)new; } - else { + else if (res->type == SDB_TYPE_BINARY) { res->data.binary.datum = new; res->data.binary.length = len1 + len2; } + else if (d1->type & SDB_TYPE_ARRAY) { + res->data.array.values = new; + res->data.array.length = len1 + len2; + if (copy_array_values(res, res, sdb_data_sizeof(res->type & 0xff))) { + /* this leaks already copied values but there's not much we can + * do and this should only happen if we're in trouble anyway */ + free(new); + res->data.array.values = NULL; + res->data.array.length = 0; + return -1; + } + } return 0; } /* data_concat */ @@ -272,6 +339,18 @@ sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src) else memset(&tmp.data.re.regex, 0, sizeof(tmp.data.re.regex)); } + else if (src->type & SDB_TYPE_ARRAY) { + if (src->data.array.values) { + size_t elem_size = sdb_data_sizeof(src->type & 0xff); + tmp.data.array.values = calloc(src->data.array.length, elem_size); + if (! tmp.data.array.values) + return -1; + if (copy_array_values(&tmp, src, elem_size)) { + sdb_data_free_datum(&tmp); + return -1; + } + } + } sdb_data_free_datum(dst); *dst = tmp; @@ -303,6 +382,13 @@ sdb_data_free_datum(sdb_data_t *datum) datum->data.re.raw = NULL; memset(&datum->data.re.regex, 0, sizeof(datum->data.re.regex)); } + else if (datum->type & SDB_TYPE_ARRAY) { + free_array_values(datum); + if (datum->data.array.values) + free(datum->data.array.values); + datum->data.array.values = NULL; + datum->data.array.length = 0; + } } /* sdb_data_free_datum */ int @@ -356,6 +442,11 @@ sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2) CMP_NULL(d1->data.re.raw, d2->data.re.raw); return strcmp(d1->data.re.raw, d2->data.re.raw); } + else if (d1->type & SDB_TYPE_ARRAY) { + /* TODO */ + errno = ENOTSUP; + return -1; + } return -1; } /* sdb_data_cmp */ @@ -365,6 +456,12 @@ sdb_data_strcmp(const sdb_data_t *d1, const sdb_data_t *d2) char d1_str[sdb_data_strlen(d1) + 1]; char d2_str[sdb_data_strlen(d2) + 1]; + if ((d1->type & SDB_TYPE_ARRAY) || (d2->type & SDB_TYPE_ARRAY)) { + /* TODO */ + errno = ENOTSUP; + return -1; + } + if (sdb_data_isnull(d1)) d1 = NULL; if (sdb_data_isnull(d2)) @@ -394,6 +491,8 @@ sdb_data_isnull(const sdb_data_t *datum) return 1; if ((datum->type == SDB_TYPE_REGEX) && (! datum->data.re.raw)) return 1; + if ((datum->type & SDB_TYPE_ARRAY) && (! datum->data.array.values)) + return 1; return 0; } /* sdb_data_isnull */ @@ -478,6 +577,11 @@ sdb_data_strlen(const sdb_data_t *datum) /* "/.../" */ return strlen(datum->data.re.raw) + 4; } + else if (datum->type & SDB_TYPE_ARRAY) { + /* TODO */ + errno = ENOTSUP; + return 0; + } return 0; } /* sdb_data_strlen */ @@ -558,6 +662,11 @@ sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted) data = tmp; } } + else if (datum->type & SDB_TYPE_ARRAY) { + /* TODO */ + errno = ENOTSUP; + return -1; + } if (data) { if (quoted == SDB_UNQUOTED) @@ -613,6 +722,11 @@ sdb_data_parse(char *str, int type, sdb_data_t *data) sdb_data_free_datum(&tmp); } } + else if (type & SDB_TYPE_ARRAY) { + /* TODO */ + errno = ENOTSUP; + return -1; + } else { errno = EINVAL; return -1; diff --git a/src/include/core/data.h b/src/include/core/data.h index c8c7482..cfdf48d 100644 --- a/src/include/core/data.h +++ b/src/include/core/data.h @@ -48,6 +48,9 @@ enum { SDB_TYPE_DATETIME, SDB_TYPE_BINARY, SDB_TYPE_REGEX, /* extended, case-insensitive POSIX regex */ + + /* flags: */ + SDB_TYPE_ARRAY = 1 << 8, }; #define SDB_TYPE_TO_STRING(t) \ @@ -58,26 +61,36 @@ enum { : ((t) == SDB_TYPE_BINARY) ? "BINARY" \ : ((t) == SDB_TYPE_REGEX) ? "REGEX" : "UNKNOWN") +union sdb_datum; +typedef union sdb_datum sdb_datum_t; + +union sdb_datum { + int64_t integer; /* SDB_TYPE_INTEGER */ + double decimal; /* SDB_TYPE_DECIMAL */ + char *string; /* SDB_TYPE_STRING */ + sdb_time_t datetime; /* SDB_TYPE_DATETIME */ + struct { + size_t length; + unsigned char *datum; + } binary; /* SDB_TYPE_BINARY */ + struct { + char *raw; + regex_t regex; + } re; /* SDB_TYPE_REGEX */ + + struct { + size_t length; + void *values; + } array; +}; + /* * sdb_data_t: - * A datum retrieved from an arbitrary data source. + * An arbitrary value of a specified type. */ typedef struct { - int type; - union { - int64_t integer; /* SDB_TYPE_INTEGER */ - double decimal; /* SDB_TYPE_DECIMAL */ - char *string; /* SDB_TYPE_STRING */ - sdb_time_t datetime; /* SDB_TYPE_DATETIME */ - struct { - size_t length; - unsigned char *datum; - } binary; /* SDB_TYPE_BINARY */ - struct { - char *raw; - regex_t regex; - } re; /* SDB_TYPE_REGEX */ - } data; + int type; /* type of the datum */ + sdb_datum_t data; } sdb_data_t; #define SDB_DATA_INIT { SDB_TYPE_NULL, { .integer = 0 } } diff --git a/t/unit/core/data_test.c b/t/unit/core/data_test.c index 702afa4..c333823 100644 --- a/t/unit/core/data_test.c +++ b/t/unit/core/data_test.c @@ -38,6 +38,10 @@ START_TEST(test_data) sdb_data_t d1, d2; int check; + int int_values[] = { 47, 11, 23 }; + char *string_values[] = { "foo", "bar", "qux" "baz" }; + size_t i; + d2.type = SDB_TYPE_INTEGER; d2.data.integer = 4711; memset(&d1, 0, sizeof(d1)); @@ -174,6 +178,48 @@ START_TEST(test_data) fail_unless(d1.type == d2.type, "sdb_data_copy() didn't copy type; got: %i; expected: %i", d1.type, d2.type); + + d2.type = SDB_TYPE_ARRAY | SDB_TYPE_INTEGER; + d2.data.array.length = SDB_STATIC_ARRAY_LEN(int_values); + d2.data.array.values = int_values; + check = sdb_data_copy(&d1, &d2); + fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check); + fail_unless(d1.type == d2.type, + "sdb_data_copy() didn't copy type; got: %i; expected: %i", + d1.type, d2.type); + fail_unless(d1.data.array.values != d2.data.array.values, + "sdb_data_copy() didn't copy values: got: %p; expected: %p", + d1.data.array.values, d2.data.array.values); + for (i = 0; i < SDB_STATIC_ARRAY_LEN(int_values); ++i) { + int *i1 = d1.data.array.values; + int *i2 = d2.data.array.values; + fail_unless(i1[i] == i2[i], + "sdb_data_copy() modified integer value %d: " + "got: %d; expected: %d", i, i1[i], i2[i]); + } + sdb_data_free_datum(&d1); + + d2.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING; + d2.data.array.length = SDB_STATIC_ARRAY_LEN(string_values); + d2.data.array.values = string_values; + check = sdb_data_copy(&d1, &d2); + fail_unless(!check, "sdb_data_copy() = %i; expected: 0", check); + fail_unless(d1.type == d2.type, + "sdb_data_copy() didn't copy type; got: %i; expected: %i", + d1.type, d2.type); + fail_unless(d1.data.array.values != d2.data.array.values, + "sdb_data_copy() didn't copy values: got: %p; expected: %p", + d1.data.array.values, d2.data.array.values); + for (i = 0; i < SDB_STATIC_ARRAY_LEN(string_values); ++i) { + char **s1 = d1.data.array.values; + char **s2 = d2.data.array.values; + fail_unless(s1[i] != s2[i], + "sdb_data_copy() didn't copy string value %d", i); + fail_unless(!strcmp(s1[i], s2[i]), + "sdb_data_copy() modified string value %d: " + "got: %s; expected: %s", i, s1[i], s2[i]); + } + sdb_data_free_datum(&d1); } END_TEST -- 2.30.2