eina: add locale-independent eina_convert_strtod_c function
authorYoungbok Shin <youngb.shin@samsung.com>
Wed, 22 Aug 2018 01:53:11 +0000 (01:53 +0000)
committerHermet Park <hermetpark@gmail.com>
Wed, 5 Dec 2018 05:51:19 +0000 (14:51 +0900)
strtod's behavior is changed by system locale.
http://man7.org/linux/man-pages/man3/strtod.3.html
https://en.wikipedia.org/wiki/Decimal_separator

Because of this, strtod(0.5) returns 0.0 in some locales.
When a given value string is locale-independent, strtod has to be
replaced to eina_convert_strtod_c function.
Internally, it calls strtod_l function with "C" locale.

@feature

Reviewed-by: Cedric BAIL <cedric.bail@free.fr>
Reviewed-by: Vincent Torri <vincent.torri@gmail.com>
Differential Revision: https://phab.enlightenment.org/D6644

src/lib/eina/eina_convert.c
src/lib/eina/eina_convert.h
src/lib/eina/eina_main.c
src/lib/eina/eina_private.h
src/tests/eina/eina_test_convert.c

index 2943b37..152ef5b 100644 (file)
@@ -453,6 +453,16 @@ eina_convert_atofp(const char *src, int length, Eina_F32p32 *fp)
    return EINA_TRUE;
 }
 
+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
+}
+
 /**
  * @}
  */
index 026fc6c..337cd88 100644 (file)
@@ -334,6 +334,22 @@ EAPI Eina_Bool eina_convert_atofp(const char  *src,
                                   Eina_F32p32 *fp) EINA_ARG_NONNULL(1, 3);
 
 /**
+ * @brief Converts a string to a floating point number.
+ *
+ * @param[in] nptr a string to convert. It shouldn't be NULL.
+ * @param[out] endptr If endptr is not NULL, a pointer to the character after the last
+ *                    character used in the conversion is stored in the location referenced
+ *                    by endptr.
+ * @return a double type floating point number.
+ *
+ * This function returns converted floating point number with locale-independency.
+ * Actually, it use "C" locale for strtod_l function internally. If you need strtod
+ * without locale-dependency, this function can replace strtod.
+ * For more information, please refer documents of strtod, strtod_l.
+ */
+EAPI double eina_convert_strtod_c(const char *nptr, char **endptr);
+
+/**
  * @}
  */
 
index 024d1e3..535e4e7 100644 (file)
@@ -85,6 +85,7 @@ 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
@@ -285,6 +286,12 @@ 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();
@@ -348,6 +355,12 @@ eina_init(void)
    return 1;
 }
 
+locale_t
+_eina_c_locale_get(void)
+{
+   return _eina_c_locale;
+}
+
 EAPI int
 eina_shutdown(void)
 {
@@ -359,6 +372,12 @@ 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 eab5958..be826e0 100644 (file)
@@ -20,6 +20,7 @@
 #define EINA_PRIVATE_H_
 
 #include <stdarg.h>
+#include <locale.h>
 
 #ifdef _WIN32
 # include <Evil.h>
@@ -151,6 +152,11 @@ Eina_Stringshare *eina_file_sanitize(const char *path);
 
 void eina_freeq_main_set(Eina_FreeQ *fq);
 
+#ifdef _WIN32
+typedef _locale_t locale_t;
+#endif
+locale_t _eina_c_locale_get(void);
+
 #include "eina_inline_private.h"
 
 #endif /* EINA_PRIVATE_H_ */
index 1b08a00..c8ccfcb 100644 (file)
@@ -160,10 +160,31 @@ _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)
+{
+   double result = eina_convert_strtod_c(str, NULL);
+
+   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);
+}
+EFL_END_TEST
+
 void
 eina_test_convert(TCase *tc)
 {
    tcase_add_test(tc, eina_convert_simple);
    tcase_add_test(tc, eina_convert_double);
    tcase_add_test(tc,     eina_convert_fp);
+   tcase_add_test(tc, eina_convert_strtod_c_simple);
 }