From bbbe912fba4f733add222e0e8cbd50db5df37944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 4 Dec 2013 19:58:38 +0100 Subject: [PATCH] Initial version of struct DStr --- include/ucore/dstr.h | 75 ++++++++++++++++ src/dstr.c | 207 +++++++++++++++++++++++++++++++++++++++++++ test/test_dstr.c | 124 ++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 include/ucore/dstr.h create mode 100644 src/dstr.c create mode 100644 test/test_dstr.c diff --git a/include/ucore/dstr.h b/include/ucore/dstr.h new file mode 100644 index 0000000..d39b8d7 --- /dev/null +++ b/include/ucore/dstr.h @@ -0,0 +1,75 @@ +#ifndef UC_DSTR_H_ +#define UC_DSTR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** A dynamic string. + * The string might not always be nul terminated, + * Normally, use uc_dstr_str() to manipulate the managed string + * instead of accessing ->str directly. + */ +struct DStr { + size_t len; + size_t allocated; + char *str; +}; + +#define UC_DSTR_STATIC_INIT {0, 0, NULL} + +/** Initialize a DStr. + * Note that str->str will be NULL after initialization. + * + */ +void uc_dstr_init(struct DStr *str); + +/** Destroy/free the string. + * (Does not free str itself, only the managed resource of the str) + */ +void uc_dstr_destroy(struct DStr *str); + +/** + * @return the available/unused tail bytes in the string + */ +size_t uc_dstr_available(const struct DStr *str); + +/** Ensure the DStr is of at least 'len' bytes, + * allocating room if needed. + */ +int uc_dstr_ensure(struct DStr *str, size_t len); + +void uc_dstr_reset(struct DStr *str); + +void uc_dstr_terminate(struct DStr *str); + +char *uc_dstr_str(struct DStr *str); + +char *uc_dstr_put(struct DStr *str, size_t len); + +size_t uc_dstr_put_str_filter(struct DStr *str, const char *s, int (*is_x)(int c)); + +int uc_dstr_put_str_sz(struct DStr *str, const char *s, size_t len); + +int uc_dstr_put_str(struct DStr *str, const char *s); + +int uc_dstr_put_char(struct DStr *str, char c); + +void uc_dstr_own(struct DStr *str, char *s); + +char *uc_dstr_steal(struct DStr *str); + +void uc_str_swap(struct DStr *a, struct DStr *b); + +size_t uc_dstr_replace(struct DStr *str, char f, char r); + +void uc_dstr_trim(struct DStr *str); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/dstr.c b/src/dstr.c new file mode 100644 index 0000000..101252d --- /dev/null +++ b/src/dstr.c @@ -0,0 +1,207 @@ +#include +#include +#include "ucore/dstr.h" +#include "ucore/utils.h" + +void uc_dstr_init(struct DStr *str) +{ + str->len = 0; + str->allocated = 0; + str->str = NULL; +} + +void uc_dstr_destroy(struct DStr *str) +{ + free(str->str); +} + +size_t uc_dstr_available(const struct DStr *str) +{ + return str->allocated - str->len; +} + +int uc_dstr_ensure(struct DStr *str, size_t len) +{ + if (str->allocated < len) { + //allocate a minimum no. of bytes, and one + //additional for the common case of nul terminating + //the string + size_t new_len = str->len + UC_MAX(7U, len) + 1; + char *new_str = realloc(str->str, new_len); + + if (new_str == NULL) { + return -1; + } + str->str = new_str; + str->allocated = new_len; + } + + return 0; +} + +void uc_dstr_reset(struct DStr *str) +{ + str->len = 0; + if (str->str) { + str->str[0] = 0; + } +} + +void uc_dstr_terminate(struct DStr *str) +{ + uc_dstr_ensure(str, str->len + 1); + str->str[str->len] = 0; +} + +char *uc_dstr_str(struct DStr *str) +{ + uc_dstr_terminate(str); + + return str->str; +} + +char *uc_dstr_put(struct DStr *str, size_t len) +{ + char *start; + + if (uc_dstr_ensure(str, str->len + len) != 0) { + return NULL; + } + + start = str->str + str->len; + str->len += len; + + return start; +} + +size_t uc_dstr_put_str_filter(struct DStr *str, const char *s, int (*is_x)(int c)) +{ + size_t len = strlen(s); + size_t i; + size_t idx = 0; + + if (uc_dstr_ensure(str, str->len + len) != 0) { + return 0; + } + + for (i = 0; i < len; i++) { + if (is_x(s[i])) { + str->str[str->len + idx]= s[i]; + idx++; + } + } + + str->len += idx; + + return idx; +} + +int uc_dstr_put_str_sz(struct DStr *str, const char *s, size_t len) +{ + char *start = uc_dstr_put(str, len); + + if (start == NULL) { + return -1; + } + memcpy(start, s, len); + + return 0; +} + +int uc_dstr_put_str(struct DStr *str, const char *s) +{ + size_t len = strlen(s); + + return uc_dstr_put_str_sz(str, s ,len); +} + +int uc_dstr_put_char(struct DStr *str, char c) +{ + char *start = uc_dstr_put(str, 1); + + if (start == NULL) { + return -1; + } + *start = c; + + return 0; +} + +void uc_dstr_own(struct DStr *str, char *s) +{ + size_t len = strlen(s); + + if (str->str) { + free(str); + } + + str->len = len; + str->allocated = len + 1; + str->str = s; +} + +char *uc_dstr_steal(struct DStr *str) +{ + char *s = str->str; + + uc_dstr_init(str); + + return s; +} + +void uc_str_swap(struct DStr *a, struct DStr *b) +{ + struct DStr tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + +size_t uc_dstr_replace(struct DStr *str, char f, char r) +{ + size_t i; + size_t count = 0; + + if (str->len == 0) { + return 0; + } + + for (i = 0; i < str->len; i++) { + if (str->str[i] == f) { + str->str[i] = r; + count++; + } + } + + return count; +} + +void uc_dstr_trim(struct DStr *str) +{ + size_t spaces; + + uc_dstr_terminate(str); + + //spaces at the start + spaces = strspn(str->str, " \t\n\v\f\r"); + if (spaces > 0) { + memmove(str->str, str->str + spaces, str->len - spaces); + str->len -= spaces; + } + + //spaces at the end + if (str->len > 0) { + size_t idx = str->len - 1; + do { + if (strchr(" \t\n\v\f\r", str->str[idx]) == NULL) { + break; + } else { + str->len--; + idx--; + } + } while (idx != 0); + } +} + + diff --git a/test/test_dstr.c b/test/test_dstr.c new file mode 100644 index 0000000..e04caf6 --- /dev/null +++ b/test/test_dstr.c @@ -0,0 +1,124 @@ +#include +#include +#include + + +START_TEST (test_dstr_basics) + struct DStr str; + int rc; + + char *s; + + uc_dstr_init(&str); + rc = uc_dstr_put_str(&str, "Hello"); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(str.len, 5); + ck_assert_str_eq(uc_dstr_str(&str), "Hello"); + + rc = uc_dstr_put_char(&str, '!'); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(str.len, 6); + ck_assert_str_eq(uc_dstr_str(&str), "Hello!"); + + rc = uc_dstr_put_str_sz(&str, " World 12345678", 13); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(str.len, 19); + ck_assert_str_eq(uc_dstr_str(&str), "Hello! World 123456"); + + uc_dstr_reset(&str); + ck_assert_int_eq(str.len, 0); + ck_assert_str_eq(uc_dstr_str(&str), ""); + + uc_dstr_reset(&str); + ck_assert_int_ge(uc_dstr_available(&str), 0); + + s = uc_dstr_put(&str, 2); + fail_if(s == NULL); + s[0] = '1'; + s[1] = '2'; + + ck_assert_str_eq(uc_dstr_str(&str), "12"); + + uc_dstr_destroy(&str); + + //just test that this doesn't crash: + uc_dstr_init(&str); + uc_dstr_destroy(&str); +END_TEST + +START_TEST (test_dstr_own_steal) + struct DStr str; + char *s; + + uc_dstr_init(&str); + uc_dstr_own(&str, strdup("Hello !")); + ck_assert_int_eq(str.len, 7); + ck_assert_str_eq(uc_dstr_str(&str), "Hello !"); + + s = uc_dstr_steal(&str); + ck_assert_str_eq(s, "Hello !"); + ck_assert_int_eq(str.len, 0); + ck_assert_str_eq(uc_dstr_str(&str), ""); + + free(s); + uc_dstr_destroy(&str); +END_TEST + +START_TEST (test_dstr_trim) + struct DStr str; + + uc_dstr_init(&str); + uc_dstr_put_str(&str, " \t \nHello !\r\n"); + uc_dstr_trim(&str); + ck_assert_str_eq(uc_dstr_str(&str), "Hello !"); + + uc_dstr_destroy(&str); + uc_dstr_init(&str); + + uc_dstr_put_str(&str, " "); + + uc_dstr_trim(&str); + ck_assert_str_eq(uc_dstr_str(&str), ""); + + uc_dstr_destroy(&str); + uc_dstr_init(&str); + + uc_dstr_trim(&str); + ck_assert_str_eq(uc_dstr_str(&str), ""); + + uc_dstr_destroy(&str); + +END_TEST + +START_TEST (test_dstr_replace) + struct DStr str; + size_t rc; + + uc_dstr_init(&str); + uc_dstr_put_str(&str, "-1-2-3-4-5-"); + rc = uc_dstr_replace(&str, '-',' '); + ck_assert_int_eq(rc, 6); + + ck_assert_str_eq(uc_dstr_str(&str), " 1 2 3 4 5 "); + rc = uc_dstr_replace(&str, 'X','2'); + ck_assert_int_eq(rc, 0); + ck_assert_str_eq(uc_dstr_str(&str), " 1 2 3 4 5 "); + + uc_dstr_destroy(&str); + +END_TEST + +Suite *dstr_suite(void) +{ + Suite *s = suite_create("dstr"); + TCase *tc = tcase_create("dstr tests"); + tcase_add_test(tc, test_dstr_basics); + tcase_add_test(tc, test_dstr_own_steal); + tcase_add_test(tc, test_dstr_trim); + tcase_add_test(tc, test_dstr_replace); + + suite_add_tcase(s, tc); + + return s; +} +