[PATCH v2] webservices: Use sscanf to convert strings to doubles.

Hans Leidekker hans at codeweavers.com
Tue Jan 7 05:11:25 CST 2020


From: "Erich E. Hoover" <erich.e.hoover at gmail.com>

v2: Cache the C locale object.

Signed-off-by: Erich E. Hoover <erich.e.hoover at gmail.com>
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/webservices/listener.c            |   5 ++
 dlls/webservices/reader.c              | 105 +++++++------------------
 dlls/webservices/tests/reader.c        |   3 +
 dlls/webservices/webservices_private.h |   2 +
 include/msvcrt/stdio.h                 |   1 +
 5 files changed, 39 insertions(+), 77 deletions(-)

diff --git a/dlls/webservices/listener.c b/dlls/webservices/listener.c
index 6801465f84..9b32369fd4 100644
--- a/dlls/webservices/listener.c
+++ b/dlls/webservices/listener.c
@@ -17,6 +17,7 @@
  */
 
 #include <stdarg.h>
+#include <locale.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -49,6 +50,8 @@ void winsock_init(void)
     InitOnceExecuteOnce( &once, winsock_startup, NULL, NULL );
 }
 
+_locale_t c_locale;
+
 /******************************************************************
  *      DllMain (webservices.@)
  */
@@ -59,11 +62,13 @@ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, void *reserved )
     case DLL_PROCESS_ATTACH:
         webservices_instance = hinst;
         DisableThreadLibraryCalls( hinst );
+        c_locale = _create_locale( LC_ALL, "C" );
         break;
 
     case DLL_PROCESS_DETACH:
         if (reserved) break;
         if (winsock_loaded) WSACleanup();
+        _free_locale( c_locale );
         break;
     }
     return TRUE;
diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c
index 181739650e..b328bb779e 100644
--- a/dlls/webservices/reader.c
+++ b/dlls/webservices/reader.c
@@ -19,6 +19,7 @@
 #include <stdarg.h>
 #include <assert.h>
 #include <float.h>
+#include <locale.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -3726,15 +3727,13 @@ static HRESULT str_to_uint64( const unsigned char *str, ULONG len, UINT64 max, U
 
 static HRESULT str_to_double( const unsigned char *str, ULONG len, double *ret )
 {
+    BOOL found_sign = FALSE, found_exponent = FALSE, found_digit = FALSE, found_decimal = FALSE;
     static const unsigned __int64 nan = 0xfff8000000000000;
     static const unsigned __int64 inf = 0x7ff0000000000000;
     static const unsigned __int64 inf_min = 0xfff0000000000000;
-    HRESULT hr = WS_E_INVALID_FORMAT;
-    const unsigned char *p = str, *q;
-    int sign = 1, exp_sign = 1, exp = 0, exp_tmp = 0, neg_exp, i, nb_digits, have_digits;
-    unsigned __int64 val = 0, tmp;
-    long double exp_val = 1.0, exp_mul = 10.0;
-    unsigned int fpword = _control87( 0, 0 );
+    const char *p = (const char *)str;
+    double tmp;
+    ULONG i;
 
     while (len && read_isspace( *p )) { p++; len--; }
     while (len && read_isspace( p[len - 1] )) { len--; }
@@ -3756,88 +3755,40 @@ static HRESULT str_to_double( const unsigned char *str, ULONG len, double *ret )
         return S_OK;
     }
 
-    *ret = 0.0;
-    if (*p == '-')
-    {
-        sign = -1;
-        p++; len--;
-    }
-    else if (*p == '+') { p++; len--; };
-    if (!len) return S_OK;
-
-    _control87( _MCW_EM | _RC_NEAR | _PC_64, _MCW_EM | _MCW_RC | _MCW_PC );
-
-    q = p;
-    while (len && isdigit( *q )) { q++; len--; }
-    have_digits = nb_digits = q - p;
-    for (i = 0; i < nb_digits; i++)
+    for (i = 0; i < len; i++)
     {
-        tmp = val * 10 + p[i] - '0';
-        if (val > MAX_UINT64 / 10 || tmp < val)
+        if (p[i] >= '0' && p[i] <= '9')
         {
-            for (; i < nb_digits; i++) exp++;
-            break;
+            found_digit = TRUE;
+            continue;
         }
-        val = tmp;
-    }
-
-    if (len)
-    {
-        if (*q == '.')
+        if (!found_sign && !found_digit && (p[i] == '+' || p[i] == '-'))
         {
-            p = ++q; len--;
-            while (len && isdigit( *q )) { q++; len--; };
-            have_digits |= nb_digits = q - p;
-            for (i = 0; i < nb_digits; i++)
-            {
-                tmp = val * 10 + p[i] - '0';
-                if (val > MAX_UINT64 / 10 || tmp < val) break;
-                val = tmp;
-                exp--;
-            }
+            found_sign = TRUE;
+            continue;
         }
-        if (len > 1 && tolower(*q) == 'e')
+        if (!found_exponent && found_digit && (p[i] == 'e' || p[i] == 'E'))
         {
-            if (!have_digits) goto done;
-            p = ++q; len--;
-            if (*p == '-')
-            {
-                exp_sign = -1;
-                p++; len--;
-            }
-            else if (*p == '+') { p++; len--; };
-
-            q = p;
-            while (len && isdigit( *q )) { q++; len--; };
-            nb_digits = q - p;
-            if (!nb_digits || len) goto done;
-            for (i = 0; i < nb_digits; i++)
-            {
-                if (exp_tmp > MAX_INT32 / 10 || (exp_tmp = exp_tmp * 10 + p[i] - '0') < 0)
-                    exp_tmp = MAX_INT32;
-            }
-            exp_tmp *= exp_sign;
-
-            if (exp < 0 && exp_tmp < 0 && exp + exp_tmp >= 0) exp = MIN_INT32;
-            else if (exp > 0 && exp_tmp > 0 && exp + exp_tmp < 0) exp = MAX_INT32;
-            else exp += exp_tmp;
+            found_exponent = found_decimal = TRUE;
+            found_digit = found_sign = FALSE;
+            continue;
+        }
+        if (!found_decimal && p[i] == '.')
+        {
+            found_decimal = TRUE;
+            continue;
         }
+        return WS_E_INVALID_FORMAT;
     }
-    if (!have_digits || len) goto done;
-
-    if ((neg_exp = exp < 0)) exp = -exp;
-    for (; exp; exp >>= 1)
+    if (!found_digit && !found_exponent)
     {
-        if (exp & 1) exp_val *= exp_mul;
-        exp_mul *= exp_mul;
+        *ret = 0;
+        return S_OK;
     }
 
-    *ret = sign * (neg_exp ? val / exp_val : val * exp_val);
-    hr = S_OK;
-
-done:
-    _control87( fpword, _MCW_EM | _MCW_RC | _MCW_PC );
-    return hr;
+    if (_snscanf_l( p, len, "%lf", c_locale, &tmp ) != 1) return WS_E_INVALID_FORMAT;
+    *ret = tmp;
+    return S_OK;
 }
 
 static HRESULT str_to_float( const unsigned char *str, ULONG len, float *ret )
diff --git a/dlls/webservices/tests/reader.c b/dlls/webservices/tests/reader.c
index 70623052de..96eee2cf77 100644
--- a/dlls/webservices/tests/reader.c
+++ b/dlls/webservices/tests/reader.c
@@ -3650,7 +3650,10 @@ static void test_double(void)
         {"<t>-0.0</t>", S_OK, 0x8000000000000000},
         {"<t>+0.0</t>", S_OK, 0},
         {"<t>-</t>", S_OK, 0},
+        {"<t>-.</t>", S_OK, 0},
         {"<t>+</t>", S_OK, 0},
+        {"<t>+.</t>", S_OK, 0},
+        {"<t>.</t>", S_OK, 0},
         {"<t>.0</t>", S_OK, 0},
         {"<t>0.</t>", S_OK, 0},
         {"<t>0</t>", S_OK, 0},
diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h
index b12032060f..6f2b90dad2 100644
--- a/dlls/webservices/webservices_private.h
+++ b/dlls/webservices/webservices_private.h
@@ -18,6 +18,8 @@
 
 #include "winhttp.h"
 
+_locale_t c_locale DECLSPEC_HIDDEN;
+
 #define STREAM_BUFSIZE 4096
 
 struct xmlbuf
diff --git a/include/msvcrt/stdio.h b/include/msvcrt/stdio.h
index 6d0aaeaa6c..fa81f3d536 100644
--- a/include/msvcrt/stdio.h
+++ b/include/msvcrt/stdio.h
@@ -191,6 +191,7 @@ int    WINAPIV sprintf_s(char*,size_t,const char*,...);
 int    WINAPIV _scprintf(const char *, ...);
 int    WINAPIV sscanf(const char*,const char*,...);
 int    WINAPIV sscanf_s(const char*,const char*,...);
+int    WINAPIV _snscanf_l(const char*,size_t,const char*,_locale_t,...);
 FILE*  __cdecl tmpfile(void);
 char*  __cdecl tmpnam(char*);
 int    __cdecl ungetc(int,FILE*);
-- 
2.20.1




More information about the wine-devel mailing list