![Alexey Dobriyan](/assets/img/avatar_default.png)
1. simple_strto*() do not contain overflow checks and crufty, libc way to indicate failure. 2. strict_strto*() also do not have overflow checks but the name and comments pretend they do. 3. Both families have only "long long" and "long" variants, but users want strtou8() 4. Both "simple" and "strict" prefixes are wrong: Simple doesn't exactly say what's so simple, strict should not exist because conversion should be strict by default. The solution is to use "k" prefix and add convertors for more types. Enter kstrtoull() kstrtoll() kstrtoul() kstrtol() kstrtouint() kstrtoint() kstrtou64() kstrtos64() kstrtou32() kstrtos32() kstrtou16() kstrtos16() kstrtou8() kstrtos8() Include runtime testsuite (somewhat incomplete) as well. strict_strto*() become deprecated, stubbed to kstrto*() and eventually will be removed altogether. Use kstrto*() in code today! Note: on some archs _kstrtoul() and _kstrtol() are left in tree, even if they'll be unused at runtime. This is temporarily solution, because I don't want to hardcode list of archs where these functions aren't needed. Current solution with sizeof() and __alignof__ at least always works. 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>
228 lines
4.1 KiB
C
228 lines
4.1 KiB
C
/*
|
|
* Convert integer string representation to an integer.
|
|
* If an integer doesn't fit into specified type, -E is returned.
|
|
*
|
|
* Integer starts with optional sign.
|
|
* kstrtou*() functions do not accept sign "-".
|
|
*
|
|
* Radix 0 means autodetection: leading "0x" implies radix 16,
|
|
* leading "0" implies radix 8, otherwise radix is 10.
|
|
* Autodetection hints work after optional sign, but not before.
|
|
*
|
|
* If -E is returned, result is not touched.
|
|
*/
|
|
#include <linux/ctype.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/math64.h>
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
|
|
static inline char _tolower(const char c)
|
|
{
|
|
return c | 0x20;
|
|
}
|
|
|
|
static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
|
|
{
|
|
unsigned long long acc;
|
|
int ok;
|
|
|
|
if (base == 0) {
|
|
if (s[0] == '0') {
|
|
if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
|
|
base = 16;
|
|
else
|
|
base = 8;
|
|
} else
|
|
base = 10;
|
|
}
|
|
if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
|
|
s += 2;
|
|
|
|
acc = 0;
|
|
ok = 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') {
|
|
if (*(s + 1) == '\0')
|
|
break;
|
|
else
|
|
return -EINVAL;
|
|
} else
|
|
return -EINVAL;
|
|
|
|
if (val >= base)
|
|
return -EINVAL;
|
|
if (acc > div_u64(ULLONG_MAX - val, base))
|
|
return -ERANGE;
|
|
acc = acc * base + val;
|
|
ok = 1;
|
|
|
|
s++;
|
|
}
|
|
if (!ok)
|
|
return -EINVAL;
|
|
*res = acc;
|
|
return 0;
|
|
}
|
|
|
|
int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
|
|
{
|
|
if (s[0] == '+')
|
|
s++;
|
|
return _kstrtoull(s, base, res);
|
|
}
|
|
EXPORT_SYMBOL(kstrtoull);
|
|
|
|
int kstrtoll(const char *s, unsigned int base, long long *res)
|
|
{
|
|
unsigned long long tmp;
|
|
int rv;
|
|
|
|
if (s[0] == '-') {
|
|
rv = _kstrtoull(s + 1, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if ((long long)(-tmp) >= 0)
|
|
return -ERANGE;
|
|
*res = -tmp;
|
|
} else {
|
|
rv = kstrtoull(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if ((long long)tmp < 0)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(kstrtoll);
|
|
|
|
/* Internal, do not use. */
|
|
int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
|
|
{
|
|
unsigned long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoull(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (unsigned long long)(unsigned long)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(_kstrtoul);
|
|
|
|
/* Internal, do not use. */
|
|
int _kstrtol(const char *s, unsigned int base, long *res)
|
|
{
|
|
long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoll(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (long long)(long)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(_kstrtol);
|
|
|
|
int kstrtouint(const char *s, unsigned int base, unsigned int *res)
|
|
{
|
|
unsigned long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoull(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (unsigned long long)(unsigned int)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(kstrtouint);
|
|
|
|
int kstrtoint(const char *s, unsigned int base, int *res)
|
|
{
|
|
long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoll(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (long long)(int)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(kstrtoint);
|
|
|
|
int kstrtou16(const char *s, unsigned int base, u16 *res)
|
|
{
|
|
unsigned long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoull(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (unsigned long long)(u16)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(kstrtou16);
|
|
|
|
int kstrtos16(const char *s, unsigned int base, s16 *res)
|
|
{
|
|
long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoll(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (long long)(s16)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(kstrtos16);
|
|
|
|
int kstrtou8(const char *s, unsigned int base, u8 *res)
|
|
{
|
|
unsigned long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoull(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (unsigned long long)(u8)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(kstrtou8);
|
|
|
|
int kstrtos8(const char *s, unsigned int base, s8 *res)
|
|
{
|
|
long long tmp;
|
|
int rv;
|
|
|
|
rv = kstrtoll(s, base, &tmp);
|
|
if (rv < 0)
|
|
return rv;
|
|
if (tmp != (long long)(s8)tmp)
|
|
return -ERANGE;
|
|
*res = tmp;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(kstrtos8);
|