Commit b39a7340 authored by Denis Vlasenko's avatar Denis Vlasenko Committed by Linus Torvalds

vsprintf.c: optimizing, part 1 (easy and obvious stuff)

* There is no point in having full "0...9a...z" constant vector,
  if we use only "0...9a...f" (and "x" for "0x").

* Post-decrement usually needs a few more instructions, so use
  pre decrement instead where makes sense:
-       while (i < precision--) {
+       while (i <= --precision) {

* if base != 10 (=> base 8 or 16), we can avoid using division
  in a loop and use mask/shift, obtaining much faster conversion.
  (More complex optimization for base 10 case is in the second patch).

Overall, size vsprintf.o shows ~80 bytes smaller text section
with this patch applied.
Signed-off-by: default avatarDouglas W Jones <jones@cs.uiowa.edu>
Signed-off-by: default avatarDenys Vlasenko <vda.linux@googlemail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 4b4e5a14
...@@ -143,12 +143,14 @@ static int skip_atoi(const char **s) ...@@ -143,12 +143,14 @@ static int skip_atoi(const char **s)
#define SPECIAL 32 /* 0x */ #define SPECIAL 32 /* 0x */
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)
{ {
char c,sign,tmp[66]; char sign,tmp[66];
const char *digits; const char *digits;
static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; /* we are called with base 8, 10 or 16, only, thus don't need "g..." */
static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const char small_digits[] = "0123456789abcdefx"; /* "ghijklmnopqrstuvwxyz"; */
static const char large_digits[] = "0123456789ABCDEFX"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
int need_pfx = ((type & SPECIAL) && base != 10);
int i; int i;
digits = (type & LARGE) ? large_digits : small_digits; digits = (type & LARGE) ? large_digits : small_digits;
...@@ -156,7 +158,6 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i ...@@ -156,7 +158,6 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i
type &= ~ZEROPAD; type &= ~ZEROPAD;
if (base < 2 || base > 36) if (base < 2 || base > 36)
return NULL; return NULL;
c = (type & ZEROPAD) ? '0' : ' ';
sign = 0; sign = 0;
if (type & SIGN) { if (type & SIGN) {
if ((signed long long) num < 0) { if ((signed long long) num < 0) {
...@@ -171,64 +172,80 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i ...@@ -171,64 +172,80 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i
size--; size--;
} }
} }
if (type & SPECIAL) { if (need_pfx) {
size--;
if (base == 16) if (base == 16)
size -= 2;
else if (base == 8)
size--; size--;
} }
/* generate full string in tmp[], in reverse order */
i = 0; i = 0;
if (num == 0) if (num == 0)
tmp[i++]='0'; tmp[i++] = '0';
else while (num != 0) else if (base != 10) { /* 8 or 16 */
tmp[i++] = digits[do_div(num,base)]; int mask = base - 1;
int shift = 3;
if (base == 16) shift = 4;
do {
tmp[i++] = digits[((unsigned char)num) & mask];
num >>= shift;
} while (num);
} else do { /* generic code, works for any base */
tmp[i++] = digits[do_div(num,10 /*base*/)];
} while (num);
/* printing 100 using %2d gives "100", not "00" */
if (i > precision) if (i > precision)
precision = i; precision = i;
/* leading space padding */
size -= precision; size -= precision;
if (!(type&(ZEROPAD+LEFT))) { if (!(type & (ZEROPAD+LEFT))) {
while(size-->0) { while(--size >= 0) {
if (buf < end) if (buf < end)
*buf = ' '; *buf = ' ';
++buf; ++buf;
} }
} }
/* sign */
if (sign) { if (sign) {
if (buf < end) if (buf < end)
*buf = sign; *buf = sign;
++buf; ++buf;
} }
if (type & SPECIAL) { /* "0x" / "0" prefix */
if (base==8) { if (need_pfx) {
if (buf < end)
*buf = '0';
++buf;
} else if (base==16) {
if (buf < end) if (buf < end)
*buf = '0'; *buf = '0';
++buf; ++buf;
if (base == 16) {
if (buf < end) if (buf < end)
*buf = digits[33]; *buf = digits[16]; /* for arbitrary base: digits[33]; */
++buf; ++buf;
} }
} }
/* zero or space padding */
if (!(type & LEFT)) { if (!(type & LEFT)) {
while (size-- > 0) { char c = (type & ZEROPAD) ? '0' : ' ';
while (--size >= 0) {
if (buf < end) if (buf < end)
*buf = c; *buf = c;
++buf; ++buf;
} }
} }
while (i < precision--) { /* hmm even more zero padding? */
while (i <= --precision) {
if (buf < end) if (buf < end)
*buf = '0'; *buf = '0';
++buf; ++buf;
} }
while (i-- > 0) { /* actual digits of result */
while (--i >= 0) {
if (buf < end) if (buf < end)
*buf = tmp[i]; *buf = tmp[i];
++buf; ++buf;
} }
while (size-- > 0) { /* trailing space padding */
while (--size >= 0) {
if (buf < end) if (buf < end)
*buf = ' '; *buf = ' ';
++buf; ++buf;
...@@ -276,7 +293,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -276,7 +293,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
used for unknown buffer sizes. */ used for unknown buffer sizes. */
if (unlikely((int) size < 0)) { if (unlikely((int) size < 0)) {
/* There can be only one.. */ /* There can be only one.. */
static int warn = 1; static char warn = 1;
WARN_ON(warn); WARN_ON(warn);
warn = 0; warn = 0;
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment