xref: /unit/src/nxt_sprintf.c (revision 1183)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru #include <math.h>
90Sigor@sysoev.ru #include <float.h>
100Sigor@sysoev.ru 
110Sigor@sysoev.ru 
120Sigor@sysoev.ru /*
130Sigor@sysoev.ru  * Supported formats:
140Sigor@sysoev.ru  *
15*1183Svbart@nginx.com  *    %[0][width][x|X]O         nxt_off_t
16*1183Svbart@nginx.com  *    %[0][width][x|X]T         nxt_time_t
170Sigor@sysoev.ru  *    %[0][width][u][x|X]z      ssize_t/size_t
180Sigor@sysoev.ru  *    %[0][width][u][x|X]d      int/u_int
190Sigor@sysoev.ru  *    %[0][width][u][x|X]l      long
200Sigor@sysoev.ru  *    %[0][width|m][u][x|X]i    nxt_int_t/nxt_uint_t
210Sigor@sysoev.ru  *    %[0][width][u][x|X]D      int32_t/uint32_t
220Sigor@sysoev.ru  *    %[0][width][u][x|X]L      int64_t/uint64_t
230Sigor@sysoev.ru  *    %[0][width|m][u][x|X]A    nxt_atomic_int_t/nxt_atomic_uint_t
240Sigor@sysoev.ru  *    %[0][width][.width]f      double, max valid number fits to %18.15f
250Sigor@sysoev.ru  *
260Sigor@sysoev.ru  *    %FD                       nxt_fd_t, int / HANDLE
270Sigor@sysoev.ru  *    %d                        nxt_socket_t, int
280Sigor@sysoev.ru  *
290Sigor@sysoev.ru  *    %PI                       nxt_pid_t, process id
300Sigor@sysoev.ru  *    %PT                       nxt_tid_t, thread id
310Sigor@sysoev.ru  *    %PF                       nxt_fid_t, fiber id
320Sigor@sysoev.ru  *    %PH                       pthread_t handle returned by pthread_self()
330Sigor@sysoev.ru  *
340Sigor@sysoev.ru  *    %s                        null-terminated string
350Sigor@sysoev.ru  *    %*s                       length and string
360Sigor@sysoev.ru  *    %FN                       nxt_file_name_t *
370Sigor@sysoev.ru  *
380Sigor@sysoev.ru  *    %M                        nxt_msec_t
390Sigor@sysoev.ru  *    %N                        nxt_nsec_t
400Sigor@sysoev.ru  *    %r                        rlim_t
410Sigor@sysoev.ru  *    %p                        void *
420Sigor@sysoev.ru  *    %b                        nxt_bool_t
430Sigor@sysoev.ru  *    %E                        nxt_err_t
440Sigor@sysoev.ru  *    %V                        nxt_str_t *
450Sigor@sysoev.ru  *    %Z                        '\0'
460Sigor@sysoev.ru  *    %n                        '\n'
470Sigor@sysoev.ru  *    %c                        char
480Sigor@sysoev.ru  *    %%                        %
490Sigor@sysoev.ru  *
500Sigor@sysoev.ru  *  Reserved:
510Sigor@sysoev.ru  *    %t                        ptrdiff_t
520Sigor@sysoev.ru  *    %S                        null-terminated wchar string
530Sigor@sysoev.ru  *    %C                        wchar
5472Sigor@sysoev.ru  *    %[0][width][u][x|X]Q      int128_t/uint128_t
550Sigor@sysoev.ru  */
560Sigor@sysoev.ru 
570Sigor@sysoev.ru 
580Sigor@sysoev.ru u_char * nxt_cdecl
590Sigor@sysoev.ru nxt_sprintf(u_char *buf, u_char *end, const char *fmt, ...)
600Sigor@sysoev.ru {
610Sigor@sysoev.ru     u_char   *p;
620Sigor@sysoev.ru     va_list  args;
630Sigor@sysoev.ru 
640Sigor@sysoev.ru     va_start(args, fmt);
650Sigor@sysoev.ru     p = nxt_vsprintf(buf, end, fmt, args);
660Sigor@sysoev.ru     va_end(args);
670Sigor@sysoev.ru 
680Sigor@sysoev.ru     return p;
690Sigor@sysoev.ru }
700Sigor@sysoev.ru 
710Sigor@sysoev.ru 
720Sigor@sysoev.ru /*
730Sigor@sysoev.ru  * nxt_sprintf_t is used:
740Sigor@sysoev.ru  *    to pass several parameters of nxt_integer() via single pointer
750Sigor@sysoev.ru  *    and to store little used variables of nxt_vsprintf().
760Sigor@sysoev.ru  */
770Sigor@sysoev.ru 
780Sigor@sysoev.ru typedef struct {
791008Szelenkov@nginx.com     u_char        *end;
801008Szelenkov@nginx.com     const u_char  *hex;
811008Szelenkov@nginx.com     uint32_t      width;
821008Szelenkov@nginx.com     int32_t       frac_width;
831008Szelenkov@nginx.com     uint8_t       max_width;
841008Szelenkov@nginx.com     u_char        padding;
850Sigor@sysoev.ru } nxt_sprintf_t;
860Sigor@sysoev.ru 
870Sigor@sysoev.ru 
880Sigor@sysoev.ru static u_char *nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64);
890Sigor@sysoev.ru static u_char *nxt_number(nxt_sprintf_t *spf, u_char *buf, double n);
900Sigor@sysoev.ru 
910Sigor@sysoev.ru 
920Sigor@sysoev.ru /* A right way of "f == 0.0". */
930Sigor@sysoev.ru #define                                                                       \
940Sigor@sysoev.ru nxt_double_is_zero(f)                                                         \
950Sigor@sysoev.ru     (fabs(f) <= FLT_EPSILON)
960Sigor@sysoev.ru 
970Sigor@sysoev.ru 
980Sigor@sysoev.ru u_char *
990Sigor@sysoev.ru nxt_vsprintf(u_char *buf, u_char *end, const char *fmt, va_list args)
1000Sigor@sysoev.ru {
1010Sigor@sysoev.ru     u_char               *p;
1020Sigor@sysoev.ru     int                  d;
1030Sigor@sysoev.ru     double               f, i;
10410Sigor@sysoev.ru     size_t               length;
1050Sigor@sysoev.ru     int64_t              i64;
1060Sigor@sysoev.ru     uint64_t             ui64, frac;
1070Sigor@sysoev.ru     nxt_str_t            *v;
1080Sigor@sysoev.ru     nxt_err_t            err;
1090Sigor@sysoev.ru     nxt_uint_t           scale, n;
1100Sigor@sysoev.ru     nxt_msec_t           ms;
1110Sigor@sysoev.ru     nxt_nsec_t           ns;
1120Sigor@sysoev.ru     nxt_bool_t           sign;
1130Sigor@sysoev.ru     nxt_sprintf_t        spf;
1140Sigor@sysoev.ru     nxt_file_name_t      *fn;
1150Sigor@sysoev.ru 
1160Sigor@sysoev.ru     static const u_char  hexadecimal[16] = "0123456789abcdef";
1170Sigor@sysoev.ru     static const u_char  HEXADECIMAL[16] = "0123456789ABCDEF";
1180Sigor@sysoev.ru     static const u_char  nan[] = "[nan]";
1190Sigor@sysoev.ru     static const u_char  infinity[] = "[infinity]";
1200Sigor@sysoev.ru 
1210Sigor@sysoev.ru     spf.end = end;
1220Sigor@sysoev.ru 
1230Sigor@sysoev.ru     while (*fmt != '\0' && buf < end) {
1240Sigor@sysoev.ru 
1250Sigor@sysoev.ru         /*
1260Sigor@sysoev.ru          * "buf < end" means that we could copy at least one character:
1270Sigor@sysoev.ru          * a plain character, "%%", "%c", or a minus without test.
1280Sigor@sysoev.ru          */
1290Sigor@sysoev.ru 
1300Sigor@sysoev.ru         if (*fmt != '%') {
1310Sigor@sysoev.ru             *buf++ = *fmt++;
1320Sigor@sysoev.ru             continue;
1330Sigor@sysoev.ru         }
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru         fmt++;
1360Sigor@sysoev.ru 
1370Sigor@sysoev.ru         /* Test some often used text formats first. */
1380Sigor@sysoev.ru 
1390Sigor@sysoev.ru         switch (*fmt) {
1400Sigor@sysoev.ru 
1410Sigor@sysoev.ru         case 'V':
1420Sigor@sysoev.ru             fmt++;
1430Sigor@sysoev.ru             v = va_arg(args, nxt_str_t *);
1440Sigor@sysoev.ru 
1450Sigor@sysoev.ru             if (nxt_fast_path(v != NULL)) {
14610Sigor@sysoev.ru                 length = v->length;
14710Sigor@sysoev.ru                 p = v->start;
1480Sigor@sysoev.ru                 goto copy;
1490Sigor@sysoev.ru             }
1500Sigor@sysoev.ru 
1510Sigor@sysoev.ru             continue;
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru         case 's':
1540Sigor@sysoev.ru             p = va_arg(args, u_char *);
1550Sigor@sysoev.ru 
1560Sigor@sysoev.ru             if (nxt_fast_path(p != NULL)) {
1570Sigor@sysoev.ru                 while (*p != '\0' && buf < end) {
1580Sigor@sysoev.ru                     *buf++ = *p++;
1590Sigor@sysoev.ru                 }
1600Sigor@sysoev.ru             }
1610Sigor@sysoev.ru 
1620Sigor@sysoev.ru             fmt++;
1630Sigor@sysoev.ru             continue;
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru         case '*':
166493Spluknet@nginx.com             length = va_arg(args, size_t);
1670Sigor@sysoev.ru 
1680Sigor@sysoev.ru             fmt++;
1690Sigor@sysoev.ru 
1700Sigor@sysoev.ru             if (*fmt == 's') {
1710Sigor@sysoev.ru                 fmt++;
1720Sigor@sysoev.ru                 p = va_arg(args, u_char *);
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru                 if (nxt_fast_path(p != NULL)) {
1750Sigor@sysoev.ru                     goto copy;
1760Sigor@sysoev.ru                 }
1770Sigor@sysoev.ru             }
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru             continue;
1800Sigor@sysoev.ru 
1810Sigor@sysoev.ru         default:
1820Sigor@sysoev.ru             break;
1830Sigor@sysoev.ru         }
1840Sigor@sysoev.ru 
1850Sigor@sysoev.ru         spf.hex = NULL;
1860Sigor@sysoev.ru         spf.width = 0;
1870Sigor@sysoev.ru         spf.frac_width = -1;
1880Sigor@sysoev.ru         spf.max_width = 0;
1890Sigor@sysoev.ru         spf.padding = (*fmt == '0') ? '0' : ' ';
1900Sigor@sysoev.ru 
1910Sigor@sysoev.ru         sign = 1;
1920Sigor@sysoev.ru 
1930Sigor@sysoev.ru         i64 = 0;
1940Sigor@sysoev.ru         ui64 = 0;
1950Sigor@sysoev.ru 
1960Sigor@sysoev.ru         while (*fmt >= '0' && *fmt <= '9') {
1970Sigor@sysoev.ru             spf.width = spf.width * 10 + (*fmt++ - '0');
1980Sigor@sysoev.ru         }
1990Sigor@sysoev.ru 
2000Sigor@sysoev.ru 
2010Sigor@sysoev.ru         for ( ;; ) {
2020Sigor@sysoev.ru             switch (*fmt) {
2030Sigor@sysoev.ru 
2040Sigor@sysoev.ru             case 'u':
2050Sigor@sysoev.ru                 sign = 0;
2060Sigor@sysoev.ru                 fmt++;
2070Sigor@sysoev.ru                 continue;
2080Sigor@sysoev.ru 
2090Sigor@sysoev.ru             case 'm':
2100Sigor@sysoev.ru                 spf.max_width = 1;
2110Sigor@sysoev.ru                 fmt++;
2120Sigor@sysoev.ru                 continue;
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru             case 'X':
2150Sigor@sysoev.ru                 spf.hex = HEXADECIMAL;
2160Sigor@sysoev.ru                 sign = 0;
2170Sigor@sysoev.ru                 fmt++;
2180Sigor@sysoev.ru                 continue;
2190Sigor@sysoev.ru 
2200Sigor@sysoev.ru             case 'x':
2210Sigor@sysoev.ru                 spf.hex = hexadecimal;
2220Sigor@sysoev.ru                 sign = 0;
2230Sigor@sysoev.ru                 fmt++;
2240Sigor@sysoev.ru                 continue;
2250Sigor@sysoev.ru 
2260Sigor@sysoev.ru             case '.':
2270Sigor@sysoev.ru                 fmt++;
2280Sigor@sysoev.ru                 spf.frac_width = 0;
2290Sigor@sysoev.ru 
2300Sigor@sysoev.ru                 while (*fmt >= '0' && *fmt <= '9') {
2310Sigor@sysoev.ru                     spf.frac_width = spf.frac_width * 10 + *fmt++ - '0';
2320Sigor@sysoev.ru                 }
2330Sigor@sysoev.ru 
2340Sigor@sysoev.ru                 break;
2350Sigor@sysoev.ru 
2360Sigor@sysoev.ru             default:
2370Sigor@sysoev.ru                 break;
2380Sigor@sysoev.ru             }
2390Sigor@sysoev.ru 
2400Sigor@sysoev.ru             break;
2410Sigor@sysoev.ru         }
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru 
2440Sigor@sysoev.ru         switch (*fmt) {
2450Sigor@sysoev.ru 
2460Sigor@sysoev.ru         case 'E':
2470Sigor@sysoev.ru             err = va_arg(args, nxt_err_t);
2480Sigor@sysoev.ru 
2490Sigor@sysoev.ru             *buf++ = '(';
2500Sigor@sysoev.ru             spf.hex = NULL;
2510Sigor@sysoev.ru             spf.width = 0;
2520Sigor@sysoev.ru             buf = nxt_integer(&spf, buf, err);
2530Sigor@sysoev.ru 
2540Sigor@sysoev.ru             if (buf < end - 1) {
2550Sigor@sysoev.ru                 *buf++ = ':';
2560Sigor@sysoev.ru                 *buf++ = ' ';
2570Sigor@sysoev.ru             }
2580Sigor@sysoev.ru 
2590Sigor@sysoev.ru             buf = nxt_strerror(err, buf, end - buf);
2600Sigor@sysoev.ru 
2610Sigor@sysoev.ru             if (buf < end) {
2620Sigor@sysoev.ru                 *buf++ = ')';
2630Sigor@sysoev.ru             }
2640Sigor@sysoev.ru 
2650Sigor@sysoev.ru             fmt++;
2660Sigor@sysoev.ru             continue;
2670Sigor@sysoev.ru 
2680Sigor@sysoev.ru         case 'O':
2690Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, nxt_off_t);
2700Sigor@sysoev.ru             sign = 1;
2710Sigor@sysoev.ru             goto number;
2720Sigor@sysoev.ru 
2730Sigor@sysoev.ru         case 'T':
2740Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, nxt_time_t);
2750Sigor@sysoev.ru             sign = 1;
2760Sigor@sysoev.ru             goto number;
2770Sigor@sysoev.ru 
2780Sigor@sysoev.ru         case 'M':
2790Sigor@sysoev.ru             ms = (nxt_msec_t) va_arg(args, nxt_msec_t);
2800Sigor@sysoev.ru             if ((nxt_msec_int_t) ms == -1 && spf.hex == NULL) {
2810Sigor@sysoev.ru                 i64 = -1;
2820Sigor@sysoev.ru                 sign = 1;
2830Sigor@sysoev.ru             } else {
2840Sigor@sysoev.ru                 ui64 = (uint64_t) ms;
2850Sigor@sysoev.ru                 sign = 0;
2860Sigor@sysoev.ru             }
2870Sigor@sysoev.ru             goto number;
2880Sigor@sysoev.ru 
2890Sigor@sysoev.ru         case 'N':
2900Sigor@sysoev.ru             ns = (nxt_nsec_t) va_arg(args, nxt_nsec_t);
2910Sigor@sysoev.ru             if ((nxt_nsec_int_t) ns == -1) {
2920Sigor@sysoev.ru                 i64 = -1;
2930Sigor@sysoev.ru                 sign = 1;
2940Sigor@sysoev.ru             } else {
2950Sigor@sysoev.ru                 ui64 = (uint64_t) ns;
2960Sigor@sysoev.ru                 sign = 0;
2970Sigor@sysoev.ru             }
2980Sigor@sysoev.ru             goto number;
2990Sigor@sysoev.ru 
3000Sigor@sysoev.ru         case 'z':
3010Sigor@sysoev.ru             if (sign) {
3020Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, ssize_t);
3030Sigor@sysoev.ru             } else {
3040Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, size_t);
3050Sigor@sysoev.ru             }
3060Sigor@sysoev.ru             goto number;
3070Sigor@sysoev.ru 
3080Sigor@sysoev.ru         case 'i':
3090Sigor@sysoev.ru             if (sign) {
3100Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_int_t);
3110Sigor@sysoev.ru             } else {
3120Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_uint_t);
3130Sigor@sysoev.ru             }
3140Sigor@sysoev.ru 
3150Sigor@sysoev.ru             if (spf.max_width != 0) {
3160Sigor@sysoev.ru                 spf.width = NXT_INT_T_LEN;
3170Sigor@sysoev.ru             }
3180Sigor@sysoev.ru 
3190Sigor@sysoev.ru             goto number;
3200Sigor@sysoev.ru 
3210Sigor@sysoev.ru         case 'd':
3220Sigor@sysoev.ru             if (sign) {
3230Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, int);
3240Sigor@sysoev.ru             } else {
3250Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, u_int);
3260Sigor@sysoev.ru             }
3270Sigor@sysoev.ru             goto number;
3280Sigor@sysoev.ru 
3290Sigor@sysoev.ru         case 'l':
3300Sigor@sysoev.ru             if (sign) {
3310Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, long);
3320Sigor@sysoev.ru             } else {
3330Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, u_long);
3340Sigor@sysoev.ru             }
3350Sigor@sysoev.ru             goto number;
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru         case 'D':
3380Sigor@sysoev.ru             if (sign) {
3390Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, int32_t);
3400Sigor@sysoev.ru             } else {
3410Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, uint32_t);
3420Sigor@sysoev.ru             }
3430Sigor@sysoev.ru             goto number;
3440Sigor@sysoev.ru 
3450Sigor@sysoev.ru         case 'L':
3460Sigor@sysoev.ru             if (sign) {
3470Sigor@sysoev.ru                 i64 = va_arg(args, int64_t);
3480Sigor@sysoev.ru             } else {
3490Sigor@sysoev.ru                 ui64 = va_arg(args, uint64_t);
3500Sigor@sysoev.ru             }
3510Sigor@sysoev.ru             goto number;
3520Sigor@sysoev.ru 
3530Sigor@sysoev.ru         case 'A':
3540Sigor@sysoev.ru             if (sign) {
3550Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_atomic_int_t);
3560Sigor@sysoev.ru             } else {
3570Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_atomic_uint_t);
3580Sigor@sysoev.ru             }
3590Sigor@sysoev.ru 
3600Sigor@sysoev.ru             if (spf.max_width != 0) {
3610Sigor@sysoev.ru                 spf.width = NXT_ATOMIC_T_LEN;
3620Sigor@sysoev.ru             }
3630Sigor@sysoev.ru 
3640Sigor@sysoev.ru             goto number;
3650Sigor@sysoev.ru 
3660Sigor@sysoev.ru         case 'b':
3670Sigor@sysoev.ru             ui64 = (uint64_t) va_arg(args, nxt_bool_t);
3680Sigor@sysoev.ru             sign = 0;
3690Sigor@sysoev.ru             goto number;
3700Sigor@sysoev.ru 
3710Sigor@sysoev.ru         case 'f':
3720Sigor@sysoev.ru             fmt++;
3730Sigor@sysoev.ru 
3740Sigor@sysoev.ru             f = va_arg(args, double);
3750Sigor@sysoev.ru 
3760Sigor@sysoev.ru             if (f < 0) {
3770Sigor@sysoev.ru                 *buf++ = '-';
3780Sigor@sysoev.ru                 f = -f;
3790Sigor@sysoev.ru             }
3800Sigor@sysoev.ru 
3810Sigor@sysoev.ru             if (nxt_slow_path(isnan(f))) {
3820Sigor@sysoev.ru                 p = (u_char *) nan;
383703Svbart@nginx.com                 length = nxt_length(nan);
3840Sigor@sysoev.ru 
3850Sigor@sysoev.ru                 goto copy;
3860Sigor@sysoev.ru 
3870Sigor@sysoev.ru             } else if (nxt_slow_path(isinf(f))) {
3880Sigor@sysoev.ru                 p = (u_char *) infinity;
389703Svbart@nginx.com                 length = nxt_length(infinity);
3900Sigor@sysoev.ru 
3910Sigor@sysoev.ru                 goto copy;
3920Sigor@sysoev.ru             }
3930Sigor@sysoev.ru 
3940Sigor@sysoev.ru             (void) modf(f, &i);
3950Sigor@sysoev.ru             frac = 0;
3960Sigor@sysoev.ru 
3970Sigor@sysoev.ru             if (spf.frac_width > 0) {
3980Sigor@sysoev.ru 
3990Sigor@sysoev.ru                 scale = 1;
4000Sigor@sysoev.ru                 for (n = spf.frac_width; n != 0; n--) {
4010Sigor@sysoev.ru                     scale *= 10;
4020Sigor@sysoev.ru                 }
4030Sigor@sysoev.ru 
4040Sigor@sysoev.ru                 frac = (uint64_t) ((f - i) * scale + 0.5);
4050Sigor@sysoev.ru 
4060Sigor@sysoev.ru                 if (frac == scale) {
4070Sigor@sysoev.ru                     i += 1;
4080Sigor@sysoev.ru                     frac = 0;
4090Sigor@sysoev.ru                 }
4100Sigor@sysoev.ru             }
4110Sigor@sysoev.ru 
4120Sigor@sysoev.ru             buf = nxt_number(&spf, buf, i);
4130Sigor@sysoev.ru 
4140Sigor@sysoev.ru             if (spf.frac_width > 0) {
4150Sigor@sysoev.ru 
4160Sigor@sysoev.ru                 if (buf < end) {
4170Sigor@sysoev.ru                     *buf++ = '.';
4180Sigor@sysoev.ru 
4190Sigor@sysoev.ru                     spf.hex = NULL;
4200Sigor@sysoev.ru                     spf.padding = '0';
4210Sigor@sysoev.ru                     spf.width = spf.frac_width;
4220Sigor@sysoev.ru                     buf = nxt_integer(&spf, buf, frac);
4230Sigor@sysoev.ru                 }
4240Sigor@sysoev.ru 
4250Sigor@sysoev.ru             } else if (spf.frac_width < 0) {
4260Sigor@sysoev.ru                 f = modf(f, &i);
4270Sigor@sysoev.ru 
4280Sigor@sysoev.ru                 if (!nxt_double_is_zero(f) && buf < end) {
4290Sigor@sysoev.ru                     *buf++ = '.';
4300Sigor@sysoev.ru 
4310Sigor@sysoev.ru                     while (!nxt_double_is_zero(f) && buf < end) {
4320Sigor@sysoev.ru                         f *= 10;
4330Sigor@sysoev.ru                         f = modf(f, &i);
4340Sigor@sysoev.ru                         *buf++ = (u_char) i + '0';
4350Sigor@sysoev.ru                     }
4360Sigor@sysoev.ru                 }
4370Sigor@sysoev.ru             }
4380Sigor@sysoev.ru 
4390Sigor@sysoev.ru             continue;
4400Sigor@sysoev.ru 
4410Sigor@sysoev.ru         case 'r':
4420Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, rlim_t);
4430Sigor@sysoev.ru             sign = 1;
4440Sigor@sysoev.ru             break;
4450Sigor@sysoev.ru 
4460Sigor@sysoev.ru         case 'p':
4470Sigor@sysoev.ru             ui64 = (uintptr_t) va_arg(args, void *);
4480Sigor@sysoev.ru             sign = 0;
4490Sigor@sysoev.ru             spf.hex = HEXADECIMAL;
4500Sigor@sysoev.ru             /*
4510Sigor@sysoev.ru              * spf.width = NXT_PTR_SIZE * 2;
4520Sigor@sysoev.ru              * spf.padding = '0';
4530Sigor@sysoev.ru              */
4540Sigor@sysoev.ru             goto number;
4550Sigor@sysoev.ru 
4560Sigor@sysoev.ru         case 'c':
4570Sigor@sysoev.ru             d = va_arg(args, int);
458611Svbart@nginx.com             *buf++ = (u_char) (d & 0xFF);
4590Sigor@sysoev.ru             fmt++;
4600Sigor@sysoev.ru 
4610Sigor@sysoev.ru             continue;
4620Sigor@sysoev.ru 
4630Sigor@sysoev.ru         case 'F':
4640Sigor@sysoev.ru             fmt++;
4650Sigor@sysoev.ru 
4660Sigor@sysoev.ru             switch (*fmt) {
4670Sigor@sysoev.ru 
4680Sigor@sysoev.ru             case 'D':
4690Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_fd_t);
4700Sigor@sysoev.ru                 sign = 1;
4710Sigor@sysoev.ru 
4720Sigor@sysoev.ru                 goto number;
4730Sigor@sysoev.ru 
4740Sigor@sysoev.ru             case 'N':
4750Sigor@sysoev.ru                 fn = va_arg(args, nxt_file_name_t *);
4760Sigor@sysoev.ru                 p = fn;
4770Sigor@sysoev.ru 
4780Sigor@sysoev.ru                 while (*p != '\0' && buf < end) {
4790Sigor@sysoev.ru                     *buf++ = *p++;
4800Sigor@sysoev.ru                 }
4810Sigor@sysoev.ru 
4820Sigor@sysoev.ru                 fmt++;
4830Sigor@sysoev.ru                 continue;
4840Sigor@sysoev.ru 
4850Sigor@sysoev.ru             default:
4860Sigor@sysoev.ru                 continue;
4870Sigor@sysoev.ru             }
4880Sigor@sysoev.ru 
4890Sigor@sysoev.ru         case 'P':
4900Sigor@sysoev.ru             fmt++;
4910Sigor@sysoev.ru 
4920Sigor@sysoev.ru             switch (*fmt) {
4930Sigor@sysoev.ru 
4940Sigor@sysoev.ru             case 'I':
4950Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_pid_t);
4960Sigor@sysoev.ru                 sign = 1;
4970Sigor@sysoev.ru                 goto number;
4980Sigor@sysoev.ru 
4990Sigor@sysoev.ru             case 'T':
500336Spluknet@nginx.com                 ui64 = (uint64_t) (uintptr_t) va_arg(args, nxt_tid_t);
5010Sigor@sysoev.ru                 sign = 0;
5020Sigor@sysoev.ru                 goto number;
503326Svbart@nginx.com #if 0
5040Sigor@sysoev.ru             case 'F':
5050Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_fid_t);
5060Sigor@sysoev.ru                 sign = 0;
5070Sigor@sysoev.ru                 goto number;
508326Svbart@nginx.com #endif
5090Sigor@sysoev.ru             case 'H':
5100Sigor@sysoev.ru                 ui64 = (uint64_t) (uintptr_t) va_arg(args, pthread_t);
5110Sigor@sysoev.ru                 spf.hex = HEXADECIMAL;
5120Sigor@sysoev.ru                 sign = 0;
5130Sigor@sysoev.ru                 goto number;
5140Sigor@sysoev.ru 
5150Sigor@sysoev.ru             default:
5160Sigor@sysoev.ru                 continue;
5170Sigor@sysoev.ru             }
5180Sigor@sysoev.ru 
5190Sigor@sysoev.ru         case 'Z':
5200Sigor@sysoev.ru             *buf++ = '\0';
5210Sigor@sysoev.ru             fmt++;
5220Sigor@sysoev.ru             continue;
5230Sigor@sysoev.ru 
5240Sigor@sysoev.ru         case 'n':
525704Sigor@sysoev.ru             *buf++ = '\n';
5260Sigor@sysoev.ru             fmt++;
5270Sigor@sysoev.ru             continue;
5280Sigor@sysoev.ru 
5290Sigor@sysoev.ru         case '%':
5300Sigor@sysoev.ru             *buf++ = '%';
5310Sigor@sysoev.ru             fmt++;
5320Sigor@sysoev.ru             continue;
5330Sigor@sysoev.ru 
5340Sigor@sysoev.ru         default:
5350Sigor@sysoev.ru             *buf++ = *fmt++;
5360Sigor@sysoev.ru             continue;
5370Sigor@sysoev.ru         }
5380Sigor@sysoev.ru 
5390Sigor@sysoev.ru     number:
5400Sigor@sysoev.ru 
5410Sigor@sysoev.ru         if (sign) {
5420Sigor@sysoev.ru             if (i64 < 0) {
5430Sigor@sysoev.ru                 *buf++ = '-';
5440Sigor@sysoev.ru                 ui64 = (uint64_t) -i64;
5450Sigor@sysoev.ru 
5460Sigor@sysoev.ru             } else {
5470Sigor@sysoev.ru                 ui64 = (uint64_t) i64;
5480Sigor@sysoev.ru             }
5490Sigor@sysoev.ru         }
5500Sigor@sysoev.ru 
5510Sigor@sysoev.ru         buf = nxt_integer(&spf, buf, ui64);
5520Sigor@sysoev.ru 
5530Sigor@sysoev.ru         fmt++;
5540Sigor@sysoev.ru         continue;
5550Sigor@sysoev.ru 
5560Sigor@sysoev.ru     copy:
5570Sigor@sysoev.ru 
55810Sigor@sysoev.ru         buf = nxt_cpymem(buf, p, nxt_min((size_t) (end - buf), length));
5590Sigor@sysoev.ru         continue;
5600Sigor@sysoev.ru     }
5610Sigor@sysoev.ru 
5620Sigor@sysoev.ru     return buf;
5630Sigor@sysoev.ru }
5640Sigor@sysoev.ru 
5650Sigor@sysoev.ru 
5660Sigor@sysoev.ru static u_char *
5670Sigor@sysoev.ru nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64)
5680Sigor@sysoev.ru {
5690Sigor@sysoev.ru     u_char  *p, *end;
57010Sigor@sysoev.ru     size_t  length;
5710Sigor@sysoev.ru     u_char  temp[NXT_INT64_T_LEN];
5720Sigor@sysoev.ru 
5730Sigor@sysoev.ru     p = temp + NXT_INT64_T_LEN;
5740Sigor@sysoev.ru 
5750Sigor@sysoev.ru     if (spf->hex == NULL) {
5760Sigor@sysoev.ru 
5770Sigor@sysoev.ru #if (NXT_32BIT)
5780Sigor@sysoev.ru 
5790Sigor@sysoev.ru         for ( ;; ) {
5800Sigor@sysoev.ru             u_char    *start;
5810Sigor@sysoev.ru             uint32_t  ui32;
5820Sigor@sysoev.ru 
5830Sigor@sysoev.ru             /*
5840Sigor@sysoev.ru              * 32-bit platforms usually lack hardware support of 64-bit
5850Sigor@sysoev.ru              * division and remainder operations.  For this reason C compiler
5860Sigor@sysoev.ru              * adds calls to the runtime library functions which provides
5870Sigor@sysoev.ru              * these operations.  These functions usually have about hundred
5880Sigor@sysoev.ru              * lines of code.
5890Sigor@sysoev.ru              *
5900Sigor@sysoev.ru              * For 32-bit numbers and some constant divisors GCC, Clang and
5910Sigor@sysoev.ru              * other compilers can use inlined multiplications and shifts
5920Sigor@sysoev.ru              * which are faster than division or remainder operations.
5930Sigor@sysoev.ru              * For example, unsigned "ui32 / 10" is compiled to
5940Sigor@sysoev.ru              *
5950Sigor@sysoev.ru              *     ((uint64_t) ui32 * 0xCCCCCCCD) >> 35
5960Sigor@sysoev.ru              *
5970Sigor@sysoev.ru              * So a 64-bit number is split to parts by 10^9.  The parts fit
5980Sigor@sysoev.ru              * to 32 bits and are processed separately as 32-bit numbers.  A
5990Sigor@sysoev.ru              * number of 64-bit division/remainder operations is significantly
6000Sigor@sysoev.ru              * decreased depending on the 64-bit number's value, it is
6010Sigor@sysoev.ru              *   0 if the 64-bit value is less than 4294967296,
6020Sigor@sysoev.ru              *   1 if the 64-bit value is greater than 4294967295
6030Sigor@sysoev.ru              *                           and less than 4294967296000000000,
6040Sigor@sysoev.ru              *   2 otherwise.
6050Sigor@sysoev.ru              */
6060Sigor@sysoev.ru 
607611Svbart@nginx.com             if (ui64 <= 0xFFFFFFFF) {
6080Sigor@sysoev.ru                 ui32 = (uint32_t) ui64;
6090Sigor@sysoev.ru                 start = NULL;
6100Sigor@sysoev.ru 
6110Sigor@sysoev.ru             } else {
6120Sigor@sysoev.ru                 ui32 = (uint32_t) (ui64 % 1000000000);
6130Sigor@sysoev.ru                 start = p - 9;
6140Sigor@sysoev.ru             }
6150Sigor@sysoev.ru 
6160Sigor@sysoev.ru             do {
6170Sigor@sysoev.ru                 *(--p) = (u_char) (ui32 % 10 + '0');
6180Sigor@sysoev.ru                 ui32 /= 10;
6190Sigor@sysoev.ru             } while (ui32 != 0);
6200Sigor@sysoev.ru 
6210Sigor@sysoev.ru             if (start == NULL) {
6220Sigor@sysoev.ru                 break;
6230Sigor@sysoev.ru             }
6240Sigor@sysoev.ru 
6250Sigor@sysoev.ru             /* Add leading zeros of part. */
6260Sigor@sysoev.ru 
6270Sigor@sysoev.ru             while (p > start) {
6280Sigor@sysoev.ru                 *(--p) = '0';
6290Sigor@sysoev.ru             }
6300Sigor@sysoev.ru 
6310Sigor@sysoev.ru             ui64 /= 1000000000;
6320Sigor@sysoev.ru         }
6330Sigor@sysoev.ru 
6340Sigor@sysoev.ru #else  /* NXT_64BIT */
6350Sigor@sysoev.ru 
6360Sigor@sysoev.ru         do {
6370Sigor@sysoev.ru             *(--p) = (u_char) (ui64 % 10 + '0');
6380Sigor@sysoev.ru             ui64 /= 10;
6390Sigor@sysoev.ru         } while (ui64 != 0);
6400Sigor@sysoev.ru 
6410Sigor@sysoev.ru #endif
6420Sigor@sysoev.ru 
6430Sigor@sysoev.ru     } else {
6440Sigor@sysoev.ru 
6450Sigor@sysoev.ru         do {
646611Svbart@nginx.com             *(--p) = spf->hex[ui64 & 0xF];
6470Sigor@sysoev.ru             ui64 >>= 4;
6480Sigor@sysoev.ru         } while (ui64 != 0);
6490Sigor@sysoev.ru     }
6500Sigor@sysoev.ru 
6510Sigor@sysoev.ru     /* Zero or space padding. */
6520Sigor@sysoev.ru 
6530Sigor@sysoev.ru     if (spf->width != 0) {
6540Sigor@sysoev.ru 
65510Sigor@sysoev.ru         length = (temp + NXT_INT64_T_LEN) - p;
65610Sigor@sysoev.ru         end = buf + (spf->width - length);
6570Sigor@sysoev.ru         end = nxt_min(end, spf->end);
6580Sigor@sysoev.ru 
6590Sigor@sysoev.ru         while (buf < end) {
6600Sigor@sysoev.ru             *buf++ = spf->padding;
6610Sigor@sysoev.ru         }
6620Sigor@sysoev.ru     }
6630Sigor@sysoev.ru 
6640Sigor@sysoev.ru     /* Number copying. */
6650Sigor@sysoev.ru 
66610Sigor@sysoev.ru     length = (temp + NXT_INT64_T_LEN) - p;
66710Sigor@sysoev.ru     end = buf + length;
6680Sigor@sysoev.ru     end = nxt_min(end, spf->end);
6690Sigor@sysoev.ru 
6700Sigor@sysoev.ru     while (buf < end) {
6710Sigor@sysoev.ru         *buf++ = *p++;
6720Sigor@sysoev.ru     }
6730Sigor@sysoev.ru 
6740Sigor@sysoev.ru     return buf;
6750Sigor@sysoev.ru }
6760Sigor@sysoev.ru 
6770Sigor@sysoev.ru 
6780Sigor@sysoev.ru static u_char *
6790Sigor@sysoev.ru nxt_number(nxt_sprintf_t *spf, u_char *buf, double n)
6800Sigor@sysoev.ru {
6810Sigor@sysoev.ru     u_char  *p, *end;
68210Sigor@sysoev.ru     size_t  length;
6830Sigor@sysoev.ru     u_char  temp[NXT_DOUBLE_LEN];
6840Sigor@sysoev.ru 
6850Sigor@sysoev.ru     p = temp + NXT_DOUBLE_LEN;
6860Sigor@sysoev.ru 
6870Sigor@sysoev.ru     do {
6880Sigor@sysoev.ru         *(--p) = (u_char) (fmod(n, 10) + '0');
6890Sigor@sysoev.ru         n = trunc(n / 10);
6900Sigor@sysoev.ru     } while (!nxt_double_is_zero(n));
6910Sigor@sysoev.ru 
6920Sigor@sysoev.ru     /* Zero or space padding. */
6930Sigor@sysoev.ru 
6940Sigor@sysoev.ru     if (spf->width != 0) {
69510Sigor@sysoev.ru         length = (temp + NXT_DOUBLE_LEN) - p;
69610Sigor@sysoev.ru         end = buf + (spf->width - length);
6970Sigor@sysoev.ru         end = nxt_min(end, spf->end);
6980Sigor@sysoev.ru 
6990Sigor@sysoev.ru         while (buf < end) {
7000Sigor@sysoev.ru             *buf++ = spf->padding;
7010Sigor@sysoev.ru         }
7020Sigor@sysoev.ru     }
7030Sigor@sysoev.ru 
7040Sigor@sysoev.ru     /* Number copying. */
7050Sigor@sysoev.ru 
70610Sigor@sysoev.ru     length = (temp + NXT_DOUBLE_LEN) - p;
7070Sigor@sysoev.ru 
70810Sigor@sysoev.ru     end = buf + length;
7090Sigor@sysoev.ru     end = nxt_min(end, spf->end);
7100Sigor@sysoev.ru 
7110Sigor@sysoev.ru     while (buf < end) {
7120Sigor@sysoev.ru         *buf++ = *p++;
7130Sigor@sysoev.ru     }
7140Sigor@sysoev.ru 
7150Sigor@sysoev.ru     return buf;
7160Sigor@sysoev.ru }
717