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