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:
Erich E. Hoover 2019-12-31 11:19:07 -07:00 committed by Alexandre Julliard
parent 34925faa7d
commit b12d6d405a
2 changed files with 160 additions and 101 deletions

View File

@ -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)

View File

@ -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.@)
*/ */