LCOV - code coverage report
Current view: top level - src/support - strtod.c (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 7 9 77.8 %
Date: 2022-07-17 01:01:28 Functions: 2 3 66.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 2 2 100.0 %

           Branch data     Line data    Source code
       1                 :            : #define _GNU_SOURCE
       2                 :            : #include "libsupport.h"
       3                 :            : #include <stdlib.h>
       4                 :            : #include <locale.h>
       5                 :            : 
       6                 :            : #if defined(__APPLE__) || defined(__FreeBSD__)
       7                 :            : #include <xlocale.h>
       8                 :            : #endif
       9                 :            : 
      10                 :            : #ifdef __cplusplus
      11                 :            : extern "C" {
      12                 :            : #endif
      13                 :            : 
      14                 :            : #if !defined(_OS_WINDOWS_)
      15                 :            : // This code path should be used for systems that support the strtod_l function
      16                 :            : 
      17                 :            : // Cache locale object
      18                 :            : static int c_locale_initialized = 0;
      19                 :            : static locale_t c_locale;
      20                 :            : 
      21                 :     381802 : locale_t get_c_locale(void)
      22                 :            : {
      23         [ +  + ]:     381802 :     if (!c_locale_initialized) {
      24                 :         30 :         c_locale_initialized = 1;
      25                 :         30 :         c_locale = newlocale(LC_ALL_MASK, "C", NULL);
      26                 :            :     }
      27                 :     381802 :     return c_locale;
      28                 :            : }
      29                 :            : 
      30                 :     381802 : JL_DLLEXPORT double jl_strtod_c(const char *nptr, char **endptr)
      31                 :            : {
      32                 :     381802 :     return strtod_l(nptr, endptr, get_c_locale());
      33                 :            : }
      34                 :            : 
      35                 :          0 : JL_DLLEXPORT float jl_strtof_c(const char *nptr, char **endptr)
      36                 :            : {
      37                 :          0 :     return strtof_l(nptr, endptr, get_c_locale());
      38                 :            : }
      39                 :            : 
      40                 :            : 
      41                 :            : #else
      42                 :            : // This code path should be used for systems that do not support the strtod_l function
      43                 :            : // Currently this is MinGW/Windows
      44                 :            : 
      45                 :            : // The following code is derived from the Python function _PyOS_ascii_strtod
      46                 :            : // see https://github.com/python/cpython/blob/master/Python/pystrtod.c
      47                 :            : //
      48                 :            : // Copyright © 2001-2020 Python Software Foundation; All Rights Reserved
      49                 :            : //
      50                 :            : // The following modifications have been made:
      51                 :            : // - Leading spaces are ignored
      52                 :            : // - Parsing of hex floats is supported in the derived version
      53                 :            : // - Python functions for tolower, isdigit and malloc have been replaced by the respective
      54                 :            : //   C stdlib functions
      55                 :            : 
      56                 :            : #include <ctype.h>
      57                 :            : #include <errno.h>
      58                 :            : 
      59                 :            : int case_insensitive_match(const char *s, const char *t)
      60                 :            : {
      61                 :            :     while (*t && tolower(*s) == *t) {
      62                 :            :         s++;
      63                 :            :         t++;
      64                 :            :     }
      65                 :            :     return *t ? 0 : 1;
      66                 :            : }
      67                 :            : 
      68                 :            : double parse_inf_or_nan(const char *p, char **endptr)
      69                 :            : {
      70                 :            :     double retval;
      71                 :            :     const char *s;
      72                 :            :     int negate = 0;
      73                 :            : 
      74                 :            :     s = p;
      75                 :            :     if (*s == '-') {
      76                 :            :         negate = 1;
      77                 :            :         s++;
      78                 :            :     }
      79                 :            :     else if (*s == '+') {
      80                 :            :         s++;
      81                 :            :     }
      82                 :            :     if (case_insensitive_match(s, "inf")) {
      83                 :            :         s += 3;
      84                 :            :         if (case_insensitive_match(s, "inity"))
      85                 :            :             s += 5;
      86                 :            :         retval = negate ? -D_PINF : D_PINF;
      87                 :            :     }
      88                 :            :     else if (case_insensitive_match(s, "nan")) {
      89                 :            :         s += 3;
      90                 :            :         retval = negate ? -D_PNAN : D_PNAN;
      91                 :            :     }
      92                 :            :     else {
      93                 :            :         s = p;
      94                 :            :         retval = -1.0;
      95                 :            :     }
      96                 :            :     *endptr = (char *)s;
      97                 :            :     return retval;
      98                 :            : }
      99                 :            : 
     100                 :            : 
     101                 :            : JL_DLLEXPORT double jl_strtod_c(const char *nptr, char **endptr)
     102                 :            : {
     103                 :            :     char *fail_pos;
     104                 :            :     double val;
     105                 :            :     struct lconv *locale_data;
     106                 :            :     const char *decimal_point;
     107                 :            :     size_t decimal_point_len;
     108                 :            :     const char *p, *decimal_point_pos;
     109                 :            :     const char *end = NULL; /* Silence gcc */
     110                 :            :     const char *digits_pos = NULL;
     111                 :            :     int negate = 0;
     112                 :            : 
     113                 :            :     fail_pos = NULL;
     114                 :            : 
     115                 :            :     locale_data = localeconv();
     116                 :            :     decimal_point = locale_data->decimal_point;
     117                 :            :     decimal_point_len = strlen(decimal_point);
     118                 :            : 
     119                 :            :     decimal_point_pos = NULL;
     120                 :            : 
     121                 :            :     p = nptr;
     122                 :            : 
     123                 :            :     /* parse leading spaces */
     124                 :            :     while (isspace((unsigned char)*p)) {
     125                 :            :         p++;
     126                 :            :     }
     127                 :            : 
     128                 :            :     /* Parse infinities and nans */
     129                 :            :     val = parse_inf_or_nan(p, endptr);
     130                 :            :     if (*endptr != p)
     131                 :            :         return val;
     132                 :            : 
     133                 :            :     /* Set errno to zero, so that we can distinguish zero results
     134                 :            :        and underflows */
     135                 :            :     errno = 0;
     136                 :            : 
     137                 :            :     /* We process the optional sign manually, then pass the remainder to
     138                 :            :        the system strtod.  This ensures that the result of an underflow
     139                 :            :        has the correct sign.  */
     140                 :            : 
     141                 :            :     /* Process leading sign, if present */
     142                 :            :     if (*p == '-') {
     143                 :            :         negate = 1;
     144                 :            :         p++;
     145                 :            :     }
     146                 :            :     else if (*p == '+') {
     147                 :            :         p++;
     148                 :            :     }
     149                 :            : 
     150                 :            :     /* This code path is used for hex floats */
     151                 :            :     if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) {
     152                 :            :         digits_pos = p;
     153                 :            :         p += 2;
     154                 :            :         /* Check that what's left begins with a digit or decimal point */
     155                 :            :         if (!isxdigit(*p) && *p != '.')
     156                 :            :             goto invalid_string;
     157                 :            : 
     158                 :            : 
     159                 :            :         if (decimal_point[0] != '.' || decimal_point[1] != 0) {
     160                 :            :             /* Look for a '.' in the input; if present, it'll need to be
     161                 :            :                swapped for the current locale's decimal point before we
     162                 :            :                call strtod.  On the other hand, if we find the current
     163                 :            :                locale's decimal point then the input is invalid. */
     164                 :            :             while (isxdigit(*p))
     165                 :            :                 p++;
     166                 :            : 
     167                 :            :             if (*p == '.') {
     168                 :            :                 decimal_point_pos = p++;
     169                 :            : 
     170                 :            :                 /* locate end of number */
     171                 :            :                 while (isxdigit(*p))
     172                 :            :                     p++;
     173                 :            : 
     174                 :            :                 if (*p == 'p' || *p == 'P')
     175                 :            :                     p++;
     176                 :            :                 if (*p == '+' || *p == '-')
     177                 :            :                     p++;
     178                 :            :                 while (isdigit(*p))
     179                 :            :                     p++;
     180                 :            :                 end = p;
     181                 :            :             }
     182                 :            :             else if (strncmp(p, decimal_point, decimal_point_len) == 0)
     183                 :            :                 goto invalid_string;
     184                 :            :             /* For the other cases, we need not convert the decimal point */
     185                 :            :         }
     186                 :            :     }
     187                 :            :     else {
     188                 :            :         /* Check that what's left begins with a digit or decimal point */
     189                 :            :         if (!isdigit(*p) && *p != '.')
     190                 :            :             goto invalid_string;
     191                 :            : 
     192                 :            :         digits_pos = p;
     193                 :            :         if (decimal_point[0] != '.' || decimal_point[1] != 0) {
     194                 :            :             /* Look for a '.' in the input; if present, it'll need to be
     195                 :            :                swapped for the current locale's decimal point before we
     196                 :            :                call strtod.  On the other hand, if we find the current
     197                 :            :                locale's decimal point then the input is invalid. */
     198                 :            :             while (isdigit(*p))
     199                 :            :                 p++;
     200                 :            : 
     201                 :            :             if (*p == '.') {
     202                 :            :                 decimal_point_pos = p++;
     203                 :            : 
     204                 :            :                 /* locate end of number */
     205                 :            :                 while (isdigit(*p))
     206                 :            :                     p++;
     207                 :            : 
     208                 :            :                 if (*p == 'e' || *p == 'E')
     209                 :            :                     p++;
     210                 :            :                 if (*p == '+' || *p == '-')
     211                 :            :                     p++;
     212                 :            :                 while (isdigit(*p))
     213                 :            :                     p++;
     214                 :            :                 end = p;
     215                 :            :             }
     216                 :            :             else if (strncmp(p, decimal_point, decimal_point_len) == 0)
     217                 :            :                 goto invalid_string;
     218                 :            :             /* For the other cases, we need not convert the decimal point */
     219                 :            :         }
     220                 :            :     }
     221                 :            : 
     222                 :            :     if (decimal_point_pos) {
     223                 :            :         char *copy, *c;
     224                 :            :         /* Create a copy of the input, with the '.' converted to the
     225                 :            :            locale-specific decimal point */
     226                 :            :         copy = (char *)malloc(end - digits_pos + 1 + decimal_point_len);
     227                 :            :         if (copy == NULL) {
     228                 :            :             *endptr = (char *)nptr;
     229                 :            :             errno = ENOMEM;
     230                 :            :             return -1.0;
     231                 :            :         }
     232                 :            : 
     233                 :            :         c = copy;
     234                 :            :         memcpy(c, digits_pos, decimal_point_pos - digits_pos);
     235                 :            :         c += decimal_point_pos - digits_pos;
     236                 :            :         memcpy(c, decimal_point, decimal_point_len);
     237                 :            :         c += decimal_point_len;
     238                 :            :         memcpy(c, decimal_point_pos + 1,
     239                 :            :                end - (decimal_point_pos + 1));
     240                 :            :         c += end - (decimal_point_pos + 1);
     241                 :            :         *c = 0;
     242                 :            : 
     243                 :            :         val = strtod(copy, &fail_pos);
     244                 :            : 
     245                 :            :         if (fail_pos)
     246                 :            :         {
     247                 :            :             if (fail_pos > decimal_point_pos)
     248                 :            :                 fail_pos = (char *)digits_pos +
     249                 :            :                     (fail_pos - copy) -
     250                 :            :                     (decimal_point_len - 1);
     251                 :            :             else
     252                 :            :                 fail_pos = (char *)digits_pos +
     253                 :            :                     (fail_pos - copy);
     254                 :            :         }
     255                 :            : 
     256                 :            :         free(copy);
     257                 :            :     }
     258                 :            :     else {
     259                 :            :         val = strtod(digits_pos, &fail_pos);
     260                 :            :     }
     261                 :            : 
     262                 :            :     if (fail_pos == digits_pos)
     263                 :            :         goto invalid_string;
     264                 :            : 
     265                 :            :     if (negate && fail_pos != nptr)
     266                 :            :         val = -val;
     267                 :            :     *endptr = fail_pos;
     268                 :            : 
     269                 :            :     return val;
     270                 :            : 
     271                 :            : invalid_string:
     272                 :            :     *endptr = (char*)nptr;
     273                 :            :     errno = EINVAL;
     274                 :            :     return -1.0;
     275                 :            : }
     276                 :            : 
     277                 :            : 
     278                 :            : JL_DLLEXPORT float jl_strtof_c(const char *nptr, char **endptr)
     279                 :            : {
     280                 :            :     return (float) jl_strtod_c(nptr, endptr);
     281                 :            : }
     282                 :            : 
     283                 :            : #endif
     284                 :            : 
     285                 :            : #ifdef __cplusplus
     286                 :            : }
     287                 :            : #endif

Generated by: LCOV version 1.14