adds cx_strcpy() and cx_strcpy_a()

Sat, 19 Apr 2025 14:43:16 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 19 Apr 2025 14:43:16 +0200
changeset 1304
57e062a4bb05
parent 1303
4022e403de60
child 1305
c34a72d8e104

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);

mercurial