add cx_strcat_m() and cx_strcat_ma() for in-place concatenation

Sat, 22 Apr 2023 13:06:18 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 22 Apr 2023 13:06:18 +0200
changeset 697
ebdce4bf262b
parent 696
1ba4ec2e7a89
child 698
7345ee0a0301

add cx_strcat_m() and cx_strcat_ma() for in-place concatenation

src/cx/string.h file | annotate | diff | comparison | revisions
src/string.c file | annotate | diff | comparison | revisions
tests/test_string.cpp file | annotate | diff | comparison | revisions
--- 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.
--- 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(
--- 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) {

mercurial