summaryrefslogtreecommitdiff
path: root/tools/include
diff options
context:
space:
mode:
authorDavid Laight <david.laight.linux@gmail.com>2026-03-08 11:37:36 +0000
committerThomas Weißschuh <linux@weissschuh.net>2026-03-20 17:55:51 +0100
commit8df70ee45b1383114cdcaa9b2fe7449cdf5f46d5 (patch)
treeb8fb1defd5ac9ab9b050522c46cd9531031d359c /tools/include
parentb43be424343407661d5e7c79a3584a37b91a88bb (diff)
tools/nolibc/printf: Prepend sign to converted number
Instead of appending the converted number to the sign, convert first and then prepend the sign (or "0x"). Use the length returned by u64toh_r() instead of calling strlen(). Needed so that zero padding can be inserted between the sign and digits in an upcoming patch. Signed-off-by: David Laight <david.laight.linux@gmail.com> Acked-by: Willy Tarreau <w@1wt.eu> Link: https://patch.msgid.link/20260308113742.12649-12-david.laight.linux@gmail.com Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Diffstat (limited to 'tools/include')
-rw-r--r--tools/include/nolibc/stdio.h36
1 files changed, 27 insertions, 9 deletions
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h
index a377f164c70d..3620aa643c81 100644
--- a/tools/include/nolibc/stdio.h
+++ b/tools/include/nolibc/stdio.h
@@ -345,9 +345,10 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list
long long signed_v;
int written, width, len;
unsigned int flags, ch_flag;
- char outbuf[21];
+ char outbuf[2 + 22 + 1];
char *out;
const char *outstr;
+ unsigned int sign_prefix;
written = 0;
while (1) {
@@ -445,32 +446,49 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list
goto do_strlen_output;
}
- out = outbuf;
+ /* The 'sign_prefix' can be zero, one or two ("0x") characters.
+ * Prepended least significant byte first stopping on a zero byte.
+ */
+ sign_prefix = 0;
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) {
/* "%d" and "%i" - signed decimal numbers. */
if (signed_v < 0) {
- *out++ = '-';
+ sign_prefix = '-';
v = -(signed_v + 1);
v++;
}
}
+ /* The value is converted offset into the buffer so that
+ * the sign/prefix can be added in front.
+ * The longest digit string is 22 + 1 for octal conversions, the
+ * space is reserved even though octal isn't currently supported.
+ */
+ out = outbuf + 2;
+
/* Convert the number to ascii in the required base. */
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) {
/* Base 10 */
- u64toa_r(v, out);
+ len = u64toa_r(v, out);
} else {
/* Base 16 */
if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p')) {
- *(out++) = '0';
- *(out++) = 'x';
+ /* "%p" needs "0x" prepending. */
+ sign_prefix = '0' << 8 | 'x';
}
- u64toh_r(v, out);
+ len = u64toh_r(v, out);
}
- outstr = outbuf;
- goto do_strlen_output;
+ /* Add the 0, 1 or 2 ("0x") sign/prefix characters at the front. */
+ for (; sign_prefix; sign_prefix >>= 8) {
+ /* Force gcc to increment len inside the loop. */
+ _NOLIBC_OPTIMIZER_HIDE_VAR(len);
+ len++;
+ *--out = sign_prefix;
+ }
+ outstr = out;
+ goto do_output;
}
if (ch == 'm') {