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