diff options
author | Andy Ross <andrew.j.ross@intel.com> | 2016-08-11 10:52:47 -0700 |
---|---|---|
committer | Anas Nashif <nashif@linux.intel.com> | 2016-09-10 00:48:59 +0000 |
commit | a4537cb0d37fe4976664b84079e0d260dbb9c1eb (patch) | |
tree | fdd206e7f89a503324c558fca53d9c5f8e868183 /lib | |
parent | cc5c3c48b60d7e5e1ad32ad382d875f073fca329 (diff) |
libc/printf: Unify & simplify number printing
Hex, octal and decimal all had separately implemented reduction loops
to generate strings. With only a little work these can all be unified
to a single implementation that works with an arbitrary base.
Performance is probably a little lower owing to the fact that
hex/octal now requires a division per character, and the extra
"reverse the string" trick at the end of the conversion. But code
size savings are substantial.
Change-Id: I11ff376aeca1483f974d328271e19918221b2a41
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libc/minimal/source/stdout/prf.c | 138 |
1 files changed, 64 insertions, 74 deletions
diff --git a/lib/libc/minimal/source/stdout/prf.c b/lib/libc/minimal/source/stdout/prf.c index f394133d2..51feb58f2 100644 --- a/lib/libc/minimal/source/stdout/prf.c +++ b/lib/libc/minimal/source/stdout/prf.c @@ -35,100 +35,90 @@ #define DOUBLE 1 #endif -static int _to_hex(char *buf, uint32_t value, int alt_form, int precision, int prefix) +static void _uc(char *buf) { - register int i; - register int temp; - char *start = buf; + for (/**/; *buf; buf++) { + if (*buf >= 'a' && *buf <= 'z') { + *buf += 'A' - 'a'; + } + } +} -#if (MAXFLD < (2 + 7)) - #error buffer size MAXFLD is too small -#endif +/* Convention note: "end" as passed in is the standard "byte after + * last character" style, but... + */ +static int _reverse_and_pad(char *start, char *end, int minlen) +{ + int len; - if (precision < 0) - precision = 1; - *buf = '\0'; - if (alt_form) { - buf[0] = '0'; - buf[1] = (prefix == 'X') ? 'X' : 'x'; - buf += 2; + while (end - start < minlen) { + *end++ = '0'; } - for (i = 7; i >= 0; i--) { - temp = (value >> (i * 4)) & 0xF; - if ((precision > i) || (temp != 0)) { - precision = i; - if (temp < 10) - *buf++ = (char) (temp + '0'); - else { - if (prefix == 'X') - *buf++ = (char) (temp - 10 + 'A'); - else - *buf++ = (char) (temp - 10 + 'a'); - } - } + + *end = 0; + len = end - start; + for (end--; end > start; end--, start++) { + char tmp = *end; + *end = *start; + *start = tmp; } - *buf = 0; + return len; +} - return buf - start; +/* Writes the specified number into the buffer in the given base, + * using the digit characters 0-9a-z (i.e. base>36 will start writing + * odd bytes), padding with leading zeros up to the minimum length. + */ +static int _to_x(char *buf, uint32_t n, int base, int minlen) +{ + char *buf0 = buf; + + do { + int d = n % base; + + n /= base; + *buf++ = '0' + d + (d > 9 ? ('a' - '0' - 10) : 0); + } while (n); + return _reverse_and_pad(buf0, buf, minlen); } -static int _to_octal(char *buf, uint32_t value, int alt_form, int precision) +static int _to_hex(char *buf, uint32_t value, + int alt_form, int precision, int prefix) { - register int i; - register int temp; - char *start = buf; + int len; + char *buf0 = buf; -#if (MAXFLD < 10) - #error buffer size MAXFLD is too small -#endif + if (alt_form) { + *buf++ = '0'; + *buf++ = 'x'; + } - if (precision < 0) - precision = 1; - *buf = '\0'; - for (i = 10; i >= 0; i--) { - temp = (value >> (i * 3)); - if (i == 10) - temp &= 0x3; - else - temp &= 0x7; - if ((precision > i) || (temp != 0)) { - precision = i; - if ((temp != 0) && alt_form) - *buf++ = '0'; - alt_form = false; - *buf++ = (char) (temp + '0'); - } + len = _to_x(buf, value, 16, precision); + if (prefix == 'X') { + _uc(buf0); } - *buf = 0; - return buf - start; + return len + (buf - buf0); } -static int _to_udec(char *buf, uint32_t value, int precision) +static int _to_octal(char *buf, uint32_t value, int alt_form, int precision) { - register uint32_t divisor; - register int i; - register int temp; - char *start = buf; - -#if (MAXFLD < 9) - #error buffer size MAXFLD is too small -#endif + char *buf0 = buf; - divisor = 1000000000; - if (precision < 0) - precision = 1; - for (i = 9; i >= 0; i--, divisor /= 10) { - temp = value / divisor; - value = value % divisor; - if ((precision > i) || (temp != 0)) { - precision = i; - *buf++ = (char) (temp + '0'); + if (alt_form) { + *buf++ = '0'; + if (!value) { + /* So we don't return "00" for a value == 0. */ + *buf++ = 0; + return 1; } } - *buf = 0; + return (buf - buf0) + _to_x(buf, value, 8, precision); +} - return buf - start; +static int _to_udec(char *buf, uint32_t value, int precision) +{ + return _to_x(buf, value, 10, precision); } static int _to_dec(char *buf, int32_t value, int fplus, int fspace, int precision) |