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>
This commit is contained in:
parent
cc5c3c48b6
commit
a4537cb0d3
1 changed files with 69 additions and 79 deletions
|
@ -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;
|
||||
|
||||
#if (MAXFLD < (2 + 7))
|
||||
#error buffer size MAXFLD is too small
|
||||
#endif
|
||||
|
||||
if (precision < 0)
|
||||
precision = 1;
|
||||
*buf = '\0';
|
||||
if (alt_form) {
|
||||
buf[0] = '0';
|
||||
buf[1] = (prefix == 'X') ? 'X' : 'x';
|
||||
buf += 2;
|
||||
}
|
||||
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');
|
||||
}
|
||||
for (/**/; *buf; buf++) {
|
||||
if (*buf >= 'a' && *buf <= 'z') {
|
||||
*buf += 'A' - 'a';
|
||||
}
|
||||
}
|
||||
*buf = 0;
|
||||
}
|
||||
|
||||
return buf - start;
|
||||
/* 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;
|
||||
|
||||
while (end - start < minlen) {
|
||||
*end++ = '0';
|
||||
}
|
||||
|
||||
*end = 0;
|
||||
len = end - start;
|
||||
for (end--; end > start; end--, start++) {
|
||||
char tmp = *end;
|
||||
*end = *start;
|
||||
*start = tmp;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* 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_hex(char *buf, uint32_t value,
|
||||
int alt_form, int precision, int prefix)
|
||||
{
|
||||
int len;
|
||||
char *buf0 = buf;
|
||||
|
||||
if (alt_form) {
|
||||
*buf++ = '0';
|
||||
*buf++ = 'x';
|
||||
}
|
||||
|
||||
len = _to_x(buf, value, 16, precision);
|
||||
if (prefix == 'X') {
|
||||
_uc(buf0);
|
||||
}
|
||||
|
||||
return len + (buf - buf0);
|
||||
}
|
||||
|
||||
static int _to_octal(char *buf, uint32_t value, int alt_form, int precision)
|
||||
{
|
||||
register int i;
|
||||
register int temp;
|
||||
char *start = buf;
|
||||
char *buf0 = buf;
|
||||
|
||||
#if (MAXFLD < 10)
|
||||
#error buffer size MAXFLD is too small
|
||||
#endif
|
||||
|
||||
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');
|
||||
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 - start;
|
||||
return (buf - buf0) + _to_x(buf, value, 8, precision);
|
||||
}
|
||||
|
||||
static int _to_udec(char *buf, uint32_t value, 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
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
*buf = 0;
|
||||
|
||||
return buf - start;
|
||||
return _to_x(buf, value, 10, precision);
|
||||
}
|
||||
|
||||
static int _to_dec(char *buf, int32_t value, int fplus, int fspace, int precision)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue