full generic support for cx_strsubs() and cx_strsubsl()

Sun, 28 Dec 2025 15:45:39 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 28 Dec 2025 15:45:39 +0100
changeset 1674
8b0f162ac88e
parent 1673
0c338b80e7dd
child 1675
36c0fb2b60b2

full generic support for cx_strsubs() and cx_strsubsl()

relates to #792

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/src/cx/string.h	Sun Dec 28 14:47:36 2025 +0100
+++ b/src/cx/string.h	Sun Dec 28 15:45:39 2025 +0100
@@ -395,6 +395,19 @@
 #endif
 
 /**
+ * Casts away constness and converts a cxstring to a cxmutstr.
+ * For internal use only!
+ * @param str
+ * @return
+ */
+CX_INLINE cxmutstr cx_mutstrcast(cxstring str) {
+    cxmutstr s;
+    s.ptr = (char*)str.ptr;
+    s.length = str.length;
+    return s;
+}
+
+/**
  * Passes the pointer in this string to the cxDefaultAllocator's @c free() function.
  *
  * The pointer in the struct is set to @c NULL, and the length is set to zero,
@@ -530,22 +543,74 @@
         cx_strcat_a(cxDefaultAllocator, str, count, __VA_ARGS__)
 
 /**
- * Returns a substring starting at the specified location.
+ * Returns a substring.
+ *
+ * Internal function - do not use.
  *
- * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
- * Use cx_strdup() to get a copy.
+ * @param string input string
+ * @param start  start location of the substring
+ * @param length the maximum length of the returned string
+ * @return a substring of @p string starting at @p start
+ * @see cx_strsubsl()
+ */
+cx_attr_nodiscard
+CX_EXPORT cxstring cx_strsubsl_(cxstring string, size_t start, size_t length);
+
+/**
+ * Returns a substring.
+ *
+ * Internal function - do not use.
  *
  * @param string input string
  * @param start  start location of the substring
  * @return a substring of @p string starting at @p start
+ * @see cx_strsubs()
+ */
+cx_attr_nodiscard
+CX_EXPORT cxstring cx_strsubs_(cxstring string, size_t start);
+
+CX_INLINE cxmutstr cx_strsubs_m_(cxmutstr string, size_t start) {
+    return cx_mutstrcast(cx_strsubs_(cx_strcast(string), start));
+}
+
+CX_INLINE cxmutstr cx_strsubsl_m_(cxmutstr string, size_t start, size_t length) {
+    return cx_mutstrcast(cx_strsubsl_(cx_strcast(string), start, length));
+}
+
+#ifdef __cplusplus
+} // extern "C"
+CX_CPPDECL cxstring cx_strsubs_cpp_(cxstring string, size_t start) {
+    return cx_strsubs_(string, start);
+}
+CX_CPPDECL cxstring cx_strsubsl_cpp_(cxstring string, size_t start, size_t length) {
+    return cx_strsubsl_(string, start, length);
+}
+CX_CPPDECL cxmutstr cx_strsubs_cpp_(cxmutstr string, size_t start) {
+    return cx_strsubs_m_(string, start);
+}
+CX_CPPDECL cxmutstr cx_strsubsl_cpp_(cxmutstr string, size_t start, size_t length) {
+    return cx_strsubsl_m_(string, start, length);
+}
+#define cx_strsubs(string, start) cx_strsubs_cpp_(cx_strcast_m(string), start)
+#define cx_strsubsl(string, start, length) cx_strsubsl_cpp_(cx_strcast_m(string), start, length)
+extern "C" {
+#else
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * @attention the new string references the same memory area as the
+ * input string and is @em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start (@c size_t) start location of the substring
+ * @return (@c cxstring or @c cxmutstr) a substring of @p string starting at @p start
  *
  * @see cx_strsubsl()
- * @see cx_strsubs_m()
- * @see cx_strsubsl_m()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubs(cxstring string, size_t start);
+#define cx_strsubs(string, start) _Generic(cx_strcast_m(string), \
+        cxstring: cx_strsubs_, \
+        cxmutstr: cx_strsubs_m_)(cx_strcast_m(string), start)
 
 /**
  * Returns a substring starting at the specified location.
@@ -563,51 +628,11 @@
  * @return a substring of @p string starting at @p start
  *
  * @see cx_strsubs()
- * @see cx_strsubs_m()
- * @see cx_strsubsl_m()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubsl(cxstring string, size_t start, size_t length);
-
-/**
- * Returns a substring starting at the specified location.
- *
- * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
- * Use cx_strdup() to get a copy.
- *
- * @param string input string
- * @param start  start location of the substring
- * @return a substring of @p string starting at @p start
- *
- * @see cx_strsubsl_m()
- * @see cx_strsubs()
- * @see cx_strsubsl()
- */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strsubs_m(cxmutstr string, size_t start);
-
-/**
- * Returns a substring starting at the specified location.
- *
- * The returned string will be limited to @p length bytes or the number
- * of bytes available in @p string, whichever is smaller.
- *
- * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
- * Use cx_strdup() to get a copy.
- *
- * @param string input string
- * @param start  start location of the substring
- * @param length the maximum length of the returned string
- * @return a substring of @p string starting at @p start
- *
- * @see cx_strsubs_m()
- * @see cx_strsubs()
- * @see cx_strsubsl()
- */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length);
+#define cx_strsubsl(string, start, length) _Generic(cx_strcast_m(string), \
+        cxstring: cx_strsubsl_, \
+        cxmutstr: cx_strsubsl_m_)(cx_strcast_m(string), start, length)
+#endif
 
 /**
  * Returns the character at the specified index offset.
--- a/src/string.c	Sun Dec 28 14:47:36 2025 +0100
+++ b/src/string.c	Sun Dec 28 15:45:39 2025 +0100
@@ -154,21 +154,14 @@
     return str;
 }
 
-cxstring cx_strsubs(
+cxstring cx_strsubs_(
         cxstring string,
         size_t start
 ) {
-    return cx_strsubsl(string, start, string.length - start);
+    return cx_strsubsl_(string, start, string.length);
 }
 
-cxmutstr cx_strsubs_m(
-        cxmutstr string,
-        size_t start
-) {
-    return cx_strsubsl_m(string, start, string.length - start);
-}
-
-cxstring cx_strsubsl(
+cxstring cx_strsubsl_(
         cxstring string,
         size_t start,
         size_t length
@@ -185,15 +178,6 @@
     return (cxstring) {string.ptr + start, length};
 }
 
-cxmutstr cx_strsubsl_m(
-        cxmutstr string,
-        size_t start,
-        size_t length
-) {
-    cxstring result = cx_strsubsl(cx_strcast(string), start, length);
-    return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
 cxstring cx_strchr(
         cxstring string,
         int chr
--- a/tests/test_string.c	Sun Dec 28 14:47:36 2025 +0100
+++ b/tests/test_string.c	Sun Dec 28 15:45:39 2025 +0100
@@ -226,34 +226,115 @@
     }
 }
 
-CX_TEST(test_strsubs) {
+CX_TEST(test_strsubs_cxs) {
     cxstring str = cx_str("A test string");
+    cxstring sub;
 
     CX_TEST_DO {
-        cxstring sub = cx_strsubs(str, 0);
+        sub = cx_strsubs(str, 0);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, str));
+
+        sub = cx_strsubs(str, 2);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test string"));
+
+        sub = cx_strsubs(str, 7);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
+
+        sub = cx_strsubs(str, 15);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, ""));
+
+        sub = cx_strsubsl(str, 2, 4);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test"));
+
+        sub = cx_strsubsl(str, 7, 3);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "str"));
+
+        sub = cx_strsubsl(str, 7, 20);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
+    }
+}
+
+CX_TEST(test_strsubs_cc) {
+    const char *str = "A test string";
+    cxstring sub;
+
+    CX_TEST_DO {
+        sub = cx_strsubs(str, 0);
         CX_TEST_ASSERT(0 == cx_strcmp(sub, str));
 
         sub = cx_strsubs(str, 2);
-        CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("test string")));
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test string"));
 
         sub = cx_strsubs(str, 7);
-        CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("string")));
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
 
         sub = cx_strsubs(str, 15);
-        CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("")));
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, ""));
 
         sub = cx_strsubsl(str, 2, 4);
-        CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("test")));
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test"));
 
         sub = cx_strsubsl(str, 7, 3);
-        CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("str")));
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "str"));
 
         sub = cx_strsubsl(str, 7, 20);
-        CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("string")));
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
+    }
+}
+
+CX_TEST(test_strsubs_cxms) {
+    cxmutstr str = cx_mutstr((char*)"A test string");
+    cxmutstr sub;
+
+    CX_TEST_DO {
+        sub = cx_strsubs(str, 0);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, str));
+
+        sub = cx_strsubs(str, 2);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test string"));
+
+        sub = cx_strsubs(str, 7);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
+
+        sub = cx_strsubs(str, 15);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, ""));
+
+        sub = cx_strsubsl(str, 2, 4);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test"));
+
+        sub = cx_strsubsl(str, 7, 3);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "str"));
 
-        // just for coverage, call the _m variant
-        cxmutstr m = cx_strsubs_m(cx_mutstrn(NULL, 0), 0);
-        CX_TEST_ASSERT(0 == cx_strcmp(m, ""));
+        sub = cx_strsubsl(str, 7, 20);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
+    }
+}
+
+CX_TEST(test_strsubs_c) {
+    char *str = "A test string";
+    cxmutstr sub;
+
+    CX_TEST_DO {
+        sub = cx_strsubs(str, 0);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, str));
+
+        sub = cx_strsubs(str, 2);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test string"));
+
+        sub = cx_strsubs(str, 7);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
+
+        sub = cx_strsubs(str, 15);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, ""));
+
+        sub = cx_strsubsl(str, 2, 4);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "test"));
+
+        sub = cx_strsubsl(str, 7, 3);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "str"));
+
+        sub = cx_strsubsl(str, 7, 20);
+        CX_TEST_ASSERT(0 == cx_strcmp(sub, "string"));
     }
 }
 
@@ -1566,7 +1647,10 @@
     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_strsubs_cxs);
+    cx_test_register(suite, test_strsubs_cxms);
+    cx_test_register(suite, test_strsubs_cc);
+    cx_test_register(suite, test_strsubs_c);
     cx_test_register(suite, test_strat);
     cx_test_register(suite, test_strchr);
     cx_test_register(suite, test_strrchr);

mercurial