xref: /unit/src/nxt_sprintf.c (revision 2115)
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  *
151183Svbart@nginx.com  *    %[0][width][x|X]O         nxt_off_t
161183Svbart@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". */
932084Salx.manpages@gmail.com #define nxt_double_is_zero(f)                                                 \
940Sigor@sysoev.ru     (fabs(f) <= FLT_EPSILON)
950Sigor@sysoev.ru 
960Sigor@sysoev.ru 
970Sigor@sysoev.ru u_char *
980Sigor@sysoev.ru nxt_vsprintf(u_char *buf, u_char *end, const char *fmt, va_list args)
990Sigor@sysoev.ru {
1000Sigor@sysoev.ru     int                  d;
1010Sigor@sysoev.ru     double               f, i;
10210Sigor@sysoev.ru     size_t               length;
1030Sigor@sysoev.ru     int64_t              i64;
1040Sigor@sysoev.ru     uint64_t             ui64, frac;
1050Sigor@sysoev.ru     nxt_str_t            *v;
1060Sigor@sysoev.ru     nxt_err_t            err;
1070Sigor@sysoev.ru     nxt_uint_t           scale, n;
1080Sigor@sysoev.ru     nxt_msec_t           ms;
1090Sigor@sysoev.ru     nxt_nsec_t           ns;
1100Sigor@sysoev.ru     nxt_bool_t           sign;
1112105Salx.manpages@gmail.com     const u_char         *p;
1120Sigor@sysoev.ru     nxt_sprintf_t        spf;
1130Sigor@sysoev.ru     nxt_file_name_t      *fn;
1140Sigor@sysoev.ru 
1150Sigor@sysoev.ru     static const u_char  hexadecimal[16] = "0123456789abcdef";
1160Sigor@sysoev.ru     static const u_char  HEXADECIMAL[16] = "0123456789ABCDEF";
1170Sigor@sysoev.ru     static const u_char  nan[] = "[nan]";
118*2115Szelenkov@nginx.com     static const u_char  null[] = "[null]";
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':
154*2115Szelenkov@nginx.com             fmt++;
155*2115Szelenkov@nginx.com 
1562105Salx.manpages@gmail.com             p = va_arg(args, const u_char *);
1570Sigor@sysoev.ru 
158*2115Szelenkov@nginx.com             if (nxt_slow_path(p == NULL)) {
159*2115Szelenkov@nginx.com                 goto copy;
1600Sigor@sysoev.ru             }
1610Sigor@sysoev.ru 
162*2115Szelenkov@nginx.com             while (*p != '\0' && buf < end) {
163*2115Szelenkov@nginx.com                 *buf++ = *p++;
164*2115Szelenkov@nginx.com             }
165*2115Szelenkov@nginx.com 
1660Sigor@sysoev.ru             continue;
1670Sigor@sysoev.ru 
1680Sigor@sysoev.ru         case '*':
169493Spluknet@nginx.com             length = va_arg(args, size_t);
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru             fmt++;
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru             if (*fmt == 's') {
1740Sigor@sysoev.ru                 fmt++;
1752105Salx.manpages@gmail.com                 p = va_arg(args, const u_char *);
1760Sigor@sysoev.ru 
177*2115Szelenkov@nginx.com                 goto copy;
1780Sigor@sysoev.ru             }
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru             continue;
1810Sigor@sysoev.ru 
1820Sigor@sysoev.ru         default:
1830Sigor@sysoev.ru             break;
1840Sigor@sysoev.ru         }
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru         spf.hex = NULL;
1870Sigor@sysoev.ru         spf.width = 0;
1880Sigor@sysoev.ru         spf.frac_width = -1;
1890Sigor@sysoev.ru         spf.max_width = 0;
1900Sigor@sysoev.ru         spf.padding = (*fmt == '0') ? '0' : ' ';
1910Sigor@sysoev.ru 
1920Sigor@sysoev.ru         sign = 1;
1930Sigor@sysoev.ru 
1940Sigor@sysoev.ru         i64 = 0;
1950Sigor@sysoev.ru         ui64 = 0;
1960Sigor@sysoev.ru 
1970Sigor@sysoev.ru         while (*fmt >= '0' && *fmt <= '9') {
1980Sigor@sysoev.ru             spf.width = spf.width * 10 + (*fmt++ - '0');
1990Sigor@sysoev.ru         }
2000Sigor@sysoev.ru 
2010Sigor@sysoev.ru 
2020Sigor@sysoev.ru         for ( ;; ) {
2030Sigor@sysoev.ru             switch (*fmt) {
2040Sigor@sysoev.ru 
2050Sigor@sysoev.ru             case 'u':
2060Sigor@sysoev.ru                 sign = 0;
2070Sigor@sysoev.ru                 fmt++;
2080Sigor@sysoev.ru                 continue;
2090Sigor@sysoev.ru 
2100Sigor@sysoev.ru             case 'm':
2110Sigor@sysoev.ru                 spf.max_width = 1;
2120Sigor@sysoev.ru                 fmt++;
2130Sigor@sysoev.ru                 continue;
2140Sigor@sysoev.ru 
2150Sigor@sysoev.ru             case 'X':
2160Sigor@sysoev.ru                 spf.hex = HEXADECIMAL;
2170Sigor@sysoev.ru                 sign = 0;
2180Sigor@sysoev.ru                 fmt++;
2190Sigor@sysoev.ru                 continue;
2200Sigor@sysoev.ru 
2210Sigor@sysoev.ru             case 'x':
2220Sigor@sysoev.ru                 spf.hex = hexadecimal;
2230Sigor@sysoev.ru                 sign = 0;
2240Sigor@sysoev.ru                 fmt++;
2250Sigor@sysoev.ru                 continue;
2260Sigor@sysoev.ru 
2270Sigor@sysoev.ru             case '.':
2280Sigor@sysoev.ru                 fmt++;
2290Sigor@sysoev.ru                 spf.frac_width = 0;
2300Sigor@sysoev.ru 
2310Sigor@sysoev.ru                 while (*fmt >= '0' && *fmt <= '9') {
2320Sigor@sysoev.ru                     spf.frac_width = spf.frac_width * 10 + *fmt++ - '0';
2330Sigor@sysoev.ru                 }
2340Sigor@sysoev.ru 
2350Sigor@sysoev.ru                 break;
2360Sigor@sysoev.ru 
2370Sigor@sysoev.ru             default:
2380Sigor@sysoev.ru                 break;
2390Sigor@sysoev.ru             }
2400Sigor@sysoev.ru 
2410Sigor@sysoev.ru             break;
2420Sigor@sysoev.ru         }
2430Sigor@sysoev.ru 
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru         switch (*fmt) {
2460Sigor@sysoev.ru 
2470Sigor@sysoev.ru         case 'E':
2480Sigor@sysoev.ru             err = va_arg(args, nxt_err_t);
2490Sigor@sysoev.ru 
2500Sigor@sysoev.ru             *buf++ = '(';
2510Sigor@sysoev.ru             spf.hex = NULL;
2520Sigor@sysoev.ru             spf.width = 0;
2530Sigor@sysoev.ru             buf = nxt_integer(&spf, buf, err);
2540Sigor@sysoev.ru 
2550Sigor@sysoev.ru             if (buf < end - 1) {
2560Sigor@sysoev.ru                 *buf++ = ':';
2570Sigor@sysoev.ru                 *buf++ = ' ';
2580Sigor@sysoev.ru             }
2590Sigor@sysoev.ru 
2600Sigor@sysoev.ru             buf = nxt_strerror(err, buf, end - buf);
2610Sigor@sysoev.ru 
2620Sigor@sysoev.ru             if (buf < end) {
2630Sigor@sysoev.ru                 *buf++ = ')';
2640Sigor@sysoev.ru             }
2650Sigor@sysoev.ru 
2660Sigor@sysoev.ru             fmt++;
2670Sigor@sysoev.ru             continue;
2680Sigor@sysoev.ru 
2690Sigor@sysoev.ru         case 'O':
2700Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, nxt_off_t);
2710Sigor@sysoev.ru             sign = 1;
2720Sigor@sysoev.ru             goto number;
2730Sigor@sysoev.ru 
2740Sigor@sysoev.ru         case 'T':
2750Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, nxt_time_t);
2760Sigor@sysoev.ru             sign = 1;
2770Sigor@sysoev.ru             goto number;
2780Sigor@sysoev.ru 
2790Sigor@sysoev.ru         case 'M':
2800Sigor@sysoev.ru             ms = (nxt_msec_t) va_arg(args, nxt_msec_t);
2810Sigor@sysoev.ru             if ((nxt_msec_int_t) ms == -1 && spf.hex == NULL) {
2820Sigor@sysoev.ru                 i64 = -1;
2830Sigor@sysoev.ru                 sign = 1;
2840Sigor@sysoev.ru             } else {
2850Sigor@sysoev.ru                 ui64 = (uint64_t) ms;
2860Sigor@sysoev.ru                 sign = 0;
2870Sigor@sysoev.ru             }
2880Sigor@sysoev.ru             goto number;
2890Sigor@sysoev.ru 
2900Sigor@sysoev.ru         case 'N':
2910Sigor@sysoev.ru             ns = (nxt_nsec_t) va_arg(args, nxt_nsec_t);
2920Sigor@sysoev.ru             if ((nxt_nsec_int_t) ns == -1) {
2930Sigor@sysoev.ru                 i64 = -1;
2940Sigor@sysoev.ru                 sign = 1;
2950Sigor@sysoev.ru             } else {
2960Sigor@sysoev.ru                 ui64 = (uint64_t) ns;
2970Sigor@sysoev.ru                 sign = 0;
2980Sigor@sysoev.ru             }
2990Sigor@sysoev.ru             goto number;
3000Sigor@sysoev.ru 
3010Sigor@sysoev.ru         case 'z':
3020Sigor@sysoev.ru             if (sign) {
3030Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, ssize_t);
3040Sigor@sysoev.ru             } else {
3050Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, size_t);
3060Sigor@sysoev.ru             }
3070Sigor@sysoev.ru             goto number;
3080Sigor@sysoev.ru 
3090Sigor@sysoev.ru         case 'i':
3100Sigor@sysoev.ru             if (sign) {
3110Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_int_t);
3120Sigor@sysoev.ru             } else {
3130Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_uint_t);
3140Sigor@sysoev.ru             }
3150Sigor@sysoev.ru 
3160Sigor@sysoev.ru             if (spf.max_width != 0) {
3170Sigor@sysoev.ru                 spf.width = NXT_INT_T_LEN;
3180Sigor@sysoev.ru             }
3190Sigor@sysoev.ru 
3200Sigor@sysoev.ru             goto number;
3210Sigor@sysoev.ru 
3220Sigor@sysoev.ru         case 'd':
3230Sigor@sysoev.ru             if (sign) {
3240Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, int);
3250Sigor@sysoev.ru             } else {
3260Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, u_int);
3270Sigor@sysoev.ru             }
3280Sigor@sysoev.ru             goto number;
3290Sigor@sysoev.ru 
3300Sigor@sysoev.ru         case 'l':
3310Sigor@sysoev.ru             if (sign) {
3320Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, long);
3330Sigor@sysoev.ru             } else {
3340Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, u_long);
3350Sigor@sysoev.ru             }
3360Sigor@sysoev.ru             goto number;
3370Sigor@sysoev.ru 
3380Sigor@sysoev.ru         case 'D':
3390Sigor@sysoev.ru             if (sign) {
3400Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, int32_t);
3410Sigor@sysoev.ru             } else {
3420Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, uint32_t);
3430Sigor@sysoev.ru             }
3440Sigor@sysoev.ru             goto number;
3450Sigor@sysoev.ru 
3460Sigor@sysoev.ru         case 'L':
3470Sigor@sysoev.ru             if (sign) {
3480Sigor@sysoev.ru                 i64 = va_arg(args, int64_t);
3490Sigor@sysoev.ru             } else {
3500Sigor@sysoev.ru                 ui64 = va_arg(args, uint64_t);
3510Sigor@sysoev.ru             }
3520Sigor@sysoev.ru             goto number;
3530Sigor@sysoev.ru 
3540Sigor@sysoev.ru         case 'A':
3550Sigor@sysoev.ru             if (sign) {
3560Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_atomic_int_t);
3570Sigor@sysoev.ru             } else {
3580Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_atomic_uint_t);
3590Sigor@sysoev.ru             }
3600Sigor@sysoev.ru 
3610Sigor@sysoev.ru             if (spf.max_width != 0) {
3620Sigor@sysoev.ru                 spf.width = NXT_ATOMIC_T_LEN;
3630Sigor@sysoev.ru             }
3640Sigor@sysoev.ru 
3650Sigor@sysoev.ru             goto number;
3660Sigor@sysoev.ru 
3670Sigor@sysoev.ru         case 'b':
3680Sigor@sysoev.ru             ui64 = (uint64_t) va_arg(args, nxt_bool_t);
3690Sigor@sysoev.ru             sign = 0;
3700Sigor@sysoev.ru             goto number;
3710Sigor@sysoev.ru 
3720Sigor@sysoev.ru         case 'f':
3730Sigor@sysoev.ru             fmt++;
3740Sigor@sysoev.ru 
3750Sigor@sysoev.ru             f = va_arg(args, double);
3760Sigor@sysoev.ru 
3770Sigor@sysoev.ru             if (f < 0) {
3780Sigor@sysoev.ru                 *buf++ = '-';
3790Sigor@sysoev.ru                 f = -f;
3800Sigor@sysoev.ru             }
3810Sigor@sysoev.ru 
3820Sigor@sysoev.ru             if (nxt_slow_path(isnan(f))) {
3832105Salx.manpages@gmail.com                 p = nan;
384703Svbart@nginx.com                 length = nxt_length(nan);
3850Sigor@sysoev.ru 
3860Sigor@sysoev.ru                 goto copy;
3870Sigor@sysoev.ru 
3880Sigor@sysoev.ru             } else if (nxt_slow_path(isinf(f))) {
3892105Salx.manpages@gmail.com                 p = infinity;
390703Svbart@nginx.com                 length = nxt_length(infinity);
3910Sigor@sysoev.ru 
3920Sigor@sysoev.ru                 goto copy;
3930Sigor@sysoev.ru             }
3940Sigor@sysoev.ru 
3950Sigor@sysoev.ru             (void) modf(f, &i);
3960Sigor@sysoev.ru             frac = 0;
3970Sigor@sysoev.ru 
3980Sigor@sysoev.ru             if (spf.frac_width > 0) {
3990Sigor@sysoev.ru 
4000Sigor@sysoev.ru                 scale = 1;
4010Sigor@sysoev.ru                 for (n = spf.frac_width; n != 0; n--) {
4020Sigor@sysoev.ru                     scale *= 10;
4030Sigor@sysoev.ru                 }
4040Sigor@sysoev.ru 
4050Sigor@sysoev.ru                 frac = (uint64_t) ((f - i) * scale + 0.5);
4060Sigor@sysoev.ru 
4070Sigor@sysoev.ru                 if (frac == scale) {
4080Sigor@sysoev.ru                     i += 1;
4090Sigor@sysoev.ru                     frac = 0;
4100Sigor@sysoev.ru                 }
4110Sigor@sysoev.ru             }
4120Sigor@sysoev.ru 
4130Sigor@sysoev.ru             buf = nxt_number(&spf, buf, i);
4140Sigor@sysoev.ru 
4150Sigor@sysoev.ru             if (spf.frac_width > 0) {
4160Sigor@sysoev.ru 
4170Sigor@sysoev.ru                 if (buf < end) {
4180Sigor@sysoev.ru                     *buf++ = '.';
4190Sigor@sysoev.ru 
4200Sigor@sysoev.ru                     spf.hex = NULL;
4210Sigor@sysoev.ru                     spf.padding = '0';
4220Sigor@sysoev.ru                     spf.width = spf.frac_width;
4230Sigor@sysoev.ru                     buf = nxt_integer(&spf, buf, frac);
4240Sigor@sysoev.ru                 }
4250Sigor@sysoev.ru 
4260Sigor@sysoev.ru             } else if (spf.frac_width < 0) {
4270Sigor@sysoev.ru                 f = modf(f, &i);
4280Sigor@sysoev.ru 
4290Sigor@sysoev.ru                 if (!nxt_double_is_zero(f) && buf < end) {
4300Sigor@sysoev.ru                     *buf++ = '.';
4310Sigor@sysoev.ru 
4320Sigor@sysoev.ru                     while (!nxt_double_is_zero(f) && buf < end) {
4330Sigor@sysoev.ru                         f *= 10;
4340Sigor@sysoev.ru                         f = modf(f, &i);
4350Sigor@sysoev.ru                         *buf++ = (u_char) i + '0';
4360Sigor@sysoev.ru                     }
4370Sigor@sysoev.ru                 }
4380Sigor@sysoev.ru             }
4390Sigor@sysoev.ru 
4400Sigor@sysoev.ru             continue;
4410Sigor@sysoev.ru 
4420Sigor@sysoev.ru         case 'r':
4430Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, rlim_t);
4440Sigor@sysoev.ru             sign = 1;
4450Sigor@sysoev.ru             break;
4460Sigor@sysoev.ru 
4470Sigor@sysoev.ru         case 'p':
4480Sigor@sysoev.ru             ui64 = (uintptr_t) va_arg(args, void *);
4490Sigor@sysoev.ru             sign = 0;
4500Sigor@sysoev.ru             spf.hex = HEXADECIMAL;
4510Sigor@sysoev.ru             /*
4520Sigor@sysoev.ru              * spf.width = NXT_PTR_SIZE * 2;
4530Sigor@sysoev.ru              * spf.padding = '0';
4540Sigor@sysoev.ru              */
4550Sigor@sysoev.ru             goto number;
4560Sigor@sysoev.ru 
4570Sigor@sysoev.ru         case 'c':
4580Sigor@sysoev.ru             d = va_arg(args, int);
459611Svbart@nginx.com             *buf++ = (u_char) (d & 0xFF);
4600Sigor@sysoev.ru             fmt++;
4610Sigor@sysoev.ru 
4620Sigor@sysoev.ru             continue;
4630Sigor@sysoev.ru 
4640Sigor@sysoev.ru         case 'F':
4650Sigor@sysoev.ru             fmt++;
4660Sigor@sysoev.ru 
4670Sigor@sysoev.ru             switch (*fmt) {
4680Sigor@sysoev.ru 
4690Sigor@sysoev.ru             case 'D':
4700Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_fd_t);
4710Sigor@sysoev.ru                 sign = 1;
4720Sigor@sysoev.ru 
4730Sigor@sysoev.ru                 goto number;
4740Sigor@sysoev.ru 
4750Sigor@sysoev.ru             case 'N':
4760Sigor@sysoev.ru                 fn = va_arg(args, nxt_file_name_t *);
4770Sigor@sysoev.ru                 p = fn;
4780Sigor@sysoev.ru 
4790Sigor@sysoev.ru                 while (*p != '\0' && buf < end) {
4800Sigor@sysoev.ru                     *buf++ = *p++;
4810Sigor@sysoev.ru                 }
4820Sigor@sysoev.ru 
4830Sigor@sysoev.ru                 fmt++;
4840Sigor@sysoev.ru                 continue;
4850Sigor@sysoev.ru 
4860Sigor@sysoev.ru             default:
4870Sigor@sysoev.ru                 continue;
4880Sigor@sysoev.ru             }
4890Sigor@sysoev.ru 
4900Sigor@sysoev.ru         case 'P':
4910Sigor@sysoev.ru             fmt++;
4920Sigor@sysoev.ru 
4930Sigor@sysoev.ru             switch (*fmt) {
4940Sigor@sysoev.ru 
4950Sigor@sysoev.ru             case 'I':
4960Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_pid_t);
4970Sigor@sysoev.ru                 sign = 1;
4980Sigor@sysoev.ru                 goto number;
4990Sigor@sysoev.ru 
5000Sigor@sysoev.ru             case 'T':
501336Spluknet@nginx.com                 ui64 = (uint64_t) (uintptr_t) va_arg(args, nxt_tid_t);
5020Sigor@sysoev.ru                 sign = 0;
5030Sigor@sysoev.ru                 goto number;
504326Svbart@nginx.com #if 0
5050Sigor@sysoev.ru             case 'F':
5060Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_fid_t);
5070Sigor@sysoev.ru                 sign = 0;
5080Sigor@sysoev.ru                 goto number;
509326Svbart@nginx.com #endif
5100Sigor@sysoev.ru             case 'H':
5110Sigor@sysoev.ru                 ui64 = (uint64_t) (uintptr_t) va_arg(args, pthread_t);
5120Sigor@sysoev.ru                 spf.hex = HEXADECIMAL;
5130Sigor@sysoev.ru                 sign = 0;
5140Sigor@sysoev.ru                 goto number;
5150Sigor@sysoev.ru 
5160Sigor@sysoev.ru             default:
5170Sigor@sysoev.ru                 continue;
5180Sigor@sysoev.ru             }
5190Sigor@sysoev.ru 
5200Sigor@sysoev.ru         case 'Z':
5210Sigor@sysoev.ru             *buf++ = '\0';
5220Sigor@sysoev.ru             fmt++;
5230Sigor@sysoev.ru             continue;
5240Sigor@sysoev.ru 
5250Sigor@sysoev.ru         case 'n':
526704Sigor@sysoev.ru             *buf++ = '\n';
5270Sigor@sysoev.ru             fmt++;
5280Sigor@sysoev.ru             continue;
5290Sigor@sysoev.ru 
5300Sigor@sysoev.ru         case '%':
5310Sigor@sysoev.ru             *buf++ = '%';
5320Sigor@sysoev.ru             fmt++;
5330Sigor@sysoev.ru             continue;
5340Sigor@sysoev.ru 
5350Sigor@sysoev.ru         default:
5360Sigor@sysoev.ru             *buf++ = *fmt++;
5370Sigor@sysoev.ru             continue;
5380Sigor@sysoev.ru         }
5390Sigor@sysoev.ru 
5400Sigor@sysoev.ru     number:
5410Sigor@sysoev.ru 
5420Sigor@sysoev.ru         if (sign) {
5430Sigor@sysoev.ru             if (i64 < 0) {
5440Sigor@sysoev.ru                 *buf++ = '-';
5450Sigor@sysoev.ru                 ui64 = (uint64_t) -i64;
5460Sigor@sysoev.ru 
5470Sigor@sysoev.ru             } else {
5480Sigor@sysoev.ru                 ui64 = (uint64_t) i64;
5490Sigor@sysoev.ru             }
5500Sigor@sysoev.ru         }
5510Sigor@sysoev.ru 
5520Sigor@sysoev.ru         buf = nxt_integer(&spf, buf, ui64);
5530Sigor@sysoev.ru 
5540Sigor@sysoev.ru         fmt++;
5550Sigor@sysoev.ru         continue;
5560Sigor@sysoev.ru 
5570Sigor@sysoev.ru     copy:
5580Sigor@sysoev.ru 
559*2115Szelenkov@nginx.com         if (nxt_slow_path(p == NULL)) {
560*2115Szelenkov@nginx.com             p = null;
561*2115Szelenkov@nginx.com             length = nxt_length(null);
562*2115Szelenkov@nginx.com 
563*2115Szelenkov@nginx.com         } else {
564*2115Szelenkov@nginx.com             length = nxt_min((size_t) (end - buf), length);
565*2115Szelenkov@nginx.com         }
566*2115Szelenkov@nginx.com 
567*2115Szelenkov@nginx.com         buf = nxt_cpymem(buf, p, length);
5680Sigor@sysoev.ru         continue;
5690Sigor@sysoev.ru     }
5700Sigor@sysoev.ru 
5710Sigor@sysoev.ru     return buf;
5720Sigor@sysoev.ru }
5730Sigor@sysoev.ru 
5740Sigor@sysoev.ru 
5750Sigor@sysoev.ru static u_char *
5760Sigor@sysoev.ru nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64)
5770Sigor@sysoev.ru {
5780Sigor@sysoev.ru     u_char  *p, *end;
57910Sigor@sysoev.ru     size_t  length;
5800Sigor@sysoev.ru     u_char  temp[NXT_INT64_T_LEN];
5810Sigor@sysoev.ru 
5820Sigor@sysoev.ru     p = temp + NXT_INT64_T_LEN;
5830Sigor@sysoev.ru 
5840Sigor@sysoev.ru     if (spf->hex == NULL) {
5850Sigor@sysoev.ru 
5860Sigor@sysoev.ru #if (NXT_32BIT)
5870Sigor@sysoev.ru 
5880Sigor@sysoev.ru         for ( ;; ) {
5890Sigor@sysoev.ru             u_char    *start;
5900Sigor@sysoev.ru             uint32_t  ui32;
5910Sigor@sysoev.ru 
5920Sigor@sysoev.ru             /*
5930Sigor@sysoev.ru              * 32-bit platforms usually lack hardware support of 64-bit
5940Sigor@sysoev.ru              * division and remainder operations.  For this reason C compiler
5950Sigor@sysoev.ru              * adds calls to the runtime library functions which provides
5960Sigor@sysoev.ru              * these operations.  These functions usually have about hundred
5970Sigor@sysoev.ru              * lines of code.
5980Sigor@sysoev.ru              *
5990Sigor@sysoev.ru              * For 32-bit numbers and some constant divisors GCC, Clang and
6000Sigor@sysoev.ru              * other compilers can use inlined multiplications and shifts
6010Sigor@sysoev.ru              * which are faster than division or remainder operations.
6020Sigor@sysoev.ru              * For example, unsigned "ui32 / 10" is compiled to
6030Sigor@sysoev.ru              *
6040Sigor@sysoev.ru              *     ((uint64_t) ui32 * 0xCCCCCCCD) >> 35
6050Sigor@sysoev.ru              *
6060Sigor@sysoev.ru              * So a 64-bit number is split to parts by 10^9.  The parts fit
6070Sigor@sysoev.ru              * to 32 bits and are processed separately as 32-bit numbers.  A
6080Sigor@sysoev.ru              * number of 64-bit division/remainder operations is significantly
6090Sigor@sysoev.ru              * decreased depending on the 64-bit number's value, it is
6100Sigor@sysoev.ru              *   0 if the 64-bit value is less than 4294967296,
6110Sigor@sysoev.ru              *   1 if the 64-bit value is greater than 4294967295
6120Sigor@sysoev.ru              *                           and less than 4294967296000000000,
6130Sigor@sysoev.ru              *   2 otherwise.
6140Sigor@sysoev.ru              */
6150Sigor@sysoev.ru 
616611Svbart@nginx.com             if (ui64 <= 0xFFFFFFFF) {
6170Sigor@sysoev.ru                 ui32 = (uint32_t) ui64;
6180Sigor@sysoev.ru                 start = NULL;
6190Sigor@sysoev.ru 
6200Sigor@sysoev.ru             } else {
6210Sigor@sysoev.ru                 ui32 = (uint32_t) (ui64 % 1000000000);
6220Sigor@sysoev.ru                 start = p - 9;
6230Sigor@sysoev.ru             }
6240Sigor@sysoev.ru 
6250Sigor@sysoev.ru             do {
6260Sigor@sysoev.ru                 *(--p) = (u_char) (ui32 % 10 + '0');
6270Sigor@sysoev.ru                 ui32 /= 10;
6280Sigor@sysoev.ru             } while (ui32 != 0);
6290Sigor@sysoev.ru 
6300Sigor@sysoev.ru             if (start == NULL) {
6310Sigor@sysoev.ru                 break;
6320Sigor@sysoev.ru             }
6330Sigor@sysoev.ru 
6340Sigor@sysoev.ru             /* Add leading zeros of part. */
6350Sigor@sysoev.ru 
6360Sigor@sysoev.ru             while (p > start) {
6370Sigor@sysoev.ru                 *(--p) = '0';
6380Sigor@sysoev.ru             }
6390Sigor@sysoev.ru 
6400Sigor@sysoev.ru             ui64 /= 1000000000;
6410Sigor@sysoev.ru         }
6420Sigor@sysoev.ru 
6430Sigor@sysoev.ru #else  /* NXT_64BIT */
6440Sigor@sysoev.ru 
6450Sigor@sysoev.ru         do {
6460Sigor@sysoev.ru             *(--p) = (u_char) (ui64 % 10 + '0');
6470Sigor@sysoev.ru             ui64 /= 10;
6480Sigor@sysoev.ru         } while (ui64 != 0);
6490Sigor@sysoev.ru 
6500Sigor@sysoev.ru #endif
6510Sigor@sysoev.ru 
6520Sigor@sysoev.ru     } else {
6530Sigor@sysoev.ru 
6540Sigor@sysoev.ru         do {
655611Svbart@nginx.com             *(--p) = spf->hex[ui64 & 0xF];
6560Sigor@sysoev.ru             ui64 >>= 4;
6570Sigor@sysoev.ru         } while (ui64 != 0);
6580Sigor@sysoev.ru     }
6590Sigor@sysoev.ru 
6600Sigor@sysoev.ru     /* Zero or space padding. */
6610Sigor@sysoev.ru 
6620Sigor@sysoev.ru     if (spf->width != 0) {
6630Sigor@sysoev.ru 
66410Sigor@sysoev.ru         length = (temp + NXT_INT64_T_LEN) - p;
66510Sigor@sysoev.ru         end = buf + (spf->width - length);
6660Sigor@sysoev.ru         end = nxt_min(end, spf->end);
6670Sigor@sysoev.ru 
6680Sigor@sysoev.ru         while (buf < end) {
6690Sigor@sysoev.ru             *buf++ = spf->padding;
6700Sigor@sysoev.ru         }
6710Sigor@sysoev.ru     }
6720Sigor@sysoev.ru 
6730Sigor@sysoev.ru     /* Number copying. */
6740Sigor@sysoev.ru 
67510Sigor@sysoev.ru     length = (temp + NXT_INT64_T_LEN) - p;
67610Sigor@sysoev.ru     end = buf + length;
6770Sigor@sysoev.ru     end = nxt_min(end, spf->end);
6780Sigor@sysoev.ru 
6790Sigor@sysoev.ru     while (buf < end) {
6800Sigor@sysoev.ru         *buf++ = *p++;
6810Sigor@sysoev.ru     }
6820Sigor@sysoev.ru 
6830Sigor@sysoev.ru     return buf;
6840Sigor@sysoev.ru }
6850Sigor@sysoev.ru 
6860Sigor@sysoev.ru 
6870Sigor@sysoev.ru static u_char *
6880Sigor@sysoev.ru nxt_number(nxt_sprintf_t *spf, u_char *buf, double n)
6890Sigor@sysoev.ru {
6900Sigor@sysoev.ru     u_char  *p, *end;
69110Sigor@sysoev.ru     size_t  length;
6920Sigor@sysoev.ru     u_char  temp[NXT_DOUBLE_LEN];
6930Sigor@sysoev.ru 
6940Sigor@sysoev.ru     p = temp + NXT_DOUBLE_LEN;
6950Sigor@sysoev.ru 
6960Sigor@sysoev.ru     do {
6970Sigor@sysoev.ru         *(--p) = (u_char) (fmod(n, 10) + '0');
6980Sigor@sysoev.ru         n = trunc(n / 10);
6990Sigor@sysoev.ru     } while (!nxt_double_is_zero(n));
7000Sigor@sysoev.ru 
7010Sigor@sysoev.ru     /* Zero or space padding. */
7020Sigor@sysoev.ru 
7030Sigor@sysoev.ru     if (spf->width != 0) {
70410Sigor@sysoev.ru         length = (temp + NXT_DOUBLE_LEN) - p;
70510Sigor@sysoev.ru         end = buf + (spf->width - length);
7060Sigor@sysoev.ru         end = nxt_min(end, spf->end);
7070Sigor@sysoev.ru 
7080Sigor@sysoev.ru         while (buf < end) {
7090Sigor@sysoev.ru             *buf++ = spf->padding;
7100Sigor@sysoev.ru         }
7110Sigor@sysoev.ru     }
7120Sigor@sysoev.ru 
7130Sigor@sysoev.ru     /* Number copying. */
7140Sigor@sysoev.ru 
71510Sigor@sysoev.ru     length = (temp + NXT_DOUBLE_LEN) - p;
7160Sigor@sysoev.ru 
71710Sigor@sysoev.ru     end = buf + length;
7180Sigor@sysoev.ru     end = nxt_min(end, spf->end);
7190Sigor@sysoev.ru 
7200Sigor@sysoev.ru     while (buf < end) {
7210Sigor@sysoev.ru         *buf++ = *p++;
7220Sigor@sysoev.ru     }
7230Sigor@sysoev.ru 
7240Sigor@sysoev.ru     return buf;
7250Sigor@sysoev.ru }
726