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