summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 3dea932)
raw | patch | inline | side by side (parent: 3dea932)
author | Sebastian Harl <sh@tokkee.org> | |
Sat, 10 Sep 2016 21:43:57 +0000 (17:43 -0400) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Sat, 10 Sep 2016 21:43:57 +0000 (17:43 -0400) |
src/include/utils/strings.h | patch | blob | history | |
src/utils/strings.c | patch | blob | history | |
t/Makefile.am | patch | blob | history | |
t/unit/utils/strings_test.c | [new file with mode: 0644] | patch | blob |
index 61298ee7cc9d0e96d72bd02a4bb9a259aa07bc5f..24f7fc11f8aa8f0bef5d7227c7c7e5d38200e11b 100644 (file)
/*
* 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
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.
diff --git a/src/utils/strings.c b/src/utils/strings.c
index c07cfa1c2de1e0153f661c2a1553753d451788bb..b6a49e10ab36831d709f559cda7de4f5cfd8d79a 100644 (file)
--- a/src/utils/strings.c
+++ b/src/utils/strings.c
#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;
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)
{
diff --git a/t/Makefile.am b/t/Makefile.am
index e29433328bb5e30e5db13895e27890f467d481d8..c719c045df4ed9842b2431d55a0ed6cfff5415f1 100644 (file)
--- a/t/Makefile.am
+++ b/t/Makefile.am
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
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
--- /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 : */
+