Eina: implement strtod in C locale and remove linkl against msvcr100.
authorVincent Torri <vincent.torri@gmail.com>
Thu, 28 Feb 2019 12:21:56 +0000 (13:21 +0100)
committerWonki Kim <wonki_.kim@samsung.com>
Fri, 8 Mar 2019 11:49:36 +0000 (20:49 +0900)
Summary:
This fixes compilation on Windows

More precisely edje_cc could not compile emotion edc files, so it was a runtime problem
because of msvcr100 link.

Add more tests than before

Test Plan: compilation

Reviewers: raster

Subscribers: zmike, stefan_schmidt, cedric, #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D7926

configure.ac
src/lib/eina/eina_convert.c
src/lib/eina/eina_main.c
src/lib/evil/meson.build
src/tests/eina/eina_test_convert.c

index 655333a..5c7f8cf 100755 (executable)
@@ -804,7 +804,7 @@ EFL_SELECT_WINDOWS_VERSION
 
 ### Checks for libraries
 
-EFL_ADD_LIBS([EVIL], [-lpsapi -lole32 -lws2_32 -lsecur32 -luuid -lmsvcr100])
+EFL_ADD_LIBS([EVIL], [-lpsapi -lole32 -lws2_32 -lsecur32 -luuid])
 
 ### Checks for header files
 
index 152ef5b..bd2d3c2 100644 (file)
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library;
  * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The code of eina_convert_strtod_c() is based on code published
+ * under the public domain license, which can be found here:
+ * https://gist.github.com/mattn/1890186
  */
 
 #ifdef HAVE_CONFIG_H
@@ -24,6 +28,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
 
 #ifdef _WIN32
 # include <Evil.h>
@@ -453,14 +459,208 @@ eina_convert_atofp(const char *src, int length, Eina_F32p32 *fp)
    return EINA_TRUE;
 }
 
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtod-strtod-l-wcstod-wcstod-l?view=vs-2017
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
 EAPI double
 eina_convert_strtod_c(const char *nptr, char **endptr)
 {
-#ifdef _WIN32
-   return _strtod_l(nptr, endptr, _eina_c_locale_get());
-#else
-   return strtod_l(nptr, endptr, _eina_c_locale_get());
-#endif
+   const char *iter;
+   const char *a;
+   double val;
+   unsigned long long integer_part;
+   int minus;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(nptr, 0.0);
+
+   a = iter = nptr;
+
+   /* ignore leading whitespaces */
+   while (isspace(*iter))
+     iter++;
+
+   /* signed or not */
+   minus = 1;
+   if (*iter == '-')
+     {
+        minus = -1;
+        iter++;
+     }
+   else if (*iter == '+')
+     iter++;
+
+   if (tolower(*iter) == 'i')
+     {
+        if ((tolower(*(iter + 1)) == 'n') &&
+            (tolower(*(iter + 2)) == 'f'))
+          iter += 3;
+        else
+          goto on_error;
+        if (tolower(*(iter + 3)) == 'i')
+          {
+             if ((tolower(*(iter + 4)) == 'n') &&
+                 (tolower(*(iter + 5)) == 'i') &&
+                 (tolower(*(iter + 6)) == 't') &&
+                 (tolower(*(iter + 7)) == 'y'))
+               iter += 5;
+             else
+               goto on_error;
+          }
+        if (endptr)
+          *endptr = (char *)iter;
+        return (minus == -1) ? -INFINITY : INFINITY;
+     }
+
+   if (tolower(*iter) == 'n')
+     {
+        if ((tolower(*(iter + 1)) == 'a') &&
+            (tolower(*(iter + 2)) == 'n'))
+          iter += 3;
+        else
+          goto on_error;
+        if (endptr)
+          *endptr = (char *)iter;
+        return (minus == -1) ? -NAN : NAN;
+     }
+
+   integer_part = 0;
+
+   /* (optional) integer part before dot */
+   if (isdigit(*iter))
+     {
+        for (; isdigit(*iter); iter++)
+          integer_part = integer_part * 10ULL + (unsigned long long)(*iter - '0');
+        a = iter;
+     }
+   else if (*iter != '.')
+     {
+        val = 0.0;
+        goto on_success;
+     }
+
+   val = (double)integer_part;
+
+   /* (optional) decimal part after dot */
+   if (*iter == '.')
+     {
+        unsigned long long decimal_part;
+        unsigned long long pow10;
+        int count;
+
+        iter++;
+
+        decimal_part = 0;
+        count = 0;
+        pow10 = 1;
+
+        if (isdigit(*iter))
+          {
+             for (; isdigit(*iter); iter++, count++)
+               {
+                  if (count < 19)
+                    {
+                       decimal_part = decimal_part * 10ULL +  + (unsigned long long)(*iter - '0');
+                       pow10 *= 10ULL;
+                    }
+               }
+          }
+        val += (double)decimal_part / (double)pow10;
+        a = iter;
+     }
+
+   /* (optional) exponent */
+   if ((*iter == 'e') || (*iter == 'E'))
+     {
+        double scale = 1.0;
+        unsigned int expo_part;
+        int minus_e;
+
+        iter++;
+
+        /* signed or not */
+        minus_e = 1;
+        if (*iter == '-')
+          {
+             minus_e = -1;
+             iter++;
+          }
+        else if (*iter == '+')
+          iter++;
+
+        /* exponential part */
+        expo_part = 0;
+        if (isdigit(*iter))
+          {
+            while (*iter == 0)
+              iter++;
+
+            for (; isdigit(*iter); iter++)
+              {
+                expo_part = expo_part * 10U + (unsigned int)(*iter - '0');
+              }
+          }
+        else if (!isdigit(*(a - 1)))
+          {
+             a = nptr;
+             goto on_success;
+          }
+        else if (*iter == 0)
+          goto on_success;
+
+        if ((val == 2.2250738585072011) && ((minus_e * (int)expo_part) == -308))
+          {
+            val *= 1.0e-308;
+            a = iter;
+            errno = ERANGE;
+            goto on_success;
+          }
+
+        if ((val == 2.2250738585072012) && ((minus_e * (int)expo_part) <= -308))
+          {
+            val *= 1.0e-308;
+            a = iter;
+            goto on_success;
+          }
+
+        a = iter;
+
+        while (expo_part >= 8U)
+          {
+            scale *= 1E8;
+            expo_part -= 8U;
+          }
+        while (expo_part > 0U)
+          {
+            scale *= 10.0;
+            expo_part--;
+          }
+
+        val = (minus_e == -1) ? (val / scale) : (val * scale);
+     }
+   else if ((iter > nptr) && !isdigit(*(iter - 1)))
+     {
+       a = nptr;
+       goto on_success;
+     }
+
+ on_success:
+   if (endptr)
+     *endptr = (char *)a;
+   return minus * val;
+
+ on_error:
+   if (endptr)
+     *endptr = (char *)nptr;
+   return 0.0;
 }
 
 /**
index 535e4e7..024d1e3 100644 (file)
@@ -85,7 +85,6 @@ static int _eina_main_count = 0;
 static int _eina_main_thread_count = 0;
 #endif
 static int _eina_log_dom = -1;
-static locale_t _eina_c_locale;
 
 #ifdef ERR
 #undef ERR
@@ -286,12 +285,6 @@ eina_init(void)
    if (EINA_LIKELY(_eina_main_count > 0))
       return ++_eina_main_count;
 
-#ifdef _WIN32
-   _eina_c_locale = _create_locale(LC_ALL, "C");
-#else
-   _eina_c_locale = newlocale(LC_ALL_MASK, "C", NULL);
-#endif
-
    srand(time(NULL));
    while (eina_seed == 0)
      eina_seed = rand();
@@ -355,12 +348,6 @@ eina_init(void)
    return 1;
 }
 
-locale_t
-_eina_c_locale_get(void)
-{
-   return _eina_c_locale;
-}
-
 EAPI int
 eina_shutdown(void)
 {
@@ -372,12 +359,6 @@ eina_shutdown(void)
    _eina_main_count--;
    if (EINA_UNLIKELY(_eina_main_count == 0))
      {
-#ifdef _WIN32
-        _free_locale(_eina_c_locale);
-#else
-        freelocale(_eina_c_locale);
-#endif
-
         eina_log_timing(_eina_log_dom,
                         EINA_LOG_STATE_START,
                         EINA_LOG_STATE_SHUTDOWN);
index ff9630a..482e733 100644 (file)
@@ -51,16 +51,15 @@ if target_machine.system() == 'windows'
   ws2_32 = cc.find_library('ws2_32')
   secur32 = cc.find_library('secur32')
   uuid = cc.find_library('uuid')
-  msvcr100 = cc.find_library('msvcr100')
 
   evil_lib = library('evil', evil_src,
-    dependencies : [psapi, ole32, ws2_32, secur32, uuid, msvcr100],
+    dependencies : [psapi, ole32, ws2_32, secur32, uuid],
     include_directories : [config_dir, include_directories('regex')],
   )
 
   evil = declare_dependency(
     include_directories: [config_dir, include_directories('regex'), include_directories('.')],
-    dependencies : [psapi, ole32, ws2_32, secur32, uuid, msvcr100],
+    dependencies : [psapi, ole32, ws2_32, secur32, uuid],
     link_with: evil_lib,
   )
 else
index c8ccfcb..be8aea3 100644 (file)
@@ -25,6 +25,7 @@
 #include <math.h>
 #include <float.h>
 #include <limits.h>
+#include <locale.h>
 
 #include <Eina.h>
 
@@ -161,22 +162,96 @@ _eina_convert_fp_check(double d, Eina_F32p32 fp, int length)
 EFL_END_TEST
 
 static void
-_eina_convert_strtod_c_check(const char *str, double expected_result)
+_eina_convert_strtod_c_check(const char *str)
 {
-   double result = eina_convert_strtod_c(str, NULL);
+   double d1;
+   double d2;
+   char *e1;
+   char *e2;
+
+   e1 = NULL;
+   d1 = eina_convert_strtod_c(str, &e1);
+
+   e2 = NULL;
+   d2 = strtod(str, &e2);
+
+   switch(fpclassify(d2))
+     {
+     case FP_NAN:
+       fail_if(fpclassify(d1) != FP_NAN);
+       break;
+     case FP_INFINITE:
+       fail_if(fpclassify(d1) != FP_INFINITE);
+       break;
+     default:
+       fail_if((fpclassify(d1) != FP_ZERO) &&
+               (fpclassify(d1) != FP_SUBNORMAL) &&
+               (fpclassify(d1) != FP_NORMAL));
+       if (!EINA_DBL_EQ(d1,d2) || (e1 != e2))
+         {
+            printf("  FP_NORMAL\n");
+            printf("  ERR: %s, %s\n", str, strerror(errno));
+            printf("    E1 **%.6f**, **%g**, %s\n", d1, d1, e1 ? e1 : "");
+            printf("    E2 **%.6f**, **%g**, %s\n", d2, d2, e2 ? e2 : "");
+            if (!EINA_DBL_EQ(d1,d2)) printf("different value\n");
+            if (e1 != e2) printf("different end position\n");
+         }
+
+       fail_if(!EINA_DBL_EQ(d1,d2) || (e1 != e2));
+       break;
+     }
 
-   fail_if(result != expected_result);
 }
 
 EFL_START_TEST(eina_convert_strtod_c_simple)
 {
-   _eina_convert_strtod_c_check("0.0", 0.0);
-   _eina_convert_strtod_c_check("0.5", 0.5);
-   _eina_convert_strtod_c_check("1.0", 1.0);
-   _eina_convert_strtod_c_check("-0.5", -0.5);
-   _eina_convert_strtod_c_check("-1.0", -1.0);
-   _eina_convert_strtod_c_check("3.45e-2", 0.0345);
-   _eina_convert_strtod_c_check("3.45e+2", 345.0);
+  char *old;
+
+   old = setlocale(LC_ALL, "C");
+   _eina_convert_strtod_c_check("0");
+   _eina_convert_strtod_c_check("-0");
+   _eina_convert_strtod_c_check(".1");
+   _eina_convert_strtod_c_check("  .");
+   _eina_convert_strtod_c_check("  1.2e3");
+   _eina_convert_strtod_c_check(" +1.2e3");
+   _eina_convert_strtod_c_check("1.2e3");
+   _eina_convert_strtod_c_check("+1.2e3");
+   _eina_convert_strtod_c_check("+1.e3");
+   _eina_convert_strtod_c_check("-1.2e3");
+   _eina_convert_strtod_c_check("-1.2e3.5");
+   _eina_convert_strtod_c_check("-1.2e");
+   _eina_convert_strtod_c_check("--1.2e3.5");
+   _eina_convert_strtod_c_check("--1-.2e3.5");
+   _eina_convert_strtod_c_check("-a");
+   _eina_convert_strtod_c_check("a");
+   _eina_convert_strtod_c_check(".1e");
+   _eina_convert_strtod_c_check(".1e0");
+   _eina_convert_strtod_c_check(".1e3");
+   _eina_convert_strtod_c_check(".1e-3");
+   _eina_convert_strtod_c_check(".1e-");
+   _eina_convert_strtod_c_check(" .e-");
+   _eina_convert_strtod_c_check(" .e");
+   _eina_convert_strtod_c_check(" e");
+   _eina_convert_strtod_c_check(" e0");
+   _eina_convert_strtod_c_check(" ee");
+   _eina_convert_strtod_c_check(" -e");
+   _eina_convert_strtod_c_check(" .9");
+   _eina_convert_strtod_c_check(" ..9");
+   _eina_convert_strtod_c_check("009");
+   _eina_convert_strtod_c_check("0.09e02");
+    /* http://thread.gmane.org/gmane.editors.vim.devel/19268/ */
+   _eina_convert_strtod_c_check("0.9999999999999999999999999999999999");
+   _eina_convert_strtod_c_check("2.2250738585072010e-308"); // BUG
+    /* PHP (slashdot.jp): http://opensource.slashdot.jp/story/11/01/08/0527259/PHP%E3%81%AE%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E5%87%A6%E7%90%86%E3%81%AB%E7%84%A1%E9%99%90%E3%83%AB%E3%83%BC%E3%83%97%E3%81%AE%E3%83%90%E3%82%B0 */
+   _eina_convert_strtod_c_check("2.2250738585072011e-308");
+    /* Gauche: http://blog.practical-scheme.net/gauche/20110203-bitten-by-floating-point-numbers-again */
+   _eina_convert_strtod_c_check("2.2250738585072012e-308");
+   _eina_convert_strtod_c_check("2.2250738585072013e-308");
+   _eina_convert_strtod_c_check("2.2250738585072014e-308");
+   _eina_convert_strtod_c_check(" NaNfoo");
+   _eina_convert_strtod_c_check(" -INFfoo");
+   _eina_convert_strtod_c_check("  InFiNiTyfoo");
+   setlocale(LC_ALL, old);
 }
 EFL_END_TEST