Sat, 19 Apr 2025 14:43:16 +0200
adds cx_strcpy() and cx_strcpy_a()
CHANGELOG | file | annotate | diff | comparison | revisions | |
docs/Writerside/topics/about.md | file | annotate | diff | comparison | revisions | |
docs/Writerside/topics/string.h.md | file | annotate | diff | comparison | revisions | |
src/cx/string.h | file | annotate | diff | comparison | revisions | |
src/string.c | file | annotate | diff | comparison | revisions | |
tests/test_string.c | file | annotate | diff | comparison | revisions |
--- a/CHANGELOG Sat Apr 19 11:36:53 2025 +0200 +++ b/CHANGELOG Sat Apr 19 14:43:16 2025 +0200 @@ -7,6 +7,7 @@ * adds cxBufferShrink() * adds cxTreeSize() * adds CX_PRIstr and CX_SFMT macros for formatting UCX strings + * adds cx_strcpy() and cx_strcpy_a() * changes grow strategy for the mempory pool to reduce reallocations * changes grow strategy for CxBuffer, which does now take the page size into account * changes the implementation of cx_strreplacen() for improved efficiency
--- a/docs/Writerside/topics/about.md Sat Apr 19 11:36:53 2025 +0200 +++ b/docs/Writerside/topics/about.md Sat Apr 19 14:43:16 2025 +0200 @@ -34,6 +34,7 @@ * adds cxBufferShrink() * adds cxTreeSize() * adds CX_PRIstr and CX_SFMT macros for formatting UCX strings +* adds cx_strcpy() and cx_strcpy_a() * changes grow strategy for the memory pool to reduce reallocations * changes grow strategy for CxBuffer, which does now take the page size into account * changes the implementation of cx_strreplacen() for improved efficiency
--- a/docs/Writerside/topics/string.h.md Sat Apr 19 11:36:53 2025 +0200 +++ b/docs/Writerside/topics/string.h.md Sat Apr 19 14:43:16 2025 +0200 @@ -39,6 +39,11 @@ cxmutstr cx_strdup_a(const CxAllocator *allocator, AnyStr string); +int cx_strcpy(cxmutstr *dest, cxstring source); + +int cx_strcpy_a(const CxAllocator *allocator, + cxmutstr *dest, cxstring source); + void cx_strfree(cxmutstr *str); void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str); @@ -56,6 +61,10 @@ and guarantees that the result string is zero-terminated. The function `cx_strdup()` is equivalent to `cx_strdup_a()`, except that it uses the default stdlib allocator. +The functions `cx_strcpy_a()` and `cx_strcpy()` copy the contents of the `source` string to the `dest` string, +and also guarantee zero-termination of the resulting string. +The memory in `dest` is either freshly allocated or re-allocated to fit the size of the string plus the terminator. + Allocated strings are always of type `cxmutstr` and can be deallocated by a call to `cx_strfree()` or `cx_strfree_a()`. The caller must make sure to use the correct allocator for deallocating a string. It is safe to call these functions multiple times on a given string, as the pointer will be nulled and the length set to zero.
--- a/src/cx/string.h Sat Apr 19 11:36:53 2025 +0200 +++ b/src/cx/string.h Sat Apr 19 14:43:16 2025 +0200 @@ -340,6 +340,47 @@ ); /** + * Copies a string. + * + * The memory in the @p dest structure is either allocated or re-allocated to fit the entire + * source string, including a zero-terminator. + * + * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is. + * + * @param alloc the allocator + * @param dest a pointer to the structure where to copy the contents to + * @param src the source string + * + * @retval zero success + * @retval non-zero if re-allocation failed + */ +cx_attr_nonnull_arg(1) +cx_attr_export +int cx_strcpy_a( + const CxAllocator *alloc, + cxmutstr *dest, + cxstring src +); + + +/** + * Copies a string. + * + * The memory in the @p dest structure is either allocated or re-allocated to fit the entire + * source string, including a zero-terminator. + * + * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is. + * + * @param alloc (@c CxAllocator*) the allocator + * @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to + * @param src (@c cxstring) the source string + * + * @retval zero success + * @retval non-zero if re-allocation failed + */ +#define cx_strcpy(dest, src) cx_strcpy_a(cxDefaultAllocator, dest, src) + +/** * Returns the accumulated length of all specified strings. * * If this sum overflows, errno is set to EOVERFLOW.
--- a/src/string.c Sat Apr 19 11:36:53 2025 +0200 +++ b/src/string.c Sat Apr 19 14:43:16 2025 +0200 @@ -80,6 +80,22 @@ str->length = 0; } +int cx_strcpy_a( + const CxAllocator *alloc, + cxmutstr *dest, + cxstring src +) { + if (cxReallocate(alloc, &dest->ptr, src.length + 1)) { + return 1; + } + + memcpy(dest->ptr, src.ptr, src.length); + dest->length = src.length; + dest->ptr[dest->length] = '\0'; + + return 0; +} + size_t cx_strlen( size_t count, ...
--- a/tests/test_string.c Sat Apr 19 11:36:53 2025 +0200 +++ b/tests/test_string.c Sat Apr 19 14:43:16 2025 +0200 @@ -95,6 +95,45 @@ cx_strfree(&dup); } +CX_TEST(test_strcpy) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + const CxAllocator *alloc = &talloc.base; + cxstring str = CX_STR("test string"); + str.length = 8; // test with a non-zero-terminated source + cxmutstr dup; + CX_TEST_DO { + // copy into a smaller string + dup = cx_strdup_a(alloc, CX_STR("hello")); + CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); + ASSERT_ZERO_TERMINATED(dup); + cx_strfree_a(alloc, &dup); + + // copy into a larger string + dup = cx_strdup_a(alloc, CX_STR("hello, world!")); + CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); + ASSERT_ZERO_TERMINATED(dup); + cx_strfree_a(alloc, &dup); + + // copy into an equal-length string + dup = cx_strdup_a(alloc, CX_STR("testing!")); + CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); + ASSERT_ZERO_TERMINATED(dup); + cx_strfree_a(alloc, &dup); + + // copy into a NULL-string + dup.ptr = NULL; + CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); + ASSERT_ZERO_TERMINATED(dup); + cx_strfree_a(alloc, &dup); + } + cx_testing_allocator_destroy(&talloc); +} + CX_TEST(test_strlen) { cxstring s1 = CX_STR("1234"); cxstring s2 = CX_STR(".:.:."); @@ -1263,6 +1302,7 @@ cx_test_register(suite, test_strfree); cx_test_register(suite, test_strdup); cx_test_register(suite, test_strdup_shortened); + cx_test_register(suite, test_strcpy); cx_test_register(suite, test_strlen); cx_test_register(suite, test_strsubs); cx_test_register(suite, test_strchr);