From a5662a2edd7ef1a4b7093b38c210426b97bca835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 3 Dec 2013 10:59:56 +0100 Subject: [PATCH 01/15] Revert "Remove the wrong heapsort implementation" This reverts commit aa04b6ae330b42f8a2faaa8a038ce7efbf60cb3d. --- include/ucore/heapsort.h | 21 ++++++++++++++++++++ src/heapsort.c | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 include/ucore/heapsort.h create mode 100644 src/heapsort.c diff --git a/include/ucore/heapsort.h b/include/ucore/heapsort.h new file mode 100644 index 0000000..c5dbf01 --- /dev/null +++ b/include/ucore/heapsort.h @@ -0,0 +1,21 @@ +#ifndef UC_HEAPSORT_H_ +#define UC_HEAPSORT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif +///compare function. Needs only to return < 0 if +//the first element is less than the second +typedef int (*uc_hs_cmp)(const void *, const void *); + +void +uc_heapsort(void *base, size_t count, size_t width, + uc_hs_cmp cmp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/heapsort.c b/src/heapsort.c new file mode 100644 index 0000000..e291c89 --- /dev/null +++ b/src/heapsort.c @@ -0,0 +1,41 @@ +#include +#include "ucore/heapsort.h" + +static void +uc_sift(unsigned char *base, size_t start, size_t count, size_t width, + uc_hs_cmp cmp) +{ + size_t root = start, child; + + while ((root * 2 + 1) < count) { + child = root * 2 + 1; + if (child < (count - 1) + && cmp(&base[child * width], &base[(child + 1) * width]) < 0) + child++; + + if (cmp(&base[root * width], &base[child * width]) < 0) { + memcpy(base + root * width, base + child * width, width); + root = child; + } else + return; + } +} + +void +uc_heapsort(void *base_, size_t count, size_t width, + uc_hs_cmp cmp) +{ + int start = count / 2 - 1, end = count - 1; + unsigned char *base = base_; + + while (start >= 0) { + uc_sift(base, start, count, width, cmp); + start--; + } + + while (end > 0) { + memcpy(base + end * width, base, width); + uc_sift(base, 0, end, width, cmp); + end--; + } +} From 70fb352656cff5ddbb11718d4becbd40ee1afe26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 3 Dec 2013 11:30:09 +0100 Subject: [PATCH 02/15] Fix heapsort, add test --- include/ucore/heapsort.h | 17 +++++-- src/heapsort.c | 26 ++++++----- test/test_heapsort.c | 99 ++++++++++++++++++++++++++++++++++++++++ test/test_runner.c | 4 +- 4 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 test/test_heapsort.c diff --git a/include/ucore/heapsort.h b/include/ucore/heapsort.h index c5dbf01..d6c9a57 100644 --- a/include/ucore/heapsort.h +++ b/include/ucore/heapsort.h @@ -8,11 +8,20 @@ extern "C" { #endif ///compare function. Needs only to return < 0 if //the first element is less than the second -typedef int (*uc_hs_cmp)(const void *, const void *); +typedef int (*uc_hs_cmp)(const void *, const void *, void *cookie); +//swap function +typedef void (*uc_hs_swp)(void *, void *); -void -uc_heapsort(void *base, size_t count, size_t width, - uc_hs_cmp cmp); +/** Sort an array. + * @param base start of array + * @param count number of elements in array + * @param width size of each element + * @param cmp compare function + * @param swap swap function + * @param cookie user pointer passed back to compare function + */ +void uc_heapsort(void *base, size_t count, size_t width, + uc_hs_cmp cmp, uc_hs_swp swap, void *cookie); #ifdef __cplusplus } diff --git a/src/heapsort.c b/src/heapsort.c index e291c89..1157da5 100644 --- a/src/heapsort.c +++ b/src/heapsort.c @@ -3,39 +3,43 @@ static void uc_sift(unsigned char *base, size_t start, size_t count, size_t width, - uc_hs_cmp cmp) + uc_hs_cmp cmp, uc_hs_swp swap, void *cookie) { size_t root = start, child; while ((root * 2 + 1) < count) { child = root * 2 + 1; - if (child < (count - 1) - && cmp(&base[child * width], &base[(child + 1) * width]) < 0) - child++; + if (child < (count - 1)) { + int rc = cmp(&base[child * width], &base[(child + 1) * width], cookie); + if (rc < 0) { + child++; + } + } - if (cmp(&base[root * width], &base[child * width]) < 0) { - memcpy(base + root * width, base + child * width, width); + if (cmp(&base[root * width], &base[child * width], cookie) < 0) { + swap(base + root * width, base + child * width); root = child; - } else + } else { return; + } } } void uc_heapsort(void *base_, size_t count, size_t width, - uc_hs_cmp cmp) + uc_hs_cmp cmp, uc_hs_swp swap, void *cookie) { int start = count / 2 - 1, end = count - 1; unsigned char *base = base_; while (start >= 0) { - uc_sift(base, start, count, width, cmp); + uc_sift(base, start, count, width, cmp, swap, cookie); start--; } while (end > 0) { - memcpy(base + end * width, base, width); - uc_sift(base, 0, end, width, cmp); + swap(base + end * width, base); + uc_sift(base, 0, end, width, cmp, swap, cookie); end--; } } diff --git a/test/test_heapsort.c b/test/test_heapsort.c new file mode 100644 index 0000000..520c6aa --- /dev/null +++ b/test/test_heapsort.c @@ -0,0 +1,99 @@ +#include +#include +#include + +struct Stuff { + char text[11]; + int order; +}; + +static void swap_stuff(void *a_, void *b_) +{ + struct Stuff *a = a_; + struct Stuff *b = b_; + struct Stuff tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + +static int cmp_stuff(const void *a_, const void *b_, void *cookie) +{ + const struct Stuff *a = a_; + const struct Stuff *b = b_; + (void)cookie; + if (a->order < b->order) + return -1; + + return 1; +} + +void print_stuff(const struct Stuff *s, size_t len) +{ + size_t i; + for(i = 0; i < len; i++) { + printf("%s:%d\n", s[i].text, s[i].order); + } +} + +START_TEST (test_heapsort_odd) + struct Stuff unordered[] = { + {"2", 2}, + {"1", 1}, + {"4", 4}, + {"5", 5}, + {"3", 3}, + }; + + struct Stuff ordered[] = { + {"1", 1}, + {"2", 2}, + {"3", 3}, + {"4", 4}, + {"5", 5}, + }; + + uc_heapsort(unordered, ARRAY_SIZE(unordered), sizeof(struct Stuff), + cmp_stuff, swap_stuff, NULL); + //print_stuff(unordered, ARRAY_SIZE(unordered)); + fail_if(memcmp(unordered, ordered, sizeof unordered) != 0); +END_TEST + +START_TEST (test_heapsort_even) + struct Stuff unordered[] = { + {"1", 1}, + {"7", 7}, + {"3", 3}, + {"4", 4}, + {"6", 6}, + {"2", 2}, + }; + + struct Stuff ordered[] = { + {"1", 1}, + {"2", 2}, + {"3", 3}, + {"4", 4}, + {"6", 6}, + {"7", 7}, + }; + + uc_heapsort(unordered, ARRAY_SIZE(unordered), sizeof(struct Stuff), + cmp_stuff, swap_stuff, NULL); + print_stuff(unordered, ARRAY_SIZE(unordered)); + fail_if(memcmp(unordered, ordered, sizeof unordered) != 0); +END_TEST + +Suite *heapsort_suite(void) +{ + Suite *s = suite_create("heapsort"); + TCase *tc = tcase_create("heapsort tests"); + tcase_add_test(tc, test_heapsort_odd); + tcase_add_test(tc, test_heapsort_even); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_runner.c b/test/test_runner.c index a797d5f..2b43008 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -27,6 +27,7 @@ extern Suite *sprintb_suite(void); extern Suite *strv_suite(void); extern Suite *ratelimit_suite(void); extern Suite *htable_suite(void); +extern Suite *heapsort_suite(void); static suite_func suites[] = { bitvec_suite, @@ -48,7 +49,8 @@ static suite_func suites[] = { ratelimit_suite, htable_suite, clock_suite, - threadqueue_suite + threadqueue_suite, + heapsort_suite }; int From 4caf2573fbceec79ccb6e4fc33df888e0f87af17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 3 Dec 2013 14:29:57 +0100 Subject: [PATCH 03/15] Check of count or size is 0 in heapsort --- src/heapsort.c | 10 +++++++++- test/test_heapsort.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/heapsort.c b/src/heapsort.c index 1157da5..45a56e5 100644 --- a/src/heapsort.c +++ b/src/heapsort.c @@ -29,9 +29,17 @@ void uc_heapsort(void *base_, size_t count, size_t width, uc_hs_cmp cmp, uc_hs_swp swap, void *cookie) { - int start = count / 2 - 1, end = count - 1; + long start; + long end; unsigned char *base = base_; + if (count == 0 || width == 0) { + return; + } + + start = count / 2 - 1, + end = count - 1; + while (start >= 0) { uc_sift(base, start, count, width, cmp, swap, cookie); start--; diff --git a/test/test_heapsort.c b/test/test_heapsort.c index 520c6aa..3e84463 100644 --- a/test/test_heapsort.c +++ b/test/test_heapsort.c @@ -81,16 +81,44 @@ START_TEST (test_heapsort_even) uc_heapsort(unordered, ARRAY_SIZE(unordered), sizeof(struct Stuff), cmp_stuff, swap_stuff, NULL); - print_stuff(unordered, ARRAY_SIZE(unordered)); fail_if(memcmp(unordered, ordered, sizeof unordered) != 0); END_TEST +START_TEST (test_heapsort_zero) + struct Stuff unordered[] = { + {"1", 1}, + {"7", 7}, + {"3", 3}, + {"4", 4}, + {"6", 6}, + {"2", 2}, + }; + + struct Stuff same[] = { + {"1", 1}, + {"7", 7}, + {"3", 3}, + {"4", 4}, + {"6", 6}, + {"2", 2}, + }; + + uc_heapsort(unordered, 0, sizeof(struct Stuff), + cmp_stuff, swap_stuff, NULL); + fail_if(memcmp(unordered, same, sizeof unordered) != 0); + + uc_heapsort(unordered, ARRAY_SIZE(unordered), 0, + cmp_stuff, swap_stuff, NULL); + fail_if(memcmp(unordered, same, sizeof unordered) != 0); +END_TEST + Suite *heapsort_suite(void) { Suite *s = suite_create("heapsort"); TCase *tc = tcase_create("heapsort tests"); tcase_add_test(tc, test_heapsort_odd); tcase_add_test(tc, test_heapsort_even); + tcase_add_test(tc, test_heapsort_zero); suite_add_tcase(s, tc); From 2b022aeddccd905f6271a66a2b6cd15954e19f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 3 Dec 2013 17:29:00 +0100 Subject: [PATCH 04/15] Make the heapsort test comparison work.. --- test/test_heapsort.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/test/test_heapsort.c b/test/test_heapsort.c index 3e84463..9635e56 100644 --- a/test/test_heapsort.c +++ b/test/test_heapsort.c @@ -37,6 +37,19 @@ void print_stuff(const struct Stuff *s, size_t len) } } +static int cmp_stuff_array(const struct Stuff *a, const struct Stuff *b, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) { + if (strcmp(a[i].text, b[i].text) != 0) + return -1; + if (a[i].order != b[i].order) + return -1; + } + + return 0; +} + START_TEST (test_heapsort_odd) struct Stuff unordered[] = { {"2", 2}, @@ -56,8 +69,8 @@ START_TEST (test_heapsort_odd) uc_heapsort(unordered, ARRAY_SIZE(unordered), sizeof(struct Stuff), cmp_stuff, swap_stuff, NULL); - //print_stuff(unordered, ARRAY_SIZE(unordered)); - fail_if(memcmp(unordered, ordered, sizeof unordered) != 0); + print_stuff(unordered, ARRAY_SIZE(unordered)); + fail_if(cmp_stuff_array(unordered, ordered, ARRAY_SIZE(unordered)) != 0); END_TEST START_TEST (test_heapsort_even) @@ -81,7 +94,7 @@ START_TEST (test_heapsort_even) uc_heapsort(unordered, ARRAY_SIZE(unordered), sizeof(struct Stuff), cmp_stuff, swap_stuff, NULL); - fail_if(memcmp(unordered, ordered, sizeof unordered) != 0); + fail_if(cmp_stuff_array(unordered, ordered, ARRAY_SIZE(unordered)) != 0); END_TEST START_TEST (test_heapsort_zero) @@ -105,11 +118,11 @@ START_TEST (test_heapsort_zero) uc_heapsort(unordered, 0, sizeof(struct Stuff), cmp_stuff, swap_stuff, NULL); - fail_if(memcmp(unordered, same, sizeof unordered) != 0); + fail_if(cmp_stuff_array(unordered, same, ARRAY_SIZE(unordered)) != 0); uc_heapsort(unordered, ARRAY_SIZE(unordered), 0, cmp_stuff, swap_stuff, NULL); - fail_if(memcmp(unordered, same, sizeof unordered) != 0); + fail_if(cmp_stuff_array(unordered, same, ARRAY_SIZE(unordered)) != 0); END_TEST Suite *heapsort_suite(void) From 43a14a5149b59a5c9491688d4247272075886337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 3 Dec 2013 17:30:37 +0100 Subject: [PATCH 05/15] Add comments to compare/swap function --- include/ucore/heapsort.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ucore/heapsort.h b/include/ucore/heapsort.h index d6c9a57..fcaf426 100644 --- a/include/ucore/heapsort.h +++ b/include/ucore/heapsort.h @@ -7,10 +7,10 @@ extern "C" { #endif ///compare function. Needs only to return < 0 if -//the first element is less than the second -typedef int (*uc_hs_cmp)(const void *, const void *, void *cookie); -//swap function -typedef void (*uc_hs_swp)(void *, void *); +//a is less tan b +typedef int (*uc_hs_cmp)(const void *a, const void *b, void *cookie); +//swap function, must swap around element a and b +typedef void (*uc_hs_swp)(void *a, void *b); /** Sort an array. * @param base start of array From f6afc8ba57225e49a01afab52aaff028f4e6788a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 3 Dec 2013 19:42:43 +0100 Subject: [PATCH 06/15] Allow UC_MIN(UC_MAX(x,y),z), though only one nesting can be done. --- include/ucore/utils.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/ucore/utils.h b/include/ucore/utils.h index f3bedef..39db850 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -14,15 +14,15 @@ //MAX of a and b #define UC_MAX(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a > _b ? _a : _b; }) + ({ __typeof__ (a) _amx = (a); \ + __typeof__ (b) _bmx = (b); \ + _amx > _bmx ? _amx : _bmx; }) //min of a and b #define UC_MIN(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a < _b ? _a : _b; }) + ({ __typeof__ (a) _amn = (a); \ + __typeof__ (b) _bmn = (b); \ + _amn < _bmn ? _amn : _bmn; }) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) From 2f93ea8ad241cb3f136aa8242f3aad39e28a605e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 3 Dec 2013 19:43:27 +0100 Subject: [PATCH 07/15] =?UTF-8?q?Limit=20overallocation=20to=20[8-256]?= =?UTF-8?q?=C2=A0bytes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/buffer.c b/src/buffer.c index e8c96a3..4a27e57 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3,6 +3,7 @@ #include #include #include "ucore/buffer.h" +#include "ucore/utils.h" GBuf* uc_new_gbuf(size_t initsz) @@ -48,7 +49,7 @@ uc_gbuf_append(GBuf *buf,const void *data,size_t len) unsigned char *gbuf; if (buf->len - buf->used < len) - if (uc_gbuf_grow(buf, len + len/2)) + if (uc_gbuf_grow(buf, len + UC_MAX(UC_MIN(8U,len/2),256U))) return -1; gbuf = buf->buf; 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 08/15] 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; +} + From 6fdaa83dee9a22a3385d9f8f19e9cfedc8a53611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 4 Dec 2013 19:58:59 +0100 Subject: [PATCH 09/15] Run DStr tests --- test/test_runner.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_runner.c b/test/test_runner.c index 2b43008..5c09bb3 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -28,6 +28,7 @@ extern Suite *strv_suite(void); extern Suite *ratelimit_suite(void); extern Suite *htable_suite(void); extern Suite *heapsort_suite(void); +extern Suite *dstr_suite(void); static suite_func suites[] = { bitvec_suite, @@ -50,7 +51,8 @@ static suite_func suites[] = { htable_suite, clock_suite, threadqueue_suite, - heapsort_suite + heapsort_suite, + dstr_suite }; int From 8749270ca4d0fe9bf184bea535629e7365d7dbe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 4 Dec 2013 20:32:06 +0100 Subject: [PATCH 10/15] Add DStr docs. Small test changes --- include/ucore/dstr.h | 68 +++++++++++++++++++++++++++++++++++++++++--- test/test_dstr.c | 3 +- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/include/ucore/dstr.h b/include/ucore/dstr.h index d39b8d7..1f03f61 100644 --- a/include/ucore/dstr.h +++ b/include/ucore/dstr.h @@ -7,13 +7,17 @@ extern "C" { #endif -/** A dynamic string. - * The string might not always be nul terminated, - * Normally, use uc_dstr_str() to manipulate the managed string +/** A dynamically managed C-string. + * + * The .str member might not always be nul terminated, + * Normally, use uc_dstr_str() to retreive the managed string * instead of accessing ->str directly. */ struct DStr { + /** Length of the managed string.*/ size_t len; + + //internal fields: size_t allocated; char *str; }; @@ -37,34 +41,90 @@ void uc_dstr_destroy(struct DStr *str); size_t uc_dstr_available(const struct DStr *str); /** Ensure the DStr is of at least 'len' bytes, - * allocating room if needed. + * allocating room if needed. + * + * @return 0 on success (!= 0 if an allocation fail) */ int uc_dstr_ensure(struct DStr *str, size_t len); +/** Reset the DStr, setting its length to 0 + */ void uc_dstr_reset(struct DStr *str); +/** nul terminate the DStr */ void uc_dstr_terminate(struct DStr *str); +/** Retreive the managed string, the returned string + * is always nul terminated. The DStr will still be managing + * the returned char *. + */ char *uc_dstr_str(struct DStr *str); +/** reserve len bytes for appending to the dstr + * you must fill in a string in the 0-len range of + * the returned pointer. + * + * @param len bytes to reseve + * @return poiinter to the start of the data to append + */ char *uc_dstr_put(struct DStr *str, size_t len); +/** Append a string, filtering out characters. + * e.g. pass in the ctype.h isdigit as the is_x function + * to append only the digits in the supplied string. + * + * @param s string to append + * @param is_x ctype.h compatible function to use as a filter. + * @return number of chars appended + */ size_t uc_dstr_put_str_filter(struct DStr *str, const char *s, int (*is_x)(int c)); +/* Append a string of the give size. + * (The nul terminator is not appended) + * + * @param s string to append + * @param len bytes to append (not including the nul terminator) + * @return 0 on success (!= 0 if an allocation fail) + */ int uc_dstr_put_str_sz(struct DStr *str, const char *s, size_t len); +/* Append a string. + * (The nul terminator is not appended) + * @return 0 on success (!= 0 if an allocation fail) + */ int uc_dstr_put_str(struct DStr *str, const char *s); +/* Append a single char. + * @return 0 on success (!= 0 if an allocation fail) + */ int uc_dstr_put_char(struct DStr *str, char c); +/* Make the DStr take ownership of the give dynamically allocated string. + * Any existing string in the DStr is freed. + */ void uc_dstr_own(struct DStr *str, char *s); +/** Release the managed string. + * The caller is now responsible for managing/free'ing the returned string. + * The DStr is considered empty afterwards. + * + * @return The dynamically allocated string that was managed. + */ char *uc_dstr_steal(struct DStr *str); +/** Swap the A and B dstr */ void uc_str_swap(struct DStr *a, struct DStr *b); +/** Replace all occurences in the DStr + * + * @param f char to find + * @param r char to replace with + */ size_t uc_dstr_replace(struct DStr *str, char f, char r); +/** Remove leading and trailing spaces in the DStr + * Spaces are the ascii ones: " \t\r\v\t\n" + */ void uc_dstr_trim(struct DStr *str); #ifdef __cplusplus diff --git a/test/test_dstr.c b/test/test_dstr.c index e04caf6..4e53443 100644 --- a/test/test_dstr.c +++ b/test/test_dstr.c @@ -65,9 +65,8 @@ START_TEST (test_dstr_own_steal) END_TEST START_TEST (test_dstr_trim) - struct DStr str; + struct DStr str = UC_DSTR_STATIC_INIT; - 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 !"); From d81e9b760a7b92a760972aef0cc8379231cd5dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 4 Dec 2013 20:44:47 +0100 Subject: [PATCH 11/15] More docs. And test put_filter --- include/ucore/dstr.h | 19 +++++++++++++------ test/test_dstr.c | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/include/ucore/dstr.h b/include/ucore/dstr.h index 1f03f61..33f7be2 100644 --- a/include/ucore/dstr.h +++ b/include/ucore/dstr.h @@ -7,11 +7,15 @@ extern "C" { #endif -/** A dynamically managed C-string. +/** A managed C-string, using dynalically allocated memory for the string. * * The .str member might not always be nul terminated, * Normally, use uc_dstr_str() to retreive the managed string * instead of accessing ->str directly. + * + * Normally, let the DSTR manage the nul terminator as manually adding + * a nul byte and potintially adding more data to the DStr would append + * it after that nul terminator. */ struct DStr { /** Length of the managed string.*/ @@ -31,12 +35,12 @@ struct DStr { void uc_dstr_init(struct DStr *str); /** Destroy/free the string. - * (Does not free str itself, only the managed resource of the str) + * (Frees str->str, not str itself) */ void uc_dstr_destroy(struct DStr *str); /** - * @return the available/unused tail bytes in the string + * @return the available/unused tail bytes in the string. */ size_t uc_dstr_available(const struct DStr *str); @@ -60,7 +64,7 @@ void uc_dstr_terminate(struct DStr *str); */ char *uc_dstr_str(struct DStr *str); -/** reserve len bytes for appending to the dstr +/** Reserve len bytes for appending to the dstr * you must fill in a string in the 0-len range of * the returned pointer. * @@ -90,16 +94,18 @@ int uc_dstr_put_str_sz(struct DStr *str, const char *s, size_t len); /* Append a string. * (The nul terminator is not appended) + * * @return 0 on success (!= 0 if an allocation fail) */ int uc_dstr_put_str(struct DStr *str, const char *s); /* Append a single char. + * * @return 0 on success (!= 0 if an allocation fail) */ int uc_dstr_put_char(struct DStr *str, char c); -/* Make the DStr take ownership of the give dynamically allocated string. +/* Make the DStr take ownership of the given dynamically allocated string. * Any existing string in the DStr is freed. */ void uc_dstr_own(struct DStr *str, char *s); @@ -112,10 +118,11 @@ void uc_dstr_own(struct DStr *str, char *s); */ char *uc_dstr_steal(struct DStr *str); -/** Swap the A and B dstr */ +/** Swap the A and B DStr */ void uc_str_swap(struct DStr *a, struct DStr *b); /** Replace all occurences in the DStr + * (This may be used to replace any embedded nul bytes too) * * @param f char to find * @param r char to replace with diff --git a/test/test_dstr.c b/test/test_dstr.c index 4e53443..1d1a9d4 100644 --- a/test/test_dstr.c +++ b/test/test_dstr.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -107,6 +108,21 @@ START_TEST (test_dstr_replace) END_TEST +START_TEST (test_dstr_filter) + struct DStr str; + size_t rc; + + uc_dstr_init(&str); + uc_dstr_put_str(&str, "AB:"); + rc = uc_dstr_put_str_filter(&str, "1:2,3 4 ", isdigit); + ck_assert_int_eq(rc, 4); + + ck_assert_str_eq(uc_dstr_str(&str), "AB:1234"); + + uc_dstr_destroy(&str); + +END_TEST + Suite *dstr_suite(void) { Suite *s = suite_create("dstr"); @@ -115,6 +131,7 @@ Suite *dstr_suite(void) tcase_add_test(tc, test_dstr_own_steal); tcase_add_test(tc, test_dstr_trim); tcase_add_test(tc, test_dstr_replace); + tcase_add_test(tc, test_dstr_filter); suite_add_tcase(s, tc); From b5e1d086a41d091e01ba6a047ea75d0412fe058e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 4 Dec 2013 21:33:16 +0100 Subject: [PATCH 12/15] Add DStr sprintf --- include/ucore/dstr.h | 7 ++++++ src/dstr.c | 55 +++++++++++++++++++++++++++++++++++++++++++- test/test_dstr.c | 20 +++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/include/ucore/dstr.h b/include/ucore/dstr.h index 33f7be2..77e462d 100644 --- a/include/ucore/dstr.h +++ b/include/ucore/dstr.h @@ -2,6 +2,7 @@ #define UC_DSTR_H_ #include +#include #ifdef __cplusplus extern "C" { @@ -134,6 +135,12 @@ size_t uc_dstr_replace(struct DStr *str, char f, char r); */ void uc_dstr_trim(struct DStr *str); +/** Append formatted string (just as sprintf)*/ +int uc_dstr_sprintf(struct DStr *str, const char *fmt, ...) __attribute__((format(printf,2,3))); + +/** va_list variant */ +int uc_dstr_vsprintf(struct DStr *str, const char *fmt, va_list ap_)__attribute__((format(printf,2,0))); + #ifdef __cplusplus } #endif diff --git a/src/dstr.c b/src/dstr.c index 101252d..59d4cbc 100644 --- a/src/dstr.c +++ b/src/dstr.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "ucore/dstr.h" #include "ucore/utils.h" @@ -26,7 +28,7 @@ int uc_dstr_ensure(struct DStr *str, size_t 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; + size_t new_len = UC_MAX(7U, len) + 1; char *new_str = realloc(str->str, new_len); if (new_str == NULL) { @@ -204,4 +206,55 @@ void uc_dstr_trim(struct DStr *str) } } +int uc_dstr_sprintf(struct DStr *str, const char *fmt, ...) +{ + va_list ap; + int rc; + + va_start(ap, fmt); + rc = uc_dstr_vsprintf(str, fmt, ap); + va_end(ap); + + return rc; +} + +int uc_dstr_vsprintf(struct DStr *str, const char *fmt, va_list ap_) +{ + va_list ap; + int rc; + int available; + int needed; + + rc = uc_dstr_ensure(str, str->len + 1); + if (rc != 0) { + return -1; + } + + available = uc_dstr_available(str); + + va_copy(ap, ap_); + needed = vsnprintf(str->str + str->len, available, fmt, ap); + va_end(ap); + + if (needed <= 0) { + return needed; + } + + if (needed >= available) { + rc = uc_dstr_ensure(str, str->len + needed); + if (rc != 0) { + return -1; + } + + available = uc_dstr_available(str); + + va_copy(ap, ap_); + needed = vsnprintf(str->str + str->len, available, fmt, ap); + va_end(ap); + } + + str->len += needed; + + return needed; +} diff --git a/test/test_dstr.c b/test/test_dstr.c index 1d1a9d4..da5b625 100644 --- a/test/test_dstr.c +++ b/test/test_dstr.c @@ -31,7 +31,7 @@ START_TEST (test_dstr_basics) ck_assert_str_eq(uc_dstr_str(&str), ""); uc_dstr_reset(&str); - ck_assert_int_ge(uc_dstr_available(&str), 0); + ck_assert_int_gt(uc_dstr_available(&str), 0); s = uc_dstr_put(&str, 2); fail_if(s == NULL); @@ -123,6 +123,23 @@ START_TEST (test_dstr_filter) END_TEST +START_TEST (test_dstr_sprintf) + struct DStr str; + size_t rc; + + uc_dstr_init(&str); + rc = uc_dstr_sprintf(&str, "%d-%d-%d", 1, 2, 3); + ck_assert_int_gt(rc, 0); + ck_assert_str_eq(uc_dstr_str(&str), "1-2-3"); + + rc = uc_dstr_sprintf(&str, " Another, bigger %s.", "string"); + ck_assert_int_gt(rc, 0); + ck_assert_str_eq(uc_dstr_str(&str), "1-2-3 Another, bigger string."); + + uc_dstr_destroy(&str); + +END_TEST + Suite *dstr_suite(void) { Suite *s = suite_create("dstr"); @@ -132,6 +149,7 @@ Suite *dstr_suite(void) tcase_add_test(tc, test_dstr_trim); tcase_add_test(tc, test_dstr_replace); tcase_add_test(tc, test_dstr_filter); + tcase_add_test(tc, test_dstr_sprintf); suite_add_tcase(s, tc); From b24f27ec9fb8d1912b3d11cda95490f7216f5cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 4 Dec 2013 23:21:30 +0100 Subject: [PATCH 13/15] Mask the upper bits of dscp instead of failing if the user supplied a too big value --- src/fd_utils.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/fd_utils.c b/src/fd_utils.c index e557f60..e14fb12 100644 --- a/src/fd_utils.c +++ b/src/fd_utils.c @@ -89,14 +89,8 @@ int uc_set_ip_dscp(int fd, unsigned int dscp) int val; //only 6 bits for dscp - if (dscp > 63) { - errno = EINVAL; - return -1; - } - - val = dscp << 2; + val = (dscp & 0x3f) << 2; return setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof val); } - From 9723a579ccb31b25be120e9bf37e566259230f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 4 Dec 2013 23:46:45 +0100 Subject: [PATCH 14/15] Clarift uc_dstr_ensure --- include/ucore/dstr.h | 8 ++++++-- src/dstr.c | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/ucore/dstr.h b/include/ucore/dstr.h index 77e462d..a003983 100644 --- a/include/ucore/dstr.h +++ b/include/ucore/dstr.h @@ -45,12 +45,16 @@ void uc_dstr_destroy(struct DStr *str); */ size_t uc_dstr_available(const struct DStr *str); -/** Ensure the DStr is of at least 'len' bytes, +/** Ensure the DStr have room for least 'len' bytes, * allocating room if needed. * + * This includes the length of the existing string, + * to ensure e.g an additional 10 bytes can be appended, + * one would do uc_dstr_ensure(str, str->len + 10) + * * @return 0 on success (!= 0 if an allocation fail) */ -int uc_dstr_ensure(struct DStr *str, size_t len); +int uc_dstr_ensure(struct DStr *str, size_t total); /** Reset the DStr, setting its length to 0 */ diff --git a/src/dstr.c b/src/dstr.c index 59d4cbc..d0ea6a6 100644 --- a/src/dstr.c +++ b/src/dstr.c @@ -22,13 +22,13 @@ size_t uc_dstr_available(const struct DStr *str) return str->allocated - str->len; } -int uc_dstr_ensure(struct DStr *str, size_t len) +int uc_dstr_ensure(struct DStr *str, size_t total) { - if (str->allocated < len) { + if (str->allocated < total) { //allocate a minimum no. of bytes, and one //additional for the common case of nul terminating //the string - size_t new_len = UC_MAX(7U, len) + 1; + size_t new_len = UC_MAX(7U, total) + 1; char *new_str = realloc(str->str, new_len); if (new_str == NULL) { From 565de6ace73d035e7fdc28d4c5044898f7d24d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Thu, 5 Dec 2013 01:24:41 +0100 Subject: [PATCH 15/15] Add uc_dstr_copy --- include/ucore/dstr.h | 9 +++++++++ src/dstr.c | 21 +++++++++++++++++++++ test/test_dstr.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/include/ucore/dstr.h b/include/ucore/dstr.h index a003983..083de2f 100644 --- a/include/ucore/dstr.h +++ b/include/ucore/dstr.h @@ -40,6 +40,15 @@ void uc_dstr_init(struct DStr *str); */ void uc_dstr_destroy(struct DStr *str); +/** Copy a DStr + * + * @param dest destination string to copy to, must not contain an existing string + * @param src DStr to copy + * + * @return 0 if success, != 0 if allocation fails + */ +int uc_dstr_copy(struct DStr *dest, const struct DStr *src); + /** * @return the available/unused tail bytes in the string. */ diff --git a/src/dstr.c b/src/dstr.c index d0ea6a6..9c64fa2 100644 --- a/src/dstr.c +++ b/src/dstr.c @@ -17,6 +17,27 @@ void uc_dstr_destroy(struct DStr *str) free(str->str); } +int uc_dstr_copy(struct DStr *dest, const struct DStr *src) +{ + char *s; + + uc_dstr_init(dest); + if (src->allocated == 0) { + return 0; + } + + s = uc_dstr_put(dest, src->len); + if (s == NULL) { + return -1; + } + + //+1 to copy a potential nul terminator, uc_dstr_put + //guarantees we have room for it. + memcpy(s, src->str, UC_MIN(src->len + 1, src->allocated)); + + return 0; +} + size_t uc_dstr_available(const struct DStr *str) { return str->allocated - str->len; diff --git a/test/test_dstr.c b/test/test_dstr.c index da5b625..6168e98 100644 --- a/test/test_dstr.c +++ b/test/test_dstr.c @@ -140,6 +140,33 @@ START_TEST (test_dstr_sprintf) END_TEST +START_TEST (test_dstr_copy) + struct DStr str; + struct DStr copy; + size_t rc; + + uc_dstr_init(&str); + rc = uc_dstr_put_str(&str, "123"); + ck_assert_int_eq(rc, 0); + + rc = uc_dstr_copy(©, &str); + ck_assert_int_eq(rc, 0); + ck_assert_str_eq(uc_dstr_str(©), uc_dstr_str(&str)); + + uc_dstr_destroy(&str); + uc_dstr_destroy(©); + uc_dstr_init(&str); + uc_dstr_init(©); + + rc = uc_dstr_copy(©, &str); + ck_assert_int_eq(rc, 0); + ck_assert_str_eq(uc_dstr_str(©), ""); + + uc_dstr_destroy(&str); + uc_dstr_destroy(©); + +END_TEST + Suite *dstr_suite(void) { Suite *s = suite_create("dstr"); @@ -150,6 +177,7 @@ Suite *dstr_suite(void) tcase_add_test(tc, test_dstr_replace); tcase_add_test(tc, test_dstr_filter); tcase_add_test(tc, test_dstr_sprintf); + tcase_add_test(tc, test_dstr_copy); suite_add_tcase(s, tc);