diff options
| author | Alexey Dobriyan <adobriyan@gmail.com> | 2011-10-31 20:12:28 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-31 20:30:56 -0400 |
| commit | 1dff46d6987484eaa31f2fb1425216ba06418be3 (patch) | |
| tree | 421e53d64a066b1f756156bb1d37154c0a5eab6a /lib | |
| parent | b3c49c05b737887443c894c66635ae68dcdf0027 (diff) | |
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>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/kstrtox.c | 75 | ||||
| -rw-r--r-- | lib/kstrtox.h | 8 | ||||
| -rw-r--r-- | lib/vsprintf.c | 33 |
3 files changed, 68 insertions, 48 deletions
diff --git a/lib/kstrtox.c b/lib/kstrtox.c index 5e066759f551..7a94c8f14e29 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c | |||
| @@ -18,26 +18,40 @@ | |||
| 18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 19 | #include <linux/types.h> | 19 | #include <linux/types.h> |
| 20 | #include <asm/uaccess.h> | 20 | #include <asm/uaccess.h> |
| 21 | #include "kstrtox.h" | ||
| 21 | 22 | ||
| 22 | static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) | 23 | const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) |
| 23 | { | 24 | { |
| 24 | unsigned long long acc; | 25 | if (*base == 0) { |
| 25 | int ok; | ||
| 26 | |||
| 27 | if (base == 0) { | ||
| 28 | if (s[0] == '0') { | 26 | if (s[0] == '0') { |
| 29 | if (_tolower(s[1]) == 'x' && isxdigit(s[2])) | 27 | if (_tolower(s[1]) == 'x' && isxdigit(s[2])) |
| 30 | base = 16; | 28 | *base = 16; |
| 31 | else | 29 | else |
| 32 | base = 8; | 30 | *base = 8; |
| 33 | } else | 31 | } else |
| 34 | base = 10; | 32 | *base = 10; |
| 35 | } | 33 | } |
| 36 | if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') | 34 | if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') |
| 37 | s += 2; | 35 | s += 2; |
| 36 | return s; | ||
| 37 | } | ||
| 38 | 38 | ||
| 39 | acc = 0; | 39 | /* |
| 40 | ok = 0; | 40 | * Convert non-negative integer string representation in explicitly given radix |
| 41 | * to an integer. | ||
| 42 | * Return number of characters consumed maybe or-ed with overflow bit. | ||
| 43 | * If overflow occurs, result integer (incorrect) is still returned. | ||
| 44 | * | ||
| 45 | * Don't you dare use this function. | ||
| 46 | */ | ||
| 47 | unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res) | ||
| 48 | { | ||
| 49 | unsigned int rv; | ||
| 50 | int overflow; | ||
| 51 | |||
| 52 | *res = 0; | ||
| 53 | rv = 0; | ||
| 54 | overflow = 0; | ||
| 41 | while (*s) { | 55 | while (*s) { |
| 42 | unsigned int val; | 56 | unsigned int val; |
| 43 | 57 | ||
| @@ -45,23 +59,40 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) | |||
| 45 | val = *s - '0'; | 59 | val = *s - '0'; |
| 46 | else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') | 60 | else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') |
| 47 | val = _tolower(*s) - 'a' + 10; | 61 | val = _tolower(*s) - 'a' + 10; |
| 48 | else if (*s == '\n' && *(s + 1) == '\0') | ||
| 49 | break; | ||
| 50 | else | 62 | else |
| 51 | return -EINVAL; | 63 | break; |
| 52 | 64 | ||
| 53 | if (val >= base) | 65 | if (val >= base) |
| 54 | return -EINVAL; | 66 | break; |
| 55 | if (acc > div_u64(ULLONG_MAX - val, base)) | 67 | if (*res > div_u64(ULLONG_MAX - val, base)) |
| 56 | return -ERANGE; | 68 | overflow = 1; |
| 57 | acc = acc * base + val; | 69 | *res = *res * base + val; |
| 58 | ok = 1; | 70 | rv++; |
| 59 | |||
| 60 | s++; | 71 | s++; |
| 61 | } | 72 | } |
| 62 | if (!ok) | 73 | if (overflow) |
| 74 | rv |= KSTRTOX_OVERFLOW; | ||
| 75 | return rv; | ||
| 76 | } | ||
| 77 | |||
| 78 | static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) | ||
| 79 | { | ||
| 80 | unsigned long long _res; | ||
| 81 | unsigned int rv; | ||
| 82 | |||
| 83 | s = _parse_integer_fixup_radix(s, &base); | ||
| 84 | rv = _parse_integer(s, base, &_res); | ||
| 85 | if (rv & KSTRTOX_OVERFLOW) | ||
| 86 | return -ERANGE; | ||
| 87 | rv &= ~KSTRTOX_OVERFLOW; | ||
| 88 | if (rv == 0) | ||
| 89 | return -EINVAL; | ||
| 90 | s += rv; | ||
| 91 | if (*s == '\n') | ||
| 92 | s++; | ||
| 93 | if (*s) | ||
| 63 | return -EINVAL; | 94 | return -EINVAL; |
| 64 | *res = acc; | 95 | *res = _res; |
| 65 | return 0; | 96 | return 0; |
| 66 | } | 97 | } |
| 67 | 98 | ||
diff --git a/lib/kstrtox.h b/lib/kstrtox.h new file mode 100644 index 000000000000..f13eeeaf441d --- /dev/null +++ b/lib/kstrtox.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef _LIB_KSTRTOX_H | ||
| 2 | #define _LIB_KSTRTOX_H | ||
| 3 | |||
| 4 | #define KSTRTOX_OVERFLOW (1U << 31) | ||
| 5 | const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); | ||
| 6 | unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res); | ||
| 7 | |||
| 8 | #endif | ||
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index d7222a9c8267..c1a3927326eb 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c | |||
| @@ -31,17 +31,7 @@ | |||
| 31 | #include <asm/div64.h> | 31 | #include <asm/div64.h> |
| 32 | #include <asm/sections.h> /* for dereference_function_descriptor() */ | 32 | #include <asm/sections.h> /* for dereference_function_descriptor() */ |
| 33 | 33 | ||
| 34 | static unsigned int simple_guess_base(const char *cp) | 34 | #include "kstrtox.h" |
| 35 | { | ||
| 36 | if (cp[0] == '0') { | ||
| 37 | if (_tolower(cp[1]) == 'x' && isxdigit(cp[2])) | ||
| 38 | return 16; | ||
| 39 | else | ||
| 40 | return 8; | ||
| 41 | } else { | ||
| 42 | return 10; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | 35 | ||
| 46 | /** | 36 | /** |
| 47 | * simple_strtoull - convert a string to an unsigned long long | 37 | * simple_strtoull - convert a string to an unsigned long long |
| @@ -51,23 +41,14 @@ static unsigned int simple_guess_base(const char *cp) | |||
| 51 | */ | 41 | */ |
| 52 | unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) | 42 | unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) |
| 53 | { | 43 | { |
| 54 | unsigned long long result = 0; | 44 | unsigned long long result; |
| 45 | unsigned int rv; | ||
| 55 | 46 | ||
| 56 | if (!base) | 47 | cp = _parse_integer_fixup_radix(cp, &base); |
| 57 | base = simple_guess_base(cp); | 48 | rv = _parse_integer(cp, base, &result); |
| 49 | /* FIXME */ | ||
| 50 | cp += (rv & ~KSTRTOX_OVERFLOW); | ||
| 58 | 51 | ||
| 59 | if (base == 16 && cp[0] == '0' && _tolower(cp[1]) == 'x') | ||
| 60 | cp += 2; | ||
| 61 | |||
| 62 | while (isxdigit(*cp)) { | ||
| 63 | unsigned int value; | ||
| 64 | |||
| 65 | value = isdigit(*cp) ? *cp - '0' : _tolower(*cp) - 'a' + 10; | ||
| 66 | if (value >= base) | ||
| 67 | break; | ||
| 68 | result = result * base + value; | ||
| 69 | cp++; | ||
| 70 | } | ||
| 71 | if (endp) | 52 | if (endp) |
| 72 | *endp = (char *)cp; | 53 | *endp = (char *)cp; |
| 73 | 54 | ||
