summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAndy Ross <andrew.j.ross@intel.com>2016-08-11 10:52:47 -0700
committerAnas Nashif <nashif@linux.intel.com>2016-09-10 00:48:59 +0000
commita4537cb0d37fe4976664b84079e0d260dbb9c1eb (patch)
treefdd206e7f89a503324c558fca53d9c5f8e868183 /lib
parentcc5c3c48b60d7e5e1ad32ad382d875f073fca329 (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.c138
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)