lib/kstrtox: common code between kstrto*() and simple_strto*() functions
Alexey Dobriyan [Tue, 1 Nov 2011 00:12:28 +0000 (17:12 -0700)]
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>

lib/kstrtox.c
lib/kstrtox.h [new file with mode: 0644]
lib/vsprintf.c

index 5e06675..7a94c8f 100644 (file)
 #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;
 
@@ -45,23 +59,40 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
                        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;
 }
 
diff --git a/lib/kstrtox.h b/lib/kstrtox.h
new file mode 100644 (file)
index 0000000..f13eeea
--- /dev/null
@@ -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
index d7222a9..c1a3927 100644 (file)
 #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;