--- a/src/ucx/string.c Fri Nov 07 11:06:58 2025 +0100 +++ b/src/ucx/string.c Sun Nov 09 23:51:03 2025 +0100 @@ -25,6 +25,10 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef MEMRCHR_NEED_GNU +#define _GNU_SOURCE +#endif + #include "cx/string.h" #include <string.h> @@ -33,6 +37,7 @@ #include <errno.h> #include <limits.h> #include <float.h> +#include <ctype.h> #ifdef _WIN32 #define cx_strcasecmp_impl _strnicmp @@ -231,19 +236,24 @@ } cxstring cx_strrchr( - cxstring string, - int chr + cxstring string, + int chr ) { +#ifdef WITH_MEMRCHR + char *ret = memrchr(string.ptr, 0xFF & chr, string.length); + if (ret == NULL) return (cxstring) {NULL, 0}; + return (cxstring) {ret, string.length - (ret - string.ptr)}; +#else chr = 0xFF & chr; size_t i = string.length; while (i > 0) { i--; - // TODO: improve by comparing multiple bytes at once if (string.ptr[i] == chr) { return cx_strsubs(string, i); } } return (cxstring) {NULL, 0}; +#endif } cxmutstr cx_strrchr_m( @@ -451,7 +461,7 @@ delim, limit, (cxstring **) output); } -int cx_strcmp( +int cx_strcmp_( cxstring s1, cxstring s2 ) { @@ -468,7 +478,7 @@ } } -int cx_strcasecmp( +int cx_strcasecmp_( cxstring s1, cxstring s2 ) { @@ -520,19 +530,13 @@ return result; } -static bool str_isspace(char c) { - // TODO: remove once UCX has public API for this - return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; -} - cxstring cx_strtrim(cxstring string) { cxstring result = string; - // TODO: optimize by comparing multiple bytes at once - while (result.length > 0 && str_isspace(*result.ptr)) { + while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) { result.ptr++; result.length--; } - while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) { + while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) { result.length--; } return result; @@ -543,7 +547,7 @@ return (cxmutstr) {(char *) result.ptr, result.length}; } -bool cx_strprefix( +bool cx_strprefix_( cxstring string, cxstring prefix ) { @@ -551,7 +555,7 @@ return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; } -bool cx_strsuffix( +bool cx_strsuffix_( cxstring string, cxstring suffix ) { @@ -560,7 +564,7 @@ suffix.ptr, suffix.length) == 0; } -bool cx_strcaseprefix( +bool cx_strcaseprefix_( cxstring string, cxstring prefix ) { @@ -572,7 +576,7 @@ #endif } -bool cx_strcasesuffix( +bool cx_strcasesuffix_( cxstring string, cxstring suffix ) { @@ -957,11 +961,6 @@ return 0; } -static bool str_isdigit(char c) { - // TODO: remove once UCX has public API for this - return c >= '0' && c <= '9'; -} - int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) { // TODO: overflow check // TODO: increase precision @@ -994,7 +993,7 @@ // parse all digits until we find the decsep size_t pos = 0; do { - if (str_isdigit(str.ptr[pos])) { + if (isdigit((unsigned char)str.ptr[pos])) { result = result * 10 + (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { break; @@ -1023,7 +1022,7 @@ // parse everything until exponent or end double factor = 1.; do { - if (str_isdigit(str.ptr[pos])) { + if (isdigit((unsigned char)str.ptr[pos])) { factor *= 0.1; result = result + factor * (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { @@ -1064,7 +1063,7 @@ // parse the exponent unsigned int exp = 0; do { - if (str_isdigit(str.ptr[pos])) { + if (isdigit((unsigned char)str.ptr[pos])) { exp = 10 * exp + (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { errno = EINVAL;