kstrtox: common code between kstrto*() and simple_strto*() functions
Subsystems (2)
PATCH 1/1 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 <[email protected]>
---
lib/kstrtox.c | 75 ++++++++++++++++++++++++++++++++++++++++----------------- lib/kstrtox.h | 8 ++++++ lib/vsprintf.c | 33 +++++-------------------- 3 files changed, 68 insertions(+), 48 deletions(-)
lib/kstrtox.c +53 -22
@@ -18,26 +18,40 @@#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 ; }
lib/kstrtox.h new file +8 -0
@@ -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
lib/vsprintf.c +7 -26
@@ -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;