diff --git a/include/ucore/strvec.h b/include/ucore/strvec.h new file mode 100644 index 0000000..3789d78 --- /dev/null +++ b/include/ucore/strvec.h @@ -0,0 +1,90 @@ +#include + +#ifndef UC_STRVEC_H_ +#define UC_STRVEC_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/** A Vector of strings.*/ +struct UCStrv +{ + /** number of strings */ + size_t cnt; + /** allocated room in strings (internal use) */ + size_t allocated; + /** Array of strings */ + char **strings; +}; + +/** Initialize a UCStrv + * + * @param strv + */ +void uc_strv_init(struct UCStrv *strv); + +/** + * Grow the UCStrv so it can hold n more items + * + * @param strv UCStrv to grow + * @param cnt expand strvec to hold this cnt more items + * @return 0 on success, otherwise failure + **/ +int uc_strv_grow(struct UCStrv *strv, size_t cnt); + +/** + * Expand the UCStrv to hold 1 more item + * + * @param strv UCStrv to expand + * @return 0 on success, otherwise failure + **/ +int uc_strv_expand(struct UCStrv *strv); + +/** + * Append a string to the UCStrv, the string will be copied in + * + * @param strv UCStrv to append to + * @param str String to append, the string will be copied. + * @return 0 on success, otherwise failure + **/ +int uc_strv_append(struct UCStrv *strv, const char *str); + +/** + * Append a string to the strvec, nocopy variang. + * The string shold be dynamically allocated, as + * destroying the UCStrv will free() all items. + * + * @param strv UCStrv to add to + * @param str the string to append + * @return 0 on success, otherwise failure + **/ +int uc_strv_append_nocopy(struct UCStrv *strv, char *str); + +/** + * Append a NULL pointer to the UCStrv, (cnt is not increased, + * so another _appended item will overwrite this NULL terminator) + * + * @param strv UCStrv to append to + * @return 0 on success, otherwise failure + **/ +int uc_strv_null_terminate(struct UCStrv *strv); + +/** + * free all strings in this UCStrv, cnt will be 0 after this + * @param strv UCStrv to clear + */ +void uc_strv_clear(struct UCStrv *strv); + +/** + * free all elements as well as the underlying array, the UCStrv is + * unusable after this call + * + * @param strv UCStrv to destroy + */ +void uc_strv_destroy(struct UCStrv *strv); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/strvec.c b/src/strvec.c new file mode 100644 index 0000000..079cbdd --- /dev/null +++ b/src/strvec.c @@ -0,0 +1,107 @@ +#include +#include +#include "ucore/strvec.h" + +void uc_strv_init(struct UCStrv *strv) +{ + strv->cnt = 0; + strv->allocated = 0; + strv->strings = NULL; +} + +int uc_strv_grow(struct UCStrv *strv, size_t cnt) +{ + + size_t allocate = strv->allocated + cnt; + void *tmp = realloc(strv->strings, sizeof *strv->strings * allocate); + + if (tmp == NULL) { + return -1; + } + strv->strings = tmp; + strv->allocated = allocate; + + return 0; +} + +int uc_strv_expand(struct UCStrv *strv) +{ + int rc = 0; + + if (strv->cnt >= strv->allocated) { + rc = uc_strv_grow(strv, 1); + } + + return rc; +} + +int uc_strv_append(struct UCStrv *strv, const char *str) +{ + int rc; + char *dup; + + rc = uc_strv_expand(strv); + if (rc != 0) { + return rc; + } + + dup = strdup(str); + if (dup == NULL) { + return -2; + } + + strv->strings[strv->cnt] = dup; + strv->cnt++; + + return 0; +} + +int uc_strv_append_nocopy(struct UCStrv *strv, char *str) +{ + int rc; + rc = uc_strv_expand(strv); + + if (rc != 0) { + return rc; + } + + strv->strings[strv->cnt] = str; + strv->cnt++; + + return 0; +} + +int uc_strv_null_terminate(struct UCStrv *strv) +{ + int rc; + + rc = uc_strv_expand(strv); + if (rc != 0) { + return rc; + } + + strv->strings[strv->cnt] = NULL; + strv->cnt++; + + return 0; +} + +void uc_strv_clear(struct UCStrv *strv) +{ + size_t i; + + for (i = 0; i < strv->cnt; i++) { + free(strv->strings[i]); + strv->strings[i] = NULL; + } + + strv->cnt = 0; +} + +void uc_strv_destroy(struct UCStrv *strv) +{ + uc_strv_clear(strv); + free(strv->strings); + strv->strings = NULL; +} + diff --git a/test/test_runner.c b/test/test_runner.c index 56af24a..3c38f59 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -23,6 +23,7 @@ extern Suite *mbuf_suite(void); extern Suite *human_bytesz_suite(void); extern Suite *dbuf_suite(void); extern Suite *sprintb_suite(void); +extern Suite *strv_suite(void); static suite_func suites[] = { bitvec_suite, @@ -41,7 +42,8 @@ static suite_func suites[] = { mbuf_suite, human_bytesz_suite, dbuf_suite, - sprintb_suite + sprintb_suite, + strv_suite }; int diff --git a/test/test_strv.c b/test/test_strv.c new file mode 100644 index 0000000..d9745b2 --- /dev/null +++ b/test/test_strv.c @@ -0,0 +1,91 @@ +#include +#include +#include + + +START_TEST (test_strv_append) + struct UCStrv strv; + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append(&strv, "string"); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + ck_assert_str_eq("string", strv.strings[0]); + + rc = uc_strv_append(&strv, "string2"); + fail_if(rc != 0); + fail_if(strv.cnt != 2); + ck_assert_str_eq("string2", strv.strings[1]); + + uc_strv_destroy(&strv); + +END_TEST + +START_TEST (test_strv_clear) + struct UCStrv strv; + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append(&strv, "string"); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + + uc_strv_clear(&strv); + fail_if(strv.cnt != 0); + uc_strv_destroy(&strv); + +END_TEST + +START_TEST (test_strv_nocopy) + struct UCStrv strv; + char *str = strdup("string"); + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append_nocopy(&strv, str); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + fail_if(strv.strings[0] != str); + + uc_strv_destroy(&strv); + +END_TEST + +START_TEST (test_strv_null_terminate) + struct UCStrv strv; + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append(&strv, "string"); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + + rc = uc_strv_null_terminate(&strv); + fail_if(rc != 0); + fail_if(strv.strings[strv.cnt] != NULL); + + uc_strv_destroy(&strv); + +END_TEST + + +Suite *strv_suite(void) +{ + Suite *s = suite_create("strvec"); + TCase *tc = tcase_create("strvec"); + + tcase_add_test(tc, test_strv_append); + tcase_add_test(tc, test_strv_clear); + tcase_add_test(tc, test_strv_nocopy); + tcase_add_test(tc, test_strv_null_terminate); + + suite_add_tcase(s, tc); + + return s; +} +