Code

Add more string utility functions and unit tests.
authorSebastian Harl <sh@tokkee.org>
Sat, 10 Sep 2016 21:43:57 +0000 (17:43 -0400)
committerSebastian Harl <sh@tokkee.org>
Sat, 10 Sep 2016 21:43:57 +0000 (17:43 -0400)
src/include/utils/strings.h
src/utils/strings.c
t/Makefile.am
t/unit/utils/strings_test.c [new file with mode: 0644]

index 61298ee..24f7fc1 100644 (file)
@@ -37,7 +37,8 @@ extern "C" {
 /*
  * stringv_copy:
  * Copy a string vector from 'src' to 'dst'. If non-NULL, 'dst' will be
- * reallocated to fit the required size.
+ * reallocated to fit the required size and old entries will be freed before
+ * overriding them.
  *
  * Returns:
  *  - 0 on success
@@ -47,6 +48,17 @@ int
 stringv_copy(char ***dst, size_t *dst_len,
                const char * const *src, size_t src_len);
 
+/*
+ * stringv_append:
+ * Append a string to a string vector.
+ *
+ * Returns:
+ *  - 0 on success
+ *  - a negative value else
+ */
+int
+stringv_append(char ***s, size_t *s_len, const char *elem);
+
 /*
  * stringv_free:
  * Free the memory used by 's' and all of it's elements.
index c07cfa1..b6a49e1 100644 (file)
 
 #include "utils/strings.h"
 
+#include <assert.h>
+
 #include <stdlib.h>
 #include <string.h>
 
 /*
- * public API
+ * private helper functions
  */
 
-int
-stringv_copy(char ***dst, size_t *dst_len,
-               const char * const *src, size_t src_len)
+static int
+ensure_len(char ***s, size_t *s_len, size_t len)
 {
        char **tmp;
-       size_t i;
 
-       if (*dst) {
-               tmp = realloc(*dst, src_len * sizeof(*tmp));
-               if (tmp)
-                       memset(tmp, 0, src_len * sizeof(*tmp));
+       if ((! s) || (! s_len))
+               return -1;
+
+       if (! len) {
+               if (*s)
+                       free(*s);
+               *s = NULL;
+               *s_len = 0;
+               return 0;
+       }
+
+       if (*s) {
+               tmp = realloc(*s, len * sizeof(*tmp));
+               if (tmp && (len > *s_len))
+                       memset(tmp + *s_len, 0, (len - *s_len) * sizeof(*tmp));
        }
        else
-               tmp = calloc(src_len, sizeof(*tmp));
+               tmp = calloc(len, sizeof(*tmp));
 
        if (! tmp)
                return -1;
 
-       *dst = tmp;
-       *dst_len = src_len;
+       *s = tmp;
+       *s_len = len;
+       return 0;
+} /* ensure_len */
+
+/*
+ * public API
+ */
+
+int
+stringv_copy(char ***dst, size_t *dst_len,
+               const char * const *src, size_t src_len)
+{
+       size_t i;
+
+       if (src_len && (! src))
+               return -1;
+       if (ensure_len(dst, dst_len, src_len))
+               return -1;
+       assert(dst);
+
        for (i = 0; i < src_len; ++i) {
+               if ((*dst)[i])
+                       free((*dst)[i]);
                (*dst)[i] = strdup(src[i]);
                if (! (*dst)[i])
                        return -1;
@@ -66,6 +98,22 @@ stringv_copy(char ***dst, size_t *dst_len,
        return 0;
 } /* stringv_copy */
 
+int
+stringv_append(char ***s, size_t *s_len, const char *elem)
+{
+       size_t i;
+
+       if ((! s_len) || ensure_len(s, s_len, *s_len + 1))
+               return -1;
+       assert(s);
+
+       i = *s_len - 1;
+       (*s)[i] = strdup(elem);
+       if (! (*s)[i])
+               return -1;
+       return 0;
+} /* stringv_append */
+
 void
 stringv_free(char ***s, size_t *s_len)
 {
index e294333..c719c04 100644 (file)
@@ -51,7 +51,8 @@ UNIT_TESTS = \
                unit/utils/llist_test \
                unit/utils/os_test \
                unit/utils/proto_test \
-               unit/utils/strbuf_test
+               unit/utils/strbuf_test \
+               unit/utils/strings_test
 
 UNIT_TEST_SOURCES = unit/testutils.c unit/testutils.h
 UNIT_TEST_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@ -I$(top_srcdir)/t/unit
@@ -144,6 +145,10 @@ unit_utils_strbuf_test_SOURCES = $(UNIT_TEST_SOURCES) unit/utils/strbuf_test.c
 unit_utils_strbuf_test_CFLAGS = $(UNIT_TEST_CFLAGS)
 unit_utils_strbuf_test_LDADD = $(UNIT_TEST_LDADD)
 
+unit_utils_strings_test_SOURCES = $(UNIT_TEST_SOURCES) unit/utils/strings_test.c
+unit_utils_strings_test_CFLAGS = $(UNIT_TEST_CFLAGS)
+unit_utils_strings_test_LDADD = $(UNIT_TEST_LDADD)
+
 TESTS += $(UNIT_TESTS)
 check_PROGRAMS += $(UNIT_TESTS)
 endif
diff --git a/t/unit/utils/strings_test.c b/t/unit/utils/strings_test.c
new file mode 100644 (file)
index 0000000..326a073
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * SysDB - t/unit/utils/strings_test.c
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#      include "config.h"
+#endif
+
+#include "core/data.h"
+#include "utils/strings.h"
+#include "testutils.h"
+
+#include <check.h>
+
+/*
+ * tests
+ */
+
+START_TEST(test_stringv)
+{
+       char **dst = NULL;
+       size_t dst_len = 0;
+
+       char *src[] = { "a", "b", "c" };
+       size_t src_len = SDB_STATIC_ARRAY_LEN(src);
+       size_t i;
+
+       int check;
+
+       /* Test no-op, empty operations. */
+       check = stringv_copy(&dst, &dst_len, NULL, 0);
+       fail_unless(check == 0,
+                       "stringv_copy(&<null>, &<0>, <null>, 0) = %d; expected: 0",
+                       check);
+       fail_unless((dst == NULL) && (dst_len == 0),
+                       "stringv_copy(&<null>, &<0>, <null>, 0) produced %p, %d; "
+                       "expected <null>, 0", dst, dst_len);
+       stringv_free(&dst, &dst_len);
+       fail_unless((dst == NULL) && (dst_len == 0),
+                       "stringv_free(&<null>, &<0>) produced %p, %d; expected <null>, 0",
+                       dst, dst_len);
+
+       /* Now, append some content. */
+       for (i = 0; i < src_len; i++) {
+               sdb_data_t d1 = {
+                       SDB_TYPE_STRING | SDB_TYPE_ARRAY,
+                       { .array = { 0, NULL } },
+               };
+               sdb_data_t d2 = d1;
+
+               char buf1[256], buf2[256];
+               size_t j;
+
+               check = stringv_append(&dst, &dst_len, src[i]);
+               fail_unless(check == 0,
+                               "stringv_append(<s>, <len>, '%s') = %d; expected: 0",
+                               src[i], check);
+               fail_unless((dst != NULL) && (dst_len == i + 1),
+                               "stringv_append(<s>, <len>, '%s') produced s=%p, len=%zu; "
+                               "expected: s=<v>, len=%zu", src[i], dst, dst_len, i + 1);
+
+               for (j = 0; j <= i; j++)
+                       if ((! dst[j]) || (strcmp(dst[j], src[j]) != 0))
+                               break;
+
+               if (j <= i) {
+                       d1.data.array.values = dst;
+                       d1.data.array.length = dst_len;
+                       sdb_data_format(&d1, buf1, sizeof(buf1), 0);
+
+                       d2.data.array.values = src;
+                       d2.data.array.length = dst_len;
+                       sdb_data_format(&d2, buf2, sizeof(buf2), 0);
+
+                       fail("stringv_append(<s>, <len>, '%s') produced unexpected result: "
+                                       "vectors differ at position %zu: '%s' <-> '%s'",
+                                       src[i], j, buf1, buf2);
+               }
+       }
+       stringv_free(&dst, &dst_len);
+
+       /* Copy all in one go. */
+       for (i = 0; i < src_len; i++) {
+               sdb_data_t d1 = {
+                       SDB_TYPE_STRING | SDB_TYPE_ARRAY,
+                       { .array = { 0, NULL } },
+               };
+               sdb_data_t d2 = d1;
+
+               char buf1[256], buf2[256];
+               size_t j;
+
+               /* stringv_copy is expected to free memory as needed, so simply copy
+                * over the old values from the previous iteration. */
+               check = stringv_copy(&dst, &dst_len, (const char * const *)src, i + 1);
+               fail_unless(check == 0,
+                               "stringv_copy(<s>, <len>, <src>, %zu) = %d; expected: 0",
+                               i, check);
+               fail_unless((dst != NULL) && (dst_len == i + 1),
+                               "stringv_copy(<s>, <len>, <src>, %zu) produced s=%p, len=%zu; "
+                               "expected: s=<v>, len=%zu", i, dst, dst_len, i + 1);
+
+               for (j = 0; j <= i; j++)
+                       if ((! dst[j]) || (strcmp(dst[j], src[j]) != 0))
+                               break;
+
+               if (j <= i) {
+                       d1.data.array.values = dst;
+                       d1.data.array.length = dst_len;
+                       sdb_data_format(&d1, buf1, sizeof(buf1), 0);
+
+                       d2.data.array.values = src;
+                       d2.data.array.length = dst_len;
+                       sdb_data_format(&d2, buf2, sizeof(buf2), 0);
+
+                       fail("stringv_copy(<s>, <len>, <src>, %zu) produced unexpected result: "
+                                       "vectors differ at position %zu: '%s' <-> '%s'",
+                                       i, j, buf1, buf2);
+               }
+       }
+       stringv_free(&dst, &dst_len);
+}
+END_TEST
+
+TEST_MAIN("utils::strings")
+{
+       TCase *tc = tcase_create("core");
+       tcase_add_test(tc, test_stringv);
+       ADD_TCASE(tc);
+}
+TEST_MAIN_END
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+