summaryrefslogtreecommitdiffstats
path: root/Python/mystrtoul.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/mystrtoul.c')
-rw-r--r--Python/mystrtoul.c238
1 files changed, 150 insertions, 88 deletions
diff --git a/Python/mystrtoul.c b/Python/mystrtoul.c
index 8e60c0c..380b37d 100644
--- a/Python/mystrtoul.c
+++ b/Python/mystrtoul.c
@@ -15,6 +15,66 @@
/* strtol and strtoul, renamed to avoid conflicts */
+
+#include <ctype.h>
+#ifndef DONT_HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+/* Static overflow check values for bases 2 through 36.
+ * smallmax[base] is the largest unsigned long i such that
+ * i * base doesn't overflow unsigned long.
+ */
+static unsigned long smallmax[] = {
+ 0, /* bases 0 and 1 are invalid */
+ 0,
+ ULONG_MAX / 2,
+ ULONG_MAX / 3,
+ ULONG_MAX / 4,
+ ULONG_MAX / 5,
+ ULONG_MAX / 6,
+ ULONG_MAX / 7,
+ ULONG_MAX / 8,
+ ULONG_MAX / 9,
+ ULONG_MAX / 10,
+ ULONG_MAX / 11,
+ ULONG_MAX / 12,
+ ULONG_MAX / 13,
+ ULONG_MAX / 14,
+ ULONG_MAX / 15,
+ ULONG_MAX / 16,
+ ULONG_MAX / 17,
+ ULONG_MAX / 18,
+ ULONG_MAX / 19,
+ ULONG_MAX / 20,
+ ULONG_MAX / 21,
+ ULONG_MAX / 22,
+ ULONG_MAX / 23,
+ ULONG_MAX / 24,
+ ULONG_MAX / 25,
+ ULONG_MAX / 26,
+ ULONG_MAX / 27,
+ ULONG_MAX / 28,
+ ULONG_MAX / 29,
+ ULONG_MAX / 30,
+ ULONG_MAX / 31,
+ ULONG_MAX / 32,
+ ULONG_MAX / 33,
+ ULONG_MAX / 34,
+ ULONG_MAX / 35,
+ ULONG_MAX / 36,
+};
+
+/* maximum digits that can't ever overflow for bases 2 through 36,
+ * calculated by [int(math.floor(math.log(2**32, i))) for i in range(2, 37)].
+ * Note that this is pessimistic if sizeof(long) > 4.
+ */
+static int digitlimit[] = {
+ 0, 0, 32, 20, 16, 13, 12, 11, 10, 10, /* 0 - 9 */
+ 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, /* 10 - 19 */
+ 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, /* 20 - 29 */
+ 6, 6, 6, 6, 6, 6, 6}; /* 30 - 36 */
+
/*
** strtoul
** This is a general purpose routine for converting
@@ -28,98 +88,100 @@
** Errors due to bad pointers will probably result in
** exceptions - we don't check for them.
*/
-
-#include <ctype.h>
-#ifndef DONT_HAVE_ERRNO_H
-#include <errno.h>
-#endif
-
unsigned long
PyOS_strtoul(register char *str, char **ptr, int base)
{
- register unsigned long result; /* return value of the function */
- register int c; /* current input character */
- register unsigned long temp; /* used in overflow testing */
- int ovf; /* true if overflow occurred */
+ register unsigned long result = 0; /* return value of the function */
+ register int c; /* current input character */
+ register int ovlimit; /* required digits to overflow */
+
+ /* skip leading white space */
+ while (*str && isspace(Py_CHARMASK(*str)))
+ ++str;
- result = 0;
- ovf = 0;
+ /* check for leading 0 or 0x for auto-base or base 16 */
+ switch (base) {
+ case 0: /* look for leading 0, 0x or 0X */
+ if (*str == '0') {
+ ++str;
+ if (*str == 'x' || *str == 'X') {
+ ++str;
+ base = 16;
+ }
+ else
+ base = 8;
+ }
+ else
+ base = 10;
+ break;
-/* catch silly bases */
- if (base != 0 && (base < 2 || base > 36))
- {
- if (ptr)
- *ptr = str;
- return 0;
- }
-
-/* skip leading white space */
- while (*str && isspace(Py_CHARMASK(*str)))
- str++;
-
-/* check for leading 0 or 0x for auto-base or base 16 */
- switch (base)
- {
- case 0: /* look for leading 0, 0x or 0X */
- if (*str == '0')
- {
- str++;
- if (*str == 'x' || *str == 'X')
- {
- str++;
- base = 16;
- }
- else
- base = 8;
+ case 16: /* skip leading 0x or 0X */
+ if (*str == '0') {
+ ++str;
+ if (*str == 'x' || *str == 'X')
+ ++str;
+ }
+ break;
}
- else
- base = 10;
- break;
-
- case 16: /* skip leading 0x or 0X */
- if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
- str += 2;
- break;
- }
-
-/* do the conversion */
- while ((c = Py_CHARMASK(*str)) != '\0')
- {
- if (isdigit(c) && c - '0' < base)
- c -= '0';
- else
- {
- if (isupper(c))
- c = tolower(c);
- if (c >= 'a' && c <= 'z')
- c -= 'a' - 10;
- else /* non-"digit" character */
- break;
- if (c >= base) /* non-"digit" character */
- break;
+
+ /* catch silly bases */
+ if (base < 2 || base > 36) {
+ if (ptr)
+ *ptr = str;
+ return 0;
}
- temp = result;
- result = result * base + c;
- if(base == 10) {
- if(((long)(result - c) / base != (long)temp)) /* overflow */
- ovf = 1;
+
+ /* skip leading zeroes */
+ while (*str == '0')
+ ++str;
+
+ /* base is guaranteed to be in [2, 36] at this point */
+ ovlimit = digitlimit[base];
+
+ /* do the conversion until non-digit character encountered */
+ while ((c = _PyLong_DigitValue[Py_CHARMASK(*str)]) < base) {
+ if (ovlimit > 0) /* no overflow check required */
+ result = result * base + c;
+ else { /* requires overflow check */
+ register unsigned long temp_result;
+
+ if (ovlimit < 0) /* guaranteed overflow */
+ goto overflowed;
+
+ /* there could be an overflow */
+ /* check overflow just from shifting */
+ if (result > smallmax[base])
+ goto overflowed;
+
+ result *= base;
+
+ /* check overflow from the digit's value */
+ temp_result = result + c;
+ if (temp_result < result)
+ goto overflowed;
+
+ result = temp_result;
+ }
+
+ ++str;
+ --ovlimit;
}
- else {
- if ((result - c) / base != temp) /* overflow */
- ovf = 1;
+
+ /* set pointer to point to the last character scanned */
+ if (ptr)
+ *ptr = str;
+
+ return result;
+
+overflowed:
+ if (ptr) {
+ /* spool through remaining digit characters */
+ while (_PyLong_DigitValue[Py_CHARMASK(*str)] < base)
+ ++str;
+ *ptr = str;
}
- str++;
- }
-
-/* set pointer to point to the last character scanned */
- if (ptr)
- *ptr = str;
- if (ovf)
- {
- result = (unsigned long) ~0L;
errno = ERANGE;
- }
- return result;
+ return (unsigned long)-1;
}
long
@@ -127,25 +189,25 @@ PyOS_strtol(char *str, char **ptr, int base)
{
long result;
char sign;
-
+
while (*str && isspace(Py_CHARMASK(*str)))
str++;
-
+
sign = *str;
if (sign == '+' || sign == '-')
str++;
-
+
result = (long) PyOS_strtoul(str, ptr, base);
-
+
/* Signal overflow if the result appears negative,
except for the largest negative integer */
if (result < 0 && !(sign == '-' && result == -result)) {
errno = ERANGE;
result = 0x7fffffff;
}
-
+
if (sign == '-')
result = -result;
-
+
return result;
}