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 \
94 nxt_double_is_zero(f) \
95 (fabs(f) <= FLT_EPSILON)
96
97
98 u_char *
nxt_vsprintf(u_char * buf,u_char * end,const char * fmt,va_list args)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 *
nxt_integer(nxt_sprintf_t * spf,u_char * buf,uint64_t ui64)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 *
nxt_number(nxt_sprintf_t * spf,u_char * buf,double n)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