Skip to content
vsprintf.c 47 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*
 *  linux/lib/vsprintf.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
 * Wirzenius wrote this portably, Torvalds fucked it up :-)
 */

Linus Torvalds's avatar
Linus Torvalds committed
 * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
 * - changed to provide snprintf and vsnprintf functions
 * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
 * - scnprintf and vscnprintf
 */

#include <stdarg.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
Linus Torvalds's avatar
Linus Torvalds committed

#include <asm/page.h>		/* for PAGE_SIZE */
Linus Torvalds's avatar
Linus Torvalds committed
#include <asm/div64.h>
#include <asm/sections.h>	/* for dereference_function_descriptor() */
Linus Torvalds's avatar
Linus Torvalds committed

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;
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
/**
 * simple_strtoull - convert a string to an unsigned long long
Linus Torvalds's avatar
Linus Torvalds committed
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
Linus Torvalds's avatar
Linus Torvalds committed
{
	unsigned long long result = 0;

	if (!base)
		base = simple_guess_base(cp);

	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;
Linus Torvalds's avatar
Linus Torvalds committed
		cp++;
	}
	if (endp)
		*endp = (char *)cp;
Linus Torvalds's avatar
Linus Torvalds committed
	return result;
}
EXPORT_SYMBOL(simple_strtoull);
Linus Torvalds's avatar
Linus Torvalds committed

/**
 * simple_strtoul - convert a string to an unsigned long
Linus Torvalds's avatar
Linus Torvalds committed
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
Linus Torvalds's avatar
Linus Torvalds committed
{
	return simple_strtoull(cp, endp, base);
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(simple_strtoul);
Linus Torvalds's avatar
Linus Torvalds committed

/**
 * simple_strtol - convert a string to a signed long
Linus Torvalds's avatar
Linus Torvalds committed
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
long simple_strtol(const char *cp, char **endp, unsigned int base)
Linus Torvalds's avatar
Linus Torvalds committed
{
	if (*cp == '-')
		return -simple_strtoul(cp + 1, endp, base);
	return simple_strtoul(cp, endp, base);
Linus Torvalds's avatar
Linus Torvalds committed
}
Linus Torvalds's avatar
Linus Torvalds committed

/**
 * simple_strtoll - convert a string to a signed long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
long long simple_strtoll(const char *cp, char **endp, unsigned int base)
Linus Torvalds's avatar
Linus Torvalds committed
{
		return -simple_strtoull(cp + 1, endp, base);
	return simple_strtoull(cp, endp, base);
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(simple_strtoll);
Linus Torvalds's avatar
Linus Torvalds committed

static noinline_for_stack
int skip_atoi(const char **s)
Linus Torvalds's avatar
Linus Torvalds committed
{
Linus Torvalds's avatar
Linus Torvalds committed

	while (isdigit(**s))
		i = i*10 + *((*s)++) - '0';
Linus Torvalds's avatar
Linus Torvalds committed
	return i;
}

/* Decimal conversion is by far the most typical, and is used
 * for /proc and /sys data. This directly impacts e.g. top performance
 * with many processes running. We optimize it for speed
 * using code from
 * http://www.cs.uiowa.edu/~jones/bcd/decimal.html
 * (with permission from the author, Douglas W. Jones). */

/* Formats correctly any integer in [0,99999].
 * Outputs from one to five digits depending on input.
 * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */
static noinline_for_stack
char *put_dec_trunc(char *buf, unsigned q)
{
	unsigned d3, d2, d1, d0;
	d1 = (q>>4) & 0xf;
	d2 = (q>>8) & 0xf;
	d3 = (q>>12);

	d0 = 6*(d3 + d2 + d1) + (q & 0xf);
	q = (d0 * 0xcd) >> 11;
	d0 = d0 - 10*q;
	*buf++ = d0 + '0'; /* least significant digit */
	d1 = q + 9*d3 + 5*d2 + d1;
	if (d1 != 0) {
		q = (d1 * 0xcd) >> 11;
		d1 = d1 - 10*q;
		*buf++ = d1 + '0'; /* next digit */

		d2 = q + 2*d2;
		if ((d2 != 0) || (d3 != 0)) {
			q = (d2 * 0xd) >> 7;
			d2 = d2 - 10*q;
			*buf++ = d2 + '0'; /* next digit */

			d3 = q + 4*d3;
			if (d3 != 0) {
				q = (d3 * 0xcd) >> 11;
				d3 = d3 - 10*q;
				*buf++ = d3 + '0';  /* next digit */
				if (q != 0)
					*buf++ = q + '0'; /* most sign. digit */
	return buf;
}
/* Same with if's removed. Always emits five digits */
static noinline_for_stack
char *put_dec_full(char *buf, unsigned q)
{
	/* BTW, if q is in [0,9999], 8-bit ints will be enough, */
	/* but anyway, gcc produces better code with full-sized ints */
	unsigned d3, d2, d1, d0;
	d1 = (q>>4) & 0xf;
	d2 = (q>>8) & 0xf;
	d3 = (q>>12);

	/*
	 * Possible ways to approx. divide by 10
	 * gcc -O2 replaces multiply with shifts and adds
	 * (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
	 * (x * 0x67) >> 10:  1100111
	 * (x * 0x34) >> 9:    110100 - same
	 * (x * 0x1a) >> 8:     11010 - same
	 * (x * 0x0d) >> 7:      1101 - same, shortest code (on i386)
	 */
	d0 = 6*(d3 + d2 + d1) + (q & 0xf);
	q = (d0 * 0xcd) >> 11;
	d0 = d0 - 10*q;
Loading
Loading full blame...