new restartable integer parsing framework.
[musl] / src / stdlib / strtoumax.c
index f190247..a2bb4d7 100644 (file)
 #include <stdlib.h>
 #include <errno.h>
 #include <ctype.h>
-#include <stdio.h>
-
-/* Lookup table for digit values. -1==255>=36 -> invalid */
-static const unsigned char digits[] = {
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
--1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
-25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
--1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
-25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-};
+#include "intparse.h"
 
 uintmax_t strtoumax(const char *s1, char **p, int base)
 {
        const unsigned char *s = (void *)s1;
-       size_t x1, z1;
-       uintmax_t x, z=0;
-       int sign = 0;
-       int shift;
-
-       if (!p) p = (char **)&s1;
-
-       /* Initial whitespace */
-       for (; isspace(*s); s++);
-
-       /* Optional sign */
-       if (*s == '-') sign = *s++;
-       else if (*s == '+') s++;
+       struct intparse ip = {0};
 
-       /* Default base 8, 10, or 16 depending on prefix */
-       if (base == 0) {
-               if (s[0] == '0') {
-                       if ((s[1]|32) == 'x') base = 16;
-                       else base = 8;
-               } else {
-                       base = 10;
-               }
-       }
+       if (p) *p = (char *)s1;
 
-       if ((unsigned)base-2 > 36-2 || digits[*s]>=base) {
-               *p = (char *)s1;
+       if (base && base-2U > 34) {
                errno = EINVAL;
                return 0;
        }
 
-       /* Main loops. Only use big types if we have to. */
-       if (base == 10) {
-               for (x1=0; isdigit(*s) && x1<=SIZE_MAX/10-10; s++)
-                       x1 = 10*x1 + *s-'0';
-               for (x=x1; isdigit(*s) && x<=UINTMAX_MAX/10-10; s++)
-                       x = 10*x + *s-'0';
-               if (isdigit(*s)) {
-                       if (isdigit(s[1]) || 10*x>UINTMAX_MAX-(*s-'0'))
-                               goto overflow;
-                       x = 10*x + *s-'0';
-               }
-       } else if (!(base & base/2)) {
-               if (base == 16) {
-                       if (s[0]=='0' && (s[1]|32)=='x' && digits[s[2]]<16)
-                               s+=2;
-                       shift=4;
-                       z1 = SIZE_MAX/16;
-                       z = UINTMAX_MAX/16;
-               } else if (base == 8) {
-                       shift=3;
-                       z1 = SIZE_MAX/8;
-                       z = UINTMAX_MAX/8;
-               } else if (base == 2) {
-                       shift=1;
-                       z1 = SIZE_MAX/2;
-                       z = UINTMAX_MAX/2;
-               } else if (base == 4) {
-                       shift=2;
-                       z1 = SIZE_MAX/4;
-                       z = UINTMAX_MAX/4;
-               } else /* if (base == 32) */ {
-                       shift=5;
-                       z1 = SIZE_MAX/32;
-                       z = UINTMAX_MAX/32;
-               }
-               for (x1=0; digits[*s]<base && x1<=z1; s++)
-                       x1 = (x1<<shift) + digits[*s];
-               for (x=x1; digits[*s]<base && x<=z; s++)
-                       x = (x<<shift) + digits[*s];
-               if (digits[*s] < base) goto overflow;
-       } else {
-               z1 = SIZE_MAX/base-base;
-               for (x1=0; digits[*s]<base && x1<=z1; s++)
-                       x1 = x1*base + digits[*s];
-               if (digits[*s]<base)
-                       z = UINTMAX_MAX/base-base;
-               for (x=x1; digits[*s]<base && x<=z; s++)
-                       x = x*base + digits[*s];
-               if (digits[*s] < base) {
-                       if (digits[s[1]]<base || x*base>UINTMAX_MAX-digits[*s])
-                               goto overflow;
-                       x = x*base + digits[*s];
-               }
-       }
+       for (; isspace(*s); s++);
+
+       ip.base = base;
+       __intparse(&ip, s, SIZE_MAX);
 
-       *p = (char *)s;
-       return sign ? -x : x;
+       if (p && ip.err != EINVAL)
+               *p = (char *)s + ip.cnt;
+
+       if (ip.err) {
+               errno = ip.err;
+               if (ip.err = EINVAL) return 0;
+               return UINTMAX_MAX;
+       }
 
-overflow:
-       for (; digits[*s] < base; s++);
-       *p = (char *)s;
-       errno = ERANGE;
-       return UINTMAX_MAX;
+       return ip.neg ? -ip.val : ip.val;
 }