Code

unixsock, data: Moved sdb_unixsock_parse_cell() to sdb_data_parse().
authorSebastian Harl <sh@tokkee.org>
Fri, 4 Jul 2014 20:48:44 +0000 (22:48 +0200)
committerSebastian Harl <sh@tokkee.org>
Fri, 4 Jul 2014 20:48:44 +0000 (22:48 +0200)
The function parses a string into a datum. For date-time values, floating
point seconds since the epoch are supported only.

Added unit tests.

src/core/data.c
src/include/core/data.h
src/utils/unixsock.c
t/unit/core/data_test.c

index ae8e662d98d22835c4dacaf23f251a5b9c1d0d29..47c54cc41e5b3e8ed881747c50f28b9a41125410 100644 (file)
@@ -30,6 +30,9 @@
 #endif /* HAVE_CONFIG_H */
 
 #include "core/data.h"
+#include "utils/error.h"
+
+#include <errno.h>
 
 #include <inttypes.h>
 
@@ -255,5 +258,61 @@ sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
        return ret;
 } /* sdb_data_format */
 
+int
+sdb_data_parse(char *str, int type, sdb_data_t *data)
+{
+       sdb_data_t tmp;
+
+       char *endptr = NULL;
+
+       errno = 0;
+       switch (type) {
+               case SDB_TYPE_INTEGER:
+                       tmp.data.integer = strtoll(str, &endptr, 0);
+                       break;
+               case SDB_TYPE_DECIMAL:
+                       tmp.data.decimal = strtod(str, &endptr);
+                       break;
+               case SDB_TYPE_STRING:
+                       tmp.data.string = str;
+                       break;
+               case SDB_TYPE_DATETIME:
+                       {
+                               double datetime = strtod(str, &endptr);
+                               tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
+                       }
+                       break;
+               case SDB_TYPE_BINARY:
+                       /* we don't support any binary information containing 0-bytes */
+                       tmp.data.binary.length = strlen(str);
+                       tmp.data.binary.datum = (unsigned char *)str;
+                       break;
+               default:
+                       errno = EINVAL;
+                       return -1;
+       }
+
+       if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
+                       || (type == SDB_TYPE_DATETIME)) {
+               if (errno || (str == endptr)) {
+                       char errbuf[1024];
+                       sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
+                                       "'%s' as numeric value (type %i): %s", str, type,
+                                       sdb_strerror(errno, errbuf, sizeof(errbuf)));
+                       return -1;
+               }
+               else if (endptr && (*endptr != '\0'))
+                       sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
+                                       "number while parsing numeric value (type %i): %s.",
+                                       type, endptr);
+       }
+
+       if (data) {
+               *data = tmp;
+               data->type = type;
+       }
+       return 0;
+} /* sdb_data_parse */
+
 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */
 
index da653ce3f47e1f9692584b016da5e3d176ac4f80..4d7fda31c11d590a865fc6aff0665292249f0014 100644 (file)
@@ -146,6 +146,26 @@ enum {
 int
 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted);
 
+/*
+ * sdb_data_parse:
+ * Parse the specified string into a datum using the specified type. The
+ * string value is expected to be a raw value of the specified type. Integer
+ * and decimal numbers may be signed or unsigned octal (base 8, if the first
+ * character of the string is "0"), sedecimal (base 16, if the string includes
+ * the "0x" prefix), or decimal. Decimal numbers may also be "infinity" or
+ * "NaN" or may use a decimal exponent. Date-time values are expected to be
+ * specified as (floating point) number of seconds since the epoch. For string
+ * and binary data, the input string is passed to the datum. The function does
+ * not allocate new memory for that purpose. Use sdb_data_copy() if you want
+ * to do that.
+ *
+ * Returns:
+ *  - 0 on success
+ *  - a negative value else
+ */
+int
+sdb_data_parse(char *str, int type, sdb_data_t *data);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index 1a4373abb4ee92bc38b0cb23982f36d93b61f490..6f4d5cc2e090350be92920ed3d074a18adaf091a 100644 (file)
@@ -91,59 +91,6 @@ sdb_unixsock_get_column_count(const char *string, const char *delim)
        return count;
 } /* sdb_unixsock_get_column_count */
 
-static int
-sdb_unixsock_parse_cell(char *string, int type, sdb_data_t *data)
-{
-       char *endptr = NULL;
-
-       switch (type) {
-               case SDB_TYPE_INTEGER:
-                       errno = 0;
-                       data->data.integer = strtoll(string, &endptr, 0);
-                       break;
-               case SDB_TYPE_DECIMAL:
-                       errno = 0;
-                       data->data.decimal = strtod(string, &endptr);
-                       break;
-               case SDB_TYPE_STRING:
-                       data->data.string = string;
-                       break;
-               case SDB_TYPE_DATETIME:
-                       {
-                               double datetime = strtod(string, &endptr);
-                               data->data.datetime = DOUBLE_TO_SDB_TIME(datetime);
-                       }
-                       break;
-               case SDB_TYPE_BINARY:
-                       /* we don't support any binary information containing 0-bytes */
-                       data->data.binary.length = strlen(string);
-                       data->data.binary.datum = (unsigned char *)string;
-                       break;
-               default:
-                       sdb_log(SDB_LOG_ERR, "unixsock: Unexpected type %i while "
-                                       "parsing query result.", type);
-                       return -1;
-       }
-
-       if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
-                       || (type == SDB_TYPE_DATETIME)) {
-               if (errno || (string == endptr)) {
-                       char errbuf[1024];
-                       sdb_log(SDB_LOG_ERR, "unixsock: Failed to parse string "
-                                       "'%s' as numeric value (type %i): %s", string, type,
-                                       sdb_strerror(errno, errbuf, sizeof(errbuf)));
-                       return -1;
-               }
-               else if (endptr && (*endptr != '\0'))
-                       sdb_log(SDB_LOG_WARNING, "unixsock: Ignoring garbage after "
-                                       "number while parsing numeric value (type %i): %s.",
-                                       type, endptr);
-       }
-
-       data->type = type;
-       return 0;
-} /* sdb_unixsock_parse_cell */
-
 static int
 sdb_unixsock_client_process_one_line(sdb_unixsock_client_t *client,
                char *line, sdb_unixsock_client_data_cb callback,
@@ -178,7 +125,7 @@ sdb_unixsock_client_process_one_line(sdb_unixsock_client_t *client,
                        ++next;
                }
 
-               if (sdb_unixsock_parse_cell(line,
+               if (sdb_data_parse(line,
                                        types ? types[i] : SDB_TYPE_STRING, &data[i]))
                        return -1;
 
index e0ff3a88cb9c5d684bcb66345dd3df7977b12e71..0ce93bced809b379c91513c6bc697c9ddc98ece3 100644 (file)
@@ -365,6 +365,59 @@ START_TEST(test_format)
 }
 END_TEST
 
+START_TEST(test_parse)
+{
+       struct {
+               char *input;
+               sdb_data_t result;
+               int expected;
+       } golden_data[] = {
+               { "4711",    { SDB_TYPE_INTEGER,  { .integer  = 4711 } },       0 },
+               { "0x10",    { SDB_TYPE_INTEGER,  { .integer  = 16 } },         0 },
+               { "010",     { SDB_TYPE_INTEGER,  { .integer  = 8 } },          0 },
+               { "abc",     { SDB_TYPE_INTEGER,  { .integer  = 0 } },         -1 },
+               { "1.2",     { SDB_TYPE_DECIMAL,  { .decimal  = 1.2 } },        0 },
+               { "0x1p+16", { SDB_TYPE_DECIMAL,  { .decimal  = 65536.0 } },    0 },
+               { "abc",     { SDB_TYPE_DECIMAL,  { .decimal  = 0.0 } },       -1 },
+               { "abc",     { SDB_TYPE_STRING,   { .string   = "abc" } },      0 },
+               { ".4",      { SDB_TYPE_DATETIME, { .datetime = 400000000 } },  0 },
+               { "abc",     { SDB_TYPE_DATETIME, { .datetime = 0 } },         -1 },
+               { "abc",     { SDB_TYPE_BINARY,
+                                        { .binary = { 3, (unsigned char *)"abc" } } }, 0 },
+       };
+
+       size_t i;
+
+       for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+               sdb_data_t result;
+               int type, check;
+
+               memset(&result, 0, sizeof(result));
+               type = golden_data[i].result.type;
+               check = sdb_data_parse(golden_data[i].input, type, &result);
+               fail_unless(check == golden_data[i].expected,
+                               "sdb_data_parse(%s, %d, <d>) = %d; expected: %d",
+                               golden_data[i].input, type, check, golden_data[i].expected);
+
+               if (check)
+                       continue;
+
+               fail_unless(sdb_data_cmp(&result, &golden_data[i].result) == 0,
+                               "sdb_data_parse(%s, %d, <d>) did not create expected result",
+                               golden_data[i].input, type);
+
+               if (type == SDB_TYPE_STRING)
+                       fail_unless(golden_data[i].input == result.data.string,
+                                       "sdb_data_parse(%s, %d, <d>) modified input string",
+                                       golden_data[i].input, type);
+               if (type == SDB_TYPE_BINARY)
+                       fail_unless(golden_data[i].input == (char *)result.data.binary.datum,
+                                       "sdb_data_parse(%s, %d, <d>) modified input string",
+                                       golden_data[i].input, type);
+       }
+}
+END_TEST
+
 Suite *
 core_data_suite(void)
 {
@@ -375,6 +428,7 @@ core_data_suite(void)
        tcase_add_test(tc, test_data);
        tcase_add_test(tc, test_cmp);
        tcase_add_test(tc, test_format);
+       tcase_add_test(tc, test_parse);
        suite_add_tcase(s, tc);
 
        return s;