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 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 *
nxt_integer(nxt_sprintf_t * spf,u_char * buf,uint64_t ui64)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 *
nxt_number(nxt_sprintf_t * spf,u_char * buf,double n)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