Commit 1dff46d6 authored by Alexey Dobriyan's avatar Alexey Dobriyan Committed by Linus Torvalds

lib/kstrtox: common code between kstrto*() and simple_strto*() functions

Currently termination logic (\0 or \n\0) is hardcoded in _kstrtoull(),
avoid that for code reuse between kstrto*() and simple_strtoull().
Essentially, make them different only in termination logic.

simple_strtoull() (and scanf(), BTW) ignores integer overflow, that's a
bug we currently don't have guts to fix, making KSTRTOX_OVERFLOW hack
necessary.

Almost forgot: patch shrinks code size by about ~80 bytes on x86_64.
Signed-off-by: default avatarAlexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b3c49c05
...@@ -18,26 +18,40 @@ ...@@ -18,26 +18,40 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "kstrtox.h"
static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
{ {
unsigned long long acc; if (*base == 0) {
int ok;
if (base == 0) {
if (s[0] == '0') { if (s[0] == '0') {
if (_tolower(s[1]) == 'x' && isxdigit(s[2])) if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
base = 16; *base = 16;
else else
base = 8; *base = 8;
} else } else
base = 10; *base = 10;
} }
if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
s += 2; s += 2;
return s;
}
acc = 0; /*
ok = 0; * Convert non-negative integer string representation in explicitly given radix
* to an integer.
* Return number of characters consumed maybe or-ed with overflow bit.
* If overflow occurs, result integer (incorrect) is still returned.
*
* Don't you dare use this function.
*/
unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res)
{
unsigned int rv;
int overflow;
*res = 0;
rv = 0;
overflow = 0;
while (*s) { while (*s) {
unsigned int val; unsigned int val;
...@@ -45,23 +59,40 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) ...@@ -45,23 +59,40 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
val = *s - '0'; val = *s - '0';
else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
val = _tolower(*s) - 'a' + 10; val = _tolower(*s) - 'a' + 10;
else if (*s == '\n' && *(s + 1) == '\0')
break;
else else
return -EINVAL; break;
if (val >= base) if (val >= base)
return -EINVAL; break;
if (acc > div_u64(ULLONG_MAX - val, base)) if (*res > div_u64(ULLONG_MAX - val, base))
return -ERANGE; overflow = 1;
acc = acc * base + val; *res = *res * base + val;
ok = 1; rv++;
s++; s++;
} }
if (!ok) if (overflow)
rv |= KSTRTOX_OVERFLOW;
return rv;
}
static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
unsigned long long _res;
unsigned int rv;
s = _parse_integer_fixup_radix(s, &base);
rv = _parse_integer(s, base, &_res);
if (rv & KSTRTOX_OVERFLOW)
return -ERANGE;
rv &= ~KSTRTOX_OVERFLOW;
if (rv == 0)
return -EINVAL;
s += rv;
if (*s == '\n')
s++;
if (*s)
return -EINVAL; return -EINVAL;
*res = acc; *res = _res;
return 0; return 0;
} }
......
#ifndef _LIB_KSTRTOX_H
#define _LIB_KSTRTOX_H
#define KSTRTOX_OVERFLOW (1U << 31)
const char *_parse_integer_fixup_radix(const char *s, unsigned int *base);
unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res);
#endif
...@@ -31,17 +31,7 @@ ...@@ -31,17 +31,7 @@
#include <asm/div64.h> #include <asm/div64.h>
#include <asm/sections.h> /* for dereference_function_descriptor() */ #include <asm/sections.h> /* for dereference_function_descriptor() */
static unsigned int simple_guess_base(const char *cp) #include "kstrtox.h"
{
if (cp[0] == '0') {
if (_tolower(cp[1]) == 'x' && isxdigit(cp[2]))
return 16;
else
return 8;
} else {
return 10;
}
}
/** /**
* simple_strtoull - convert a string to an unsigned long long * simple_strtoull - convert a string to an unsigned long long
...@@ -51,23 +41,14 @@ static unsigned int simple_guess_base(const char *cp) ...@@ -51,23 +41,14 @@ static unsigned int simple_guess_base(const char *cp)
*/ */
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{ {
unsigned long long result = 0; unsigned long long result;
unsigned int rv;
if (!base) cp = _parse_integer_fixup_radix(cp, &base);
base = simple_guess_base(cp); rv = _parse_integer(cp, base, &result);
/* FIXME */
cp += (rv & ~KSTRTOX_OVERFLOW);
if (base == 16 && cp[0] == '0' && _tolower(cp[1]) == 'x')
cp += 2;
while (isxdigit(*cp)) {
unsigned int value;
value = isdigit(*cp) ? *cp - '0' : _tolower(*cp) - 'a' + 10;
if (value >= base)
break;
result = result * base + value;
cp++;
}
if (endp) if (endp)
*endp = (char *)cp; *endp = (char *)cp;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment