From 61e96ed1855b508dc544c5ab2a39de450483f208 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Sat, 10 Sep 2016 17:43:57 -0400 Subject: [PATCH] Add more string utility functions and unit tests. --- src/include/utils/strings.h | 14 +++- src/utils/strings.c | 72 ++++++++++++++--- t/Makefile.am | 7 +- t/unit/utils/strings_test.c | 157 ++++++++++++++++++++++++++++++++++++ 4 files changed, 236 insertions(+), 14 deletions(-) create mode 100644 t/unit/utils/strings_test.c diff --git a/src/include/utils/strings.h b/src/include/utils/strings.h index 61298ee..24f7fc1 100644 --- a/src/include/utils/strings.h +++ b/src/include/utils/strings.h @@ -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. diff --git a/src/utils/strings.c b/src/utils/strings.c index c07cfa1..b6a49e1 100644 --- a/src/utils/strings.c +++ b/src/utils/strings.c @@ -31,34 +31,66 @@ #include "utils/strings.h" +#include + #include #include /* - * 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) { diff --git a/t/Makefile.am b/t/Makefile.am index e294333..c719c04 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -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 index 0000000..326a073 --- /dev/null +++ b/t/unit/utils/strings_test.c @@ -0,0 +1,157 @@ +/* + * SysDB - t/unit/utils/strings_test.c + * Copyright (C) 2016 Sebastian 'tokkee' Harl + * 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 + +/* + * 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(&, &<0>, , 0) = %d; expected: 0", + check); + fail_unless((dst == NULL) && (dst_len == 0), + "stringv_copy(&, &<0>, , 0) produced %p, %d; " + "expected , 0", dst, dst_len); + stringv_free(&dst, &dst_len); + fail_unless((dst == NULL) && (dst_len == 0), + "stringv_free(&, &<0>) produced %p, %d; expected , 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') = %d; expected: 0", + src[i], check); + fail_unless((dst != NULL) && (dst_len == i + 1), + "stringv_append(, , '%s') produced s=%p, len=%zu; " + "expected: s=, 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') 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(, , , %zu) = %d; expected: 0", + i, check); + fail_unless((dst != NULL) && (dst_len == i + 1), + "stringv_copy(, , , %zu) produced s=%p, len=%zu; " + "expected: s=, 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(, , , %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 : */ + -- 2.30.2