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; |
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 |
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; |