Permalink
Browse files

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: 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...
1 parent b3c49c0 commit 1dff46d6987484eaa31f2fb1425216ba06418be3 Alexey Dobriyan committed with torvalds Nov 1, 2011
Showing with 68 additions and 48 deletions.
  1. +53 −22 lib/kstrtox.c
  2. +8 −0 lib/kstrtox.h
  3. +7 −26 lib/vsprintf.c
View
@@ -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;
}
View
@@ -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
View
@@ -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
@@ -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;

0 comments on commit 1dff46d

Please sign in to comment.