From 8a7cb97e02bf0448946d4a2ddd355d394e9d8804 Mon Sep 17 00:00:00 2001 From: Vincent Torri Date: Thu, 28 Feb 2019 13:21:56 +0100 Subject: [PATCH] Eina: implement strtod in C locale and remove linkl against msvcr100. 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 | 2 +- src/lib/eina/eina_convert.c | 210 ++++++++++++++++++++++++++++- src/lib/eina/eina_main.c | 19 --- src/lib/evil/meson.build | 5 +- src/tests/eina/eina_test_convert.c | 95 +++++++++++-- 5 files changed, 293 insertions(+), 38 deletions(-) diff --git a/configure.ac b/configure.ac index 07ca867a85..212b0d6dea 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/lib/eina/eina_convert.c b/src/lib/eina/eina_convert.c index 152ef5be4a..bd2d3c2479 100644 --- a/src/lib/eina/eina_convert.c +++ b/src/lib/eina/eina_convert.c @@ -14,6 +14,10 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; * if not, see . + * + * 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 #include #include +#include +#include #ifdef _WIN32 # include @@ -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; } /** diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c index 535e4e7611..024d1e3eaa 100644 --- a/src/lib/eina/eina_main.c +++ b/src/lib/eina/eina_main.c @@ -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); diff --git a/src/lib/evil/meson.build b/src/lib/evil/meson.build index ff9630aa2b..482e733637 100644 --- a/src/lib/evil/meson.build +++ b/src/lib/evil/meson.build @@ -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 diff --git a/src/tests/eina/eina_test_convert.c b/src/tests/eina/eina_test_convert.c index c8ccfcb772..be8aea3942 100644 --- a/src/tests/eina/eina_test_convert.c +++ b/src/tests/eina/eina_test_convert.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -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