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 u_char *p;
101 int d;
102 double f, i;
103 size_t length;
104 int64_t i64;
105 uint64_t ui64, frac;
106 nxt_str_t *v;
107 nxt_err_t err;
108 nxt_uint_t scale, n;
109 nxt_msec_t ms;
110 nxt_nsec_t ns;
111 nxt_bool_t sign;
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, 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, 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 = (u_char *) nan;
382 length = nxt_length(nan);
383
384 goto copy;
385
386 } else if (nxt_slow_path(isinf(f))) {
387 p = (u_char *) 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 *
nxt_integer(nxt_sprintf_t * spf,u_char * buf,uint64_t ui64)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 *
nxt_number(nxt_sprintf_t * spf,u_char * buf,double n)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