Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alexey Dobriyan and committed by
Linus Torvalds
1dff46d6 b3c49c05

+68 -48
+53 -22
lib/kstrtox.c
··· 18 18 #include <linux/module.h> 19 19 #include <linux/types.h> 20 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 - int ok; 26 - 27 - if (base == 0) { 25 + if (*base == 0) { 28 26 if (s[0] == '0') { 29 27 if (_tolower(s[1]) == 'x' && isxdigit(s[2])) 30 - base = 16; 28 + *base = 16; 31 29 else 32 - base = 8; 30 + *base = 8; 33 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 35 s += 2; 36 + return s; 37 + } 38 38 39 - acc = 0; 40 - ok = 0; 39 + /* 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 55 while (*s) { 42 56 unsigned int val; 43 57 ··· 59 45 val = *s - '0'; 60 46 else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') 61 47 val = _tolower(*s) - 'a' + 10; 62 - else if (*s == '\n' && *(s + 1) == '\0') 63 - break; 64 48 else 65 - return -EINVAL; 49 + break; 66 50 67 51 if (val >= base) 68 - return -EINVAL; 69 - if (acc > div_u64(ULLONG_MAX - val, base)) 70 - return -ERANGE; 71 - acc = acc * base + val; 72 - ok = 1; 73 - 52 + break; 53 + if (*res > div_u64(ULLONG_MAX - val, base)) 54 + overflow = 1; 55 + *res = *res * base + val; 56 + rv++; 74 57 s++; 75 58 } 76 - if (!ok) 59 + if (overflow) 60 + rv |= KSTRTOX_OVERFLOW; 61 + return rv; 62 + } 63 + 64 + static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) 65 + { 66 + unsigned long long _res; 67 + unsigned int rv; 68 + 69 + s = _parse_integer_fixup_radix(s, &base); 70 + rv = _parse_integer(s, base, &_res); 71 + if (rv & KSTRTOX_OVERFLOW) 72 + return -ERANGE; 73 + rv &= ~KSTRTOX_OVERFLOW; 74 + if (rv == 0) 77 75 return -EINVAL; 78 - *res = acc; 76 + s += rv; 77 + if (*s == '\n') 78 + s++; 79 + if (*s) 80 + return -EINVAL; 81 + *res = _res; 79 82 return 0; 80 83 } 81 84
+8
lib/kstrtox.h
··· 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
+7 -26
lib/vsprintf.c
··· 31 31 #include <asm/div64.h> 32 32 #include <asm/sections.h> /* for dereference_function_descriptor() */ 33 33 34 - static unsigned int simple_guess_base(const char *cp) 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 - } 34 + #include "kstrtox.h" 45 35 46 36 /** 47 37 * simple_strtoull - convert a string to an unsigned long long ··· 41 51 */ 42 52 unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) 43 53 { 44 - unsigned long long result = 0; 54 + unsigned long long result; 55 + unsigned int rv; 45 56 46 - if (!base) 47 - base = simple_guess_base(cp); 57 + cp = _parse_integer_fixup_radix(cp, &base); 58 + rv = _parse_integer(cp, base, &result); 59 + /* FIXME */ 60 + cp += (rv & ~KSTRTOX_OVERFLOW); 48 61 49 - if (base == 16 && cp[0] == '0' && _tolower(cp[1]) == 'x') 50 - cp += 2; 51 - 52 - while (isxdigit(*cp)) { 53 - unsigned int value; 54 - 55 - value = isdigit(*cp) ? *cp - '0' : _tolower(*cp) - 'a' + 10; 56 - if (value >= base) 57 - break; 58 - result = result * base + value; 59 - cp++; 60 - } 61 62 if (endp) 62 63 *endp = (char *)cp; 63 64