Code

data: Added basic support for arrays.
authorSebastian Harl <sh@tokkee.org>
Mon, 20 Oct 2014 06:03:13 +0000 (08:03 +0200)
committerSebastian Harl <sh@tokkee.org>
Mon, 20 Oct 2014 06:03:13 +0000 (08:03 +0200)
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
src/include/core/data.h
t/unit/core/data_test.c

index c1e99a8cab82ed742b76a8d8165fe44ceb999269..0e40474edaabab45e98b9dc30d31b2b244de33d9 100644 (file)
  * private helper functions
  */
 
  * 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)
 /* 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;
 
        unsigned char *s1, *s2;
        size_t len1, len2;
 
+       /* TODO: support array plus element */
        if (d1->type != d2->type)
                return -1;
 
        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;
        }
                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;
 
        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;
        }
        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;
        }
                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 */
 
        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
                        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;
 
        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));
        }
                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
 } /* 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);
        }
                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 */
 
        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];
 
        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))
        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;
                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 */
 
        return 0;
 } /* sdb_data_isnull */
 
@@ -478,6 +577,11 @@ sdb_data_strlen(const sdb_data_t *datum)
                /* "/.../" */
                return strlen(datum->data.re.raw) + 4;
        }
                /* "/.../" */
                return strlen(datum->data.re.raw) + 4;
        }
+       else if (datum->type & SDB_TYPE_ARRAY) {
+               /* TODO */
+               errno = ENOTSUP;
+               return 0;
+       }
        return 0;
 } /* sdb_data_strlen */
 
        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;
                }
        }
                        data = tmp;
                }
        }
+       else if (datum->type & SDB_TYPE_ARRAY) {
+               /* TODO */
+               errno = ENOTSUP;
+               return -1;
+       }
 
        if (data) {
                if (quoted == SDB_UNQUOTED)
 
        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);
                }
        }
                        sdb_data_free_datum(&tmp);
                }
        }
+       else if (type & SDB_TYPE_ARRAY) {
+               /* TODO */
+               errno = ENOTSUP;
+               return -1;
+       }
        else {
                errno = EINVAL;
                return -1;
        else {
                errno = EINVAL;
                return -1;
index c8c7482e796f4438aeafdb8eebe4588223636e7b..cfdf48db8ea7d63fd1c2ef718901706bd10ca2e0 100644 (file)
@@ -48,6 +48,9 @@ enum {
        SDB_TYPE_DATETIME,
        SDB_TYPE_BINARY,
        SDB_TYPE_REGEX, /* extended, case-insensitive POSIX regex */
        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) \
 };
 
 #define SDB_TYPE_TO_STRING(t) \
@@ -58,26 +61,36 @@ enum {
                : ((t) == SDB_TYPE_BINARY) ? "BINARY" \
                : ((t) == SDB_TYPE_REGEX) ? "REGEX" : "UNKNOWN")
 
                : ((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:
 /*
  * sdb_data_t:
- * A datum retrieved from an arbitrary data source.
+ * An arbitrary value of a specified type.
  */
 typedef struct {
  */
 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 } }
 
 } sdb_data_t;
 #define SDB_DATA_INIT { SDB_TYPE_NULL, { .integer = 0 } }
 
index 702afa487a0fa9aa7fbc31090f7d133f770ffe20..c33382386e8c99e60ac08c69af95cd6cba0486e5 100644 (file)
@@ -38,6 +38,10 @@ START_TEST(test_data)
        sdb_data_t d1, d2;
        int check;
 
        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));
        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);
        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
 
 }
 END_TEST