1 2 /* 3 * Copyright (C) Igor Sysoev 4 * Copyright (C) NGINX, Inc. 5 */ 6 7 #include <nxt_main.h> 8 #include <math.h> 9 #include <float.h> 10 11 12 /* 13 * Supported formats: 14 * 15 * %[0][width][x|X]O nxt_off_t 16 * %[0][width][x|X]T nxt_time_t 17 * %[0][width][u][x|X]z ssize_t/size_t 18 * %[0][width][u][x|X]d int/u_int 19 * %[0][width][u][x|X]l long 20 * %[0][width|m][u][x|X]i nxt_int_t/nxt_uint_t 21 * %[0][width][u][x|X]D int32_t/uint32_t 22 * %[0][width][u][x|X]L int64_t/uint64_t 23 * %[0][width|m][u][x|X]A nxt_atomic_int_t/nxt_atomic_uint_t 24 * %[0][width][.width]f double, max valid number fits to %18.15f 25 * 26 * %FD nxt_fd_t, int / HANDLE 27 * %d nxt_socket_t, int 28 * 29 * %PI nxt_pid_t, process id 30 * %PT nxt_tid_t, thread id 31 * %PF nxt_fid_t, fiber id 32 * %PH pthread_t handle returned by pthread_self() 33 * 34 * %s null-terminated string 35 * %*s length and string 36 * %FN nxt_file_name_t * 37 * 38 * %M nxt_msec_t 39 * %N nxt_nsec_t 40 * %r rlim_t 41 * %p void * 42 * %b nxt_bool_t 43 * %E nxt_err_t 44 * %V nxt_str_t * 45 * %Z '\0' 46 * %n '\n' 47 * %c char 48 * %% % 49 * 50 * Reserved: 51 * %t ptrdiff_t 52 * %S null-terminated wchar string 53 * %C wchar 54 * %[0][width][u][x|X]Q int128_t/uint128_t 55 */ 56 57 58 u_char * nxt_cdecl 59 nxt_sprintf(u_char *buf, u_char *end, const char *fmt, ...) 60 { 61 u_char *p; 62 va_list args; 63 64 va_start(args, fmt); 65 p = nxt_vsprintf(buf, end, fmt, args); 66 va_end(args); 67 68 return p; 69 } 70 71 72 /* 73 * nxt_sprintf_t is used: 74 * to pass several parameters of nxt_integer() via single pointer 75 * and to store little used variables of nxt_vsprintf(). 76 */ 77 78 typedef struct { 79 u_char *end; 80 const u_char *hex; 81 uint32_t width; 82 int32_t frac_width; 83 uint8_t max_width; 84 u_char padding; 85 } nxt_sprintf_t; 86 87 88 static u_char *nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64); 89 static u_char *nxt_number(nxt_sprintf_t *spf, u_char *buf, double n); 90 91 92 /* A right way of "f == 0.0". */ 93 #define nxt_double_is_zero(f) \ 94 (fabs(f) <= FLT_EPSILON) 95 96 97 u_char * 98 nxt_vsprintf(u_char *buf, u_char *end, const char *fmt, va_list args) 99 { 100 int d; 101 double f, i; 102 size_t length; 103 int64_t i64; 104 uint64_t ui64, frac; 105 nxt_str_t *v; 106 nxt_err_t err; 107 nxt_uint_t scale, n; 108 nxt_msec_t ms; 109 nxt_nsec_t ns; 110 nxt_bool_t sign; 111 const u_char *p; 112 nxt_sprintf_t spf; 113 nxt_file_name_t *fn; 114 115 static const u_char hexadecimal[16] = "0123456789abcdef"; 116 static const u_char HEXADECIMAL[16] = "0123456789ABCDEF"; 117 static const u_char nan[] = "[nan]"; 118 static const u_char null[] = "[null]"; 119 static const u_char infinity[] = "[infinity]"; 120 121 spf.end = end; 122 123 while (*fmt != '\0' && buf < end) { 124 125 /* 126 * "buf < end" means that we could copy at least one character: 127 * a plain character, "%%", "%c", or a minus without test. 128 */ 129 130 if (*fmt != '%') { 131 *buf++ = *fmt++; 132 continue; 133 } 134 135 fmt++; 136 137 /* Test some often used text formats first. */ 138 139 switch (*fmt) { 140 141 case 'V': 142 fmt++; 143 v = va_arg(args, nxt_str_t *); 144 145 if (nxt_fast_path(v != NULL)) { 146 length = v->length; 147 p = v->start; 148 goto copy; 149 } 150 151 continue; 152 153 case 's': 154 fmt++; 155 156 p = va_arg(args, const u_char *); 157 158 if (nxt_slow_path(p == NULL)) { 159 goto copy; 160 } 161 162 while (*p != '\0' && buf < end) { 163 *buf++ = *p++; 164 } 165 166 continue; 167 168 case '*': 169 length = va_arg(args, size_t); 170 171 fmt++; 172 173 if (*fmt == 's') { 174 fmt++; 175 p = va_arg(args, const u_char *); 176 177 goto copy; 178 } 179 180 continue; 181 182 default: 183 break; 184 } 185 186 spf.hex = NULL; 187 spf.width = 0; 188 spf.frac_width = -1; 189 spf.max_width = 0; 190 spf.padding = (*fmt == '0') ? '0' : ' '; 191 192 sign = 1; 193 194 i64 = 0; 195 ui64 = 0; 196 197 while (*fmt >= '0' && *fmt <= '9') { 198 spf.width = spf.width * 10 + (*fmt++ - '0'); 199 } 200 201 202 for ( ;; ) { 203 switch (*fmt) { 204 205 case 'u': 206 sign = 0; 207 fmt++; 208 continue; 209 210 case 'm': 211 spf.max_width = 1; 212 fmt++; 213 continue; 214 215 case 'X': 216 spf.hex = HEXADECIMAL; 217 sign = 0; 218 fmt++; 219 continue; 220 221 case 'x': 222 spf.hex = hexadecimal; 223 sign = 0; 224 fmt++; 225 continue; 226 227 case '.': 228 fmt++; 229 spf.frac_width = 0; 230 231 while (*fmt >= '0' && *fmt <= '9') { 232 spf.frac_width = spf.frac_width * 10 + *fmt++ - '0'; 233 } 234 235 break; 236 237 default: 238 break; 239 } 240 241 break; 242 } 243 244 245 switch (*fmt) { 246 247 case 'E': 248 err = va_arg(args, nxt_err_t); 249 250 *buf++ = '('; 251 spf.hex = NULL; 252 spf.width = 0; 253 buf = nxt_integer(&spf, buf, err); 254 255 if (buf < end - 1) { 256 *buf++ = ':'; 257 *buf++ = ' '; 258 } 259 260 buf = nxt_strerror(err, buf, end - buf); 261 262 if (buf < end) { 263 *buf++ = ')'; 264 } 265 266 fmt++; 267 continue; 268 269 case 'O': 270 i64 = (int64_t) va_arg(args, nxt_off_t); 271 sign = 1; 272 goto number; 273 274 case 'T': 275 i64 = (int64_t) va_arg(args, nxt_time_t); 276 sign = 1; 277 goto number; 278 279 case 'M': 280 ms = (nxt_msec_t) va_arg(args, nxt_msec_t); 281 if ((nxt_msec_int_t) ms == -1 && spf.hex == NULL) { 282 i64 = -1; 283 sign = 1; 284 } else { 285 ui64 = (uint64_t) ms; 286 sign = 0; 287 } 288 goto number; 289 290 case 'N': 291 ns = (nxt_nsec_t) va_arg(args, nxt_nsec_t); 292 if ((nxt_nsec_int_t) ns == -1) { 293 i64 = -1; 294 sign = 1; 295 } else { 296 ui64 = (uint64_t) ns; 297 sign = 0; 298 } 299 goto number; 300 301 case 'z': 302 if (sign) { 303 i64 = (int64_t) va_arg(args, ssize_t); 304 } else { 305 ui64 = (uint64_t) va_arg(args, size_t); 306 } 307 goto number; 308 309 case 'i': 310 if (sign) { 311 i64 = (int64_t) va_arg(args, nxt_int_t); 312 } else { 313 ui64 = (uint64_t) va_arg(args, nxt_uint_t); 314 } 315 316 if (spf.max_width != 0) { 317 spf.width = NXT_INT_T_LEN; 318 } 319 320 goto number; 321 322 case 'd': 323 if (sign) { 324 i64 = (int64_t) va_arg(args, int); 325 } else { 326 ui64 = (uint64_t) va_arg(args, u_int); 327 } 328 goto number; 329 330 case 'l': 331 if (sign) { 332 i64 = (int64_t) va_arg(args, long); 333 } else { 334 ui64 = (uint64_t) va_arg(args, u_long); 335 } 336 goto number; 337 338 case 'D': 339 if (sign) { 340 i64 = (int64_t) va_arg(args, int32_t); 341 } else { 342 ui64 = (uint64_t) va_arg(args, uint32_t); 343 } 344 goto number; 345 346 case 'L': 347 if (sign) { 348 i64 = va_arg(args, int64_t); 349 } else { 350 ui64 = va_arg(args, uint64_t); 351 } 352 goto number; 353 354 case 'A': 355 if (sign) { 356 i64 = (int64_t) va_arg(args, nxt_atomic_int_t); 357 } else { 358 ui64 = (uint64_t) va_arg(args, nxt_atomic_uint_t); 359 } 360 361 if (spf.max_width != 0) { 362 spf.width = NXT_ATOMIC_T_LEN; 363 } 364 365 goto number; 366 367 case 'b': 368 ui64 = (uint64_t) va_arg(args, nxt_bool_t); 369 sign = 0; 370 goto number; 371 372 case 'f': 373 fmt++; 374 375 f = va_arg(args, double); 376 377 if (f < 0) { 378 *buf++ = '-'; 379 f = -f; 380 } 381 382 if (nxt_slow_path(isnan(f))) { 383 p = nan; 384 length = nxt_length(nan); 385 386 goto copy; 387 388 } else if (nxt_slow_path(isinf(f))) { 389 p = infinity; 390 length = nxt_length(infinity); 391 392 goto copy; 393 } 394 395 (void) modf(f, &i); 396 frac = 0; 397 398 if (spf.frac_width > 0) { 399 400 scale = 1; 401 for (n = spf.frac_width; n != 0; n--) { 402 scale *= 10; 403 } 404 405 frac = (uint64_t) ((f - i) * scale + 0.5); 406 407 if (frac == scale) { 408 i += 1; 409 frac = 0; 410 } 411 } 412 413 buf = nxt_number(&spf, buf, i); 414 415 if (spf.frac_width > 0) { 416 417 if (buf < end) { 418 *buf++ = '.'; 419 420 spf.hex = NULL; 421 spf.padding = '0'; 422 spf.width = spf.frac_width; 423 buf = nxt_integer(&spf, buf, frac); 424 } 425 426 } else if (spf.frac_width < 0) { 427 f = modf(f, &i); 428 429 if (!nxt_double_is_zero(f) && buf < end) { 430 *buf++ = '.'; 431 432 while (!nxt_double_is_zero(f) && buf < end) { 433 f *= 10; 434 f = modf(f, &i); 435 *buf++ = (u_char) i + '0'; 436 } 437 } 438 } 439 440 continue; 441 442 case 'r': 443 i64 = (int64_t) va_arg(args, rlim_t); 444 sign = 1; 445 break; 446 447 case 'p': 448 ui64 = (uintptr_t) va_arg(args, void *); 449 sign = 0; 450 spf.hex = HEXADECIMAL; 451 /* 452 * spf.width = NXT_PTR_SIZE * 2; 453 * spf.padding = '0'; 454 */ 455 goto number; 456 457 case 'c': 458 d = va_arg(args, int); 459 *buf++ = (u_char) (d & 0xFF); 460 fmt++; 461 462 continue; 463 464 case 'F': 465 fmt++; 466 467 switch (*fmt) { 468 469 case 'D': 470 i64 = (int64_t) va_arg(args, nxt_fd_t); 471 sign = 1; 472 473 goto number; 474 475 case 'N': 476 fn = va_arg(args, nxt_file_name_t *); 477 p = fn; 478 479 while (*p != '\0' && buf < end) { 480 *buf++ = *p++; 481 } 482 483 fmt++; 484 continue; 485 486 default: 487 continue; 488 } 489 490 case 'P': 491 fmt++; 492 493 switch (*fmt) { 494 495 case 'I': 496 i64 = (int64_t) va_arg(args, nxt_pid_t); 497 sign = 1; 498 goto number; 499 500 case 'T': 501 ui64 = (uint64_t) (uintptr_t) va_arg(args, nxt_tid_t); 502 sign = 0; 503 goto number; 504 #if 0 505 case 'F': 506 ui64 = (uint64_t) va_arg(args, nxt_fid_t); 507 sign = 0; 508 goto number; 509 #endif 510 case 'H': 511 ui64 = (uint64_t) (uintptr_t) va_arg(args, pthread_t); 512 spf.hex = HEXADECIMAL; 513 sign = 0; 514 goto number; 515 516 default: 517 continue; 518 } 519 520 case 'Z': 521 *buf++ = '\0'; 522 fmt++; 523 continue; 524 525 case 'n': 526 *buf++ = '\n'; 527 fmt++; 528 continue; 529 530 case '%': 531 *buf++ = '%'; 532 fmt++; 533 continue; 534 535 default: 536 *buf++ = *fmt++; 537 continue; 538 } 539 540 number: 541 542 if (sign) { 543 if (i64 < 0) { 544 *buf++ = '-'; 545 ui64 = (uint64_t) -i64; 546 547 } else { 548 ui64 = (uint64_t) i64; 549 } 550 } 551 552 buf = nxt_integer(&spf, buf, ui64); 553 554 fmt++; 555 continue; 556 557 copy: 558 559 if (nxt_slow_path(p == NULL)) { 560 p = null; 561 length = nxt_length(null); 562 563 } else { 564 length = nxt_min((size_t) (end - buf), length); 565 } 566 567 buf = nxt_cpymem(buf, p, length); 568 continue; 569 } 570 571 return buf; 572 } 573 574 575 static u_char * 576 nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64) 577 { 578 u_char *p, *end; 579 size_t length; 580 u_char temp[NXT_INT64_T_LEN]; 581 582 p = temp + NXT_INT64_T_LEN; 583 584 if (spf->hex == NULL) { 585 586 #if (NXT_32BIT) 587 588 for ( ;; ) { 589 u_char *start; 590 uint32_t ui32; 591 592 /* 593 * 32-bit platforms usually lack hardware support of 64-bit 594 * division and remainder operations. For this reason C compiler 595 * adds calls to the runtime library functions which provides 596 * these operations. These functions usually have about hundred 597 * lines of code. 598 * 599 * For 32-bit numbers and some constant divisors GCC, Clang and 600 * other compilers can use inlined multiplications and shifts 601 * which are faster than division or remainder operations. 602 * For example, unsigned "ui32 / 10" is compiled to 603 * 604 * ((uint64_t) ui32 * 0xCCCCCCCD) >> 35 605 * 606 * So a 64-bit number is split to parts by 10^9. The parts fit 607 * to 32 bits and are processed separately as 32-bit numbers. A 608 * number of 64-bit division/remainder operations is significantly 609 * decreased depending on the 64-bit number's value, it is 610 * 0 if the 64-bit value is less than 4294967296, 611 * 1 if the 64-bit value is greater than 4294967295 612 * and less than 4294967296000000000, 613 * 2 otherwise. 614 */ 615 616 if (ui64 <= 0xFFFFFFFF) { 617 ui32 = (uint32_t) ui64; 618 start = NULL; 619 620 } else { 621 ui32 = (uint32_t) (ui64 % 1000000000); 622 start = p - 9; 623 } 624 625 do { 626 *(--p) = (u_char) (ui32 % 10 + '0'); 627 ui32 /= 10; 628 } while (ui32 != 0); 629 630 if (start == NULL) { 631 break; 632 } 633 634 /* Add leading zeros of part. */ 635 636 while (p > start) { 637 *(--p) = '0'; 638 } 639 640 ui64 /= 1000000000; 641 } 642 643 #else /* NXT_64BIT */ 644 645 do { 646 *(--p) = (u_char) (ui64 % 10 + '0'); 647 ui64 /= 10; 648 } while (ui64 != 0); 649 650 #endif 651 652 } else { 653 654 do { 655 *(--p) = spf->hex[ui64 & 0xF]; 656 ui64 >>= 4; 657 } while (ui64 != 0); 658 } 659 660 /* Zero or space padding. */ 661 662 if (spf->width != 0) { 663 664 length = (temp + NXT_INT64_T_LEN) - p; 665 end = buf + (spf->width - length); 666 end = nxt_min(end, spf->end); 667 668 while (buf < end) { 669 *buf++ = spf->padding; 670 } 671 } 672 673 /* Number copying. */ 674 675 length = (temp + NXT_INT64_T_LEN) - p; 676 end = buf + length; 677 end = nxt_min(end, spf->end); 678 679 while (buf < end) { 680 *buf++ = *p++; 681 } 682 683 return buf; 684 } 685 686 687 static u_char * 688 nxt_number(nxt_sprintf_t *spf, u_char *buf, double n) 689 { 690 u_char *p, *end; 691 size_t length; 692 u_char temp[NXT_DOUBLE_LEN]; 693 694 p = temp + NXT_DOUBLE_LEN; 695 696 do { 697 *(--p) = (u_char) (fmod(n, 10) + '0'); 698 n = trunc(n / 10); 699 } while (!nxt_double_is_zero(n)); 700 701 /* Zero or space padding. */ 702 703 if (spf->width != 0) { 704 length = (temp + NXT_DOUBLE_LEN) - p; 705 end = buf + (spf->width - length); 706 end = nxt_min(end, spf->end); 707 708 while (buf < end) { 709 *buf++ = spf->padding; 710 } 711 } 712 713 /* Number copying. */ 714 715 length = (temp + NXT_DOUBLE_LEN) - p; 716 717 end = buf + length; 718 end = nxt_min(end, spf->end); 719 720 while (buf < end) { 721 *buf++ = *p++; 722 } 723 724 return buf; 725 } 726