ucx/string.c

changeset 471
063a9f29098c
parent 440
7c4b9cba09ca
equal deleted inserted replaced
470:118e2386d5b4 471:063a9f29098c
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 #define CX_STR_IMPLEMENTATION
29 #include "cx/string.h" 28 #include "cx/string.h"
30 29
31 #include <string.h> 30 #include <string.h>
32 #include <stdarg.h> 31 #include <stdarg.h>
33 #include <ctype.h>
34 #include <assert.h> 32 #include <assert.h>
35 #include <errno.h> 33 #include <errno.h>
36 #include <limits.h> 34 #include <limits.h>
37 #include <float.h> 35 #include <float.h>
38 36
220 218
221 cxstring cx_strchr( 219 cxstring cx_strchr(
222 cxstring string, 220 cxstring string,
223 int chr 221 int chr
224 ) { 222 ) {
225 chr = 0xFF & chr; 223 char *ret = memchr(string.ptr, 0xFF & chr, string.length);
226 // TODO: improve by comparing multiple bytes at once 224 if (ret == NULL) return (cxstring) {NULL, 0};
227 for (size_t i = 0; i < string.length; i++) { 225 return (cxstring) {ret, string.length - (ret - string.ptr)};
228 if (string.ptr[i] == chr) {
229 return cx_strsubs(string, i);
230 }
231 }
232 return (cxstring) {NULL, 0};
233 } 226 }
234 227
235 cxmutstr cx_strchr_m( 228 cxmutstr cx_strchr_m(
236 cxmutstr string, 229 cxmutstr string,
237 int chr 230 int chr
263 cxstring result = cx_strrchr(cx_strcast(string), chr); 256 cxstring result = cx_strrchr(cx_strcast(string), chr);
264 return (cxmutstr) {(char *) result.ptr, result.length}; 257 return (cxmutstr) {(char *) result.ptr, result.length};
265 } 258 }
266 259
267 #ifndef CX_STRSTR_SBO_SIZE 260 #ifndef CX_STRSTR_SBO_SIZE
268 #define CX_STRSTR_SBO_SIZE 512 261 #define CX_STRSTR_SBO_SIZE 128
269 #endif 262 #endif
270 const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE; 263 const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE;
271 264
272 cxstring cx_strstr( 265 cxstring cx_strstr(
273 cxstring haystack, 266 cxstring haystack,
293 // local prefix table 286 // local prefix table
294 size_t s_prefix_table[CX_STRSTR_SBO_SIZE]; 287 size_t s_prefix_table[CX_STRSTR_SBO_SIZE];
295 288
296 // check needle length and use appropriate prefix table 289 // check needle length and use appropriate prefix table
297 // if the pattern exceeds static prefix table, allocate on the heap 290 // if the pattern exceeds static prefix table, allocate on the heap
298 bool useheap = needle.length >= CX_STRSTR_SBO_SIZE; 291 const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
299 register size_t *ptable = useheap ? calloc(needle.length + 1, 292 register size_t *ptable = useheap ? calloc(needle.length + 1,
300 sizeof(size_t)) : s_prefix_table; 293 sizeof(size_t)) : s_prefix_table;
301 294
302 // keep counter in registers 295 // keep counter in registers
303 register size_t i, j; 296 register size_t i, j;
332 break; 325 break;
333 } 326 }
334 } 327 }
335 328
336 // if prefix table was allocated on the heap, free it 329 // if prefix table was allocated on the heap, free it
337 if (ptable != s_prefix_table) { 330 if (useheap) {
338 free(ptable); 331 free(ptable);
339 } 332 }
340 333
341 return result; 334 return result;
342 } 335 }
510 const cxstring *left = s1; 503 const cxstring *left = s1;
511 const cxstring *right = s2; 504 const cxstring *right = s2;
512 return cx_strcasecmp(*left, *right); 505 return cx_strcasecmp(*left, *right);
513 } 506 }
514 507
515 cxmutstr cx_strdup_a( 508 cxmutstr cx_strdup_a_(
516 const CxAllocator *allocator, 509 const CxAllocator *allocator,
517 cxstring string 510 cxstring string
518 ) { 511 ) {
519 cxmutstr result = { 512 cxmutstr result = {
520 cxMalloc(allocator, string.length + 1), 513 cxMalloc(allocator, string.length + 1),
527 memcpy(result.ptr, string.ptr, string.length); 520 memcpy(result.ptr, string.ptr, string.length);
528 result.ptr[string.length] = '\0'; 521 result.ptr[string.length] = '\0';
529 return result; 522 return result;
530 } 523 }
531 524
525 static bool str_isspace(char c) {
526 // TODO: remove once UCX has public API for this
527 return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
528 }
529
532 cxstring cx_strtrim(cxstring string) { 530 cxstring cx_strtrim(cxstring string) {
533 cxstring result = string; 531 cxstring result = string;
534 // TODO: optimize by comparing multiple bytes at once 532 // TODO: optimize by comparing multiple bytes at once
535 while (result.length > 0 && isspace(*result.ptr)) { 533 while (result.length > 0 && str_isspace(*result.ptr)) {
536 result.ptr++; 534 result.ptr++;
537 result.length--; 535 result.length--;
538 } 536 }
539 while (result.length > 0 && isspace(result.ptr[result.length - 1])) { 537 while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) {
540 result.length--; 538 result.length--;
541 } 539 }
542 return result; 540 return result;
543 } 541 }
544 542
588 return strncasecmp(string.ptr + string.length - suffix.length, 586 return strncasecmp(string.ptr + string.length - suffix.length,
589 suffix.ptr, suffix.length) == 0; 587 suffix.ptr, suffix.length) == 0;
590 #endif 588 #endif
591 } 589 }
592 590
593 void cx_strlower(cxmutstr string) {
594 for (size_t i = 0; i < string.length; i++) {
595 string.ptr[i] = (char) tolower(string.ptr[i]);
596 }
597 }
598
599 void cx_strupper(cxmutstr string) {
600 for (size_t i = 0; i < string.length; i++) {
601 string.ptr[i] = (char) toupper(string.ptr[i]);
602 }
603 }
604
605 #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE 591 #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
606 #define CX_STRREPLACE_INDEX_BUFFER_SIZE 64 592 #define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
607 #endif 593 #endif
608 594
609 struct cx_strreplace_ibuf { 595 struct cx_strreplace_ibuf {
611 struct cx_strreplace_ibuf *next; 597 struct cx_strreplace_ibuf *next;
612 unsigned int len; 598 unsigned int len;
613 }; 599 };
614 600
615 static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) { 601 static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
602 // remember, the first data is on the stack!
603 buf = buf->next;
616 while (buf) { 604 while (buf) {
617 struct cx_strreplace_ibuf *next = buf->next; 605 struct cx_strreplace_ibuf *next = buf->next;
618 free(buf->buf); 606 free(buf->buf);
619 free(buf); 607 free(buf);
620 buf = next; 608 buf = next;
622 } 610 }
623 611
624 cxmutstr cx_strreplacen_a( 612 cxmutstr cx_strreplacen_a(
625 const CxAllocator *allocator, 613 const CxAllocator *allocator,
626 cxstring str, 614 cxstring str,
627 cxstring pattern, 615 cxstring search,
628 cxstring replacement, 616 cxstring replacement,
629 size_t replmax 617 size_t replmax
630 ) { 618 ) {
631 619
632 if (pattern.length == 0 || pattern.length > str.length || replmax == 0) 620 if (search.length == 0 || search.length > str.length || replmax == 0)
633 return cx_strdup_a(allocator, str); 621 return cx_strdup_a(allocator, str);
634 622
635 // Compute expected buffer length 623 // Compute expected buffer length
636 size_t ibufmax = str.length / pattern.length; 624 size_t ibufmax = str.length / search.length;
637 size_t ibuflen = replmax < ibufmax ? replmax : ibufmax; 625 size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
638 if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) { 626 if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) {
639 ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE; 627 ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE;
640 } 628 }
641 629
642 // Allocate first index buffer 630 // First index buffer can be on the stack
643 struct cx_strreplace_ibuf *firstbuf, *curbuf; 631 struct cx_strreplace_ibuf ibuf, *curbuf = &ibuf;
644 firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf)); 632 size_t ibuf_sbo[CX_STRREPLACE_INDEX_BUFFER_SIZE];
645 if (!firstbuf) return cx_mutstrn(NULL, 0); 633 ibuf.buf = ibuf_sbo;
646 firstbuf->buf = calloc(ibuflen, sizeof(size_t)); 634 ibuf.next = NULL;
647 if (!firstbuf->buf) { 635 ibuf.len = 0;
648 free(firstbuf);
649 return cx_mutstrn(NULL, 0);
650 }
651 636
652 // Search occurrences 637 // Search occurrences
653 cxstring searchstr = str; 638 cxstring searchstr = str;
654 size_t found = 0; 639 size_t found = 0;
655 do { 640 do {
656 cxstring match = cx_strstr(searchstr, pattern); 641 cxstring match = cx_strstr(searchstr, search);
657 if (match.length > 0) { 642 if (match.length > 0) {
658 // Allocate next buffer in chain, if required 643 // Allocate next buffer in chain, if required
659 if (curbuf->len == ibuflen) { 644 if (curbuf->len == ibuflen) {
660 struct cx_strreplace_ibuf *nextbuf = 645 struct cx_strreplace_ibuf *nextbuf =
661 calloc(1, sizeof(struct cx_strreplace_ibuf)); 646 calloc(1, sizeof(struct cx_strreplace_ibuf));
662 if (!nextbuf) { 647 if (!nextbuf) {
663 cx_strrepl_free_ibuf(firstbuf); 648 cx_strrepl_free_ibuf(&ibuf);
664 return cx_mutstrn(NULL, 0); 649 return cx_mutstrn(NULL, 0);
665 } 650 }
666 nextbuf->buf = calloc(ibuflen, sizeof(size_t)); 651 nextbuf->buf = calloc(ibuflen, sizeof(size_t));
667 if (!nextbuf->buf) { 652 if (!nextbuf->buf) {
668 free(nextbuf); 653 free(nextbuf);
669 cx_strrepl_free_ibuf(firstbuf); 654 cx_strrepl_free_ibuf(&ibuf);
670 return cx_mutstrn(NULL, 0); 655 return cx_mutstrn(NULL, 0);
671 } 656 }
672 curbuf->next = nextbuf; 657 curbuf->next = nextbuf;
673 curbuf = nextbuf; 658 curbuf = nextbuf;
674 } 659 }
675 660
676 // Record match index 661 // Record match index
677 found++; 662 found++;
678 size_t idx = match.ptr - str.ptr; 663 size_t idx = match.ptr - str.ptr;
679 curbuf->buf[curbuf->len++] = idx; 664 curbuf->buf[curbuf->len++] = idx;
680 searchstr.ptr = match.ptr + pattern.length; 665 searchstr.ptr = match.ptr + search.length;
681 searchstr.length = str.length - idx - pattern.length; 666 searchstr.length = str.length - idx - search.length;
682 } else { 667 } else {
683 break; 668 break;
684 } 669 }
685 } while (searchstr.length > 0 && found < replmax); 670 } while (searchstr.length > 0 && found < replmax);
686 671
687 // Allocate result string 672 // Allocate result string
688 cxmutstr result; 673 cxmutstr result;
689 { 674 {
690 ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length; 675 long long adjlen = (long long) replacement.length - (long long) search.length;
691 size_t rcount = 0; 676 size_t rcount = 0;
692 curbuf = firstbuf; 677 curbuf = &ibuf;
693 do { 678 do {
694 rcount += curbuf->len; 679 rcount += curbuf->len;
695 curbuf = curbuf->next; 680 curbuf = curbuf->next;
696 } while (curbuf); 681 } while (curbuf);
697 result.length = str.length + rcount * adjlen; 682 result.length = str.length + rcount * adjlen;
698 result.ptr = cxMalloc(allocator, result.length + 1); 683 result.ptr = cxMalloc(allocator, result.length + 1);
699 if (!result.ptr) { 684 if (!result.ptr) {
700 cx_strrepl_free_ibuf(firstbuf); 685 cx_strrepl_free_ibuf(&ibuf);
701 return cx_mutstrn(NULL, 0); 686 return cx_mutstrn(NULL, 0);
702 } 687 }
703 } 688 }
704 689
705 // Build result string 690 // Build result string
706 curbuf = firstbuf; 691 curbuf = &ibuf;
707 size_t srcidx = 0; 692 size_t srcidx = 0;
708 char *destptr = result.ptr; 693 char *destptr = result.ptr;
709 do { 694 do {
710 for (size_t i = 0; i < curbuf->len; i++) { 695 for (size_t i = 0; i < curbuf->len; i++) {
711 // Copy source part up to next match 696 // Copy source part up to next match
716 destptr += srclen; 701 destptr += srclen;
717 srcidx += srclen; 702 srcidx += srclen;
718 } 703 }
719 704
720 // Copy the replacement and skip the source pattern 705 // Copy the replacement and skip the source pattern
721 srcidx += pattern.length; 706 srcidx += search.length;
722 memcpy(destptr, replacement.ptr, replacement.length); 707 memcpy(destptr, replacement.ptr, replacement.length);
723 destptr += replacement.length; 708 destptr += replacement.length;
724 } 709 }
725 curbuf = curbuf->next; 710 curbuf = curbuf->next;
726 } while (curbuf); 711 } while (curbuf);
728 713
729 // Result is guaranteed to be zero-terminated 714 // Result is guaranteed to be zero-terminated
730 result.ptr[result.length] = '\0'; 715 result.ptr[result.length] = '\0';
731 716
732 // Free index buffer 717 // Free index buffer
733 cx_strrepl_free_ibuf(firstbuf); 718 cx_strrepl_free_ibuf(&ibuf);
734 719
735 return result; 720 return result;
736 } 721 }
737 722
738 CxStrtokCtx cx_strtok( 723 CxStrtokCtx cx_strtok_(
739 cxstring str, 724 cxstring str,
740 cxstring delim, 725 cxstring delim,
741 size_t limit 726 size_t limit
742 ) { 727 ) {
743 CxStrtokCtx ctx; 728 CxStrtokCtx ctx;
751 ctx.delim_more = NULL; 736 ctx.delim_more = NULL;
752 ctx.delim_more_count = 0; 737 ctx.delim_more_count = 0;
753 return ctx; 738 return ctx;
754 } 739 }
755 740
756 CxStrtokCtx cx_strtok_m(
757 cxmutstr str,
758 cxstring delim,
759 size_t limit
760 ) {
761 return cx_strtok(cx_strcast(str), delim, limit);
762 }
763
764 bool cx_strtok_next( 741 bool cx_strtok_next(
765 CxStrtokCtx *ctx, 742 CxStrtokCtx *ctx,
766 cxstring *token 743 cxstring *token
767 ) { 744 ) {
768 // abortion criteria 745 // abortion criteria
830 return -1; \ 807 return -1; \
831 } \ 808 } \
832 *output = (rtype) result; \ 809 *output = (rtype) result; \
833 return 0 810 return 0
834 811
835 int cx_strtos_lc(cxstring str, short *output, int base, const char *groupsep) { 812 int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep) {
836 cx_strtoX_signed_impl(short, SHRT_MIN, SHRT_MAX); 813 cx_strtoX_signed_impl(short, SHRT_MIN, SHRT_MAX);
837 } 814 }
838 815
839 int cx_strtoi_lc(cxstring str, int *output, int base, const char *groupsep) { 816 int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep) {
840 cx_strtoX_signed_impl(int, INT_MIN, INT_MAX); 817 cx_strtoX_signed_impl(int, INT_MIN, INT_MAX);
841 } 818 }
842 819
843 int cx_strtol_lc(cxstring str, long *output, int base, const char *groupsep) { 820 int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep) {
844 cx_strtoX_signed_impl(long, LONG_MIN, LONG_MAX); 821 cx_strtoX_signed_impl(long, LONG_MIN, LONG_MAX);
845 } 822 }
846 823
847 int cx_strtoll_lc(cxstring str, long long *output, int base, const char *groupsep) { 824 int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep) {
848 // strategy: parse as unsigned, check range, negate if required 825 // strategy: parse as unsigned, check range, negate if required
849 bool neg = false; 826 bool neg = false;
850 size_t start_unsigned = 0; 827 size_t start_unsigned = 0;
851 828
852 // trim already, to search for a sign character 829 // emptiness check
853 str = cx_strtrim(str);
854 if (str.length == 0) { 830 if (str.length == 0) {
855 errno = EINVAL; 831 errno = EINVAL;
856 return -1; 832 return -1;
857 } 833 }
858 834
888 *output = (long long) v; 864 *output = (long long) v;
889 return 0; 865 return 0;
890 } 866 }
891 } 867 }
892 868
893 int cx_strtoi8_lc(cxstring str, int8_t *output, int base, const char *groupsep) { 869 int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep) {
894 cx_strtoX_signed_impl(int8_t, INT8_MIN, INT8_MAX); 870 cx_strtoX_signed_impl(int8_t, INT8_MIN, INT8_MAX);
895 } 871 }
896 872
897 int cx_strtoi16_lc(cxstring str, int16_t *output, int base, const char *groupsep) { 873 int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep) {
898 cx_strtoX_signed_impl(int16_t, INT16_MIN, INT16_MAX); 874 cx_strtoX_signed_impl(int16_t, INT16_MIN, INT16_MAX);
899 } 875 }
900 876
901 int cx_strtoi32_lc(cxstring str, int32_t *output, int base, const char *groupsep) { 877 int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep) {
902 cx_strtoX_signed_impl(int32_t, INT32_MIN, INT32_MAX); 878 cx_strtoX_signed_impl(int32_t, INT32_MIN, INT32_MAX);
903 } 879 }
904 880
905 int cx_strtoi64_lc(cxstring str, int64_t *output, int base, const char *groupsep) { 881 int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep) {
906 assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms 882 assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms
907 return cx_strtoll_lc(str, (long long*) output, base, groupsep); 883 return cx_strtoll_lc(str, (long long*) output, base, groupsep);
908 }
909
910 int cx_strtoz_lc(cxstring str, ssize_t *output, int base, const char *groupsep) {
911 #if SSIZE_MAX == INT32_MAX
912 return cx_strtoi32_lc(str, (int32_t*) output, base, groupsep);
913 #elif SSIZE_MAX == INT64_MAX
914 return cx_strtoll_lc(str, (long long*) output, base, groupsep);
915 #else
916 #error "unsupported ssize_t size"
917 #endif
918 } 884 }
919 885
920 #define cx_strtoX_unsigned_impl(rtype, rmax) \ 886 #define cx_strtoX_unsigned_impl(rtype, rmax) \
921 uint64_t result; \ 887 uint64_t result; \
922 if (cx_strtou64_lc(str, &result, base, groupsep)) { \ 888 if (cx_strtou64_lc(str, &result, base, groupsep)) { \
927 return -1; \ 893 return -1; \
928 } \ 894 } \
929 *output = (rtype) result; \ 895 *output = (rtype) result; \
930 return 0 896 return 0
931 897
932 int cx_strtous_lc(cxstring str, unsigned short *output, int base, const char *groupsep) { 898 int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep) {
933 cx_strtoX_unsigned_impl(unsigned short, USHRT_MAX); 899 cx_strtoX_unsigned_impl(unsigned short, USHRT_MAX);
934 } 900 }
935 901
936 int cx_strtou_lc(cxstring str, unsigned int *output, int base, const char *groupsep) { 902 int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep) {
937 cx_strtoX_unsigned_impl(unsigned int, UINT_MAX); 903 cx_strtoX_unsigned_impl(unsigned int, UINT_MAX);
938 } 904 }
939 905
940 int cx_strtoul_lc(cxstring str, unsigned long *output, int base, const char *groupsep) { 906 int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep) {
941 cx_strtoX_unsigned_impl(unsigned long, ULONG_MAX); 907 cx_strtoX_unsigned_impl(unsigned long, ULONG_MAX);
942 } 908 }
943 909
944 int cx_strtoull_lc(cxstring str, unsigned long long *output, int base, const char *groupsep) { 910 int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep) {
945 // some sanity checks 911 // some sanity checks
946 str = cx_strtrim(str);
947 if (str.length == 0) { 912 if (str.length == 0) {
948 errno = EINVAL; 913 errno = EINVAL;
949 return -1; 914 return -1;
950 } 915 }
951 if (!(base == 2 || base == 8 || base == 10 || base == 16)) { 916 if (!(base == 2 || base == 8 || base == 10 || base == 16)) {
1019 984
1020 *output = result; 985 *output = result;
1021 return 0; 986 return 0;
1022 } 987 }
1023 988
1024 int cx_strtou8_lc(cxstring str, uint8_t *output, int base, const char *groupsep) { 989 int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep) {
1025 cx_strtoX_unsigned_impl(uint8_t, UINT8_MAX); 990 cx_strtoX_unsigned_impl(uint8_t, UINT8_MAX);
1026 } 991 }
1027 992
1028 int cx_strtou16_lc(cxstring str, uint16_t *output, int base, const char *groupsep) { 993 int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep) {
1029 cx_strtoX_unsigned_impl(uint16_t, UINT16_MAX); 994 cx_strtoX_unsigned_impl(uint16_t, UINT16_MAX);
1030 } 995 }
1031 996
1032 int cx_strtou32_lc(cxstring str, uint32_t *output, int base, const char *groupsep) { 997 int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep) {
1033 cx_strtoX_unsigned_impl(uint32_t, UINT32_MAX); 998 cx_strtoX_unsigned_impl(uint32_t, UINT32_MAX);
1034 } 999 }
1035 1000
1036 int cx_strtou64_lc(cxstring str, uint64_t *output, int base, const char *groupsep) { 1001 int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep) {
1037 assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms 1002 assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms
1038 return cx_strtoull_lc(str, (unsigned long long*) output, base, groupsep); 1003 return cx_strtoull_lc(str, (unsigned long long*) output, base, groupsep);
1039 } 1004 }
1040 1005
1041 int cx_strtouz_lc(cxstring str, size_t *output, int base, const char *groupsep) { 1006 int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep) {
1042 #if SIZE_MAX == UINT32_MAX 1007 #if SIZE_MAX == UINT32_MAX
1043 return cx_strtou32_lc(str, (uint32_t*) output, base, groupsep); 1008 return cx_strtou32_lc_(str, (uint32_t*) output, base, groupsep);
1044 #elif SIZE_MAX == UINT64_MAX 1009 #elif SIZE_MAX == UINT64_MAX
1045 return cx_strtoull_lc(str, (unsigned long long *) output, base, groupsep); 1010 return cx_strtoull_lc_(str, (unsigned long long *) output, base, groupsep);
1046 #else 1011 #else
1047 #error "unsupported size_t size" 1012 #error "unsupported size_t size"
1048 #endif 1013 #endif
1049 } 1014 }
1050 1015
1051 int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) { 1016 int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep) {
1052 // use string to double and add a range check 1017 // use string to double and add a range check
1053 double d; 1018 double d;
1054 int ret = cx_strtod_lc(str, &d, decsep, groupsep); 1019 int ret = cx_strtod_lc_(str, &d, decsep, groupsep);
1055 if (ret != 0) return ret; 1020 if (ret != 0) return ret;
1056 // note: FLT_MIN is the smallest POSITIVE number that can be represented 1021 // note: FLT_MIN is the smallest POSITIVE number that can be represented
1057 double test = d < 0 ? -d : d; 1022 double test = d < 0 ? -d : d;
1058 if (test < FLT_MIN || test > FLT_MAX) { 1023 if (test < FLT_MIN || test > FLT_MAX) {
1059 errno = ERANGE; 1024 errno = ERANGE;
1061 } 1026 }
1062 *output = (float) d; 1027 *output = (float) d;
1063 return 0; 1028 return 0;
1064 } 1029 }
1065 1030
1066 int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) { 1031 static bool str_isdigit(char c) {
1032 // TODO: remove once UCX has public API for this
1033 return c >= '0' && c <= '9';
1034 }
1035
1036 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) {
1067 // TODO: overflow check 1037 // TODO: overflow check
1068 // TODO: increase precision 1038 // TODO: increase precision
1069 1039
1070 // trim and check 1040 // emptiness check
1071 str = cx_strtrim(str);
1072 if (str.length == 0) { 1041 if (str.length == 0) {
1073 errno = EINVAL; 1042 errno = EINVAL;
1074 return -1; 1043 return -1;
1075 } 1044 }
1076 1045
1094 } 1063 }
1095 1064
1096 // parse all digits until we find the decsep 1065 // parse all digits until we find the decsep
1097 size_t pos = 0; 1066 size_t pos = 0;
1098 do { 1067 do {
1099 if (isdigit(str.ptr[pos])) { 1068 if (str_isdigit(str.ptr[pos])) {
1100 result = result * 10 + (str.ptr[pos] - '0'); 1069 result = result * 10 + (str.ptr[pos] - '0');
1101 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { 1070 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1102 break; 1071 break;
1103 } 1072 }
1104 } while (++pos < str.length); 1073 } while (++pos < str.length);
1123 } 1092 }
1124 } 1093 }
1125 // parse everything until exponent or end 1094 // parse everything until exponent or end
1126 double factor = 1.; 1095 double factor = 1.;
1127 do { 1096 do {
1128 if (isdigit(str.ptr[pos])) { 1097 if (str_isdigit(str.ptr[pos])) {
1129 factor *= 0.1; 1098 factor *= 0.1;
1130 result = result + factor * (str.ptr[pos] - '0'); 1099 result = result + factor * (str.ptr[pos] - '0');
1131 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { 1100 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1132 break; 1101 break;
1133 } 1102 }
1164 } 1133 }
1165 1134
1166 // parse the exponent 1135 // parse the exponent
1167 unsigned int exp = 0; 1136 unsigned int exp = 0;
1168 do { 1137 do {
1169 if (isdigit(str.ptr[pos])) { 1138 if (str_isdigit(str.ptr[pos])) {
1170 exp = 10 * exp + (str.ptr[pos] - '0'); 1139 exp = 10 * exp + (str.ptr[pos] - '0');
1171 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { 1140 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1172 errno = EINVAL; 1141 errno = EINVAL;
1173 return -1; 1142 return -1;
1174 } 1143 }

mercurial