LCOV - code coverage report
Current view: top level - Python - mystrtoul.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit acb105a7c1f] Lines: 67 115 58.3 %
Date: 2022-07-20 13:12:14 Functions: 2 2 100.0 %
Branches: 50 111 45.0 %

           Branch data     Line data    Source code
       1                 :            : #include "Python.h"
       2                 :            : #include "pycore_long.h"          // _PyLong_DigitValue
       3                 :            : 
       4                 :            : #if defined(__sgi) && !defined(_SGI_MP_SOURCE)
       5                 :            : #define _SGI_MP_SOURCE
       6                 :            : #endif
       7                 :            : 
       8                 :            : /* strtol and strtoul, renamed to avoid conflicts */
       9                 :            : 
      10                 :            : 
      11                 :            : #include <ctype.h>
      12                 :            : #ifdef HAVE_ERRNO_H
      13                 :            : #include <errno.h>
      14                 :            : #endif
      15                 :            : 
      16                 :            : /* Static overflow check values for bases 2 through 36.
      17                 :            :  * smallmax[base] is the largest unsigned long i such that
      18                 :            :  * i * base doesn't overflow unsigned long.
      19                 :            :  */
      20                 :            : static const unsigned long smallmax[] = {
      21                 :            :     0, /* bases 0 and 1 are invalid */
      22                 :            :     0,
      23                 :            :     ULONG_MAX / 2,
      24                 :            :     ULONG_MAX / 3,
      25                 :            :     ULONG_MAX / 4,
      26                 :            :     ULONG_MAX / 5,
      27                 :            :     ULONG_MAX / 6,
      28                 :            :     ULONG_MAX / 7,
      29                 :            :     ULONG_MAX / 8,
      30                 :            :     ULONG_MAX / 9,
      31                 :            :     ULONG_MAX / 10,
      32                 :            :     ULONG_MAX / 11,
      33                 :            :     ULONG_MAX / 12,
      34                 :            :     ULONG_MAX / 13,
      35                 :            :     ULONG_MAX / 14,
      36                 :            :     ULONG_MAX / 15,
      37                 :            :     ULONG_MAX / 16,
      38                 :            :     ULONG_MAX / 17,
      39                 :            :     ULONG_MAX / 18,
      40                 :            :     ULONG_MAX / 19,
      41                 :            :     ULONG_MAX / 20,
      42                 :            :     ULONG_MAX / 21,
      43                 :            :     ULONG_MAX / 22,
      44                 :            :     ULONG_MAX / 23,
      45                 :            :     ULONG_MAX / 24,
      46                 :            :     ULONG_MAX / 25,
      47                 :            :     ULONG_MAX / 26,
      48                 :            :     ULONG_MAX / 27,
      49                 :            :     ULONG_MAX / 28,
      50                 :            :     ULONG_MAX / 29,
      51                 :            :     ULONG_MAX / 30,
      52                 :            :     ULONG_MAX / 31,
      53                 :            :     ULONG_MAX / 32,
      54                 :            :     ULONG_MAX / 33,
      55                 :            :     ULONG_MAX / 34,
      56                 :            :     ULONG_MAX / 35,
      57                 :            :     ULONG_MAX / 36,
      58                 :            : };
      59                 :            : 
      60                 :            : /* maximum digits that can't ever overflow for bases 2 through 36,
      61                 :            :  * calculated by [int(math.floor(math.log(2**32, i))) for i in range(2, 37)].
      62                 :            :  * Note that this is pessimistic if sizeof(long) > 4.
      63                 :            :  */
      64                 :            : #if SIZEOF_LONG == 4
      65                 :            : static const int digitlimit[] = {
      66                 :            :     0,  0, 32, 20, 16, 13, 12, 11, 10, 10,  /*  0 -  9 */
      67                 :            :     9,  9,  8,  8,  8,  8,  8,  7,  7,  7,  /* 10 - 19 */
      68                 :            :     7,  7,  7,  7,  6,  6,  6,  6,  6,  6,  /* 20 - 29 */
      69                 :            :     6,  6,  6,  6,  6,  6,  6};             /* 30 - 36 */
      70                 :            : #elif SIZEOF_LONG == 8
      71                 :            : /* [int(math.floor(math.log(2**64, i))) for i in range(2, 37)] */
      72                 :            : static const int digitlimit[] = {
      73                 :            :          0,   0, 64, 40, 32, 27, 24, 22, 21, 20,  /*  0 -  9 */
      74                 :            :     19,  18, 17, 17, 16, 16, 16, 15, 15, 15,  /* 10 - 19 */
      75                 :            :     14,  14, 14, 14, 13, 13, 13, 13, 13, 13,  /* 20 - 29 */
      76                 :            :     13,  12, 12, 12, 12, 12, 12};             /* 30 - 36 */
      77                 :            : #else
      78                 :            : #error "Need table for SIZEOF_LONG"
      79                 :            : #endif
      80                 :            : 
      81                 :            : /*
      82                 :            : **      strtoul
      83                 :            : **              This is a general purpose routine for converting
      84                 :            : **              an ascii string to an integer in an arbitrary base.
      85                 :            : **              Leading white space is ignored.  If 'base' is zero
      86                 :            : **              it looks for a leading 0b, 0o or 0x to tell which
      87                 :            : **              base.  If these are absent it defaults to 10.
      88                 :            : **              Base must be 0 or between 2 and 36 (inclusive).
      89                 :            : **              If 'ptr' is non-NULL it will contain a pointer to
      90                 :            : **              the end of the scan.
      91                 :            : **              Errors due to bad pointers will probably result in
      92                 :            : **              exceptions - we don't check for them.
      93                 :            : */
      94                 :            : unsigned long
      95                 :    5873766 : PyOS_strtoul(const char *str, char **ptr, int base)
      96                 :            : {
      97                 :    5873766 :     unsigned long result = 0; /* return value of the function */
      98                 :            :     int c;             /* current input character */
      99                 :            :     int ovlimit;       /* required digits to overflow */
     100                 :            : 
     101                 :            :     /* skip leading white space */
     102   [ +  -  -  + ]:    5873766 :     while (*str && Py_ISSPACE(*str))
     103                 :          0 :         ++str;
     104                 :            : 
     105                 :            :     /* check for leading 0b, 0o or 0x for auto-base or base 16 */
     106   [ +  -  -  -  :    5873766 :     switch (base) {
                      - ]
     107                 :    5873766 :     case 0:             /* look for leading 0b, 0o or 0x */
     108         [ +  + ]:    5873766 :         if (*str == '0') {
     109                 :    1813334 :             ++str;
     110   [ +  +  +  + ]:    1813334 :             if (*str == 'x' || *str == 'X') {
     111                 :            :                 /* there must be at least one digit after 0x */
     112         [ -  + ]:     194000 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 16) {
     113         [ #  # ]:          0 :                     if (ptr)
     114                 :          0 :                         *ptr = (char *)str;
     115                 :          0 :                     return 0;
     116                 :            :                 }
     117                 :     194000 :                 ++str;
     118                 :     194000 :                 base = 16;
     119   [ +  +  +  + ]:    1619334 :             } else if (*str == 'o' || *str == 'O') {
     120                 :            :                 /* there must be at least one digit after 0o */
     121         [ -  + ]:       4002 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 8) {
     122         [ #  # ]:          0 :                     if (ptr)
     123                 :          0 :                         *ptr = (char *)str;
     124                 :          0 :                     return 0;
     125                 :            :                 }
     126                 :       4002 :                 ++str;
     127                 :       4002 :                 base = 8;
     128   [ +  +  +  + ]:    1615332 :             } else if (*str == 'b' || *str == 'B') {
     129                 :            :                 /* there must be at least one digit after 0b */
     130         [ -  + ]:        754 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 2) {
     131         [ #  # ]:          0 :                     if (ptr)
     132                 :          0 :                         *ptr = (char *)str;
     133                 :          0 :                     return 0;
     134                 :            :                 }
     135                 :        754 :                 ++str;
     136                 :        754 :                 base = 2;
     137                 :            :             } else {
     138                 :            :                 /* skip all zeroes... */
     139         [ +  + ]:    1614922 :                 while (*str == '0')
     140                 :        344 :                     ++str;
     141         [ -  + ]:    1614578 :                 while (Py_ISSPACE(*str))
     142                 :          0 :                     ++str;
     143         [ +  - ]:    1614578 :                 if (ptr)
     144                 :    1614578 :                     *ptr = (char *)str;
     145                 :    1614578 :                 return 0;
     146                 :            :             }
     147                 :            :         }
     148                 :            :         else
     149                 :    4060432 :             base = 10;
     150                 :    4259188 :         break;
     151                 :            : 
     152                 :            :     /* even with explicit base, skip leading 0? prefix */
     153                 :          0 :     case 16:
     154         [ #  # ]:          0 :         if (*str == '0') {
     155                 :          0 :             ++str;
     156   [ #  #  #  # ]:          0 :             if (*str == 'x' || *str == 'X') {
     157                 :            :                 /* there must be at least one digit after 0x */
     158         [ #  # ]:          0 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 16) {
     159         [ #  # ]:          0 :                     if (ptr)
     160                 :          0 :                         *ptr = (char *)str;
     161                 :          0 :                     return 0;
     162                 :            :                 }
     163                 :          0 :                 ++str;
     164                 :            :             }
     165                 :            :         }
     166                 :          0 :         break;
     167                 :          0 :     case 8:
     168         [ #  # ]:          0 :         if (*str == '0') {
     169                 :          0 :             ++str;
     170   [ #  #  #  # ]:          0 :             if (*str == 'o' || *str == 'O') {
     171                 :            :                 /* there must be at least one digit after 0o */
     172         [ #  # ]:          0 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 8) {
     173         [ #  # ]:          0 :                     if (ptr)
     174                 :          0 :                         *ptr = (char *)str;
     175                 :          0 :                     return 0;
     176                 :            :                 }
     177                 :          0 :                 ++str;
     178                 :            :             }
     179                 :            :         }
     180                 :          0 :         break;
     181                 :          0 :     case 2:
     182         [ #  # ]:          0 :         if(*str == '0') {
     183                 :          0 :             ++str;
     184   [ #  #  #  # ]:          0 :             if (*str == 'b' || *str == 'B') {
     185                 :            :                 /* there must be at least one digit after 0b */
     186         [ #  # ]:          0 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 2) {
     187         [ #  # ]:          0 :                     if (ptr)
     188                 :          0 :                         *ptr = (char *)str;
     189                 :          0 :                     return 0;
     190                 :            :                 }
     191                 :          0 :                 ++str;
     192                 :            :             }
     193                 :            :         }
     194                 :          0 :         break;
     195                 :            :     }
     196                 :            : 
     197                 :            :     /* catch silly bases */
     198   [ +  -  -  + ]:    4259188 :     if (base < 2 || base > 36) {
     199         [ #  # ]:          0 :         if (ptr)
     200                 :          0 :             *ptr = (char *)str;
     201                 :          0 :         return 0;
     202                 :            :     }
     203                 :            : 
     204                 :            :     /* skip leading zeroes */
     205         [ +  + ]:    4321258 :     while (*str == '0')
     206                 :      62070 :         ++str;
     207                 :            : 
     208                 :            :     /* base is guaranteed to be in [2, 36] at this point */
     209                 :    4259188 :     ovlimit = digitlimit[base];
     210                 :            : 
     211                 :            :     /* do the conversion until non-digit character encountered */
     212         [ +  + ]:   14492272 :     while ((c = _PyLong_DigitValue[Py_CHARMASK(*str)]) < base) {
     213         [ +  + ]:   10233776 :         if (ovlimit > 0) /* no overflow check required */
     214                 :   10232719 :             result = result * base + c;
     215                 :            :         else { /* requires overflow check */
     216                 :            :             unsigned long temp_result;
     217                 :            : 
     218         [ +  + ]:       1057 :             if (ovlimit < 0) /* guaranteed overflow */
     219                 :        183 :                 goto overflowed;
     220                 :            : 
     221                 :            :             /* there could be an overflow */
     222                 :            :             /* check overflow just from shifting */
     223         [ +  + ]:        874 :             if (result > smallmax[base])
     224                 :        495 :                 goto overflowed;
     225                 :            : 
     226                 :        379 :             result *= base;
     227                 :            : 
     228                 :            :             /* check overflow from the digit's value */
     229                 :        379 :             temp_result = result + c;
     230         [ +  + ]:        379 :             if (temp_result < result)
     231                 :         14 :                 goto overflowed;
     232                 :            : 
     233                 :        365 :             result = temp_result;
     234                 :            :         }
     235                 :            : 
     236                 :   10233084 :         ++str;
     237                 :   10233084 :         --ovlimit;
     238                 :            :     }
     239                 :            : 
     240                 :            :     /* set pointer to point to the last character scanned */
     241         [ +  - ]:    4258496 :     if (ptr)
     242                 :    4258496 :         *ptr = (char *)str;
     243                 :            : 
     244                 :    4258496 :     return result;
     245                 :            : 
     246                 :        692 : overflowed:
     247         [ +  - ]:        692 :     if (ptr) {
     248                 :            :         /* spool through remaining digit characters */
     249         [ +  + ]:      10236 :         while (_PyLong_DigitValue[Py_CHARMASK(*str)] < base)
     250                 :       9544 :             ++str;
     251                 :        692 :         *ptr = (char *)str;
     252                 :            :     }
     253                 :        692 :     errno = ERANGE;
     254                 :        692 :     return (unsigned long)-1;
     255                 :            : }
     256                 :            : 
     257                 :            : /* Checking for overflow in PyOS_strtol is a PITA; see comments
     258                 :            :  * about PY_ABS_LONG_MIN in longobject.c.
     259                 :            :  */
     260                 :            : #define PY_ABS_LONG_MIN         (0-(unsigned long)LONG_MIN)
     261                 :            : 
     262                 :            : long
     263                 :    4060432 : PyOS_strtol(const char *str, char **ptr, int base)
     264                 :            : {
     265                 :            :     long result;
     266                 :            :     unsigned long uresult;
     267                 :            :     char sign;
     268                 :            : 
     269   [ +  -  -  + ]:    4060432 :     while (*str && Py_ISSPACE(*str))
     270                 :          0 :         str++;
     271                 :            : 
     272                 :    4060432 :     sign = *str;
     273   [ +  -  -  + ]:    4060432 :     if (sign == '+' || sign == '-')
     274                 :          0 :         str++;
     275                 :            : 
     276                 :    4060432 :     uresult = PyOS_strtoul(str, ptr, base);
     277                 :            : 
     278         [ +  + ]:    4060432 :     if (uresult <= (unsigned long)LONG_MAX) {
     279                 :    4059620 :         result = (long)uresult;
     280         [ -  + ]:    4059620 :         if (sign == '-')
     281                 :          0 :             result = -result;
     282                 :            :     }
     283   [ -  +  -  - ]:        812 :     else if (sign == '-' && uresult == PY_ABS_LONG_MIN) {
     284                 :          0 :         result = LONG_MIN;
     285                 :            :     }
     286                 :            :     else {
     287                 :        812 :         errno = ERANGE;
     288                 :        812 :         result = LONG_MAX;
     289                 :            :     }
     290                 :    4060432 :     return result;
     291                 :            : }

Generated by: LCOV version 1.14