diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/internal/intparse.c | 105 | ||||
| -rw-r--r-- | src/internal/intparse.h | 11 | ||||
| -rw-r--r-- | src/stdlib/strtoimax.c | 37 | ||||
| -rw-r--r-- | src/stdlib/strtoumax.c | 123 | ||||
| -rw-r--r-- | src/stdlib/wcstoimax.c | 36 | ||||
| -rw-r--r-- | src/stdlib/wcstoumax.c | 41 | 
6 files changed, 197 insertions, 156 deletions
| diff --git a/src/internal/intparse.c b/src/internal/intparse.c new file mode 100644 index 00000000..21b07b74 --- /dev/null +++ b/src/internal/intparse.c @@ -0,0 +1,105 @@ +#include <stdint.h> +#include <limits.h> +#include <stdlib.h> +#include <errno.h> +#include "intparse.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, +}; + +#define SLIM (UINT_MAX/36-1) +#define LLIM (UINTMAX_MAX/36-1) + +int __intparse(struct intparse *v, const void *buf, size_t n) +{ +	const unsigned char *s = buf; +	int d, b = v->base; + +	v->cnt += n; +	for (; n; n--, s++) switch (v->state) { +	case 0: +		v->state++; +		if (*s=='+' || *s=='-') { +			v->neg = *s=='-'; +			continue; +		} +	case 1: +		v->state++; +		if (*s=='0' && (!b || b==16)) continue; +		if (!b) v->base = b = 10; +		v->state++; +		goto firstdigit; +	case 2: +		v->state++; +		if ((!b || b==16) && (*s|32) == 'x') { +			v->base = b = 16; +			continue; +		} +		if (!b) v->base = b = 8; +		goto seconddigit; +	case 3: +	firstdigit: +		if (digits[*s] >= b) { +			v->err = EINVAL; +			return 0; +		} +	seconddigit: +		v->state++; +	case 4: +		if (b==10) { +			for (; n && *s-'0'<10U && v->small<=SLIM; n--, s++) +				v->small = v->small * 10 + (*s-'0'); +		} else if ((b&-b) == b) { +			int bs = "\0\1\2\4\7\3\6\5"[(0x17*b)>>5&7]; +			for (; n && (d=digits[*s])<b && v->small<=SLIM; n--, s++) +				v->small = (v->small<<bs) + d; +		} else { +			for (; n && (d=digits[*s])<b && v->small<=SLIM; n--, s++) +				v->small = v->small * b + d; +		} +		if (!n) return 1; +		v->state++; +		v->val = v->small; +	case 5: +		for (; n && (d=digits[*s])<b && v->val<=LLIM; n--, s++) +			v->val = v->val * b + d; +		if (!n) return 1; +		if (d >= b) goto finished; +		if (v->val < (UINTMAX_MAX-d)/b) +			v->val = v->val * b + d; +		else +			v->err = ERANGE; +		v->state++; +		n--; s++; +	case 6: +		if (n && digits[*s]<b) { +			v->err = ERANGE; +			v->val = UINTMAX_MAX; + +			n--; s++; +		} +		for (; n && digits[*s]<b; n--, s++); +		if (!n) return 1; +	} +	return 1; +finished: +	v->cnt -= n; +	return 0; +} diff --git a/src/internal/intparse.h b/src/internal/intparse.h new file mode 100644 index 00000000..78e800d1 --- /dev/null +++ b/src/internal/intparse.h @@ -0,0 +1,11 @@ +#include <stdint.h> +#include <stddef.h> + +struct intparse { +	uintmax_t val; +	unsigned small; +	size_t cnt; +	char neg, base, state, err; +}; + +int __intparse(struct intparse *, const void *, size_t); diff --git a/src/stdlib/strtoimax.c b/src/stdlib/strtoimax.c index aeb0397f..247f91d4 100644 --- a/src/stdlib/strtoimax.c +++ b/src/stdlib/strtoimax.c @@ -1,25 +1,38 @@  #include <inttypes.h>  #include <errno.h>  #include <ctype.h> +#include "intparse.h"  intmax_t strtoimax(const char *s1, char **p, int base)  { -	const unsigned char *s = (const void *)s1; -	int sign = 0; -	uintmax_t x; +	const unsigned char *s = (void *)s1; +	struct intparse ip = {0}; + +	if (p) *p = (char *)s1; + +	if (base && base-2U > 34) { +		errno = EINVAL; +		return 0; +	} -	/* Initial whitespace */  	for (; isspace(*s); s++); -	/* Optional sign */ -	if (*s == '-') sign = *s++; -	else if (*s == '+') s++; +	ip.base = base; +	__intparse(&ip, s, SIZE_MAX); + +	if (p && ip.err != EINVAL) +		*p = (char *)s + ip.cnt; + +	if (ip.err) { +		errno = ip.err; +		if (ip.err = EINVAL) return 0; +		return ip.neg ? INTMAX_MIN : INTMAX_MAX; +	} -	x = strtoumax((const void *)s, p, base); -	if (x > INTMAX_MAX) { -		if (!sign || -x != INTMAX_MIN) +	if (ip.val > INTMAX_MAX) { +		if (!ip.neg || -ip.val != INTMAX_MIN)  			errno = ERANGE; -		return sign ? INTMAX_MIN : INTMAX_MAX; +		return ip.neg ? INTMAX_MIN : INTMAX_MAX;  	} -	return sign ? -x : x; +	return ip.neg ? -ip.val : ip.val;  } 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;  } diff --git a/src/stdlib/wcstoimax.c b/src/stdlib/wcstoimax.c index 59894f60..b83206b7 100644 --- a/src/stdlib/wcstoimax.c +++ b/src/stdlib/wcstoimax.c @@ -2,24 +2,38 @@  #include <wctype.h>  #include <inttypes.h>  #include <errno.h> +#include "intparse.h"  intmax_t wcstoimax(const wchar_t *s, wchar_t **p, int base)  { -	int sign = 0; -	uintmax_t x; +	struct intparse ip = {0}; +	unsigned char tmp; + +	if (p) *p = (wchar_t *)s; + +	if (base && base-2U > 34) { +		errno = EINVAL; +		return 0; +	} -	/* Initial whitespace */  	for (; iswspace(*s); s++); -	/* Optional sign */ -	if (*s == '-') sign = *s++; -	else if (*s == '+') s++; +	ip.base = base; +	for (; *s<256 && (tmp=*s, __intparse(&ip, &tmp, 1)); s++); + +	if (p && ip.err != EINVAL) +		*p = (wchar_t *)s; + +	if (ip.err) { +		errno = ip.err; +		if (ip.err = EINVAL) return 0; +		return ip.neg ? INTMAX_MIN : INTMAX_MAX; +	} -	x = wcstoumax(s, p, base); -	if (x > INTMAX_MAX) { -		if (!sign || -x != INTMAX_MIN) +	if (ip.val > INTMAX_MAX) { +		if (!ip.neg || -ip.val != INTMAX_MIN)  			errno = ERANGE; -		return sign ? INTMAX_MIN : INTMAX_MAX; +		return ip.neg ? INTMAX_MIN : INTMAX_MAX;  	} -	return sign ? -x : x; +	return ip.neg ? -ip.val : ip.val;  } diff --git a/src/stdlib/wcstoumax.c b/src/stdlib/wcstoumax.c index 86528ef1..e30b0638 100644 --- a/src/stdlib/wcstoumax.c +++ b/src/stdlib/wcstoumax.c @@ -3,46 +3,33 @@  #include <stdlib.h>  #include <inttypes.h>  #include <errno.h> +#include "intparse.h"  uintmax_t wcstoumax(const wchar_t *s, wchar_t **p, int base)  { -	/* Large enough for largest value in binary */ -	char buf[sizeof(uintmax_t)*8+2]; -	int sign = 0, skipped=0; +	struct intparse ip = {0}; +	unsigned char tmp; -	if (!p) p = (wchar_t **)&s; +	if (p) *p = (wchar_t *)s; -	if (base && (unsigned)base-2 > 36-2) { -		*p = (wchar_t *)s; +	if (base && base-2U > 34) {  		errno = EINVAL;  		return 0;  	} -	/* Initial whitespace */  	for (; iswspace(*s); s++); -	/* Optional sign */ -	if (*s == '-') sign = *s++; -	else if (*s == '+') s++; - -	/* Skip leading zeros but don't allow leading zeros before "0x". */ -	for (; s[0]=='0' && s[1]=='0'; s++) skipped=1; -	if (skipped && (base==0 || base==16) && (s[1]|32)=='x') { -		*p = (wchar_t *)(s+1); -		return 0; -	} - -	/* Convert to normal char string so we can use strtoumax */ -	buf[0] = sign; -	if (wcstombs(buf+!!sign, s, sizeof buf-1) == -1) return 0; -	buf[sizeof buf-1]=0; +	ip.base = base; +	for (; *s<256 && (tmp=*s, __intparse(&ip, &tmp, 1)); s++); -	/* Compute final position */ -	if (p) { -		if ((base==0 || base==16) && s[0]=='0' && (s[1]|32)=='x' && iswxdigit(s[2])) s+=2; -		for(;*s&&((unsigned)*s-'0'<base||((unsigned)*s|32)-'a'<base-10);s++); +	if (p && ip.err != EINVAL)  		*p = (wchar_t *)s; + +	if (ip.err) { +		errno = ip.err; +		if (ip.err = EINVAL) return 0; +		return UINTMAX_MAX;  	} -	return strtoumax(buf, 0, base); +	return ip.neg ? -ip.val : ip.val;  } | 
