msvcrt: Rework strtod_helper to be reusable.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com> Signed-off-by: Piotr Caban <piotr@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
34925faa7d
commit
b12d6d405a
|
@ -1181,6 +1181,7 @@ int __cdecl MSVCRT__toupper_l(int,MSVCRT__locale_t);
|
||||||
int __cdecl MSVCRT__tolower_l(int,MSVCRT__locale_t);
|
int __cdecl MSVCRT__tolower_l(int,MSVCRT__locale_t);
|
||||||
int __cdecl MSVCRT__towupper_l(MSVCRT_wint_t,MSVCRT__locale_t);
|
int __cdecl MSVCRT__towupper_l(MSVCRT_wint_t,MSVCRT__locale_t);
|
||||||
int __cdecl MSVCRT__towlower_l(MSVCRT_wint_t,MSVCRT__locale_t);
|
int __cdecl MSVCRT__towlower_l(MSVCRT_wint_t,MSVCRT__locale_t);
|
||||||
|
int __cdecl MSVCRT__toupper(int); /* only use on lower-case ASCII characters */
|
||||||
int __cdecl MSVCRT__stricmp(const char*, const char*);
|
int __cdecl MSVCRT__stricmp(const char*, const char*);
|
||||||
int __cdecl MSVCRT__strnicmp(const char*, const char*, MSVCRT_size_t);
|
int __cdecl MSVCRT__strnicmp(const char*, const char*, MSVCRT_size_t);
|
||||||
int __cdecl MSVCRT__strnicoll_l(const char*, const char*, MSVCRT_size_t, MSVCRT__locale_t);
|
int __cdecl MSVCRT__strnicoll_l(const char*, const char*, MSVCRT_size_t, MSVCRT__locale_t);
|
||||||
|
@ -1190,6 +1191,7 @@ int __cdecl MSVCRT_strcmp(const char*, const char*);
|
||||||
char* __cdecl MSVCRT_strstr(const char*, const char*);
|
char* __cdecl MSVCRT_strstr(const char*, const char*);
|
||||||
unsigned int __cdecl MSVCRT__get_output_format(void);
|
unsigned int __cdecl MSVCRT__get_output_format(void);
|
||||||
char* __cdecl MSVCRT_strtok_s(char*, const char*, char**);
|
char* __cdecl MSVCRT_strtok_s(char*, const char*, char**);
|
||||||
|
double parse_double(MSVCRT_wchar_t (*)(void*), void (*)(void*), void*, MSVCRT_pthreadlocinfo, int*);
|
||||||
|
|
||||||
/* Maybe one day we'll enable the invalid parameter handlers with the full set of information (msvcrXXd)
|
/* Maybe one day we'll enable the invalid parameter handlers with the full set of information (msvcrXXd)
|
||||||
* #define MSVCRT_INVALID_PMT(x) MSVCRT_call_invalid_parameter_handler(x, __FUNCTION__, __FILE__, __LINE__, 0)
|
* #define MSVCRT_INVALID_PMT(x) MSVCRT_call_invalid_parameter_handler(x, __FUNCTION__, __FILE__, __LINE__, 0)
|
||||||
|
|
|
@ -455,28 +455,30 @@ static inline int hex2int(char c)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double strtod16(int sign, const char *p, char **end,
|
static double strtod16(MSVCRT_wchar_t get(void *ctx), void unget(void *ctx),
|
||||||
MSVCRT_pthreadlocinfo locinfo, int *err)
|
void *ctx, int sign, MSVCRT_pthreadlocinfo locinfo, int *err)
|
||||||
{
|
{
|
||||||
|
BOOL found_digit = FALSE, found_dp = FALSE;
|
||||||
enum round round = ROUND_ZERO;
|
enum round round = ROUND_ZERO;
|
||||||
BOOL found_digit = FALSE;
|
MSVCRT_wchar_t nch;
|
||||||
ULONGLONG m = 0;
|
ULONGLONG m = 0;
|
||||||
int val, exp = 0;
|
int val, exp = 0;
|
||||||
|
|
||||||
|
nch = get(ctx);
|
||||||
while(m < MSVCRT_UI64_MAX/16)
|
while(m < MSVCRT_UI64_MAX/16)
|
||||||
{
|
{
|
||||||
val = hex2int(*p);
|
val = hex2int(nch);
|
||||||
if (val == -1) break;
|
if (val == -1) break;
|
||||||
found_digit = TRUE;
|
found_digit = TRUE;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
|
|
||||||
m = m*16 + val;
|
m = m*16 + val;
|
||||||
}
|
}
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
val = hex2int(*p);
|
val = hex2int(nch);
|
||||||
if (val == -1) break;
|
if (val == -1) break;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
exp += 4;
|
exp += 4;
|
||||||
|
|
||||||
if (val || round != ROUND_ZERO)
|
if (val || round != ROUND_ZERO)
|
||||||
|
@ -487,29 +489,33 @@ static double strtod16(int sign, const char *p, char **end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*p == *locinfo->lconv->decimal_point)
|
if(nch == *locinfo->lconv->decimal_point)
|
||||||
p++;
|
{
|
||||||
|
found_dp = TRUE;
|
||||||
|
nch = get(ctx);
|
||||||
|
}
|
||||||
else if (!found_digit)
|
else if (!found_digit)
|
||||||
{
|
{
|
||||||
if(end) *end = (char*)p - 1;
|
if(nch!=MSVCRT_WEOF) unget(ctx);
|
||||||
|
unget(ctx);
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(m <= MSVCRT_UI64_MAX/16)
|
while(m <= MSVCRT_UI64_MAX/16)
|
||||||
{
|
{
|
||||||
val = hex2int(*p);
|
val = hex2int(nch);
|
||||||
if (val == -1) break;
|
if (val == -1) break;
|
||||||
found_digit = TRUE;
|
found_digit = TRUE;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
|
|
||||||
m = m*16 + val;
|
m = m*16 + val;
|
||||||
exp -= 4;
|
exp -= 4;
|
||||||
}
|
}
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
val = hex2int(*p);
|
val = hex2int(nch);
|
||||||
if (val == -1) break;
|
if (val == -1) break;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
|
|
||||||
if (val || round != ROUND_ZERO)
|
if (val || round != ROUND_ZERO)
|
||||||
{
|
{
|
||||||
|
@ -521,39 +527,44 @@ static double strtod16(int sign, const char *p, char **end,
|
||||||
|
|
||||||
if (!found_digit)
|
if (!found_digit)
|
||||||
{
|
{
|
||||||
if(end) *end = (char*)p - 2;
|
if (nch != MSVCRT_WEOF) unget(ctx);
|
||||||
|
if (found_dp) unget(ctx);
|
||||||
|
unget(ctx);
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*p=='p' || *p=='P') {
|
if(nch=='p' || nch=='P') {
|
||||||
|
BOOL found_sign = FALSE;
|
||||||
int e=0, s=1;
|
int e=0, s=1;
|
||||||
|
|
||||||
p++;
|
nch = get(ctx);
|
||||||
if(*p == '-') {
|
if(nch == '-') {
|
||||||
|
found_sign = TRUE;
|
||||||
s = -1;
|
s = -1;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
} else if(*p == '+')
|
} else if(nch == '+') {
|
||||||
p++;
|
found_sign = TRUE;
|
||||||
|
nch = get(ctx);
|
||||||
if(*p>='0' && *p<='9') {
|
}
|
||||||
while(*p>='0' && *p<='9') {
|
if(nch>='0' && nch<='9') {
|
||||||
if(e>INT_MAX/10 || (e=e*10+*p-'0')<0)
|
while(nch>='0' && nch<='9') {
|
||||||
|
if(e>INT_MAX/10 || (e=e*10+nch-'0')<0)
|
||||||
e = INT_MAX;
|
e = INT_MAX;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
}
|
}
|
||||||
|
if((nch!=MSVCRT_WEOF) && (nch < '0' || nch > '9')) unget(ctx);
|
||||||
e *= s;
|
e *= s;
|
||||||
|
|
||||||
if(exp<0 && e<0 && exp+e>=0) exp = INT_MIN;
|
if(exp<0 && e<0 && exp+e>=0) exp = INT_MIN;
|
||||||
else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
|
else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
|
||||||
else exp += e;
|
else exp += e;
|
||||||
} else {
|
} else {
|
||||||
if(*p=='-' || *p=='+')
|
if(nch != MSVCRT_WEOF) unget(ctx);
|
||||||
p--;
|
if(found_sign) unget(ctx);
|
||||||
p--;
|
unget(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end) *end = (char*)p;
|
|
||||||
return make_double(sign, exp, m, round, err);
|
return make_double(sign, exp, m, round, err);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -650,111 +661,120 @@ static double convert_e10_to_e2(int sign, int e10, ULONGLONG m, int *err)
|
||||||
return make_double(sign, e2, u128.u[0], ROUND_DOWN, err);
|
return make_double(sign, e2, u128.u[0], ROUND_DOWN, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale, int *err)
|
double parse_double(MSVCRT_wchar_t (*get)(void *ctx), void (*unget)(void *ctx),
|
||||||
|
void *ctx, MSVCRT_pthreadlocinfo locinfo, int *err)
|
||||||
{
|
{
|
||||||
MSVCRT_pthreadlocinfo locinfo;
|
#if _MSVCR_VER >= 140
|
||||||
|
MSVCRT_wchar_t _infinity[] = { 'i', 'n', 'f', 'i', 'n', 'i', 't', 'y', 0 };
|
||||||
|
MSVCRT_wchar_t _nan[] = { 'n', 'a', 'n', 0 };
|
||||||
|
MSVCRT_wchar_t *str_match = NULL;
|
||||||
|
int matched=0;
|
||||||
|
#endif
|
||||||
|
BOOL found_digit = FALSE, found_dp = FALSE, found_sign = FALSE;
|
||||||
unsigned __int64 d=0, hlp;
|
unsigned __int64 d=0, hlp;
|
||||||
|
MSVCRT_wchar_t nch;
|
||||||
int exp=0, sign=1;
|
int exp=0, sign=1;
|
||||||
const char *p;
|
|
||||||
BOOL found_digit = FALSE;
|
|
||||||
|
|
||||||
if(err)
|
nch = get(ctx);
|
||||||
*err = 0;
|
if(nch == '-') {
|
||||||
else if(!MSVCRT_CHECK_PMT(str != NULL)) {
|
found_sign = TRUE;
|
||||||
if (end)
|
|
||||||
*end = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!locale)
|
|
||||||
locinfo = get_locinfo();
|
|
||||||
else
|
|
||||||
locinfo = locale->locinfo;
|
|
||||||
|
|
||||||
p = str;
|
|
||||||
while(MSVCRT__isspace_l((unsigned char)*p, locale))
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if(*p == '-') {
|
|
||||||
sign = -1;
|
sign = -1;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
} else if(*p == '+')
|
} else if(nch == '+') {
|
||||||
p++;
|
found_sign = TRUE;
|
||||||
|
nch = get(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
#if _MSVCR_VER >= 140
|
#if _MSVCR_VER >= 140
|
||||||
if(MSVCRT__tolower_l(p[0], locale) == 'i' && MSVCRT__tolower_l(p[1], locale) == 'n'
|
if(nch == _infinity[0] || nch == MSVCRT__toupper(_infinity[0]))
|
||||||
&& MSVCRT__tolower_l(p[2], locale) == 'f') {
|
str_match = _infinity;
|
||||||
if(end)
|
if(nch == _nan[0] || nch == MSVCRT__toupper(_nan[0]))
|
||||||
*end = (char*) &p[3];
|
str_match = _nan;
|
||||||
if(MSVCRT__tolower_l(p[3], locale) == 'i' && MSVCRT__tolower_l(p[4], locale) == 'n'
|
while(str_match && nch != MSVCRT_WEOF &&
|
||||||
&& MSVCRT__tolower_l(p[5], locale) == 'i' && MSVCRT__tolower_l(p[6], locale) == 't'
|
(nch == str_match[matched] || nch == MSVCRT__toupper(str_match[matched]))) {
|
||||||
&& MSVCRT__tolower_l(p[7], locale) == 'y' && end)
|
nch = get(ctx);
|
||||||
*end = (char*) &p[8];
|
matched++;
|
||||||
return sign*INFINITY;
|
|
||||||
}
|
}
|
||||||
if(MSVCRT__tolower_l(p[0], locale) == 'n' &&
|
if(str_match) {
|
||||||
MSVCRT__tolower_l(p[1], locale) == 'a' &&
|
int keep = 0;
|
||||||
MSVCRT__tolower_l(p[2], locale) == 'n') {
|
if(matched >= 8) keep = 8;
|
||||||
if(end)
|
else if(matched >= 3) keep = 3;
|
||||||
*end = (char*) &p[3];
|
if(nch != MSVCRT_WEOF) unget(ctx);
|
||||||
return NAN;
|
for (; matched > keep; matched--) {
|
||||||
|
unget(ctx);
|
||||||
|
}
|
||||||
|
if(keep) {
|
||||||
|
if (str_match == _infinity) return sign*INFINITY;
|
||||||
|
if (str_match == _nan) return sign*NAN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
|
if(nch == '0') {
|
||||||
p += 2;
|
nch = get(ctx);
|
||||||
return strtod16(sign, p, end, locinfo, err);
|
if(nch == 'x' || nch == 'X')
|
||||||
|
return strtod16(get, unget, ctx, sign, locinfo, err);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while(*p>='0' && *p<='9') {
|
while(nch>='0' && nch<='9') {
|
||||||
found_digit = TRUE;
|
found_digit = TRUE;
|
||||||
hlp = d * 10 + *p++ - '0';
|
hlp = d * 10 + nch - '0';
|
||||||
|
nch = get(ctx);
|
||||||
if(d>MSVCRT_UI64_MAX/10 || hlp<d) {
|
if(d>MSVCRT_UI64_MAX/10 || hlp<d) {
|
||||||
exp++;
|
exp++;
|
||||||
break;
|
break;
|
||||||
} else
|
} else
|
||||||
d = hlp;
|
d = hlp;
|
||||||
}
|
}
|
||||||
while(*p>='0' && *p<='9') {
|
while(nch>='0' && nch<='9') {
|
||||||
exp++;
|
exp++;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*p == *locinfo->lconv->decimal_point)
|
if(nch == *locinfo->lconv->decimal_point) {
|
||||||
p++;
|
found_dp = TRUE;
|
||||||
|
nch = get(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
while(*p>='0' && *p<='9') {
|
while(nch>='0' && nch<='9') {
|
||||||
found_digit = TRUE;
|
found_digit = TRUE;
|
||||||
hlp = d * 10 + *p++ - '0';
|
hlp = d * 10 + nch - '0';
|
||||||
|
nch = get(ctx);
|
||||||
if(d>MSVCRT_UI64_MAX/10 || hlp<d)
|
if(d>MSVCRT_UI64_MAX/10 || hlp<d)
|
||||||
break;
|
break;
|
||||||
d = hlp;
|
d = hlp;
|
||||||
exp--;
|
exp--;
|
||||||
}
|
}
|
||||||
while(*p>='0' && *p<='9')
|
while(nch>='0' && nch<='9')
|
||||||
p++;
|
nch = get(ctx);
|
||||||
|
|
||||||
if(!found_digit) {
|
if(!found_digit) {
|
||||||
if(end)
|
if(nch != MSVCRT_WEOF) unget(ctx);
|
||||||
*end = (char*)str;
|
if(found_dp) unget(ctx);
|
||||||
|
if(found_sign) unget(ctx);
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*p=='e' || *p=='E' || *p=='d' || *p=='D') {
|
if(nch=='e' || nch=='E' || nch=='d' || nch=='D') {
|
||||||
int e=0, s=1;
|
int e=0, s=1;
|
||||||
|
|
||||||
p++;
|
nch = get(ctx);
|
||||||
if(*p == '-') {
|
if(nch == '-') {
|
||||||
|
found_sign = TRUE;
|
||||||
s = -1;
|
s = -1;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
} else if(*p == '+')
|
} else if(nch == '+') {
|
||||||
p++;
|
found_sign = TRUE;
|
||||||
|
nch = get(ctx);
|
||||||
|
} else {
|
||||||
|
found_sign = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if(*p>='0' && *p<='9') {
|
if(nch>='0' && nch<='9') {
|
||||||
while(*p>='0' && *p<='9') {
|
while(nch>='0' && nch<='9') {
|
||||||
if(e>INT_MAX/10 || (e=e*10+*p-'0')<0)
|
if(e>INT_MAX/10 || (e=e*10+nch-'0')<0)
|
||||||
e = INT_MAX;
|
e = INT_MAX;
|
||||||
p++;
|
nch = get(ctx);
|
||||||
}
|
}
|
||||||
e *= s;
|
e *= s;
|
||||||
|
|
||||||
|
@ -762,15 +782,12 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale
|
||||||
else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
|
else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
|
||||||
else exp += e;
|
else exp += e;
|
||||||
} else {
|
} else {
|
||||||
if(*p=='-' || *p=='+')
|
if(nch != MSVCRT_WEOF) unget(ctx);
|
||||||
p--;
|
if(found_sign) unget(ctx);
|
||||||
p--;
|
unget(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(end)
|
|
||||||
*end = (char*)p;
|
|
||||||
|
|
||||||
if(!err) err = MSVCRT__errno();
|
if(!err) err = MSVCRT__errno();
|
||||||
if(!d) return make_double(sign, exp, d, ROUND_ZERO, err);
|
if(!d) return make_double(sign, exp, d, ROUND_ZERO, err);
|
||||||
if(exp > MSVCRT_DBL_MAX_10_EXP)
|
if(exp > MSVCRT_DBL_MAX_10_EXP)
|
||||||
|
@ -783,6 +800,46 @@ static double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale
|
||||||
return convert_e10_to_e2(sign, exp, d, err);
|
return convert_e10_to_e2(sign, exp, d, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MSVCRT_wchar_t strtod_str_get(void *ctx)
|
||||||
|
{
|
||||||
|
const char **p = ctx;
|
||||||
|
if (!**p) return MSVCRT_WEOF;
|
||||||
|
return *(*p)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strtod_str_unget(void *ctx)
|
||||||
|
{
|
||||||
|
const char **p = ctx;
|
||||||
|
(*p)--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double strtod_helper(const char *str, char **end, MSVCRT__locale_t locale, int *err)
|
||||||
|
{
|
||||||
|
MSVCRT_pthreadlocinfo locinfo;
|
||||||
|
const char *beg, *p;
|
||||||
|
double ret;
|
||||||
|
|
||||||
|
if (err) *err = 0;
|
||||||
|
if (!MSVCRT_CHECK_PMT(str != NULL)) {
|
||||||
|
if (end) *end = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!locale)
|
||||||
|
locinfo = get_locinfo();
|
||||||
|
else
|
||||||
|
locinfo = locale->locinfo;
|
||||||
|
|
||||||
|
p = str;
|
||||||
|
while(MSVCRT__isspace_l((unsigned char)*p, locale))
|
||||||
|
p++;
|
||||||
|
beg = p;
|
||||||
|
|
||||||
|
ret = parse_double(strtod_str_get, strtod_str_unget, &p, locinfo, err);
|
||||||
|
if (end) *end = (p == beg ? (char*)str : (char*)p);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
* strtod_l (MSVCRT.@)
|
* strtod_l (MSVCRT.@)
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue