# HG changeset patch # User Mike Becker # Date 1526313363 -7200 # Node ID 90b6d69bb4997c2fd1f376655c1896dd8327871f # Parent 353d71349e619eaea0b2688f597b054c89ecd254# Parent d1f814633049b9362957ce66ffe961ef74e56d3b merges constsstr branch diff -r 353d71349e61 -r 90b6d69bb499 configure.ac --- a/configure.ac Mon May 14 15:58:51 2018 +0200 +++ b/configure.ac Mon May 14 17:56:03 2018 +0200 @@ -27,8 +27,9 @@ # # the package version must match the macros in ucx.h -AC_INIT([ucx], [1.1.0], [olaf.wintermann@gmail.com]) -AC_SUBST([UCX_LIB_VERSION], [2:0:1]) +# the lib version must follow the libtool versioning convention +AC_INIT([ucx], [2.0.0], [olaf.wintermann@gmail.com]) +AC_SUBST([UCX_LIB_VERSION], [3:0:0]) # don't place everything in the project root AC_CONFIG_AUX_DIR([build-aux]) diff -r 353d71349e61 -r 90b6d69bb499 src/string.c --- a/src/string.c Mon May 14 15:58:51 2018 +0200 +++ b/src/string.c Mon May 14 17:56:03 2018 +0200 @@ -50,13 +50,29 @@ return string; } -size_t sstrnlen(size_t n, sstr_t s, ...) { +scstr_t scstr(const char *cstring) { + scstr_t string; + string.ptr = cstring; + string.length = strlen(cstring); + return string; +} + +scstr_t scstrn(const char *cstring, size_t length) { + scstr_t string; + string.ptr = cstring; + string.length = length; + return string; +} + + +size_t ucx_strnlen(size_t n, ...) { va_list ap; - size_t size = s.length; - va_start(ap, s); + va_start(ap, n); + + size_t size = 0; - for (size_t i = 1 ; i < n ; i++) { - sstr_t str = va_arg(ap, sstr_t); + for (size_t i = 0 ; i < n ; i++) { + scstr_t str = va_arg(ap, scstr_t); if(((size_t)-1) - str.length < size) { size = 0; break; @@ -71,8 +87,7 @@ static sstr_t sstrvcat_a( UcxAllocator *a, size_t count, - sstr_t s1, - sstr_t s2, + scstr_t s1, va_list ap) { sstr_t str; str.ptr = NULL; @@ -81,11 +96,13 @@ return str; } + scstr_t s2 = va_arg (ap, scstr_t); + if(((size_t)-1) - s1.length < s2.length) { return str; } - sstr_t *strings = (sstr_t*) calloc(count, sizeof(sstr_t)); + scstr_t *strings = (scstr_t*) calloc(count, sizeof(scstr_t)); if(!strings) { return str; } @@ -96,7 +113,7 @@ size_t slen = s1.length + s2.length; int error = 0; for (size_t i=2;i str_length) { + return 0; + } + + if(length > str_length - start) { + length = str_length - start; + } + *newlen = length; + *newpos = start; + return 1; } sstr_t sstrsubs(sstr_t s, size_t start) { @@ -154,44 +193,80 @@ } sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) { - sstr_t new_sstr; - if (start >= s.length) { - new_sstr.ptr = NULL; - new_sstr.length = 0; - } else { - if (length > s.length-start) { - length = s.length-start; + size_t pos; + sstr_t ret = { NULL, 0 }; + if(ucx_substring(s.length, start, length, &ret.length, &pos)) { + ret.ptr = s.ptr + pos; + } + return ret; +} + +scstr_t scstrsubs(scstr_t s, size_t start) { + return scstrsubsl (s, start, s.length-start); +} + +scstr_t scstrsubsl(scstr_t s, size_t start, size_t length) { + size_t pos; + scstr_t ret = { NULL, 0 }; + if(ucx_substring(s.length, start, length, &ret.length, &pos)) { + ret.ptr = s.ptr + pos; + } + return ret; +} + + +int ucx_strchr(const char *string, size_t length, int chr, size_t *pos) { + for(size_t i=0;i 0) { + for(size_t i=length ; i>0 ; i--) { + if(string[i-1] == chr) { + *pos = i-1; + return 1; + } + } + } + return 0; } sstr_t sstrchr(sstr_t s, int c) { - for(size_t i=0;i 0) { - for(size_t i=s.length;i>0;i--) { - if(s.ptr[i-1] == c) { - return sstrsubs(s, i-1); - } - } + size_t pos = 0; + if(ucx_strrchr(s.ptr, s.length, c, &pos)) { + return sstrsubs(s, pos); } - sstr_t n; - n.ptr = NULL; - n.length = 0; - return n; + return sstrn(NULL, 0); +} + +scstr_t scstrchr(scstr_t s, int c) { + size_t pos = 0; + if(ucx_strchr(s.ptr, s.length, c, &pos)) { + return scstrsubs(s, pos); + } + return scstrn(NULL, 0); +} + +scstr_t scstrrchr(scstr_t s, int c) { + size_t pos = 0; + if(ucx_strrchr(s.ptr, s.length, c, &pos)) { + return scstrsubs(s, pos); + } + return scstrn(NULL, 0); } #define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \ @@ -202,13 +277,21 @@ else ((size_t*)ptable)[index] = src;\ } while (0); -sstr_t sstrstr(sstr_t string, sstr_t match) { - if (match.length == 0) { - return string; + +const char* ucx_strstr( + const char *str, + size_t length, + const char *match, + size_t matchlen, + size_t *newlen) +{ + *newlen = length; + if (matchlen == 0) { + return str; } - /* prepare default return value in case of no match */ - sstr_t result = sstrn(NULL, 0); + const char *result = NULL; + size_t resultlen = 0; /* * IMPORTANT: @@ -223,9 +306,9 @@ /* check pattern length and use appropriate prefix table */ /* if the pattern exceeds static prefix table, allocate on the heap */ - register int useheap = match.length > 255; + register int useheap = matchlen > 255; register void* ptable = useheap ? - calloc(match.length+1, sizeof(size_t)): s_prefix_table; + calloc(matchlen+1, sizeof(size_t)): s_prefix_table; /* keep counter in registers */ register size_t i, j; @@ -233,8 +316,8 @@ /* fill prefix table */ i = 0; j = 0; ptable_w(useheap, ptable, i, j); - while (i < match.length) { - while (j >= 1 && match.ptr[j-1] != match.ptr[i]) { + while (i < matchlen) { + while (j >= 1 && match[j-1] != match[i]) { ptable_r(j, useheap, ptable, j-1); } i++; j++; @@ -243,15 +326,15 @@ /* search */ i = 0; j = 1; - while (i < string.length) { - while (j >= 1 && string.ptr[i] != match.ptr[j-1]) { + while (i < length) { + while (j >= 1 && str[i] != match[j-1]) { ptable_r(j, useheap, ptable, j-1); } i++; j++; - if (j-1 == match.length) { - size_t start = i - match.length; - result.ptr = string.ptr + start; - result.length = string.length - start; + if (j-1 == matchlen) { + size_t start = i - matchlen; + result = str + start; + resultlen = length - start; break; } } @@ -261,17 +344,54 @@ free(ptable); } + *newlen = resultlen; + return result; +} + +sstr_t ucx_sstrstr(sstr_t string, scstr_t match) { + sstr_t result; + + size_t reslen; + const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen); + if(!resstr) { + result.ptr = NULL; + result.length = 0; + return result; + } + + size_t pos = resstr - string.ptr; + result.ptr = string.ptr + pos; + result.length = reslen; + + return result; +} + +scstr_t ucx_scstrstr(scstr_t string, scstr_t match) { + scstr_t result; + + size_t reslen; + const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen); + if(!resstr) { + result.ptr = NULL; + result.length = 0; + return result; + } + + size_t pos = resstr - string.ptr; + result.ptr = string.ptr + pos; + result.length = reslen; + return result; } #undef ptable_r #undef ptable_w -sstr_t* sstrsplit(sstr_t s, sstr_t d, ssize_t *n) { - return sstrsplit_a(ucx_default_allocator(), s, d, n); +sstr_t* ucx_strsplit(scstr_t s, scstr_t d, ssize_t *n) { + return ucx_strsplit_a(ucx_default_allocator(), s, d, n); } -sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t s, sstr_t d, ssize_t *n) { +sstr_t* ucx_strsplit_a(UcxAllocator *allocator, scstr_t s, scstr_t d, ssize_t *n) { if (s.length == 0 || d.length == 0) { *n = -1; return NULL; @@ -300,10 +420,10 @@ sstr_t* result = (sstr_t*) alcalloc(allocator, arrlen, sizeof(sstr_t)); if (result) { - sstr_t curpos = s; + scstr_t curpos = s; ssize_t j = 1; while (1) { - sstr_t match; + scstr_t match; /* optimize for one byte delimiters */ if (d.length == 1) { match = curpos; @@ -315,13 +435,13 @@ match.length--; } } else { - match = sstrstr(curpos, d); + match = scstrstr(curpos, d); } if (match.length > 0) { /* is this our last try? */ if (nmax == 0 || j < nmax) { /* copy the current string to the array */ - sstr_t item = sstrn(curpos.ptr, match.ptr - curpos.ptr); + scstr_t item = scstrn(curpos.ptr, match.ptr - curpos.ptr); result[j-1] = sstrdup_a(allocator, item); size_t processed = item.length + d.length; curpos.ptr += processed; @@ -367,7 +487,7 @@ return result; } -int sstrcmp(sstr_t s1, sstr_t s2) { +int ucx_str_cmp(scstr_t s1, scstr_t s2) { if (s1.length == s2.length) { return memcmp(s1.ptr, s2.ptr, s1.length); } else if (s1.length > s2.length) { @@ -377,7 +497,7 @@ } } -int sstrcasecmp(sstr_t s1, sstr_t s2) { +int ucx_str_casecmp(scstr_t s1, scstr_t s2) { if (s1.length == s2.length) { #ifdef _WIN32 return _strnicmp(s1.ptr, s2.ptr, s1.length); @@ -391,11 +511,11 @@ } } -sstr_t sstrdup(sstr_t s) { +sstr_t scstrdup(scstr_t s) { return sstrdup_a(ucx_default_allocator(), s); } -sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t s) { +sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t s) { sstr_t newstring; newstring.ptr = (char*)almalloc(allocator, s.length + 1); if (newstring.ptr) { @@ -410,21 +530,38 @@ return newstring; } -sstr_t sstrtrim(sstr_t string) { - sstr_t newstr = string; + +size_t ucx_strtrim(const char *s, size_t len, size_t *newlen) { + const char *newptr = s; + size_t length = len; - while (newstr.length > 0 && isspace(*newstr.ptr)) { - newstr.ptr++; - newstr.length--; + while(length > 0 && isspace(*newptr)) { + newptr++; + length--; } - while (newstr.length > 0 && isspace(newstr.ptr[newstr.length-1])) { - newstr.length--; + while(length > 0 && isspace(newptr[length-1])) { + length--; } + *newlen = length; + return newptr - s; +} + +sstr_t sstrtrim(sstr_t string) { + sstr_t newstr; + newstr.ptr = string.ptr + + ucx_strtrim(string.ptr, string.length, &newstr.length); return newstr; } -int sstrprefix(sstr_t string, sstr_t prefix) { +scstr_t scstrtrim(scstr_t string) { + scstr_t newstr; + newstr.ptr = string.ptr + + ucx_strtrim(string.ptr, string.length, &newstr.length); + return newstr; +} + +int ucx_strprefix(scstr_t string, scstr_t prefix) { if (string.length == 0) { return prefix.length == 0; } @@ -439,7 +576,7 @@ } } -int sstrsuffix(sstr_t string, sstr_t suffix) { +int ucx_strsuffix(scstr_t string, scstr_t suffix) { if (string.length == 0) { return suffix.length == 0; } @@ -455,7 +592,7 @@ } } -sstr_t sstrlower(sstr_t string) { +sstr_t ucx_strlower(scstr_t string) { sstr_t ret = sstrdup(string); for (size_t i = 0; i < ret.length ; i++) { ret.ptr[i] = tolower(ret.ptr[i]); @@ -463,7 +600,7 @@ return ret; } -sstr_t sstrlower_a(UcxAllocator *allocator, sstr_t string) { +sstr_t ucx_strlower_a(UcxAllocator *allocator, scstr_t string) { sstr_t ret = sstrdup_a(allocator, string); for (size_t i = 0; i < ret.length ; i++) { ret.ptr[i] = tolower(ret.ptr[i]); @@ -471,7 +608,7 @@ return ret; } -sstr_t sstrupper(sstr_t string) { +sstr_t ucx_strupper(scstr_t string) { sstr_t ret = sstrdup(string); for (size_t i = 0; i < ret.length ; i++) { ret.ptr[i] = toupper(ret.ptr[i]); @@ -479,10 +616,24 @@ return ret; } -sstr_t sstrupper_a(UcxAllocator *allocator, sstr_t string) { +sstr_t ucx_strupper_a(UcxAllocator *allocator, scstr_t string) { sstr_t ret = sstrdup_a(allocator, string); for (size_t i = 0; i < ret.length ; i++) { ret.ptr[i] = toupper(ret.ptr[i]); } return ret; } + +// private string conversion functions +scstr_t ucx_sc2sc(scstr_t c) { + return c; +} +scstr_t ucx_ss2sc(sstr_t str) { + scstr_t cs; + cs.ptr = str.ptr; + cs.length = str.length; + return cs; +} +scstr_t ucx_ss2c_s(scstr_t c) { + return c; +} diff -r 353d71349e61 -r 90b6d69bb499 src/ucx/string.h --- a/src/ucx/string.h Mon May 14 15:58:51 2018 +0200 +++ b/src/ucx/string.h Mon May 14 17:56:03 2018 +0200 @@ -67,7 +67,6 @@ #ifdef __cplusplus extern "C" { #endif - /** * The UCX string structure. */ @@ -79,6 +78,56 @@ size_t length; } sstr_t; +typedef struct { + const char *ptr; + size_t length; +} scstr_t; + +#ifdef __cplusplus +} +#endif + + +#ifdef __cplusplus +inline scstr_t s2scstr(sstr_t s) { + scstr_t c; + c.ptr = s.ptr; + c.length = s.ptr; + return c; +} +inline scstr_t s2scstr(scstr_t c) { + return c; +} +#define SCSTR s2scstr +#else + +scstr_t ucx_sc2sc(scstr_t c); +scstr_t ucx_ss2sc(sstr_t str); +#if __STDC_VERSION__ >= 201112L +#define SCSTR(str) _Generic(str, sstr_t: ucx_ss2sc, scstr_t: ucx_sc2sc)(str) +#elif defined(__GNUC__) || defined(__clang__) +#define SCSTR(str) __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(str), sstr_t), \ + ucx_ss2sc, \ + ucx_sc2sc)(str) +#elif defined(__sun) +#define SCSTR(str) ({typeof(str) ucx_tmp_var_str = str; \ + scstr_t ucx_tmp_var_c; \ + ucx_tmp_var_c.ptr = ucx_tmp_var_str.ptr;\ + ucx_tmp_var_c.length = ucx_tmp_var_str.length;\ + ucx_tmp_var_c; }) +#else +scstr_t ucx_ss2c_s(); +#define SCSTR ucx_ss2c_s +#endif /* C11 feature test */ + +#endif /* C++ */ + +#ifdef __cplusplus +extern "C" { +#endif + + /** * Creates a new sstr_t based on a C string. * @@ -110,6 +159,9 @@ sstr_t sstrn(char *cstring, size_t length); +scstr_t scstr(const char *cstring); +scstr_t scstrn(const char *cstring, size_t length); + /** * Returns the cumulated length of all specified strings. * @@ -123,7 +175,9 @@ * @param ... all other strings * @return the cumulated length of all strings */ -size_t sstrnlen(size_t count, sstr_t string, ...); +size_t ucx_strnlen(size_t count, ...); + +#define sstrnlen(count, ...) ucx_strnlen(count, __VA_ARGS__) /** * Concatenates two or more strings. @@ -136,11 +190,12 @@ * * @param count the total number of strings to concatenate * @param s1 first string - * @param s2 second string * @param ... all remaining strings * @return the concatenated string */ -sstr_t sstrcat(size_t count, sstr_t s1, sstr_t s2, ...); +sstr_t ucx_strcat(size_t count, scstr_t s1, ...); + +#define sstrcat(count, s1, ...) ucx_strcat(count, SCSTR(s1), __VA_ARGS__) /** * Concatenates two or more strings using a UcxAllocator. @@ -150,12 +205,12 @@ * @param a the allocator to use * @param count the total number of strings to concatenate * @param s1 first string - * @param s2 second string * @param ... all remaining strings * @return the concatenated string */ -sstr_t sstrcat_a(UcxAllocator *a, size_t count, sstr_t s1, sstr_t s2, ...); +sstr_t ucx_strcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...); +#define sstrcat_a(count, s1, ...) ucx_strcat_a(count, SCSTR(s1), __VA_ARGS__) /** * Returns a substring starting at the specified location. @@ -191,6 +246,13 @@ */ sstr_t sstrsubsl(sstr_t string, size_t start, size_t length); +scstr_t scstrsubs(scstr_t s, size_t start); +scstr_t scstrsubsl(scstr_t string, size_t start, size_t length); + + +int ucx_strchr(const char *string, size_t length, int chr, size_t *pos); +int ucx_strrchr(const char *string, size_t length, int chr, size_t *pos); + /** * Returns a substring starting at the location of the first occurrence of the * specified character. @@ -219,6 +281,17 @@ */ sstr_t sstrrchr(sstr_t string, int chr); + +scstr_t scstrchr(scstr_t string, int chr); +scstr_t scstrrchr(scstr_t string, int chr); + +const char* ucx_strstr( + const char *str, + size_t length, + const char *match, + size_t matchlen, + size_t *newlen); + /** * Returns a substring starting at the location of the first occurrence of the * specified string. @@ -234,7 +307,11 @@ * match, or an empty string, if the sequence is not * present in string */ -sstr_t sstrstr(sstr_t string, sstr_t match); +sstr_t ucx_sstrstr(sstr_t string, scstr_t match); +#define sstrstr(string, match) ucx_sstrstr(string, SCSTR(match)) + +scstr_t ucx_scstrstr(scstr_t string, scstr_t match); +#define scstrstr(string, match) ucx_scstrstr(string, SCSTR(match)) /** * Splits a string into parts by using a delimiter string. @@ -283,7 +360,9 @@ * * @see sstrsplit_a() */ -sstr_t* sstrsplit(sstr_t string, sstr_t delim, ssize_t *count); +sstr_t* ucx_strsplit(scstr_t string, scstr_t delim, ssize_t *count); + +#define sstrsplit(s, delim, count) ucx_strsplit(SCSTR(s), SCSTR(delim), count) /** * Performing sstrsplit() using a UcxAllocator. @@ -307,9 +386,11 @@ * * @see sstrsplit() */ -sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t string, sstr_t delim, +sstr_t* ucx_strsplit_a(UcxAllocator *allocator, scstr_t string, scstr_t delim, ssize_t *count); +#define sstrsplit_a(a, s, d, c) ucx_strsplit_a(a, SCSTR(s), SCSTR(d, c)) + /** * Compares two UCX strings with standard memcmp(). * @@ -322,7 +403,9 @@ * length of s1 is greater than the length of s2 or the result of * memcmp() otherwise (i.e. 0 if the strings match) */ -int sstrcmp(sstr_t s1, sstr_t s2); +int ucx_str_cmp(scstr_t s1, scstr_t s2); + +#define sstrcmp(s1, s2) ucx_str_cmp(SCSTR(s1), SCSTR(s2)) /** * Compares two UCX strings ignoring the case. @@ -338,7 +421,9 @@ * first two differing characters otherwise (i.e. 0 if the strings match and * no characters differ) */ -int sstrcasecmp(sstr_t s1, sstr_t s2); +int ucx_str_casecmp(scstr_t s1, scstr_t s2); + +#define sstrcasecmp(s1, s2) ucx_str_casecmp(SCSTR(s1), SCSTR(s2)) /** * Creates a duplicate of the specified string. @@ -354,7 +439,9 @@ * @return a duplicate of the string * @see sstrdup_a() */ -sstr_t sstrdup(sstr_t string); +sstr_t scstrdup(scstr_t string); + +#define sstrdup(s) scstrdup(SCSTR(s)) /** * Creates a duplicate of the specified string using a UcxAllocator. @@ -372,7 +459,12 @@ * @return a duplicate of the string * @see sstrdup() */ -sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t string); +sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t string); + +#define sstrdup_a(allocator, s) scstrdup_a(allocator, SCSTR(s)) + + +size_t ucx_strtrim(const char *str, size_t length, size_t *newlen); /** * Omits leading and trailing spaces. @@ -393,13 +485,17 @@ */ sstr_t sstrtrim(sstr_t string); +scstr_t scstrtrim(scstr_t string); + /** * Checks, if a string has a specific prefix. * @param string the string to check * @param prefix the prefix the string should have * @return 1, if and only if the string has the specified prefix, 0 otherwise */ -int sstrprefix(sstr_t string, sstr_t prefix); +int ucx_strprefix(scstr_t string, scstr_t prefix); + +#define sstrprefix(string, prefix) ucx_strprefix(SCSTR(string), SCSTR(prefix)) /** * Checks, if a string has a specific suffix. @@ -407,7 +503,9 @@ * @param suffix the suffix the string should have * @return 1, if and only if the string has the specified suffix, 0 otherwise */ -int sstrsuffix(sstr_t string, sstr_t suffix); +int ucx_strsuffix(scstr_t string, scstr_t suffix); + +#define sstrsuffix(string, prefix) ucx_strsuffix(SCSTR(string), SCSTR(prefix)) /** * Returns a lower case version of a string. @@ -419,7 +517,9 @@ * @return the resulting lower case string * @see sstrdup() */ -sstr_t sstrlower(sstr_t string); +sstr_t ucx_strlower(scstr_t string); + +#define sstrlower(string) ucx_strlower(SCSTR(string)) /** * Returns a lower case version of a string. @@ -432,7 +532,9 @@ * @return the resulting lower case string * @see sstrdup_a() */ -sstr_t sstrlower_a(UcxAllocator *allocator, sstr_t string); +sstr_t ucx_strlower_a(UcxAllocator *allocator, scstr_t string); + +#define sstrlower_a(allocator, string) ucx_strlower_a(allocator, SCSTR(string)) /** * Returns a upper case version of a string. @@ -444,7 +546,9 @@ * @return the resulting upper case string * @see sstrdup() */ -sstr_t sstrupper(sstr_t string); +sstr_t ucx_strupper(scstr_t string); + +#define sstrupper(string) ucx_strupper(SCSTR(string)) /** * Returns a upper case version of a string. @@ -457,7 +561,9 @@ * @return the resulting upper case string * @see sstrdup_a() */ -sstr_t sstrupper_a(UcxAllocator *allocator, sstr_t string); +sstr_t ucx_strupper_a(UcxAllocator *allocator, scstr_t string); + +#define sstrupper_a(allocator, string) ucx_strupper_a(allocator, string) #ifdef __cplusplus } diff -r 353d71349e61 -r 90b6d69bb499 src/ucx/ucx.h --- a/src/ucx/ucx.h Mon May 14 15:58:51 2018 +0200 +++ b/src/ucx/ucx.h Mon May 14 17:56:03 2018 +0200 @@ -37,10 +37,10 @@ #define UCX_H /** Major UCX version as integer constant. */ -#define UCX_VERSION_MAJOR 1 +#define UCX_VERSION_MAJOR 2 /** Minor UCX version as integer constant. */ -#define UCX_VERSION_MINOR 1 +#define UCX_VERSION_MINOR 0 /** Version constant which ensures to increase monotonically. */ #define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) diff -r 353d71349e61 -r 90b6d69bb499 test/string_tests.c --- a/test/string_tests.c Mon May 14 15:58:51 2018 +0200 +++ b/test/string_tests.c Mon May 14 17:56:03 2018 +0200 @@ -77,7 +77,12 @@ UCX_TEST_BEGIN sstr_t notfound = sstrchr(str, 'x'); - UCX_TEST_ASSERT(notfound.length == 0, "string length not 0"); + UCX_TEST_ASSERT(notfound.length == 0, + "string length not 0 after forward search w/o result"); + + notfound = sstrrchr(str, 'x'); + UCX_TEST_ASSERT(notfound.length == 0, + "string length not 0 after reverse search w/o result"); sstr_t result = sstrchr(str, 'w'); UCX_TEST_ASSERT(result.length == 35, "sstrchr returned wrong length");