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