[uClibc]new version of strtol and strtoul
Manuel Novoa III
mnovoa3 at bellsouth.net
Wed Dec 13 05:06:57 UTC 2000
Erik,
Here's an implementation of strtol and strtoul (trickier that I thought).
It seemed best to implement one worker function to handle both.
I didn't start out that way, but then I found out that it strtoul
was expected to handle a sign. Most unintuitive...
Anyway, I ran test/stdlib/teststrtol.c for this (endptr) implementation
and the implementation in libc-2.1.3. The output was identical.
Here are some sizes for i386 -Os -fomit-frame-pointer.
Current versions:
text data bss dec hex filename
575 0 0 575 23f strtol.o
539 0 0 539 21b strtoul.o
1114 total
New version:
text data bss dec hex filename
351 0 0 351 15f strto_l.o (no endptr or errno)
383 0 0 383 17f strto_l.o (endptr but no errno)
415 0 0 415 19f strto_l.o (endptr and errno)
So I have a full implementation of both functions in less space than each
one currently takes. You could even save a few more bytes if you implemented
strtol and strtoul as inlines in stdlib.h.
New version minus the mini functions for strtol and strtoul.
text data bss dec hex filename
304 0 0 304 130 strto_l.o (no endptr or errno)
336 0 0 336 150 strto_l.o (endptr but no errno)
366 0 0 366 16e strto_l.o (endptr and errno)
Manuel
------------------------ stdlib/strto_l.c -------------------------------
/*
* Copyright (C) 2000 Manuel Novoa III
*
* Notes:
*
* The primary objective of this implementation was minimal size.
*
* Note: Assumes char layout 0-9.*A-Z.*a-z for ordinals values.
*
* There are a couple of compile-time options below.
*
*/
/*****************************************************************************/
/* OPTIONS */
/*****************************************************************************/
/* Set if we want strtod to set errno appropriately. */
/* NOTE: Implies _STRTO_ENDPTR below */
#define _STRTO_ERRNO 0
/* Set if we want support for the endptr arg. */
/* Implied by _STRTO_ERRNO. */
#define _STRTO_ENDPTR 1
/*****************************************************************************/
/* Don't change anything that follows. */
/*****************************************************************************/
#if _STRTO_ERRNO
#undef _STRTO_ENDPTR
#define _STRTO_ENDPTR 1
#endif
/*****************************************************************************/
/* Are there actually an machines where this might fail? */
#if 'A' > 'a'
#error ordering assumption violated : 'A' > 'a'
#endif
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#if _STRTO_ERRNO
#include <errno.h>
#endif
/*
* This is the main work fuction which handles both strtol (uflag = 0) and
* strtoul (uflag = 1).
*/
unsigned long _strto_l(const char *str, char **endptr, int base, int uflag)
{
unsigned long number = 0;
unsigned long cutoff;
char *pos = (char *) str;
#if _STRTO_ENDPTR
char *fail_char = (char *) str;
#endif
int digit, cutoff_digit;
int negative;
while (isspace(*pos)) { /* skip leading whitespace */
++pos;
}
/* handle optional sign */
negative = 0;
switch(*pos) {
case '-': negative = 1; /* fall through to increment pos */
case '+': ++pos;
}
if ((base == 16) && (*pos == '0')) { /* handle option prefix */
++pos;
#if _STRTO_ENDPTR
fail_char = pos;
#endif
if ((*pos == 'x') || (*pos == 'X')) {
++pos;
}
}
if (base == 0) { /* dynamic base */
base = 10; /* default is 10 */
if (*pos == '0') {
++pos;
base -= 2; /* now base is 8 (or 16) */
#if _STRTO_ENDPTR
fail_char = pos;
#endif
if ((*pos == 'x') || (*pos == 'X')) {
base += 8; /* base is 16 */
++pos;
}
}
}
if ((base < 2) || (base > 36)) { /* illegal base */
goto DONE;
}
cutoff = ULONG_MAX / base;
cutoff_digit = ULONG_MAX - cutoff * base;
while (1) {
digit = 40;
if ((*pos >= '0') && (*pos <= '9')) {
digit = (*pos - '0');
} else if (*pos >= 'a') {
digit = (*pos - 'a' + 10);
} else if (*pos >= 'A') {
digit = (*pos - 'A' + 10);
} else break;
if (digit >= base) {
break;
}
++pos;
#if _STRTO_ENDPTR
fail_char = pos;
#endif
/* adjust number, with overflow check */
if ((number > cutoff)
|| ((number == cutoff) && (digit > cutoff_digit))) {
number = ULONG_MAX;
if (uflag) {
negative = 0; /* since unsigned returns ULONG_MAX */
}
#if _STRTO_ERRNO
errno = ERANGE;
#endif
} else {
number = number * base + digit;
}
}
DONE:
#if _STRTO_ENDPTR
if (endptr) {
*endptr = fail_char;
}
#endif
if (negative) {
if (!uflag && (number > ((unsigned long)(-(1+LONG_MIN)))+1)) {
#if _STRTO_ERRNO
errno = ERANGE;
#endif
return (unsigned long) LONG_MIN;
}
return (unsigned long)(-((long)number));
} else {
if (!uflag && (number > (unsigned long) LONG_MAX)) {
#if _STRTO_ERRNO
errno = ERANGE;
#endif
return LONG_MAX;
}
return number;
}
}
unsigned long strtoul(const char *str, char **endptr, int base)
{
return _strto_l(str, endptr, base, 1);
}
long strtol(const char *str, char **endptr, int base)
{
return _strto_l(str, endptr, base, 0);
}
More information about the uClibc
mailing list