| 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 26 * POSSIBILITY OF SUCH DAMAGE. |
26 * POSSIBILITY OF SUCH DAMAGE. |
| 27 */ |
27 */ |
| |
28 #ifdef MEMRCHR_NEED_GNU |
| |
29 #define _GNU_SOURCE |
| |
30 #endif |
| |
31 |
| 28 #include "cx/string.h" |
32 #include "cx/string.h" |
| 29 |
33 |
| 30 #include <string.h> |
34 #include <string.h> |
| 31 #include <stdarg.h> |
35 #include <stdarg.h> |
| 32 #include <assert.h> |
36 #include <assert.h> |
| 33 #include <errno.h> |
37 #include <errno.h> |
| 34 #include <limits.h> |
38 #include <limits.h> |
| 35 #include <float.h> |
39 #include <float.h> |
| |
40 #include <ctype.h> |
| 36 |
41 |
| 37 #ifdef _WIN32 |
42 #ifdef _WIN32 |
| 38 #define cx_strcasecmp_impl _strnicmp |
43 #define cx_strcasecmp_impl _strnicmp |
| 39 #else |
44 #else |
| 40 #include <strings.h> |
45 #include <strings.h> |
| 229 cxstring result = cx_strchr(cx_strcast(string), chr); |
234 cxstring result = cx_strchr(cx_strcast(string), chr); |
| 230 return (cxmutstr) {(char *) result.ptr, result.length}; |
235 return (cxmutstr) {(char *) result.ptr, result.length}; |
| 231 } |
236 } |
| 232 |
237 |
| 233 cxstring cx_strrchr( |
238 cxstring cx_strrchr( |
| 234 cxstring string, |
239 cxstring string, |
| 235 int chr |
240 int chr |
| 236 ) { |
241 ) { |
| |
242 #ifdef WITH_MEMRCHR |
| |
243 char *ret = memrchr(string.ptr, 0xFF & chr, string.length); |
| |
244 if (ret == NULL) return (cxstring) {NULL, 0}; |
| |
245 return (cxstring) {ret, string.length - (ret - string.ptr)}; |
| |
246 #else |
| 237 chr = 0xFF & chr; |
247 chr = 0xFF & chr; |
| 238 size_t i = string.length; |
248 size_t i = string.length; |
| 239 while (i > 0) { |
249 while (i > 0) { |
| 240 i--; |
250 i--; |
| 241 // TODO: improve by comparing multiple bytes at once |
|
| 242 if (string.ptr[i] == chr) { |
251 if (string.ptr[i] == chr) { |
| 243 return cx_strsubs(string, i); |
252 return cx_strsubs(string, i); |
| 244 } |
253 } |
| 245 } |
254 } |
| 246 return (cxstring) {NULL, 0}; |
255 return (cxstring) {NULL, 0}; |
| |
256 #endif |
| 247 } |
257 } |
| 248 |
258 |
| 249 cxmutstr cx_strrchr_m( |
259 cxmutstr cx_strrchr_m( |
| 250 cxmutstr string, |
260 cxmutstr string, |
| 251 int chr |
261 int chr |
| 449 ) { |
459 ) { |
| 450 return cx_strsplit_a(allocator, cx_strcast(string), |
460 return cx_strsplit_a(allocator, cx_strcast(string), |
| 451 delim, limit, (cxstring **) output); |
461 delim, limit, (cxstring **) output); |
| 452 } |
462 } |
| 453 |
463 |
| 454 int cx_strcmp( |
464 int cx_strcmp_( |
| 455 cxstring s1, |
465 cxstring s1, |
| 456 cxstring s2 |
466 cxstring s2 |
| 457 ) { |
467 ) { |
| 458 if (s1.length == s2.length) { |
468 if (s1.length == s2.length) { |
| 459 return strncmp(s1.ptr, s2.ptr, s1.length); |
469 return strncmp(s1.ptr, s2.ptr, s1.length); |
| 518 memcpy(result.ptr, string.ptr, string.length); |
528 memcpy(result.ptr, string.ptr, string.length); |
| 519 result.ptr[string.length] = '\0'; |
529 result.ptr[string.length] = '\0'; |
| 520 return result; |
530 return result; |
| 521 } |
531 } |
| 522 |
532 |
| 523 static bool str_isspace(char c) { |
|
| 524 // TODO: remove once UCX has public API for this |
|
| 525 return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; |
|
| 526 } |
|
| 527 |
|
| 528 cxstring cx_strtrim(cxstring string) { |
533 cxstring cx_strtrim(cxstring string) { |
| 529 cxstring result = string; |
534 cxstring result = string; |
| 530 // TODO: optimize by comparing multiple bytes at once |
535 while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) { |
| 531 while (result.length > 0 && str_isspace(*result.ptr)) { |
|
| 532 result.ptr++; |
536 result.ptr++; |
| 533 result.length--; |
537 result.length--; |
| 534 } |
538 } |
| 535 while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) { |
539 while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) { |
| 536 result.length--; |
540 result.length--; |
| 537 } |
541 } |
| 538 return result; |
542 return result; |
| 539 } |
543 } |
| 540 |
544 |
| 541 cxmutstr cx_strtrim_m(cxmutstr string) { |
545 cxmutstr cx_strtrim_m(cxmutstr string) { |
| 542 cxstring result = cx_strtrim(cx_strcast(string)); |
546 cxstring result = cx_strtrim(cx_strcast(string)); |
| 543 return (cxmutstr) {(char *) result.ptr, result.length}; |
547 return (cxmutstr) {(char *) result.ptr, result.length}; |
| 544 } |
548 } |
| 545 |
549 |
| 546 bool cx_strprefix( |
550 bool cx_strprefix_( |
| 547 cxstring string, |
551 cxstring string, |
| 548 cxstring prefix |
552 cxstring prefix |
| 549 ) { |
553 ) { |
| 550 if (string.length < prefix.length) return false; |
554 if (string.length < prefix.length) return false; |
| 551 return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; |
555 return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; |
| 552 } |
556 } |
| 553 |
557 |
| 554 bool cx_strsuffix( |
558 bool cx_strsuffix_( |
| 555 cxstring string, |
559 cxstring string, |
| 556 cxstring suffix |
560 cxstring suffix |
| 557 ) { |
561 ) { |
| 558 if (string.length < suffix.length) return false; |
562 if (string.length < suffix.length) return false; |
| 559 return memcmp(string.ptr + string.length - suffix.length, |
563 return memcmp(string.ptr + string.length - suffix.length, |
| 560 suffix.ptr, suffix.length) == 0; |
564 suffix.ptr, suffix.length) == 0; |
| 561 } |
565 } |
| 562 |
566 |
| 563 bool cx_strcaseprefix( |
567 bool cx_strcaseprefix_( |
| 564 cxstring string, |
568 cxstring string, |
| 565 cxstring prefix |
569 cxstring prefix |
| 566 ) { |
570 ) { |
| 567 if (string.length < prefix.length) return false; |
571 if (string.length < prefix.length) return false; |
| 568 #ifdef _WIN32 |
572 #ifdef _WIN32 |
| 955 } |
959 } |
| 956 *output = (float) d; |
960 *output = (float) d; |
| 957 return 0; |
961 return 0; |
| 958 } |
962 } |
| 959 |
963 |
| 960 static bool str_isdigit(char c) { |
|
| 961 // TODO: remove once UCX has public API for this |
|
| 962 return c >= '0' && c <= '9'; |
|
| 963 } |
|
| 964 |
|
| 965 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) { |
964 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) { |
| 966 // TODO: overflow check |
965 // TODO: overflow check |
| 967 // TODO: increase precision |
966 // TODO: increase precision |
| 968 |
967 |
| 969 // emptiness check |
968 // emptiness check |
| 992 } |
991 } |
| 993 |
992 |
| 994 // parse all digits until we find the decsep |
993 // parse all digits until we find the decsep |
| 995 size_t pos = 0; |
994 size_t pos = 0; |
| 996 do { |
995 do { |
| 997 if (str_isdigit(str.ptr[pos])) { |
996 if (isdigit((unsigned char)str.ptr[pos])) { |
| 998 result = result * 10 + (str.ptr[pos] - '0'); |
997 result = result * 10 + (str.ptr[pos] - '0'); |
| 999 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { |
998 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { |
| 1000 break; |
999 break; |
| 1001 } |
1000 } |
| 1002 } while (++pos < str.length); |
1001 } while (++pos < str.length); |
| 1062 } |
1061 } |
| 1063 |
1062 |
| 1064 // parse the exponent |
1063 // parse the exponent |
| 1065 unsigned int exp = 0; |
1064 unsigned int exp = 0; |
| 1066 do { |
1065 do { |
| 1067 if (str_isdigit(str.ptr[pos])) { |
1066 if (isdigit((unsigned char)str.ptr[pos])) { |
| 1068 exp = 10 * exp + (str.ptr[pos] - '0'); |
1067 exp = 10 * exp + (str.ptr[pos] - '0'); |
| 1069 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { |
1068 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { |
| 1070 errno = EINVAL; |
1069 errno = EINVAL; |
| 1071 return -1; |
1070 return -1; |
| 1072 } |
1071 } |