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