# HG changeset patch # User Mike Becker # Date 1682161578 -7200 # Node ID ebdce4bf262bca690abbb363b1c9da015c719fb9 # Parent 1ba4ec2e7a89562f9787770497d02ae038807d52 add cx_strcat_m() and cx_strcat_ma() for in-place concatenation diff -r 1ba4ec2e7a89 -r ebdce4bf262b src/cx/string.h --- a/src/cx/string.h Sat Apr 22 12:29:00 2023 +0200 +++ b/src/cx/string.h Sat Apr 22 13:06:18 2023 +0200 @@ -296,28 +296,50 @@ ); /** - * Concatenates two or more strings. + * Concatenates strings. * * The resulting string will be allocated by the specified allocator. - * So developers \em must pass the return value to cx_strfree() eventually. - * - * \note It is guaranteed that there is only one allocation. - * It is also guaranteed that the returned string is zero-terminated. + * So developers \em must pass the return value to cx_strfree_a() eventually. + * + * If \p str already contains a string, the memory will be reallocated and + * the other strings are appended. Otherwise, new memory is allocated. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. * * @param alloc the allocator to use - * @param count the total number of strings to concatenate - * @param ... all strings + * @param str the string the other strings shall be concatenated to + * @param count the number of the other following strings to concatenate + * @param ... all other strings * @return the concatenated string */ __attribute__((__warn_unused_result__, __nonnull__)) -cxmutstr cx_strcat_a( +cxmutstr cx_strcat_ma( CxAllocator const *alloc, + cxmutstr str, size_t count, ... ); /** - * Concatenates two or more strings. + * Concatenates strings and returns a new string. + * + * The resulting string will be allocated by the specified allocator. + * So developers \em must pass the return value to cx_strfree_a() eventually. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param alloc the allocator to use + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat_a(alloc, count, ...) \ +cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + +/** + * Concatenates strings and returns a new string. * * The resulting string will be allocated by standard \c malloc(). * So developers \em must pass the return value to cx_strfree() eventually. @@ -325,12 +347,32 @@ * \note It is guaranteed that there is only one allocation. * It is also guaranteed that the returned string is zero-terminated. * - * @param count the total number of strings to concatenate - * @param ... all strings + * @param count the number of the other following strings to concatenate + * @param ... all other strings * @return the concatenated string */ #define cx_strcat(count, ...) \ -cx_strcat_a(cxDefaultAllocator, count, __VA_ARGS__) +cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + +/** + * Concatenates strings. + * + * The resulting string will be allocated by standard \c malloc(). + * So developers \em must pass the return value to cx_strfree() eventually. + * + * If \p str already contains a string, the memory will be reallocated and + * the other strings are appended. Otherwise, new memory is allocated. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param str the string the other strings shall be concatenated to + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat_m(str, count, ...) \ +cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) /** * Returns a substring starting at the specified location. diff -r 1ba4ec2e7a89 -r ebdce4bf262b src/string.c --- a/src/string.c Sat Apr 22 12:29:00 2023 +0200 +++ b/src/string.c Sat Apr 22 13:06:18 2023 +0200 @@ -98,11 +98,14 @@ return size; } -cxmutstr cx_strcat_a( +cxmutstr cx_strcat_ma( CxAllocator const *alloc, + cxmutstr str, size_t count, ... ) { + if (count == 0) return str; + cxstring *strings = calloc(count, sizeof(cxstring)); if (!strings) abort(); @@ -110,34 +113,38 @@ va_start(ap, count); // get all args and overall length - size_t slen = 0; + size_t slen = str.length; cx_for_n(i, count) { cxstring s = va_arg (ap, cxstring); strings[i] = s; slen += s.length; } + va_end(ap); - // create new string - cxmutstr result; - result.ptr = cxMalloc(alloc, slen + 1); - result.length = slen; - if (result.ptr == NULL) abort(); + // reallocate or create new string + if (str.ptr == NULL) { + str.ptr = cxMalloc(alloc, slen + 1); + } else { + str.ptr = cxRealloc(alloc, str.ptr, slen + 1); + } + if (str.ptr == NULL) abort(); // concatenate strings - size_t pos = 0; + size_t pos = str.length; + str.length = slen; cx_for_n(i, count) { cxstring s = strings[i]; - memcpy(result.ptr + pos, s.ptr, s.length); + memcpy(str.ptr + pos, s.ptr, s.length); pos += s.length; } // terminate string - result.ptr[result.length] = '\0'; + str.ptr[str.length] = '\0'; // free temporary array free(strings); - return result; + return str; } cxstring cx_strsubs( diff -r 1ba4ec2e7a89 -r ebdce4bf262b tests/test_string.cpp --- a/tests/test_string.cpp Sat Apr 22 12:29:00 2023 +0200 +++ b/tests/test_string.cpp Sat Apr 22 13:06:18 2023 +0200 @@ -273,6 +273,13 @@ EXPECT_EQ(cx_strcmp(cx_strcast(t5), CX_STR("561234")), 0); EXPECT_ZERO_TERMINATED(t5); cx_strfree(&t5); + + // use an initial string + cxmutstr t6 = cx_strdup(CX_STR("Hello")); + t6 = cx_strcat_m(t6, 2, CX_STR(", "), CX_STR("World!")); + EXPECT_EQ(cx_strcmp(cx_strcast(t6), CX_STR("Hello, World!")), 0); + EXPECT_ZERO_TERMINATED(t6); + cx_strfree(&t6); } TEST(String, strsplit) {