xref: /unit/src/nxt_sprintf.c (revision 2105)
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;
111*2105Salx.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]";
1180Sigor@sysoev.ru     static const u_char  infinity[] = "[infinity]";
1190Sigor@sysoev.ru 
1200Sigor@sysoev.ru     spf.end = end;
1210Sigor@sysoev.ru 
1220Sigor@sysoev.ru     while (*fmt != '\0' && buf < end) {
1230Sigor@sysoev.ru 
1240Sigor@sysoev.ru         /*
1250Sigor@sysoev.ru          * "buf < end" means that we could copy at least one character:
1260Sigor@sysoev.ru          * a plain character, "%%", "%c", or a minus without test.
1270Sigor@sysoev.ru          */
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru         if (*fmt != '%') {
1300Sigor@sysoev.ru             *buf++ = *fmt++;
1310Sigor@sysoev.ru             continue;
1320Sigor@sysoev.ru         }
1330Sigor@sysoev.ru 
1340Sigor@sysoev.ru         fmt++;
1350Sigor@sysoev.ru 
1360Sigor@sysoev.ru         /* Test some often used text formats first. */
1370Sigor@sysoev.ru 
1380Sigor@sysoev.ru         switch (*fmt) {
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru         case 'V':
1410Sigor@sysoev.ru             fmt++;
1420Sigor@sysoev.ru             v = va_arg(args, nxt_str_t *);
1430Sigor@sysoev.ru 
1440Sigor@sysoev.ru             if (nxt_fast_path(v != NULL)) {
14510Sigor@sysoev.ru                 length = v->length;
14610Sigor@sysoev.ru                 p = v->start;
1470Sigor@sysoev.ru                 goto copy;
1480Sigor@sysoev.ru             }
1490Sigor@sysoev.ru 
1500Sigor@sysoev.ru             continue;
1510Sigor@sysoev.ru 
1520Sigor@sysoev.ru         case 's':
153*2105Salx.manpages@gmail.com             p = va_arg(args, const u_char *);
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru             if (nxt_fast_path(p != NULL)) {
1560Sigor@sysoev.ru                 while (*p != '\0' && buf < end) {
1570Sigor@sysoev.ru                     *buf++ = *p++;
1580Sigor@sysoev.ru                 }
1590Sigor@sysoev.ru             }
1600Sigor@sysoev.ru 
1610Sigor@sysoev.ru             fmt++;
1620Sigor@sysoev.ru             continue;
1630Sigor@sysoev.ru 
1640Sigor@sysoev.ru         case '*':
165493Spluknet@nginx.com             length = va_arg(args, size_t);
1660Sigor@sysoev.ru 
1670Sigor@sysoev.ru             fmt++;
1680Sigor@sysoev.ru 
1690Sigor@sysoev.ru             if (*fmt == 's') {
1700Sigor@sysoev.ru                 fmt++;
171*2105Salx.manpages@gmail.com                 p = va_arg(args, const u_char *);
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru                 if (nxt_fast_path(p != NULL)) {
1740Sigor@sysoev.ru                     goto copy;
1750Sigor@sysoev.ru                 }
1760Sigor@sysoev.ru             }
1770Sigor@sysoev.ru 
1780Sigor@sysoev.ru             continue;
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru         default:
1810Sigor@sysoev.ru             break;
1820Sigor@sysoev.ru         }
1830Sigor@sysoev.ru 
1840Sigor@sysoev.ru         spf.hex = NULL;
1850Sigor@sysoev.ru         spf.width = 0;
1860Sigor@sysoev.ru         spf.frac_width = -1;
1870Sigor@sysoev.ru         spf.max_width = 0;
1880Sigor@sysoev.ru         spf.padding = (*fmt == '0') ? '0' : ' ';
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru         sign = 1;
1910Sigor@sysoev.ru 
1920Sigor@sysoev.ru         i64 = 0;
1930Sigor@sysoev.ru         ui64 = 0;
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru         while (*fmt >= '0' && *fmt <= '9') {
1960Sigor@sysoev.ru             spf.width = spf.width * 10 + (*fmt++ - '0');
1970Sigor@sysoev.ru         }
1980Sigor@sysoev.ru 
1990Sigor@sysoev.ru 
2000Sigor@sysoev.ru         for ( ;; ) {
2010Sigor@sysoev.ru             switch (*fmt) {
2020Sigor@sysoev.ru 
2030Sigor@sysoev.ru             case 'u':
2040Sigor@sysoev.ru                 sign = 0;
2050Sigor@sysoev.ru                 fmt++;
2060Sigor@sysoev.ru                 continue;
2070Sigor@sysoev.ru 
2080Sigor@sysoev.ru             case 'm':
2090Sigor@sysoev.ru                 spf.max_width = 1;
2100Sigor@sysoev.ru                 fmt++;
2110Sigor@sysoev.ru                 continue;
2120Sigor@sysoev.ru 
2130Sigor@sysoev.ru             case 'X':
2140Sigor@sysoev.ru                 spf.hex = HEXADECIMAL;
2150Sigor@sysoev.ru                 sign = 0;
2160Sigor@sysoev.ru                 fmt++;
2170Sigor@sysoev.ru                 continue;
2180Sigor@sysoev.ru 
2190Sigor@sysoev.ru             case 'x':
2200Sigor@sysoev.ru                 spf.hex = hexadecimal;
2210Sigor@sysoev.ru                 sign = 0;
2220Sigor@sysoev.ru                 fmt++;
2230Sigor@sysoev.ru                 continue;
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru             case '.':
2260Sigor@sysoev.ru                 fmt++;
2270Sigor@sysoev.ru                 spf.frac_width = 0;
2280Sigor@sysoev.ru 
2290Sigor@sysoev.ru                 while (*fmt >= '0' && *fmt <= '9') {
2300Sigor@sysoev.ru                     spf.frac_width = spf.frac_width * 10 + *fmt++ - '0';
2310Sigor@sysoev.ru                 }
2320Sigor@sysoev.ru 
2330Sigor@sysoev.ru                 break;
2340Sigor@sysoev.ru 
2350Sigor@sysoev.ru             default:
2360Sigor@sysoev.ru                 break;
2370Sigor@sysoev.ru             }
2380Sigor@sysoev.ru 
2390Sigor@sysoev.ru             break;
2400Sigor@sysoev.ru         }
2410Sigor@sysoev.ru 
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru         switch (*fmt) {
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru         case 'E':
2460Sigor@sysoev.ru             err = va_arg(args, nxt_err_t);
2470Sigor@sysoev.ru 
2480Sigor@sysoev.ru             *buf++ = '(';
2490Sigor@sysoev.ru             spf.hex = NULL;
2500Sigor@sysoev.ru             spf.width = 0;
2510Sigor@sysoev.ru             buf = nxt_integer(&spf, buf, err);
2520Sigor@sysoev.ru 
2530Sigor@sysoev.ru             if (buf < end - 1) {
2540Sigor@sysoev.ru                 *buf++ = ':';
2550Sigor@sysoev.ru                 *buf++ = ' ';
2560Sigor@sysoev.ru             }
2570Sigor@sysoev.ru 
2580Sigor@sysoev.ru             buf = nxt_strerror(err, buf, end - buf);
2590Sigor@sysoev.ru 
2600Sigor@sysoev.ru             if (buf < end) {
2610Sigor@sysoev.ru                 *buf++ = ')';
2620Sigor@sysoev.ru             }
2630Sigor@sysoev.ru 
2640Sigor@sysoev.ru             fmt++;
2650Sigor@sysoev.ru             continue;
2660Sigor@sysoev.ru 
2670Sigor@sysoev.ru         case 'O':
2680Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, nxt_off_t);
2690Sigor@sysoev.ru             sign = 1;
2700Sigor@sysoev.ru             goto number;
2710Sigor@sysoev.ru 
2720Sigor@sysoev.ru         case 'T':
2730Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, nxt_time_t);
2740Sigor@sysoev.ru             sign = 1;
2750Sigor@sysoev.ru             goto number;
2760Sigor@sysoev.ru 
2770Sigor@sysoev.ru         case 'M':
2780Sigor@sysoev.ru             ms = (nxt_msec_t) va_arg(args, nxt_msec_t);
2790Sigor@sysoev.ru             if ((nxt_msec_int_t) ms == -1 && spf.hex == NULL) {
2800Sigor@sysoev.ru                 i64 = -1;
2810Sigor@sysoev.ru                 sign = 1;
2820Sigor@sysoev.ru             } else {
2830Sigor@sysoev.ru                 ui64 = (uint64_t) ms;
2840Sigor@sysoev.ru                 sign = 0;
2850Sigor@sysoev.ru             }
2860Sigor@sysoev.ru             goto number;
2870Sigor@sysoev.ru 
2880Sigor@sysoev.ru         case 'N':
2890Sigor@sysoev.ru             ns = (nxt_nsec_t) va_arg(args, nxt_nsec_t);
2900Sigor@sysoev.ru             if ((nxt_nsec_int_t) ns == -1) {
2910Sigor@sysoev.ru                 i64 = -1;
2920Sigor@sysoev.ru                 sign = 1;
2930Sigor@sysoev.ru             } else {
2940Sigor@sysoev.ru                 ui64 = (uint64_t) ns;
2950Sigor@sysoev.ru                 sign = 0;
2960Sigor@sysoev.ru             }
2970Sigor@sysoev.ru             goto number;
2980Sigor@sysoev.ru 
2990Sigor@sysoev.ru         case 'z':
3000Sigor@sysoev.ru             if (sign) {
3010Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, ssize_t);
3020Sigor@sysoev.ru             } else {
3030Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, size_t);
3040Sigor@sysoev.ru             }
3050Sigor@sysoev.ru             goto number;
3060Sigor@sysoev.ru 
3070Sigor@sysoev.ru         case 'i':
3080Sigor@sysoev.ru             if (sign) {
3090Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_int_t);
3100Sigor@sysoev.ru             } else {
3110Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_uint_t);
3120Sigor@sysoev.ru             }
3130Sigor@sysoev.ru 
3140Sigor@sysoev.ru             if (spf.max_width != 0) {
3150Sigor@sysoev.ru                 spf.width = NXT_INT_T_LEN;
3160Sigor@sysoev.ru             }
3170Sigor@sysoev.ru 
3180Sigor@sysoev.ru             goto number;
3190Sigor@sysoev.ru 
3200Sigor@sysoev.ru         case 'd':
3210Sigor@sysoev.ru             if (sign) {
3220Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, int);
3230Sigor@sysoev.ru             } else {
3240Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, u_int);
3250Sigor@sysoev.ru             }
3260Sigor@sysoev.ru             goto number;
3270Sigor@sysoev.ru 
3280Sigor@sysoev.ru         case 'l':
3290Sigor@sysoev.ru             if (sign) {
3300Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, long);
3310Sigor@sysoev.ru             } else {
3320Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, u_long);
3330Sigor@sysoev.ru             }
3340Sigor@sysoev.ru             goto number;
3350Sigor@sysoev.ru 
3360Sigor@sysoev.ru         case 'D':
3370Sigor@sysoev.ru             if (sign) {
3380Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, int32_t);
3390Sigor@sysoev.ru             } else {
3400Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, uint32_t);
3410Sigor@sysoev.ru             }
3420Sigor@sysoev.ru             goto number;
3430Sigor@sysoev.ru 
3440Sigor@sysoev.ru         case 'L':
3450Sigor@sysoev.ru             if (sign) {
3460Sigor@sysoev.ru                 i64 = va_arg(args, int64_t);
3470Sigor@sysoev.ru             } else {
3480Sigor@sysoev.ru                 ui64 = va_arg(args, uint64_t);
3490Sigor@sysoev.ru             }
3500Sigor@sysoev.ru             goto number;
3510Sigor@sysoev.ru 
3520Sigor@sysoev.ru         case 'A':
3530Sigor@sysoev.ru             if (sign) {
3540Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_atomic_int_t);
3550Sigor@sysoev.ru             } else {
3560Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_atomic_uint_t);
3570Sigor@sysoev.ru             }
3580Sigor@sysoev.ru 
3590Sigor@sysoev.ru             if (spf.max_width != 0) {
3600Sigor@sysoev.ru                 spf.width = NXT_ATOMIC_T_LEN;
3610Sigor@sysoev.ru             }
3620Sigor@sysoev.ru 
3630Sigor@sysoev.ru             goto number;
3640Sigor@sysoev.ru 
3650Sigor@sysoev.ru         case 'b':
3660Sigor@sysoev.ru             ui64 = (uint64_t) va_arg(args, nxt_bool_t);
3670Sigor@sysoev.ru             sign = 0;
3680Sigor@sysoev.ru             goto number;
3690Sigor@sysoev.ru 
3700Sigor@sysoev.ru         case 'f':
3710Sigor@sysoev.ru             fmt++;
3720Sigor@sysoev.ru 
3730Sigor@sysoev.ru             f = va_arg(args, double);
3740Sigor@sysoev.ru 
3750Sigor@sysoev.ru             if (f < 0) {
3760Sigor@sysoev.ru                 *buf++ = '-';
3770Sigor@sysoev.ru                 f = -f;
3780Sigor@sysoev.ru             }
3790Sigor@sysoev.ru 
3800Sigor@sysoev.ru             if (nxt_slow_path(isnan(f))) {
381*2105Salx.manpages@gmail.com                 p = nan;
382703Svbart@nginx.com                 length = nxt_length(nan);
3830Sigor@sysoev.ru 
3840Sigor@sysoev.ru                 goto copy;
3850Sigor@sysoev.ru 
3860Sigor@sysoev.ru             } else if (nxt_slow_path(isinf(f))) {
387*2105Salx.manpages@gmail.com                 p = infinity;
388703Svbart@nginx.com                 length = nxt_length(infinity);
3890Sigor@sysoev.ru 
3900Sigor@sysoev.ru                 goto copy;
3910Sigor@sysoev.ru             }
3920Sigor@sysoev.ru 
3930Sigor@sysoev.ru             (void) modf(f, &i);
3940Sigor@sysoev.ru             frac = 0;
3950Sigor@sysoev.ru 
3960Sigor@sysoev.ru             if (spf.frac_width > 0) {
3970Sigor@sysoev.ru 
3980Sigor@sysoev.ru                 scale = 1;
3990Sigor@sysoev.ru                 for (n = spf.frac_width; n != 0; n--) {
4000Sigor@sysoev.ru                     scale *= 10;
4010Sigor@sysoev.ru                 }
4020Sigor@sysoev.ru 
4030Sigor@sysoev.ru                 frac = (uint64_t) ((f - i) * scale + 0.5);
4040Sigor@sysoev.ru 
4050Sigor@sysoev.ru                 if (frac == scale) {
4060Sigor@sysoev.ru                     i += 1;
4070Sigor@sysoev.ru                     frac = 0;
4080Sigor@sysoev.ru                 }
4090Sigor@sysoev.ru             }
4100Sigor@sysoev.ru 
4110Sigor@sysoev.ru             buf = nxt_number(&spf, buf, i);
4120Sigor@sysoev.ru 
4130Sigor@sysoev.ru             if (spf.frac_width > 0) {
4140Sigor@sysoev.ru 
4150Sigor@sysoev.ru                 if (buf < end) {
4160Sigor@sysoev.ru                     *buf++ = '.';
4170Sigor@sysoev.ru 
4180Sigor@sysoev.ru                     spf.hex = NULL;
4190Sigor@sysoev.ru                     spf.padding = '0';
4200Sigor@sysoev.ru                     spf.width = spf.frac_width;
4210Sigor@sysoev.ru                     buf = nxt_integer(&spf, buf, frac);
4220Sigor@sysoev.ru                 }
4230Sigor@sysoev.ru 
4240Sigor@sysoev.ru             } else if (spf.frac_width < 0) {
4250Sigor@sysoev.ru                 f = modf(f, &i);
4260Sigor@sysoev.ru 
4270Sigor@sysoev.ru                 if (!nxt_double_is_zero(f) && buf < end) {
4280Sigor@sysoev.ru                     *buf++ = '.';
4290Sigor@sysoev.ru 
4300Sigor@sysoev.ru                     while (!nxt_double_is_zero(f) && buf < end) {
4310Sigor@sysoev.ru                         f *= 10;
4320Sigor@sysoev.ru                         f = modf(f, &i);
4330Sigor@sysoev.ru                         *buf++ = (u_char) i + '0';
4340Sigor@sysoev.ru                     }
4350Sigor@sysoev.ru                 }
4360Sigor@sysoev.ru             }
4370Sigor@sysoev.ru 
4380Sigor@sysoev.ru             continue;
4390Sigor@sysoev.ru 
4400Sigor@sysoev.ru         case 'r':
4410Sigor@sysoev.ru             i64 = (int64_t) va_arg(args, rlim_t);
4420Sigor@sysoev.ru             sign = 1;
4430Sigor@sysoev.ru             break;
4440Sigor@sysoev.ru 
4450Sigor@sysoev.ru         case 'p':
4460Sigor@sysoev.ru             ui64 = (uintptr_t) va_arg(args, void *);
4470Sigor@sysoev.ru             sign = 0;
4480Sigor@sysoev.ru             spf.hex = HEXADECIMAL;
4490Sigor@sysoev.ru             /*
4500Sigor@sysoev.ru              * spf.width = NXT_PTR_SIZE * 2;
4510Sigor@sysoev.ru              * spf.padding = '0';
4520Sigor@sysoev.ru              */
4530Sigor@sysoev.ru             goto number;
4540Sigor@sysoev.ru 
4550Sigor@sysoev.ru         case 'c':
4560Sigor@sysoev.ru             d = va_arg(args, int);
457611Svbart@nginx.com             *buf++ = (u_char) (d & 0xFF);
4580Sigor@sysoev.ru             fmt++;
4590Sigor@sysoev.ru 
4600Sigor@sysoev.ru             continue;
4610Sigor@sysoev.ru 
4620Sigor@sysoev.ru         case 'F':
4630Sigor@sysoev.ru             fmt++;
4640Sigor@sysoev.ru 
4650Sigor@sysoev.ru             switch (*fmt) {
4660Sigor@sysoev.ru 
4670Sigor@sysoev.ru             case 'D':
4680Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_fd_t);
4690Sigor@sysoev.ru                 sign = 1;
4700Sigor@sysoev.ru 
4710Sigor@sysoev.ru                 goto number;
4720Sigor@sysoev.ru 
4730Sigor@sysoev.ru             case 'N':
4740Sigor@sysoev.ru                 fn = va_arg(args, nxt_file_name_t *);
4750Sigor@sysoev.ru                 p = fn;
4760Sigor@sysoev.ru 
4770Sigor@sysoev.ru                 while (*p != '\0' && buf < end) {
4780Sigor@sysoev.ru                     *buf++ = *p++;
4790Sigor@sysoev.ru                 }
4800Sigor@sysoev.ru 
4810Sigor@sysoev.ru                 fmt++;
4820Sigor@sysoev.ru                 continue;
4830Sigor@sysoev.ru 
4840Sigor@sysoev.ru             default:
4850Sigor@sysoev.ru                 continue;
4860Sigor@sysoev.ru             }
4870Sigor@sysoev.ru 
4880Sigor@sysoev.ru         case 'P':
4890Sigor@sysoev.ru             fmt++;
4900Sigor@sysoev.ru 
4910Sigor@sysoev.ru             switch (*fmt) {
4920Sigor@sysoev.ru 
4930Sigor@sysoev.ru             case 'I':
4940Sigor@sysoev.ru                 i64 = (int64_t) va_arg(args, nxt_pid_t);
4950Sigor@sysoev.ru                 sign = 1;
4960Sigor@sysoev.ru                 goto number;
4970Sigor@sysoev.ru 
4980Sigor@sysoev.ru             case 'T':
499336Spluknet@nginx.com                 ui64 = (uint64_t) (uintptr_t) va_arg(args, nxt_tid_t);
5000Sigor@sysoev.ru                 sign = 0;
5010Sigor@sysoev.ru                 goto number;
502326Svbart@nginx.com #if 0
5030Sigor@sysoev.ru             case 'F':
5040Sigor@sysoev.ru                 ui64 = (uint64_t) va_arg(args, nxt_fid_t);
5050Sigor@sysoev.ru                 sign = 0;
5060Sigor@sysoev.ru                 goto number;
507326Svbart@nginx.com #endif
5080Sigor@sysoev.ru             case 'H':
5090Sigor@sysoev.ru                 ui64 = (uint64_t) (uintptr_t) va_arg(args, pthread_t);
5100Sigor@sysoev.ru                 spf.hex = HEXADECIMAL;
5110Sigor@sysoev.ru                 sign = 0;
5120Sigor@sysoev.ru                 goto number;
5130Sigor@sysoev.ru 
5140Sigor@sysoev.ru             default:
5150Sigor@sysoev.ru                 continue;
5160Sigor@sysoev.ru             }
5170Sigor@sysoev.ru 
5180Sigor@sysoev.ru         case 'Z':
5190Sigor@sysoev.ru             *buf++ = '\0';
5200Sigor@sysoev.ru             fmt++;
5210Sigor@sysoev.ru             continue;
5220Sigor@sysoev.ru 
5230Sigor@sysoev.ru         case 'n':
524704Sigor@sysoev.ru             *buf++ = '\n';
5250Sigor@sysoev.ru             fmt++;
5260Sigor@sysoev.ru             continue;
5270Sigor@sysoev.ru 
5280Sigor@sysoev.ru         case '%':
5290Sigor@sysoev.ru             *buf++ = '%';
5300Sigor@sysoev.ru             fmt++;
5310Sigor@sysoev.ru             continue;
5320Sigor@sysoev.ru 
5330Sigor@sysoev.ru         default:
5340Sigor@sysoev.ru             *buf++ = *fmt++;
5350Sigor@sysoev.ru             continue;
5360Sigor@sysoev.ru         }
5370Sigor@sysoev.ru 
5380Sigor@sysoev.ru     number:
5390Sigor@sysoev.ru 
5400Sigor@sysoev.ru         if (sign) {
5410Sigor@sysoev.ru             if (i64 < 0) {
5420Sigor@sysoev.ru                 *buf++ = '-';
5430Sigor@sysoev.ru                 ui64 = (uint64_t) -i64;
5440Sigor@sysoev.ru 
5450Sigor@sysoev.ru             } else {
5460Sigor@sysoev.ru                 ui64 = (uint64_t) i64;
5470Sigor@sysoev.ru             }
5480Sigor@sysoev.ru         }
5490Sigor@sysoev.ru 
5500Sigor@sysoev.ru         buf = nxt_integer(&spf, buf, ui64);
5510Sigor@sysoev.ru 
5520Sigor@sysoev.ru         fmt++;
5530Sigor@sysoev.ru         continue;
5540Sigor@sysoev.ru 
5550Sigor@sysoev.ru     copy:
5560Sigor@sysoev.ru 
55710Sigor@sysoev.ru         buf = nxt_cpymem(buf, p, nxt_min((size_t) (end - buf), length));
5580Sigor@sysoev.ru         continue;
5590Sigor@sysoev.ru     }
5600Sigor@sysoev.ru 
5610Sigor@sysoev.ru     return buf;
5620Sigor@sysoev.ru }
5630Sigor@sysoev.ru 
5640Sigor@sysoev.ru 
5650Sigor@sysoev.ru static u_char *
5660Sigor@sysoev.ru nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64)
5670Sigor@sysoev.ru {
5680Sigor@sysoev.ru     u_char  *p, *end;
56910Sigor@sysoev.ru     size_t  length;
5700Sigor@sysoev.ru     u_char  temp[NXT_INT64_T_LEN];
5710Sigor@sysoev.ru 
5720Sigor@sysoev.ru     p = temp + NXT_INT64_T_LEN;
5730Sigor@sysoev.ru 
5740Sigor@sysoev.ru     if (spf->hex == NULL) {
5750Sigor@sysoev.ru 
5760Sigor@sysoev.ru #if (NXT_32BIT)
5770Sigor@sysoev.ru 
5780Sigor@sysoev.ru         for ( ;; ) {
5790Sigor@sysoev.ru             u_char    *start;
5800Sigor@sysoev.ru             uint32_t  ui32;
5810Sigor@sysoev.ru 
5820Sigor@sysoev.ru             /*
5830Sigor@sysoev.ru              * 32-bit platforms usually lack hardware support of 64-bit
5840Sigor@sysoev.ru              * division and remainder operations.  For this reason C compiler
5850Sigor@sysoev.ru              * adds calls to the runtime library functions which provides
5860Sigor@sysoev.ru              * these operations.  These functions usually have about hundred
5870Sigor@sysoev.ru              * lines of code.
5880Sigor@sysoev.ru              *
5890Sigor@sysoev.ru              * For 32-bit numbers and some constant divisors GCC, Clang and
5900Sigor@sysoev.ru              * other compilers can use inlined multiplications and shifts
5910Sigor@sysoev.ru              * which are faster than division or remainder operations.
5920Sigor@sysoev.ru              * For example, unsigned "ui32 / 10" is compiled to
5930Sigor@sysoev.ru              *
5940Sigor@sysoev.ru              *     ((uint64_t) ui32 * 0xCCCCCCCD) >> 35
5950Sigor@sysoev.ru              *
5960Sigor@sysoev.ru              * So a 64-bit number is split to parts by 10^9.  The parts fit
5970Sigor@sysoev.ru              * to 32 bits and are processed separately as 32-bit numbers.  A
5980Sigor@sysoev.ru              * number of 64-bit division/remainder operations is significantly
5990Sigor@sysoev.ru              * decreased depending on the 64-bit number's value, it is
6000Sigor@sysoev.ru              *   0 if the 64-bit value is less than 4294967296,
6010Sigor@sysoev.ru              *   1 if the 64-bit value is greater than 4294967295
6020Sigor@sysoev.ru              *                           and less than 4294967296000000000,
6030Sigor@sysoev.ru              *   2 otherwise.
6040Sigor@sysoev.ru              */
6050Sigor@sysoev.ru 
606611Svbart@nginx.com             if (ui64 <= 0xFFFFFFFF) {
6070Sigor@sysoev.ru                 ui32 = (uint32_t) ui64;
6080Sigor@sysoev.ru                 start = NULL;
6090Sigor@sysoev.ru 
6100Sigor@sysoev.ru             } else {
6110Sigor@sysoev.ru                 ui32 = (uint32_t) (ui64 % 1000000000);
6120Sigor@sysoev.ru                 start = p - 9;
6130Sigor@sysoev.ru             }
6140Sigor@sysoev.ru 
6150Sigor@sysoev.ru             do {
6160Sigor@sysoev.ru                 *(--p) = (u_char) (ui32 % 10 + '0');
6170Sigor@sysoev.ru                 ui32 /= 10;
6180Sigor@sysoev.ru             } while (ui32 != 0);
6190Sigor@sysoev.ru 
6200Sigor@sysoev.ru             if (start == NULL) {
6210Sigor@sysoev.ru                 break;
6220Sigor@sysoev.ru             }
6230Sigor@sysoev.ru 
6240Sigor@sysoev.ru             /* Add leading zeros of part. */
6250Sigor@sysoev.ru 
6260Sigor@sysoev.ru             while (p > start) {
6270Sigor@sysoev.ru                 *(--p) = '0';
6280Sigor@sysoev.ru             }
6290Sigor@sysoev.ru 
6300Sigor@sysoev.ru             ui64 /= 1000000000;
6310Sigor@sysoev.ru         }
6320Sigor@sysoev.ru 
6330Sigor@sysoev.ru #else  /* NXT_64BIT */
6340Sigor@sysoev.ru 
6350Sigor@sysoev.ru         do {
6360Sigor@sysoev.ru             *(--p) = (u_char) (ui64 % 10 + '0');
6370Sigor@sysoev.ru             ui64 /= 10;
6380Sigor@sysoev.ru         } while (ui64 != 0);
6390Sigor@sysoev.ru 
6400Sigor@sysoev.ru #endif
6410Sigor@sysoev.ru 
6420Sigor@sysoev.ru     } else {
6430Sigor@sysoev.ru 
6440Sigor@sysoev.ru         do {
645611Svbart@nginx.com             *(--p) = spf->hex[ui64 & 0xF];
6460Sigor@sysoev.ru             ui64 >>= 4;
6470Sigor@sysoev.ru         } while (ui64 != 0);
6480Sigor@sysoev.ru     }
6490Sigor@sysoev.ru 
6500Sigor@sysoev.ru     /* Zero or space padding. */
6510Sigor@sysoev.ru 
6520Sigor@sysoev.ru     if (spf->width != 0) {
6530Sigor@sysoev.ru 
65410Sigor@sysoev.ru         length = (temp + NXT_INT64_T_LEN) - p;
65510Sigor@sysoev.ru         end = buf + (spf->width - length);
6560Sigor@sysoev.ru         end = nxt_min(end, spf->end);
6570Sigor@sysoev.ru 
6580Sigor@sysoev.ru         while (buf < end) {
6590Sigor@sysoev.ru             *buf++ = spf->padding;
6600Sigor@sysoev.ru         }
6610Sigor@sysoev.ru     }
6620Sigor@sysoev.ru 
6630Sigor@sysoev.ru     /* Number copying. */
6640Sigor@sysoev.ru 
66510Sigor@sysoev.ru     length = (temp + NXT_INT64_T_LEN) - p;
66610Sigor@sysoev.ru     end = buf + length;
6670Sigor@sysoev.ru     end = nxt_min(end, spf->end);
6680Sigor@sysoev.ru 
6690Sigor@sysoev.ru     while (buf < end) {
6700Sigor@sysoev.ru         *buf++ = *p++;
6710Sigor@sysoev.ru     }
6720Sigor@sysoev.ru 
6730Sigor@sysoev.ru     return buf;
6740Sigor@sysoev.ru }
6750Sigor@sysoev.ru 
6760Sigor@sysoev.ru 
6770Sigor@sysoev.ru static u_char *
6780Sigor@sysoev.ru nxt_number(nxt_sprintf_t *spf, u_char *buf, double n)
6790Sigor@sysoev.ru {
6800Sigor@sysoev.ru     u_char  *p, *end;
68110Sigor@sysoev.ru     size_t  length;
6820Sigor@sysoev.ru     u_char  temp[NXT_DOUBLE_LEN];
6830Sigor@sysoev.ru 
6840Sigor@sysoev.ru     p = temp + NXT_DOUBLE_LEN;
6850Sigor@sysoev.ru 
6860Sigor@sysoev.ru     do {
6870Sigor@sysoev.ru         *(--p) = (u_char) (fmod(n, 10) + '0');
6880Sigor@sysoev.ru         n = trunc(n / 10);
6890Sigor@sysoev.ru     } while (!nxt_double_is_zero(n));
6900Sigor@sysoev.ru 
6910Sigor@sysoev.ru     /* Zero or space padding. */
6920Sigor@sysoev.ru 
6930Sigor@sysoev.ru     if (spf->width != 0) {
69410Sigor@sysoev.ru         length = (temp + NXT_DOUBLE_LEN) - p;
69510Sigor@sysoev.ru         end = buf + (spf->width - length);
6960Sigor@sysoev.ru         end = nxt_min(end, spf->end);
6970Sigor@sysoev.ru 
6980Sigor@sysoev.ru         while (buf < end) {
6990Sigor@sysoev.ru             *buf++ = spf->padding;
7000Sigor@sysoev.ru         }
7010Sigor@sysoev.ru     }
7020Sigor@sysoev.ru 
7030Sigor@sysoev.ru     /* Number copying. */
7040Sigor@sysoev.ru 
70510Sigor@sysoev.ru     length = (temp + NXT_DOUBLE_LEN) - p;
7060Sigor@sysoev.ru 
70710Sigor@sysoev.ru     end = buf + length;
7080Sigor@sysoev.ru     end = nxt_min(end, spf->end);
7090Sigor@sysoev.ru 
7100Sigor@sysoev.ru     while (buf < end) {
7110Sigor@sysoev.ru         *buf++ = *p++;
7120Sigor@sysoev.ru     }
7130Sigor@sysoev.ru 
7140Sigor@sysoev.ru     return buf;
7150Sigor@sysoev.ru }
716