src/string.c

changeset 1063
e453e717876e
parent 1061
c7d23892eab5
equal deleted inserted replaced
1062:8baed9b38bc6 1063:e453e717876e
32 #include <stdarg.h> 32 #include <stdarg.h>
33 #include <ctype.h> 33 #include <ctype.h>
34 #include <assert.h> 34 #include <assert.h>
35 #include <errno.h> 35 #include <errno.h>
36 #include <limits.h> 36 #include <limits.h>
37 #include <float.h>
37 38
38 #ifndef _WIN32 39 #ifndef _WIN32
39 40
40 #include <strings.h> // for strncasecmp() 41 #include <strings.h> // for strncasecmp()
41 42
959 } 960 }
960 size_t start = 0; 961 size_t start = 0;
961 962
962 // if base is 2 or 16, some leading stuff may appear 963 // if base is 2 or 16, some leading stuff may appear
963 if (base == 2) { 964 if (base == 2) {
964 if (str.ptr[0] == 'b' || str.ptr[0] == 'B') { 965 if ((str.ptr[0] | 32) == 'b') {
965 start = 1; 966 start = 1;
966 } else if (str.ptr[0] == '0' && str.length > 1) { 967 } else if (str.ptr[0] == '0' && str.length > 1) {
967 if (str.ptr[1] == 'b' || str.ptr[1] == 'B') { 968 if ((str.ptr[1] | 32) == 'b') {
968 start = 2; 969 start = 2;
969 } 970 }
970 } 971 }
971 } else if (base == 16) { 972 } else if (base == 16) {
972 if (str.ptr[0] == 'x' || str.ptr[0] == 'X' || str.ptr[0] == '#') { 973 if ((str.ptr[0] | 32) == 'x' || str.ptr[0] == '#') {
973 start = 1; 974 start = 1;
974 } else if (str.ptr[0] == '0' && str.length > 1) { 975 } else if (str.ptr[0] == '0' && str.length > 1) {
975 if (str.ptr[1] == 'x' || str.ptr[1] == 'X') { 976 if ((str.ptr[1] | 32) == 'x') {
976 start = 2; 977 start = 2;
977 } 978 }
978 } 979 }
979 } 980 }
980 981
1041 #error "unsupported size_t size" 1042 #error "unsupported size_t size"
1042 #endif 1043 #endif
1043 } 1044 }
1044 1045
1045 int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) { 1046 int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) {
1046 // TODO: replace temporary implementation 1047 // use string to double and add a range check
1047 (void) groupsep; // unused in temp impl 1048 double d;
1048 (void) decsep; // unused in temp impl 1049 int ret = cx_strtod_lc(str, &d, decsep, groupsep);
1049 char *s = malloc(str.length + 1); 1050 if (ret != 0) return ret;
1050 memcpy(s, str.ptr, str.length); 1051 // note: FLT_MIN is the smallest POSITIVE number that can be represented
1051 s[str.length] = '\0'; 1052 double test = d < 0 ? -d : d;
1052 char *e; 1053 if (test < FLT_MIN || test > FLT_MAX) {
1053 *output = strtof(s, &e); 1054 errno = ERANGE;
1054 int r = !(e && *e == '\0'); 1055 return -1;
1055 free(s); 1056 }
1056 return r; 1057 *output = (float) d;
1058 return 0;
1057 } 1059 }
1058 1060
1059 int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) { 1061 int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) {
1060 // TODO: replace temporary implementation 1062 // TODO: overflow check
1061 (void) groupsep; // unused in temp impl 1063 // TODO: increase precision
1062 (void) decsep; // unused in temp impl 1064
1063 char *s = malloc(str.length + 1); 1065 // trim and check
1064 memcpy(s, str.ptr, str.length); 1066 str = cx_strtrim(str);
1065 s[str.length] = '\0'; 1067 if (str.length == 0) {
1066 char *e; 1068 errno = EINVAL;
1067 *output = strtod(s, &e); 1069 return -1;
1068 int r = !(e && *e == '\0'); 1070 }
1069 free(s); 1071
1070 return r; 1072 double result = 0.;
1071 } 1073 int sign = 1;
1074
1075 // check if there is a sign
1076 if (str.ptr[0] == '-') {
1077 sign = -1;
1078 str.ptr++;
1079 str.length--;
1080 } else if (str.ptr[0] == '+') {
1081 str.ptr++;
1082 str.length--;
1083 }
1084
1085 // there must be at least one char to parse
1086 if (str.length == 0) {
1087 errno = EINVAL;
1088 return -1;
1089 }
1090
1091 // parse all digits until we find the decsep
1092 size_t pos = 0;
1093 do {
1094 if (isdigit(str.ptr[pos])) {
1095 result = result * 10 + (str.ptr[pos] - '0');
1096 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1097 break;
1098 }
1099 } while (++pos < str.length);
1100
1101 // already done?
1102 if (pos == str.length) {
1103 *output = result * sign;
1104 return 0;
1105 }
1106
1107 // is the next char the decsep?
1108 if (str.ptr[pos] == decsep) {
1109 pos++;
1110 // it may end with the decsep, if it did not start with it
1111 if (pos == str.length) {
1112 if (str.length == 1) {
1113 errno = EINVAL;
1114 return -1;
1115 } else {
1116 *output = result * sign;
1117 return 0;
1118 }
1119 }
1120 // parse everything until exponent or end
1121 double factor = 1.;
1122 do {
1123 if (isdigit(str.ptr[pos])) {
1124 factor *= 0.1;
1125 result = result + factor * (str.ptr[pos] - '0');
1126 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1127 break;
1128 }
1129 } while (++pos < str.length);
1130 }
1131
1132 // no exponent?
1133 if (pos == str.length) {
1134 *output = result * sign;
1135 return 0;
1136 }
1137
1138 // now the next separator MUST be the exponent separator
1139 // and at least one char must follow
1140 if ((str.ptr[pos] | 32) != 'e' || str.length <= pos + 1) {
1141 errno = EINVAL;
1142 return -1;
1143 }
1144 pos++;
1145
1146 // check if we have a sign for the exponent
1147 double factor = 10.;
1148 if (str.ptr[pos] == '-') {
1149 factor = .1;
1150 pos++;
1151 } else if (str.ptr[pos] == '+') {
1152 pos++;
1153 }
1154
1155 // at least one digit must follow
1156 if (pos == str.length) {
1157 errno = EINVAL;
1158 return -1;
1159 }
1160
1161 // parse the exponent
1162 unsigned int exp = 0;
1163 do {
1164 if (isdigit(str.ptr[pos])) {
1165 exp = 10 * exp + (str.ptr[pos] - '0');
1166 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1167 errno = EINVAL;
1168 return -1;
1169 }
1170 } while (++pos < str.length);
1171
1172 // apply the exponent by fast exponentiation
1173 do {
1174 if (exp & 1) {
1175 result *= factor;
1176 }
1177 factor *= factor;
1178 } while ((exp >>= 1) > 0);
1179
1180 // store the result and exit
1181 *output = result * sign;
1182 return 0;
1183 }

mercurial