mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
lib: kstrtox: add kstrtoudec64() and kstrtodec64()
Add helpers that parses decimal numbers into 64-bit number, i.e., decimal point numbers with pre-defined scale are parsed into a 64-bit value (fixed precision). After the decimal point, digits beyond the specified scale are ignored. Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
This commit is contained in:
committed by
Jonathan Cameron
parent
1eafa5eb97
commit
b659b96320
@@ -97,6 +97,9 @@ int __must_check kstrtou8(const char *s, unsigned int base, u8 *res);
|
||||
int __must_check kstrtos8(const char *s, unsigned int base, s8 *res);
|
||||
int __must_check kstrtobool(const char *s, bool *res);
|
||||
|
||||
int __must_check kstrtoudec64(const char *s, unsigned int scale, u64 *res);
|
||||
int __must_check kstrtodec64(const char *s, unsigned int scale, s64 *res);
|
||||
|
||||
int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res);
|
||||
int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res);
|
||||
int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res);
|
||||
|
||||
104
lib/kstrtox.c
104
lib/kstrtox.c
@@ -17,6 +17,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
@@ -394,6 +395,109 @@ int kstrtobool(const char *s, bool *res)
|
||||
}
|
||||
EXPORT_SYMBOL(kstrtobool);
|
||||
|
||||
static int _kstrtoudec64(const char *s, unsigned int scale, u64 *res)
|
||||
{
|
||||
unsigned int rv_int, rv_frac;
|
||||
u64 _res = 0;
|
||||
|
||||
rv_int = _parse_integer(s, 10, &_res);
|
||||
if (rv_int & KSTRTOX_OVERFLOW)
|
||||
return -ERANGE;
|
||||
s += rv_int;
|
||||
|
||||
if (*s == '.')
|
||||
s++; /* skip decimal point */
|
||||
|
||||
rv_frac = _parse_integer(s, 10, &_res, scale, _res);
|
||||
if (rv_frac & KSTRTOX_OVERFLOW)
|
||||
return -ERANGE;
|
||||
s += rv_frac;
|
||||
|
||||
/*
|
||||
* Check input beyond rv_int and rv_frac to cover cases like ".5" with
|
||||
* scale 0, which is considered a valid input, being parsed as 0.
|
||||
*/
|
||||
if (!rv_int && !rv_frac && !isdigit(*s))
|
||||
return -EINVAL;
|
||||
|
||||
while (isdigit(*s)) /* truncate digits */
|
||||
s++;
|
||||
|
||||
if (*s == '\n')
|
||||
s++;
|
||||
if (*s)
|
||||
return -EINVAL;
|
||||
|
||||
if (_res && ((scale - rv_frac) > 19 /* log10(2^64) = 19.26 */ ||
|
||||
check_mul_overflow(_res, int_pow(10, scale - rv_frac), &_res)))
|
||||
return -ERANGE;
|
||||
|
||||
*res = _res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kstrtoudec64() - Convert a string to an unsigned 64-bit scaled decimal value.
|
||||
* @s: The start of the string. The string must be null-terminated, and may also
|
||||
* include a single newline before its terminating null. The first character
|
||||
* may also be a plus sign, but not a minus sign.
|
||||
* @scale: The number of digits to the right of the decimal point.
|
||||
* @res: Where to write the result of the conversion on success.
|
||||
*
|
||||
* For example, a scale of 3 with input "123.45" results in 123450. Note that
|
||||
* trailing zeros in the fractional part input to match the scale are not
|
||||
* required. Also, digits beyond the specified scale are ignored.
|
||||
*
|
||||
* Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
|
||||
*/
|
||||
noinline
|
||||
int kstrtoudec64(const char *s, unsigned int scale, u64 *res)
|
||||
{
|
||||
if (s[0] == '+')
|
||||
s++;
|
||||
return _kstrtoudec64(s, scale, res);
|
||||
}
|
||||
EXPORT_SYMBOL(kstrtoudec64);
|
||||
|
||||
/**
|
||||
* kstrtodec64() - Convert a string to a signed 64-bit scaled decimal value.
|
||||
* @s: The start of the string. The string must be null-terminated, and may also
|
||||
* include a single newline before its terminating null. The first character
|
||||
* may also be a plus sign or a minus sign.
|
||||
* @scale: The number of digits to the right of the decimal point.
|
||||
* @res: Where to write the result of the conversion on success.
|
||||
*
|
||||
* For example, a scale of 4 with input "-3.141592" results in -31415. Note
|
||||
* that digits beyond the specified scale are ignored. Also, trailing zeros in
|
||||
* the fractional part input to match the scale are not required.
|
||||
*
|
||||
* Return: 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
|
||||
*/
|
||||
noinline
|
||||
int kstrtodec64(const char *s, unsigned int scale, s64 *res)
|
||||
{
|
||||
u64 tmp;
|
||||
int rv;
|
||||
|
||||
if (s[0] == '-') {
|
||||
rv = _kstrtoudec64(s + 1, scale, &tmp);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
if ((s64)-tmp > 0)
|
||||
return -ERANGE;
|
||||
*res = -tmp;
|
||||
} else {
|
||||
rv = kstrtoudec64(s, scale, &tmp);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
if ((s64)tmp < 0)
|
||||
return -ERANGE;
|
||||
*res = tmp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(kstrtodec64);
|
||||
|
||||
/*
|
||||
* Since "base" would be a nonsense argument, this open-codes the
|
||||
* _from_user helper instead of using the helper macro below.
|
||||
|
||||
Reference in New Issue
Block a user