forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kstrto*: converting strings to integers done (hopefully) right
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 <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
Showing
8 changed files
with
1,039 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
* 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); |
Oops, something went wrong.