Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1dff46d6 authored by Alexey Dobriyan's avatar Alexey Dobriyan Committed by Linus Torvalds
Browse files

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: default avatarAlexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b3c49c05
Loading
Loading
Loading
Loading
+53 −22
Original line number Original line Diff line number Diff line
@@ -18,26 +18,40 @@
#include <linux/module.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/types.h>
#include <asm/uaccess.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;
	if (*base == 0) {
	int ok;

	if (base == 0) {
		if (s[0] == '0') {
		if (s[0] == '0') {
			if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
			if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
				base = 16;
				*base = 16;
			else
			else
				base = 8;
				*base = 8;
		} else
		} 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;
		s += 2;
	return s;
}

/*
 * 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;


	acc = 0;
	*res = 0;
	ok = 0;
	rv = 0;
	overflow = 0;
	while (*s) {
	while (*s) {
		unsigned int val;
		unsigned int val;


@@ -45,23 +59,40 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
			val = *s - '0';
			val = *s - '0';
		else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
		else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
			val = _tolower(*s) - 'a' + 10;
			val = _tolower(*s) - 'a' + 10;
		else if (*s == '\n' && *(s + 1) == '\0')
			break;
		else
		else
			return -EINVAL;
			break;


		if (val >= base)
		if (val >= base)
			return -EINVAL;
			break;
		if (acc > div_u64(ULLONG_MAX - val, base))
		if (*res > div_u64(ULLONG_MAX - val, base))
			return -ERANGE;
			overflow = 1;
		acc = acc * base + val;
		*res = *res * base + val;
		ok = 1;
		rv++;

		s++;
		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;
		return -EINVAL;
	*res = acc;
	*res = _res;
	return 0;
	return 0;
}
}


lib/kstrtox.h

0 → 100644
+8 −0
Original line number Original line Diff line number Diff line
#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
+7 −26
Original line number Original line Diff line number Diff line
@@ -31,17 +31,7 @@
#include <asm/div64.h>
#include <asm/div64.h>
#include <asm/sections.h>	/* for dereference_function_descriptor() */
#include <asm/sections.h>	/* for dereference_function_descriptor() */


static unsigned int simple_guess_base(const char *cp)
#include "kstrtox.h"
{
	if (cp[0] == '0') {
		if (_tolower(cp[1]) == 'x' && isxdigit(cp[2]))
			return 16;
		else
			return 8;
	} else {
		return 10;
	}
}


/**
/**
 * simple_strtoull - convert a string to an unsigned long long
 * 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 simple_strtoull(const char *cp, char **endp, unsigned int base)
{
{
	unsigned long long result = 0;
	unsigned long long result;
	unsigned int rv;


	if (!base)
	cp = _parse_integer_fixup_radix(cp, &base);
		base = simple_guess_base(cp);
	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)
	if (endp)
		*endp = (char *)cp;
		*endp = (char *)cp;