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