blob: 14e7cd1e7c4e4fd4f08a8c5946bbb2f639247d89 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2008-2014 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
24#include <assert.h>
25#include <limits.h>
26#include <printf.h>
27#include <stdarg.h>
28#include <sys/types.h>
29#include <stdio.h>
30#include <string.h>
31#include <platform/debug.h>
32
33#if WITH_NO_FP
34#define FLOAT_PRINTF 0
35#else
36#define FLOAT_PRINTF 1
37#endif
38
39int sprintf(char *str, const char *fmt, ...)
40{
41 int err;
42
43 va_list ap;
44 va_start(ap, fmt);
45 err = vsprintf(str, fmt, ap);
46 va_end(ap);
47
48 return err;
49}
50
51int snprintf(char *str, size_t len, const char *fmt, ...)
52{
53 int err;
54
55 va_list ap;
56 va_start(ap, fmt);
57 err = vsnprintf(str, len, fmt, ap);
58 va_end(ap);
59
60 return err;
61}
62
63
64#define LONGFLAG 0x00000001
65#define LONGLONGFLAG 0x00000002
66#define HALFFLAG 0x00000004
67#define HALFHALFFLAG 0x00000008
68#define SIZETFLAG 0x00000010
69#define INTMAXFLAG 0x00000020
70#define PTRDIFFFLAG 0x00000040
71#define ALTFLAG 0x00000080
72#define CAPSFLAG 0x00000100
73#define SHOWSIGNFLAG 0x00000200
74#define SIGNEDFLAG 0x00000400
75#define LEFTFORMATFLAG 0x00000800
76#define LEADZEROFLAG 0x00001000
77#define BLANKPOSFLAG 0x00002000
78
79__NO_INLINE static char *longlong_to_string(char *buf, unsigned long long n, size_t len, uint flag, char *signchar)
80{
81 size_t pos = len;
82 int negative = 0;
83
84 if ((flag & SIGNEDFLAG) && (long long)n < 0) {
85 negative = 1;
86 n = -n;
87 }
88
89 buf[--pos] = 0;
90
91 /* only do the math if the number is >= 10 */
92 while (n >= 10) {
93 int digit = n % 10;
94
95 n /= 10;
96
97 buf[--pos] = digit + '0';
98 }
99 buf[--pos] = n + '0';
100
101 if (negative)
102 *signchar = '-';
103 else if ((flag & SHOWSIGNFLAG))
104 *signchar = '+';
105 else if ((flag & BLANKPOSFLAG))
106 *signchar = ' ';
107 else
108 *signchar = '\0';
109
110 return &buf[pos];
111}
112
113static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
114static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
115
116__NO_INLINE static char *longlong_to_hexstring(char *buf, unsigned long long u, size_t len, uint flag)
117{
118 size_t pos = len;
119 const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
120
121 buf[--pos] = 0;
122 do {
123 unsigned int digit = u % 16;
124 u /= 16;
125
126 buf[--pos] = table[digit];
127 } while (u != 0);
128
129 return &buf[pos];
130}
131
132#if FLOAT_PRINTF
133union double_int {
134 double d;
135 uint64_t i;
136};
137
138#define OUT(c) buf[pos++] = (c)
139#define OUTSTR(str) do { for (size_t i = 0; (str)[i] != 0; i++) OUT((str)[i]); } while (0)
140
141/* print up to a 4 digit exponent as string, with sign */
142__NO_INLINE static size_t exponent_to_string(char *buf, int32_t exponent)
143{
144 size_t pos = 0;
145
146 /* handle sign */
147 if (exponent < 0) {
148 OUT('-');
149 exponent = -exponent;
150 } else {
151 OUT('+');
152 }
153
154 /* see how far we need to bump into the string to print from the right */
155 if (exponent >= 1000) pos += 4;
156 else if (exponent >= 100) pos += 3;
157 else if (exponent >= 10) pos += 2;
158 else pos++;
159
160 /* print decimal string, from the right */
161 uint i = pos;
162 do {
163 uint digit = (uint32_t)exponent % 10;
164
165 buf[--i] = digit + '0';
166
167 exponent /= 10;
168 } while (exponent != 0);
169
170 /* return number of characters printed */
171 return pos;
172}
173
174__NO_INLINE static char *double_to_string(char *buf, size_t len, double d, uint flag)
175{
176 size_t pos = 0;
177 union double_int u = { d };
178
179 uint32_t exponent = (u.i >> 52) & 0x7ff;
180 uint64_t fraction = (u.i & ((1ULL << 52) - 1));
181 bool neg = !!(u.i & (1ULL << 63));
182
183 /* start constructing the string */
184 if (neg) {
185 OUT('-');
186 d = -d;
187 }
188
189 /* longest:
190 * 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000o
191 */
192
193 /* look for special cases */
194 if (exponent == 0x7ff) {
195 if (fraction == 0) {
196 /* infinity */
197 if (flag & CAPSFLAG) OUTSTR("INF");
198 else OUTSTR("inf");
199 } else {
200 /* NaN */
201 if (flag & CAPSFLAG) OUTSTR("NAN");
202 else OUTSTR("nan");
203 }
204 } else if (exponent == 0) {
205 if (fraction == 0) {
206 /* zero */
207 OUTSTR("0.000000");
208 } else {
209 /* denormalized */
210 /* XXX does not handle */
211 if (flag & CAPSFLAG) OUTSTR("DEN");
212 else OUTSTR("den");
213 }
214 } else {
215 /* see if it's in the range of floats we can easily print */
216 int exponent_signed = exponent - 1023;
217 if (exponent_signed < -52 || exponent_signed > 52) {
218 OUTSTR("<range>");
219 } else {
220 /* start by walking backwards through the string */
221#define OUTREV(c) do { if (&buf[pos] == buf) goto done; else buf[--pos] = (c); } while (0)
222 pos = len;
223 OUTREV(0);
224
225 /* reserve space for the fractional component first */
226 for (int i = 0; i <= 6; i++)
227 OUTREV('0');
228 size_t decimal_spot = pos;
229
230 /* print the integer portion */
231 uint64_t u;
232 if (exponent_signed >= 0) {
233 u = fraction;
234 u |= (1ULL<<52);
235 u >>= (52 - exponent_signed);
236
237 char *s = longlong_to_string(buf, u, pos + 1, flag, &(char) {0});
238
239 pos = s - buf;
240 } else {
241 /* exponent is negative */
242 u = 0;
243 OUTREV('0');
244 }
245
246 buf[decimal_spot] = '.';
247
248 /* handle the fractional part */
249 uint32_t frac = ((d - u) * 1000000) + .5;
250
251 uint i = decimal_spot + 6 + 1;
252 while (frac != 0) {
253 uint digit = frac % 10;
254
255 buf[--i] = digit + '0';
256
257 frac /= 10;
258 }
259
260 if (neg)
261 OUTREV('-');
262
263done:
264 /* separate return path, since we've been walking backwards through the string */
265 return &buf[pos];
266 }
267#undef OUTREV
268 }
269
270 buf[pos] = 0;
271 return buf;
272}
273
274__NO_INLINE static char *double_to_hexstring(char *buf, size_t len, double d, uint flag)
275{
276 size_t pos = 0;
277 union double_int u = { d };
278
279 uint32_t exponent = (u.i >> 52) & 0x7ff;
280 uint64_t fraction = (u.i & ((1ULL << 52) - 1));
281 bool neg = !!(u.i & (1ULL << 63));
282
283 /* start constructing the string */
284 if (neg) {
285 OUT('-');
286 }
287
288 /* look for special cases */
289 if (exponent == 0x7ff) {
290 if (fraction == 0) {
291 /* infinity */
292 if (flag & CAPSFLAG) OUTSTR("INF");
293 else OUTSTR("inf");
294 } else {
295 /* NaN */
296 if (flag & CAPSFLAG) OUTSTR("NAN");
297 else OUTSTR("nan");
298 }
299 } else if (exponent == 0) {
300 if (fraction == 0) {
301 /* zero */
302 if (flag & CAPSFLAG) OUTSTR("0X0P+0");
303 else OUTSTR("0x0p+0");
304 } else {
305 /* denormalized */
306 /* XXX does not handle */
307 if (flag & CAPSFLAG) OUTSTR("DEN");
308 else OUTSTR("den");
309 }
310 } else {
311 /* regular normalized numbers:
312 * 0x1p+1
313 * 0x1.0000000000001p+1
314 * 0X1.FFFFFFFFFFFFFP+1023
315 * 0x1.FFFFFFFFFFFFFP+1023
316 */
317 int exponent_signed = exponent - 1023;
318
319 /* implicit 1. */
320 if (flag & CAPSFLAG) OUTSTR("0X1");
321 else OUTSTR("0x1");
322
323 /* select the appropriate hex case table */
324 const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
325
326 int zero_count = 0;
327 bool output_dot = false;
328 for (int i = 52 - 4; i >= 0; i -= 4) {
329 uint digit = (fraction >> i) & 0xf;
330
331 if (digit == 0) {
332 zero_count++;
333 } else {
334 /* output a . the first time we output a char */
335 if (!output_dot) {
336 OUT('.');
337 output_dot = true;
338 }
339 /* if we have a non zero digit, see if we need to output a string of zeros */
340 while (zero_count > 0) {
341 OUT('0');
342 zero_count--;
343 }
344 buf[pos++] = table[digit];
345 }
346 }
347
348 /* handle the exponent */
349 buf[pos++] = (flag & CAPSFLAG) ? 'P' : 'p';
350 pos += exponent_to_string(&buf[pos], exponent_signed);
351 }
352
353 buf[pos] = 0;
354 return buf;
355}
356
357#undef OUT
358#undef OUTSTR
359
360#endif // FLOAT_PRINTF
361
362int vsprintf(char *str, const char *fmt, va_list ap)
363{
364 return vsnprintf(str, INT_MAX, fmt, ap);
365}
366
367struct _output_args {
368 char *outstr;
369 size_t len;
370 size_t pos;
371};
372
373static int _vsnprintf_output(const char *str, size_t len, void *state)
374{
375 struct _output_args *args = state;
376
377 size_t count = 0;
378 while (count < len) {
379 if (args->pos < args->len) {
380 args->outstr[args->pos++] = *str;
381 }
382
383 str++;
384 count++;
385 }
386
387 return count;
388}
389
390int vsnprintf(char *str, size_t len, const char *fmt, va_list ap)
391{
392 struct _output_args args;
393 int wlen;
394
395 args.outstr = str;
396 args.len = len;
397 args.pos = 0;
398
399 wlen = _printf_engine(&_vsnprintf_output, (void *)&args, fmt, ap);
400 if (args.pos >= len)
401 str[len-1] = '\0';
402 else
403 str[wlen] = '\0';
404 return wlen;
405}
406
407int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap)
408{
409 int err = 0;
410 char c;
411 unsigned char uc;
412 const char *s;
413 size_t string_len;
414 unsigned long long n;
415 void *ptr;
416 int flags;
417 unsigned int format_num;
418 char signchar;
419 size_t chars_written = 0;
420 char num_buffer[32];
421
422#define OUTPUT_STRING(str, len) do { err = out(str, len, state); if (err < 0) { goto exit; } else { chars_written += err; } } while(0)
423#define OUTPUT_CHAR(c) do { char __temp[1] = { c }; OUTPUT_STRING(__temp, 1); } while (0)
424
425 for (;;) {
426 /* reset the format state */
427 flags = 0;
428 format_num = 0;
429 signchar = '\0';
430
431 /* handle regular chars that aren't format related */
432 s = fmt;
433 string_len = 0;
434 while ((c = *fmt++) != 0) {
435 if (c == '%')
436 break; /* we saw a '%', break and start parsing format */
437 string_len++;
438 }
439
440 /* output the string we've accumulated */
441 OUTPUT_STRING(s, string_len);
442
443 /* make sure we haven't just hit the end of the string */
444 if (c == 0)
445 break;
446
447next_format:
448 /* grab the next format character */
449 c = *fmt++;
450 if (c == 0)
451 break;
452
453 switch (c) {
454 case '0'...'9':
455 if (c == '0' && format_num == 0)
456 flags |= LEADZEROFLAG;
457 format_num *= 10;
458 format_num += c - '0';
459 goto next_format;
460 case '.':
461 /* XXX for now eat numeric formatting */
462 goto next_format;
463 case '%':
464 OUTPUT_CHAR('%');
465 break;
466 case 'c':
467 uc = va_arg(ap, unsigned int);
468 OUTPUT_CHAR(uc);
469 break;
470 case 's':
471 s = va_arg(ap, const char *);
472 if (s == 0)
473 s = "<null>";
474 flags &= ~LEADZEROFLAG; /* doesn't make sense for strings */
475 goto _output_string;
476 case '-':
477 flags |= LEFTFORMATFLAG;
478 goto next_format;
479 case '+':
480 flags |= SHOWSIGNFLAG;
481 goto next_format;
482 case ' ':
483 flags |= BLANKPOSFLAG;
484 goto next_format;
485 case '#':
486 flags |= ALTFLAG;
487 goto next_format;
488 case 'l':
489 if (flags & LONGFLAG)
490 flags |= LONGLONGFLAG;
491 flags |= LONGFLAG;
492 goto next_format;
493 case 'h':
494 if (flags & HALFFLAG)
495 flags |= HALFHALFFLAG;
496 flags |= HALFFLAG;
497 goto next_format;
498 case 'z':
499 flags |= SIZETFLAG;
500 goto next_format;
501 case 'j':
502 flags |= INTMAXFLAG;
503 goto next_format;
504 case 't':
505 flags |= PTRDIFFFLAG;
506 goto next_format;
507 case 'i':
508 case 'd':
509 n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) :
510 (flags & LONGFLAG) ? va_arg(ap, long) :
511 (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) :
512 (flags & HALFFLAG) ? (short)va_arg(ap, int) :
513 (flags & SIZETFLAG) ? va_arg(ap, ssize_t) :
514 (flags & INTMAXFLAG) ? va_arg(ap, intmax_t) :
515 (flags & PTRDIFFFLAG) ? va_arg(ap, ptrdiff_t) :
516 va_arg(ap, int);
517 flags |= SIGNEDFLAG;
518 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
519 goto _output_string;
520 case 'u':
521 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
522 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
523 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
524 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
525 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
526 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
527 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
528 va_arg(ap, unsigned int);
529 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
530 goto _output_string;
531 case 'p':
532 flags |= LONGFLAG | ALTFLAG;
533 goto hex;
534 case 'X':
535 flags |= CAPSFLAG;
536 /* fallthrough */
537hex:
538 case 'x':
539 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
540 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
541 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
542 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
543 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
544 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
545 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
546 va_arg(ap, unsigned int);
547 s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags);
548 if (flags & ALTFLAG) {
549 OUTPUT_CHAR('0');
550 OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x');
551 }
552 goto _output_string;
553 case 'n':
554 ptr = va_arg(ap, void *);
555 if (flags & LONGLONGFLAG)
556 *(long long *)ptr = chars_written;
557 else if (flags & LONGFLAG)
558 *(long *)ptr = chars_written;
559 else if (flags & HALFHALFFLAG)
560 *(signed char *)ptr = chars_written;
561 else if (flags & HALFFLAG)
562 *(short *)ptr = chars_written;
563 else if (flags & SIZETFLAG)
564 *(size_t *)ptr = chars_written;
565 else
566 *(int *)ptr = chars_written;
567 break;
568#if FLOAT_PRINTF
569 case 'F':
570 flags |= CAPSFLAG;
571 /* fallthrough */
572 case 'f': {
573 double d = va_arg(ap, double);
574 s = double_to_string(num_buffer, sizeof(num_buffer), d, flags);
575 goto _output_string;
576 }
577 case 'A':
578 flags |= CAPSFLAG;
579 /* fallthrough */
580 case 'a': {
581 double d = va_arg(ap, double);
582 s = double_to_hexstring(num_buffer, sizeof(num_buffer), d, flags);
583 goto _output_string;
584 }
585#endif
586 default:
587 OUTPUT_CHAR('%');
588 OUTPUT_CHAR(c);
589 break;
590 }
591
592 /* move on to the next field */
593 continue;
594
595 /* shared output code */
596_output_string:
597 string_len = strlen(s);
598
599 if (flags & LEFTFORMATFLAG) {
600 /* left justify the text */
601 OUTPUT_STRING(s, string_len);
602 uint written = err;
603
604 /* pad to the right (if necessary) */
605 for (; format_num > written; format_num--)
606 OUTPUT_CHAR(' ');
607 } else {
608 /* right justify the text (digits) */
609
610 /* if we're going to print a sign digit,
611 it'll chew up one byte of the format size */
612 if (signchar != '\0' && format_num > 0)
613 format_num--;
614
615 /* output the sign char before the leading zeros */
616 if (flags & LEADZEROFLAG && signchar != '\0')
617 OUTPUT_CHAR(signchar);
618
619 /* pad according to the format string */
620 for (; format_num > string_len; format_num--)
621 OUTPUT_CHAR(flags & LEADZEROFLAG ? '0' : ' ');
622
623 /* if not leading zeros, output the sign char just before the number */
624 if (!(flags & LEADZEROFLAG) && signchar != '\0')
625 OUTPUT_CHAR(signchar);
626
627 /* output the string */
628 OUTPUT_STRING(s, string_len);
629 }
630 continue;
631 }
632
633#undef OUTPUT_STRING
634#undef OUTPUT_CHAR
635
636exit:
637 return (err < 0) ? err : (int)chars_written;
638}