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