# HG changeset patch # User Mike Becker # Date 1766929656 -3600 # Node ID 0c338b80e7ddda63c4403905257547c2b6aa19aa # Parent 94360453bce42d9a07ffae58a6ccb96f7b20a302 add cx_strcast_m() relates to #792 diff -r 94360453bce4 -r 0c338b80e7dd CHANGELOG --- a/CHANGELOG Sun Dec 28 14:10:14 2025 +0100 +++ b/CHANGELOG Sun Dec 28 14:47:36 2025 +0100 @@ -12,6 +12,7 @@ * adds cx_hash_key_as_string() * adds support for CxHashKey pointers in CX_HASH_KEY() and all map functions * adds CX_NULLSTR convenience macro + * adds cx_strcast_m() * adds cx_strat() * adds cx_bstr() and cx_bstr_m() * adds cxBufferMaximumCapacity() diff -r 94360453bce4 -r 0c338b80e7dd docs/Writerside/topics/about.md --- a/docs/Writerside/topics/about.md Sun Dec 28 14:10:14 2025 +0100 +++ b/docs/Writerside/topics/about.md Sun Dec 28 14:47:36 2025 +0100 @@ -39,6 +39,7 @@ * adds cx_hash_key_as_string() * adds support for CxHashKey pointers in CX_HASH_KEY() and all map functions * adds CX_NULLSTR convenience macro +* adds cx_strcast_m() * adds cx_strat() * adds cx_bstr() and cx_bstr_m() * adds cxBufferMaximumCapacity() diff -r 94360453bce4 -r 0c338b80e7dd docs/Writerside/topics/string.h.md --- a/docs/Writerside/topics/string.h.md Sun Dec 28 14:10:14 2025 +0100 +++ b/docs/Writerside/topics/string.h.md Sun Dec 28 14:47:36 2025 +0100 @@ -14,6 +14,8 @@ > To simplify documentation, we introduce the pseudo-type `AnyStr` with the meaning that > any UCX string and any C string are supported. > The implementation is actually hidden behind a macro which uses `cx_strcast()` to guarantee compatibility. +> Similarly we introduce `UcxStr` with the meaning that it is either a `cxstring` or a `cxmutstr`, +> created by `cx_strcast_m()`. {style="note"} ```C @@ -49,10 +51,11 @@ void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str); -#define CX_NULLSTR cx_mutstr(NULL) -#define CX_SFMT(s) (int) (s).length, (s).ptr -#define CX_PRIstr ".*s" -#define cx_strcast(s) // converts any string to cxstring +#define CX_NULLSTR cx_mutstr(NULL) +#define CX_SFMT(s) (int) (s).length, (s).ptr +#define CX_PRIstr ".*s" +#define cx_strcast(s) // converts any string to cxstring +#define cx_strcast_m(s) // converts any string to a UcxStr ``` The functions `cx_str()` and `cx_mutstr()` create a UCX string from a `const char*` or a `char*` @@ -75,6 +78,10 @@ When you want to use a UCX string in a `printf`-like function, you can use the macro `CX_PRIstr` for the format specifier, and the `CX_SFMT(s)` macro to expand the arguments. +The macros `cx_strcast()` and `cx_strcast_m()` take any supported string (`AnyStr`) and convert it either to a `cxstring` or a `cxmutstr`. +While `cx_strcast()` _always_ converts to a `cxstring`, `cx_strcast_m()` decides depending on the constness of the string to return either a `cxstring` or a `cxmutstr`. +You should rarely need those macros in your code, as most UCX functions are already implemented behind macros that do the conversions for you. + > When you want to convert a string _literal_ into a UCX string, you can also use the `cx_str()` function. > With optimizations turned on, this function gets inlined and optimizes the call to `strlen()` out, giving > you a `cxstring` structure at zero cost with the length calculated at compile-time. @@ -154,31 +161,19 @@ ```C #include -char cx_strat(cxstring str, off_t index); - -cxstring cx_strchr(cxstring string, int chr); - -cxstring cx_strrchr(cxstring string, int chr); +char cx_strat(AnyStr str, off_t index); -cxstring cx_strstr(cxstring string, AnyStr search); +UcxStr cx_strchr(AnyStr string, int chr); -cxstring cx_strsubs(cxstring string, size_t start); - -cxstring cx_strsubsl(cxstring string, size_t start, size_t length); +UcxStr cx_strrchr(AnyStr string, int chr); -cxstring cx_strtrim(cxstring string); +UcxStr cx_strstr(AnyStr string, AnyStr search); -cxmutstr cx_strchr_m(cxmutstr string, int chr); - -cxmutstr cx_strrchr_m(cxmutstr string, int chr); +UcxStr cx_strsubs(AnyStr string, size_t start); -cxmutstr cx_strstr_m(cxmutstr string, AnyStr search); - -cxmutstr cx_strsubs_m(cxmutstr string, size_t start); +UcxStr cx_strsubsl(AnyStr string, size_t start, size_t length); -cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length); - -cxmutstr cx_strtrim_m(cxmutstr string); +UcxStr cx_strtrim(AnyStr string); ``` The function `cx_strat()` returns the character at the specified `index`. diff -r 94360453bce4 -r 0c338b80e7dd src/cx/string.h --- a/src/cx/string.h Sun Dec 28 14:10:14 2025 +0100 +++ b/src/cx/string.h Sun Dec 28 14:47:36 2025 +0100 @@ -243,41 +243,60 @@ #ifdef __cplusplus cx_attr_nodiscard -CX_CPPDECL cxstring cx_strcast(cxmutstr str) { - return cx_strn(str.ptr, str.length); +CX_CPPDECL cxmutstr cx_strcast_m(cxmutstr str) { + return str; } cx_attr_nodiscard -CX_CPPDECL cxstring cx_strcast(cxstring str) { +CX_CPPDECL cxstring cx_strcast_m(cxstring str) { return str; } cx_attr_nodiscard -CX_CPPDECL cxstring cx_strcast(const char *str) { +CX_CPPDECL cxmutstr cx_strcast_m(char *str) { + return cx_mutstr(str); +} +cx_attr_nodiscard +CX_CPPDECL cxmutstr cx_strcast_m(unsigned char *str) { + return cx_mutstr(reinterpret_cast(str)); +} +cx_attr_nodiscard +CX_CPPDECL cxstring cx_strcast_m(const char *str) { return cx_str(str); } cx_attr_nodiscard -CX_CPPDECL cxstring cx_strcast(const unsigned char *str) { +CX_CPPDECL cxstring cx_strcast_m(const unsigned char *str) { return cx_str(reinterpret_cast(str)); } +cx_attr_nodiscard +CX_CPPDECL cxstring cx_strcast_(cxmutstr str) { + return cx_strn(str.ptr, str.length); +} +cx_attr_nodiscard +CX_CPPDECL cxstring cx_strcast_(cxstring str) { + return str; +} +#define cx_strcast(s) cx_strcast_(cx_strcast_m(s)) extern "C" { #else /** * Internal function, do not use. * @param str * @return + * @see cx_strcast_m() * @see cx_strcast() */ cx_attr_nodiscard -CX_INLINE cxstring cx_strcast_m(cxmutstr str) { - return (cxstring) {str.ptr, str.length}; +CX_INLINE cxmutstr cx_strcast_cxms(cxmutstr str) { + return str; } /** * Internal function, do not use. * @param str * @return + * @see cx_strcast_m() * @see cx_strcast() */ cx_attr_nodiscard -CX_INLINE cxstring cx_strcast_c(cxstring str) { +CX_INLINE cxstring cx_strcast_cxs(cxstring str) { return str; } @@ -285,10 +304,35 @@ * Internal function, do not use. * @param str * @return + * @see cx_strcast_m() * @see cx_strcast() */ cx_attr_nodiscard -CX_INLINE cxstring cx_strcast_u(const unsigned char *str) { +CX_INLINE cxmutstr cx_strcast_uc(unsigned char *str) { + return cx_mutstr((char*)str); +} + +/** + * Internal function, do not use. + * @param str + * @return + * @see cx_strcast_m() + * @see cx_strcast() + */ +cx_attr_nodiscard +CX_INLINE cxmutstr cx_strcast_c(char *str) { + return cx_mutstr(str); +} + +/** + * Internal function, do not use. + * @param str + * @return + * @see cx_strcast_m() + * @see cx_strcast() + */ +cx_attr_nodiscard +CX_INLINE cxstring cx_strcast_ucc(const unsigned char *str) { return cx_str((const char*)str); } @@ -296,10 +340,11 @@ * Internal function, do not use. * @param str * @return + * @see cx_strcast_m() * @see cx_strcast() */ cx_attr_nodiscard -CX_INLINE cxstring cx_strcast_z(const char *str) { +CX_INLINE cxstring cx_strcast_cc(const char *str) { return cx_str(str); } @@ -307,15 +352,46 @@ * Wraps any string into an UCX string. * * @param str (any supported string type) the string to cast - * @return (@c cxstring) the string wrapped as UCX string + * @return (@c cxstring) or (@c cxmutstr) the string wrapped as UCX string + */ +#define cx_strcast_m(str) _Generic((str), \ + cxmutstr: cx_strcast_cxms, \ + cxstring: cx_strcast_cxs, \ + const unsigned char*: cx_strcast_ucc, \ + unsigned char *: cx_strcast_uc, \ + const char*: cx_strcast_cc, \ + char *: cx_strcast_c) (str) + +/** + * Internal function, do not use. + * @param str + * @return */ -#define cx_strcast(str) _Generic((str), \ - cxmutstr: cx_strcast_m, \ - cxstring: cx_strcast_c, \ - const unsigned char*: cx_strcast_u, \ - unsigned char *: cx_strcast_u, \ - const char*: cx_strcast_z, \ - char *: cx_strcast_z) (str) +CX_INLINE cxstring cx_strcast_1(cxmutstr str) { + return (cxstring){str.ptr, str.length}; +} + +/** + * Internal function, do not use. + * @param str + * @return + */ +CX_INLINE cxstring cx_strcast_2(cxstring str) { + return str; +} + +/** internal conversion macro */ +#define cx_strcast_(str) _Generic((str), \ + cxmutstr: cx_strcast_1, \ + cxstring: cx_strcast_2)(str) + +/** + * Converts any string to a cxstring. + * + * @param str (any supported string type) the string to cast + * @return he string converted to a (@c cxstring) + */ +#define cx_strcast(str) cx_strcast_(cx_strcast_m(str)) #endif /** diff -r 94360453bce4 -r 0c338b80e7dd tests/test_string.c --- a/tests/test_string.c Sun Dec 28 14:10:14 2025 +0100 +++ b/tests/test_string.c Sun Dec 28 14:47:36 2025 +0100 @@ -63,15 +63,19 @@ } } -CX_TEST(test_string_cast) { +CX_TEST(test_strcast) { char *c1 = (char*) "123"; const char *c2 = "abcde"; unsigned char *c3 = (unsigned char*) "4711"; unsigned const char *c4 = (unsigned const char*) "xyz0815"; + cxstring c5 = cx_str("foobar"); + cxmutstr c6 = cx_mutstr((char*)"hello world"); cxstring s1 = cx_strcast(c1); cxstring s2 = cx_strcast(c2); cxstring s3 = cx_strcast(c3); cxstring s4 = cx_strcast(c4); + cxstring s5 = cx_strcast(c5); + cxstring s6 = cx_strcast(c6); CX_TEST_DO { CX_TEST_ASSERT(s1.length == 3); CX_TEST_ASSERT(strncmp(s1.ptr, "123", 3) == 0); @@ -81,6 +85,39 @@ CX_TEST_ASSERT(strncmp(s3.ptr, "4711", 4) == 0); CX_TEST_ASSERT(s4.length == 7); CX_TEST_ASSERT(strncmp(s4.ptr, "xyz0815", 7) == 0); + CX_TEST_ASSERT(s5.length == 6); + CX_TEST_ASSERT(strncmp(s5.ptr, "foobar", 6) == 0); + CX_TEST_ASSERT(s6.length == 11); + CX_TEST_ASSERT(strncmp(s6.ptr, "hello world", 11) == 0); + } +} + +CX_TEST(test_strcast_m) { + char *c1 = (char*) "123"; + const char *c2 = "abcde"; + unsigned char *c3 = (unsigned char*) "4711"; + unsigned const char *c4 = (unsigned const char*) "xyz0815"; + cxstring c5 = cx_str("foobar"); + cxmutstr c6 = cx_mutstr((char*)"hello world"); + cxmutstr s1 = cx_strcast_m(c1); + cxstring s2 = cx_strcast_m(c2); + cxmutstr s3 = cx_strcast_m(c3); + cxstring s4 = cx_strcast_m(c4); + cxstring s5 = cx_strcast_m(c5); + cxmutstr s6 = cx_strcast_m(c6); + CX_TEST_DO { + CX_TEST_ASSERT(s1.length == 3); + CX_TEST_ASSERT(strncmp(s1.ptr, "123", 3) == 0); + CX_TEST_ASSERT(s2.length == 5); + CX_TEST_ASSERT(strncmp(s2.ptr, "abcde", 5) == 0); + CX_TEST_ASSERT(s3.length == 4); + CX_TEST_ASSERT(strncmp(s3.ptr, "4711", 4) == 0); + CX_TEST_ASSERT(s4.length == 7); + CX_TEST_ASSERT(strncmp(s4.ptr, "xyz0815", 7) == 0); + CX_TEST_ASSERT(s5.length == 6); + CX_TEST_ASSERT(strncmp(s5.ptr, "foobar", 6) == 0); + CX_TEST_ASSERT(s6.length == 11); + CX_TEST_ASSERT(strncmp(s6.ptr, "hello world", 11) == 0); } } @@ -1513,7 +1550,7 @@ cxstring str = cx_str("Hello, World!"); CX_TEST_DO { char actual[64]; - snprintf(actual, 64, "Test %"CX_PRIstr " Success.", CX_SFMT(str)); + snprintf(actual, 64, "Test %" CX_PRIstr " Success.", CX_SFMT(str)); CX_TEST_ASSERT(0 == strncmp("Test Hello, World! Success.", actual, 64)); } } @@ -1522,7 +1559,8 @@ CxTestSuite *suite = cx_test_suite_new("string"); cx_test_register(suite, test_string_construct); - cx_test_register(suite, test_string_cast); + cx_test_register(suite, test_strcast); + cx_test_register(suite, test_strcast_m); cx_test_register(suite, test_strfree); cx_test_register(suite, test_strdup); cx_test_register(suite, test_strdup_shortened);