LCOV - code coverage report
Current view: | top level - Python - mystrtoul.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 65 | 115 | 56.5 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 2 | 2 | 100.0 %
|  |  | Branches: | 45 | 111 | 40.5 %

               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                 :     832141 : PyOS_strtoul(const char *str, char **ptr, int base)
          96                 :            : {
          97                 :     832141 :     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   [ +  -  -  + ]:     832141 :     while (*str && Py_ISSPACE(*str))
         103                 :          0 :         ++str;
         104                 :            :
         105                 :            :     /* check for leading 0b, 0o or 0x for auto-base or base 16 */
         106   [ +  -  -  -  :     832141 :     switch (base) {
                          - ]
         107                 :     832141 :     case 0:             /* look for leading 0b, 0o or 0x */
         108         [ +  + ]:     832141 :         if (*str == '0') {
         109                 :     266553 :             ++str;
         110   [ +  +  -  + ]:     266553 :             if (*str == 'x' || *str == 'X') {
         111                 :            :                 /* there must be at least one digit after 0x */
         112         [ -  + ]:        834 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 16) {
         113         [ #  # ]:          0 :                     if (ptr)
         114                 :          0 :                         *ptr = (char *)str;
         115                 :          0 :                     return 0;
         116                 :            :                 }
         117                 :        834 :                 ++str;
         118                 :        834 :                 base = 16;
         119   [ +  +  -  + ]:     265719 :             } else if (*str == 'o' || *str == 'O') {
         120                 :            :                 /* there must be at least one digit after 0o */
         121         [ -  + ]:        213 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 8) {
         122         [ #  # ]:          0 :                     if (ptr)
         123                 :          0 :                         *ptr = (char *)str;
         124                 :          0 :                     return 0;
         125                 :            :                 }
         126                 :        213 :                 ++str;
         127                 :        213 :                 base = 8;
         128   [ +  +  -  + ]:     265506 :             } else if (*str == 'b' || *str == 'B') {
         129                 :            :                 /* there must be at least one digit after 0b */
         130         [ -  + ]:         13 :                 if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 2) {
         131         [ #  # ]:          0 :                     if (ptr)
         132                 :          0 :                         *ptr = (char *)str;
         133                 :          0 :                     return 0;
         134                 :            :                 }
         135                 :         13 :                 ++str;
         136                 :         13 :                 base = 2;
         137                 :            :             } else {
         138                 :            :                 /* skip all zeroes... */
         139         [ -  + ]:     265493 :                 while (*str == '0')
         140                 :          0 :                     ++str;
         141         [ -  + ]:     265493 :                 while (Py_ISSPACE(*str))
         142                 :          0 :                     ++str;
         143         [ +  - ]:     265493 :                 if (ptr)
         144                 :     265493 :                     *ptr = (char *)str;
         145                 :     265493 :                 return 0;
         146                 :            :             }
         147                 :            :         }
         148                 :            :         else
         149                 :     565588 :             base = 10;
         150                 :     566648 :         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   [ +  -  -  + ]:     566648 :     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         [ +  + ]:     567642 :     while (*str == '0')
         206                 :        994 :         ++str;
         207                 :            :
         208                 :            :     /* base is guaranteed to be in [2, 36] at this point */
         209                 :     566648 :     ovlimit = digitlimit[base];
         210                 :            :
         211                 :            :     /* do the conversion until non-digit character encountered */
         212         [ +  + ]:    1908377 :     while ((c = _PyLong_DigitValue[Py_CHARMASK(*str)]) < base) {
         213         [ +  + ]:    1341752 :         if (ovlimit > 0) /* no overflow check required */
         214                 :    1341717 :             result = result * base + c;
         215                 :            :         else { /* requires overflow check */
         216                 :            :             unsigned long temp_result;
         217                 :            :
         218         [ +  + ]:         35 :             if (ovlimit < 0) /* guaranteed overflow */
         219                 :          9 :                 goto overflowed;
         220                 :            :
         221                 :            :             /* there could be an overflow */
         222                 :            :             /* check overflow just from shifting */
         223         [ +  + ]:         26 :             if (result > smallmax[base])
         224                 :         14 :                 goto overflowed;
         225                 :            :
         226                 :         12 :             result *= base;
         227                 :            :
         228                 :            :             /* check overflow from the digit's value */
         229                 :         12 :             temp_result = result + c;
         230         [ -  + ]:         12 :             if (temp_result < result)
         231                 :          0 :                 goto overflowed;
         232                 :            :
         233                 :         12 :             result = temp_result;
         234                 :            :         }
         235                 :            :
         236                 :    1341729 :         ++str;
         237                 :    1341729 :         --ovlimit;
         238                 :            :     }
         239                 :            :
         240                 :            :     /* set pointer to point to the last character scanned */
         241         [ +  - ]:     566625 :     if (ptr)
         242                 :     566625 :         *ptr = (char *)str;
         243                 :            :
         244                 :     566625 :     return result;
         245                 :            :
         246                 :         23 : overflowed:
         247         [ +  - ]:         23 :     if (ptr) {
         248                 :            :         /* spool through remaining digit characters */
         249         [ +  + ]:        453 :         while (_PyLong_DigitValue[Py_CHARMASK(*str)] < base)
         250                 :        430 :             ++str;
         251                 :         23 :         *ptr = (char *)str;
         252                 :            :     }
         253                 :         23 :     errno = ERANGE;
         254                 :         23 :     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                 :     565588 : PyOS_strtol(const char *str, char **ptr, int base)
         264                 :            : {
         265                 :            :     long result;
         266                 :            :     unsigned long uresult;
         267                 :            :     char sign;
         268                 :            :
         269   [ +  -  -  + ]:     565588 :     while (*str && Py_ISSPACE(*str))
         270                 :          0 :         str++;
         271                 :            :
         272                 :     565588 :     sign = *str;
         273   [ +  -  -  + ]:     565588 :     if (sign == '+' || sign == '-')
         274                 :          0 :         str++;
         275                 :            :
         276                 :     565588 :     uresult = PyOS_strtoul(str, ptr, base);
         277                 :            :
         278         [ +  + ]:     565588 :     if (uresult <= (unsigned long)LONG_MAX) {
         279                 :     565558 :         result = (long)uresult;
         280         [ -  + ]:     565558 :         if (sign == '-')
         281                 :          0 :             result = -result;
         282                 :            :     }
         283   [ -  +  -  - ]:         30 :     else if (sign == '-' && uresult == PY_ABS_LONG_MIN) {
         284                 :          0 :         result = LONG_MIN;
         285                 :            :     }
         286                 :            :     else {
         287                 :         30 :         errno = ERANGE;
         288                 :         30 :         result = LONG_MAX;
         289                 :            :     }
         290                 :     565588 :     return result;
         291                 :            : }

Generated by: LCOV version 1.14