about summary refs log tree commit diff
path: root/src/stdlib/strtoumax.c
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-07-14 00:51:45 -0400
committerRich Felker <dalias@aerifal.cx>2011-07-14 00:51:45 -0400
commitecc9c5fcfa4831b290cc1a63c0346cbb0c1fcf42 (patch)
tree2fb20d623af9622cb8ac9f461e542ca23fc6d791 /src/stdlib/strtoumax.c
parent0e2331c9b6e0c0b4f24019d4062f4c655d28cbaf (diff)
downloadmusl-ecc9c5fcfa4831b290cc1a63c0346cbb0c1fcf42.tar.gz
musl-ecc9c5fcfa4831b290cc1a63c0346cbb0c1fcf42.tar.xz
musl-ecc9c5fcfa4831b290cc1a63c0346cbb0c1fcf42.zip
new restartable integer parsing framework.
this fixes a number of bugs in integer parsing due to lazy haphazard
wrapping, as well as some misinterpretations of the standard. the new
parser is able to work character-at-a-time or on whole strings, making
it easy to support the wide functions without unbounded space for
conversion. it will also be possible to update scanf to use the new
parser.
Diffstat (limited to 'src/stdlib/strtoumax.c')
-rw-r--r--src/stdlib/strtoumax.c123
1 files changed, 17 insertions, 106 deletions
diff --git a/src/stdlib/strtoumax.c b/src/stdlib/strtoumax.c
index f1902476..a2bb4d7d 100644
--- a/src/stdlib/strtoumax.c
+++ b/src/stdlib/strtoumax.c
@@ -2,122 +2,33 @@
 #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;
 }