Skip to content

Commit

Permalink
lib/kstrtox: common code between kstrto*() and simple_strto*() functions
Browse files Browse the repository at this point in the history
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: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Alexey Dobriyan authored and torvalds committed Nov 1, 2011
1 parent b3c49c0 commit 1dff46d
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 48 deletions.
75 changes: 53 additions & 22 deletions lib/kstrtox.c
Expand Up @@ -18,50 +18,81 @@
#include <linux/module.h>
#include <linux/types.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;
int ok;

if (base == 0) {
if (*base == 0) {
if (s[0] == '0') {
if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
base = 16;
*base = 16;
else
base = 8;
*base = 8;
} 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;
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) {
unsigned int val;

if ('0' <= *s && *s <= '9')
val = *s - '0';
else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
val = _tolower(*s) - 'a' + 10;
else if (*s == '\n' && *(s + 1) == '\0')
break;
else
return -EINVAL;
break;

if (val >= base)
return -EINVAL;
if (acc > div_u64(ULLONG_MAX - val, base))
return -ERANGE;
acc = acc * base + val;
ok = 1;

break;
if (*res > div_u64(ULLONG_MAX - val, base))
overflow = 1;
*res = *res * base + val;
rv++;
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;
*res = acc;
*res = _res;
return 0;
}

Expand Down
8 changes: 8 additions & 0 deletions lib/kstrtox.h
@@ -0,0 +1,8 @@
#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
33 changes: 7 additions & 26 deletions lib/vsprintf.c
Expand Up @@ -31,17 +31,7 @@
#include <asm/div64.h>
#include <asm/sections.h> /* for dereference_function_descriptor() */

static unsigned int simple_guess_base(const char *cp)
{
if (cp[0] == '0') {
if (_tolower(cp[1]) == 'x' && isxdigit(cp[2]))
return 16;
else
return 8;
} else {
return 10;
}
}
#include "kstrtox.h"

/**
* simple_strtoull - convert a string to an unsigned long long
Expand All @@ -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 result = 0;
unsigned long long result;
unsigned int rv;

if (!base)
base = simple_guess_base(cp);
cp = _parse_integer_fixup_radix(cp, &base);
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)
*endp = (char *)cp;

Expand Down

0 comments on commit 1dff46d

Please sign in to comment.