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