Coverage Report

Created: 2026-02-12 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/njs/src/njs_sprintf.c
Line
Count
Source
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
8
#include <njs_main.h>
9
10
11
/*
12
 * Supported formats:
13
 *
14
 *    %[0][width][x][X]O        njs_off_t
15
 *    %[0][width]T              njs_time_t
16
 *    %[0][width][u][x|X]z      ssize_t/size_t
17
 *    %[0][width][u][x|X]d      int/u_int
18
 *    %[0][width][u][x|X]l      long
19
 *    %[0][width|m][u][x|X]i    njs_int_t/njs_uint_t
20
 *    %[0][width][u][x|X]D      int32_t/uint32_t
21
 *    %[0][width][u][x|X]L      int64_t/uint64_t
22
 *    %[0][width][.width]f      double, max valid number fits to %18.15f
23
 *
24
 *    %d                        int
25
 *
26
 *    %s                        null-terminated string
27
 *    %*s                       length and string
28
 *
29
 *    %p                        void *
30
 *    %P                        symbolized function address
31
 *    %b                        njs_bool_t
32
 *    %V                        njs_str_t *
33
 *    %Z                        '\0'
34
 *    %n                        '\n'
35
 *    %c                        char
36
 *    %%                        %
37
 *
38
 *  Reserved:
39
 *    %t                        ptrdiff_t
40
 *    %S                        null-terminated wchar string
41
 *    %C                        wchar
42
 *    %[0][width][u][x|X]Q      int128_t/uint128_t
43
 */
44
45
46
u_char *
47
njs_sprintf(u_char *buf, u_char *end, const char *fmt, ...)
48
65.3k
{
49
65.3k
    u_char   *p;
50
65.3k
    va_list  args;
51
52
65.3k
    va_start(args, fmt);
53
65.3k
    p = njs_vsprintf(buf, end, fmt, args);
54
65.3k
    va_end(args);
55
56
65.3k
    return p;
57
65.3k
}
58
59
60
/*
61
 * njs_sprintf_t is used:
62
 *    to pass several parameters of njs_integer() via single pointer
63
 *    and to store little used variables of njs_vsprintf().
64
 */
65
66
typedef struct {
67
   u_char        *end;
68
   const u_char  *hex;
69
   uint32_t      width;
70
   int32_t       frac_width;
71
   uint8_t       max_width;
72
   u_char        padding;
73
} njs_sprintf_t;
74
75
76
static u_char *njs_integer(njs_sprintf_t *spf, u_char *buf, uint64_t ui64);
77
static u_char *njs_float(njs_sprintf_t *spf, u_char *buf, double n);
78
79
80
/* A right way of "f == 0.0". */
81
0
#define  njs_double_is_zero(f)  (fabs(f) <= FLT_EPSILON)
82
83
84
u_char *
85
njs_vsprintf(u_char *buf, u_char *end, const char *fmt, va_list args)
86
527k
{
87
527k
    u_char         *p;
88
527k
    int            d;
89
527k
    double         f, i;
90
527k
    size_t         size, length;
91
527k
    int64_t        i64;
92
527k
    uint64_t       ui64, frac;
93
527k
    njs_str_t      *v;
94
527k
    njs_uint_t     scale, n;
95
527k
    njs_bool_t     sign;
96
527k
    njs_sprintf_t  spf;
97
98
527k
    static const u_char  hexadecimal[] = "0123456789abcdef";
99
527k
    static const u_char  HEXADECIMAL[] = "0123456789ABCDEF";
100
527k
    static const u_char  nan[] = "[nan]";
101
527k
    static const u_char  infinity[] = "[infinity]";
102
103
527k
    spf.end = end;
104
105
10.8M
    while (*fmt != '\0' && buf < end) {
106
107
        /*
108
         * "buf < end" means that we could copy at least one character:
109
         * a plain character, "%%", "%c", or a minus without test.
110
         */
111
112
10.3M
        if (*fmt != '%') {
113
9.58M
            *buf++ = *fmt++;
114
9.58M
            continue;
115
9.58M
        }
116
117
751k
        fmt++;
118
119
        /* Test some often used text formats first. */
120
121
751k
        switch (*fmt) {
122
123
101k
        case 'V':
124
101k
            fmt++;
125
101k
            v = va_arg(args, njs_str_t *);
126
127
101k
            if (njs_fast_path(v != NULL)) {
128
101k
                length = v->length;
129
101k
                p = v->start;
130
101k
                goto copy;
131
101k
            }
132
133
0
            continue;
134
135
420k
        case 's':
136
420k
            p = va_arg(args, u_char *);
137
138
420k
            if (njs_fast_path(p != NULL)) {
139
11.8M
                while (*p != '\0' && buf < end) {
140
11.3M
                    *buf++ = *p++;
141
11.3M
                }
142
420k
            }
143
144
420k
            fmt++;
145
420k
            continue;
146
147
52.3k
        case '*':
148
52.3k
            length = va_arg(args, size_t);
149
150
52.3k
            fmt++;
151
152
52.3k
            if (*fmt == 's') {
153
52.3k
                fmt++;
154
52.3k
                p = va_arg(args, u_char *);
155
156
52.3k
                if (njs_fast_path(p != NULL)) {
157
52.3k
                    goto copy;
158
52.3k
                }
159
52.3k
            }
160
161
0
            continue;
162
163
177k
        default:
164
177k
            break;
165
751k
        }
166
167
177k
        spf.hex = NULL;
168
177k
        spf.width = 0;
169
177k
        spf.frac_width = -1;
170
177k
        spf.max_width = 0;
171
177k
        spf.padding = (*fmt == '0') ? '0' : ' ';
172
173
177k
        sign = 1;
174
175
177k
        i64 = 0;
176
177k
        ui64 = 0;
177
178
436k
        while (*fmt >= '0' && *fmt <= '9') {
179
259k
            spf.width = spf.width * 10 + (*fmt++ - '0');
180
259k
        }
181
182
183
205k
        for ( ;; ) {
184
205k
            switch (*fmt) {
185
186
28.3k
            case 'u':
187
28.3k
                sign = 0;
188
28.3k
                fmt++;
189
28.3k
                continue;
190
191
0
            case 'm':
192
0
                spf.max_width = 1;
193
0
                fmt++;
194
0
                continue;
195
196
0
            case 'X':
197
0
                spf.hex = HEXADECIMAL;
198
0
                sign = 0;
199
0
                fmt++;
200
0
                continue;
201
202
0
            case 'x':
203
0
                spf.hex = hexadecimal;
204
0
                sign = 0;
205
0
                fmt++;
206
0
                continue;
207
208
0
            case '.':
209
0
                fmt++;
210
0
                spf.frac_width = 0;
211
212
0
                while (*fmt >= '0' && *fmt <= '9') {
213
0
                    spf.frac_width = spf.frac_width * 10 + *fmt++ - '0';
214
0
                }
215
216
0
                break;
217
218
177k
            default:
219
177k
                break;
220
205k
            }
221
222
177k
            break;
223
205k
        }
224
225
226
177k
        switch (*fmt) {
227
228
0
        case 'O':
229
0
            i64 = (int64_t) va_arg(args, njs_off_t);
230
0
            sign = 1;
231
0
            goto number;
232
233
0
        case 'T':
234
0
            i64 = (int64_t) va_arg(args, njs_time_t);
235
0
            sign = 1;
236
0
            goto number;
237
238
525
        case 'z':
239
525
            if (sign) {
240
525
                i64 = (int64_t) va_arg(args, ssize_t);
241
525
            } else {
242
0
                ui64 = (uint64_t) va_arg(args, size_t);
243
0
            }
244
525
            goto number;
245
246
0
        case 'i':
247
0
            if (sign) {
248
0
                i64 = (int64_t) va_arg(args, njs_int_t);
249
0
            } else {
250
0
                ui64 = (uint64_t) va_arg(args, njs_uint_t);
251
0
            }
252
253
0
            if (spf.max_width != 0) {
254
0
                spf.width = NJS_INT_T_LEN;
255
0
            }
256
257
0
            goto number;
258
259
34.2k
        case 'd':
260
34.2k
            if (sign) {
261
34.2k
                i64 = (int64_t) va_arg(args, int);
262
34.2k
            } else {
263
0
                ui64 = (uint64_t) va_arg(args, u_int);
264
0
            }
265
34.2k
            goto number;
266
267
0
        case 'l':
268
0
            if (sign) {
269
0
                i64 = (int64_t) va_arg(args, long);
270
0
            } else {
271
0
                ui64 = (uint64_t) va_arg(args, u_long);
272
0
            }
273
0
            goto number;
274
275
28.3k
        case 'D':
276
28.3k
            if (sign) {
277
0
                i64 = (int64_t) va_arg(args, int32_t);
278
28.3k
            } else {
279
28.3k
                ui64 = (uint64_t) va_arg(args, uint32_t);
280
28.3k
            }
281
28.3k
            goto number;
282
283
98.3k
        case 'L':
284
98.3k
            if (sign) {
285
98.3k
                i64 = va_arg(args, int64_t);
286
98.3k
            } else {
287
0
                ui64 = va_arg(args, uint64_t);
288
0
            }
289
98.3k
            goto number;
290
291
0
        case 'b':
292
0
            ui64 = (uint64_t) va_arg(args, njs_bool_t);
293
0
            sign = 0;
294
0
            goto number;
295
296
0
        case 'f':
297
0
            fmt++;
298
299
0
            f = va_arg(args, double);
300
301
0
            if (f < 0) {
302
0
                *buf++ = '-';
303
0
                f = -f;
304
0
            }
305
306
0
            if (njs_slow_path(isnan(f))) {
307
0
                p = (u_char *) nan;
308
0
                length = njs_length(nan);
309
310
0
                goto copy;
311
312
0
            } else if (njs_slow_path(isinf(f))) {
313
0
                p = (u_char *) infinity;
314
0
                length = njs_length(infinity);
315
316
0
                goto copy;
317
0
            }
318
319
0
            (void) modf(f, &i);
320
0
            frac = 0;
321
322
0
            if (spf.frac_width > 0) {
323
324
0
                scale = 1;
325
0
                for (n = spf.frac_width; n != 0; n--) {
326
0
                    scale *= 10;
327
0
                }
328
329
0
                frac = (uint64_t) ((f - i) * scale + 0.5);
330
331
0
                if (frac == scale) {
332
0
                    i += 1;
333
0
                    frac = 0;
334
0
                }
335
0
            }
336
337
0
            buf = njs_float(&spf, buf, i);
338
339
0
            if (spf.frac_width > 0) {
340
341
0
                if (buf < end) {
342
0
                    *buf++ = '.';
343
344
0
                    spf.hex = NULL;
345
0
                    spf.padding = '0';
346
0
                    spf.width = spf.frac_width;
347
0
                    buf = njs_integer(&spf, buf, frac);
348
0
                }
349
350
0
            } else if (spf.frac_width < 0) {
351
0
                f = modf(f, &i);
352
353
0
                if (!njs_double_is_zero(f) && buf < end) {
354
0
                    *buf++ = '.';
355
356
0
                    while (!njs_double_is_zero(f) && buf < end) {
357
0
                        f *= 10;
358
0
                        f = modf(f, &i);
359
0
                        *buf++ = (u_char) i + '0';
360
0
                    }
361
0
                }
362
0
            }
363
364
0
            continue;
365
366
0
        case 'p':
367
0
            ui64 = (uintptr_t) va_arg(args, void *);
368
0
            sign = 0;
369
0
            spf.hex = HEXADECIMAL;
370
            /*
371
             * spf.width = NJS_PTR_SIZE * 2;
372
             * spf.padding = '0';
373
             */
374
0
            goto number;
375
376
0
        case 'P':
377
0
            buf = njs_addr2line(buf, end, va_arg(args, void *));
378
0
            fmt++;
379
0
            continue;
380
381
15.6k
        case 'c':
382
15.6k
            d = va_arg(args, int);
383
15.6k
            *buf++ = (u_char) (d & 0xFF);
384
15.6k
            fmt++;
385
386
15.6k
            continue;
387
388
0
        case 'Z':
389
0
            *buf++ = '\0';
390
0
            fmt++;
391
0
            continue;
392
393
0
        case 'n':
394
0
            *buf++ = '\n';
395
0
            fmt++;
396
0
            continue;
397
398
0
        case '%':
399
0
            *buf++ = '%';
400
0
            fmt++;
401
0
            continue;
402
403
0
        default:
404
0
            *buf++ = *fmt++;
405
0
            continue;
406
177k
        }
407
408
161k
    number:
409
410
161k
        if (sign) {
411
133k
            if (i64 < 0) {
412
127
                *buf++ = '-';
413
127
                ui64 = (uint64_t) -i64;
414
415
132k
            } else {
416
132k
                ui64 = (uint64_t) i64;
417
132k
            }
418
133k
        }
419
420
161k
        buf = njs_integer(&spf, buf, ui64);
421
422
161k
        fmt++;
423
161k
        continue;
424
425
153k
    copy:
426
427
153k
        size = njs_min((size_t) (end - buf), length);
428
429
153k
        if (size != 0) {
430
151k
            buf = njs_cpymem(buf, p, size);
431
151k
        }
432
433
153k
        continue;
434
177k
    }
435
436
527k
    return buf;
437
527k
}
438
439
440
static u_char *
441
njs_integer(njs_sprintf_t *spf, u_char *buf, uint64_t ui64)
442
161k
{
443
161k
    u_char  *p, *end;
444
161k
    size_t  length;
445
161k
    u_char  temp[NJS_INT64_T_LEN];
446
447
161k
    p = temp + NJS_INT64_T_LEN;
448
449
161k
    if (spf->hex == NULL) {
450
451
#if (NJS_32BIT)
452
453
        for ( ;; ) {
454
            u_char    *start;
455
            uint32_t  ui32;
456
457
            /*
458
             * 32-bit platforms usually lack hardware support of 64-bit
459
             * division and remainder operations.  For this reason C compiler
460
             * adds calls to the runtime library functions which provides
461
             * these operations.  These functions usually have about hundred
462
             * lines of code.
463
             *
464
             * For 32-bit numbers and some constant divisors GCC, Clang and
465
             * other compilers can use inlined multiplications and shifts
466
             * which are faster than division or remainder operations.
467
             * For example, unsigned "ui32 / 10" is compiled to
468
             *
469
             *     ((uint64_t) ui32 * 0xCCCCCCCD) >> 35
470
             *
471
             * So a 64-bit number is split to parts by 10^9.  The parts fit
472
             * to 32 bits and are processed separately as 32-bit numbers.  A
473
             * number of 64-bit division/remainder operations is significantly
474
             * decreased depending on the 64-bit number's value, it is
475
             *   0 if the 64-bit value is less than 4294967296,
476
             *   1 if the 64-bit value is greater than 4294967295
477
             *                           and less than 4294967296000000000,
478
             *   2 otherwise.
479
             */
480
481
            if (ui64 <= 0xFFFFFFFF) {
482
                ui32 = (uint32_t) ui64;
483
                start = NULL;
484
485
            } else {
486
                ui32 = (uint32_t) (ui64 % 1000000000);
487
                start = p - 9;
488
            }
489
490
            do {
491
                *(--p) = (u_char) (ui32 % 10 + '0');
492
                ui32 /= 10;
493
            } while (ui32 != 0);
494
495
            if (start == NULL) {
496
                break;
497
            }
498
499
            /* Add leading zeros of part. */
500
501
            while (p > start) {
502
                *(--p) = '0';
503
            }
504
505
            ui64 /= 1000000000;
506
        }
507
508
#else  /* NJS_64BIT */
509
510
223k
        do {
511
223k
            *(--p) = (u_char) (ui64 % 10 + '0');
512
223k
            ui64 /= 10;
513
223k
        } while (ui64 != 0);
514
515
161k
#endif
516
517
161k
    } else {
518
519
0
        do {
520
0
            *(--p) = spf->hex[ui64 & 0xF];
521
0
            ui64 >>= 4;
522
0
        } while (ui64 != 0);
523
0
    }
524
525
161k
    length = (temp + NJS_INT64_T_LEN) - p;
526
527
    /* Zero or space padding. */
528
529
161k
    if (length < spf->width) {
530
111k
        end = buf + spf->width - length;
531
111k
        end = njs_min(end, spf->end);
532
533
229k
        while (buf < end) {
534
118k
            *buf++ = spf->padding;
535
118k
        }
536
111k
    }
537
538
    /* Number copying. */
539
540
161k
    end = buf + length;
541
161k
    end = njs_min(end, spf->end);
542
543
384k
    while (buf < end) {
544
223k
        *buf++ = *p++;
545
223k
    }
546
547
161k
    return buf;
548
161k
}
549
550
551
static u_char *
552
njs_float(njs_sprintf_t *spf, u_char *buf, double n)
553
0
{
554
0
    u_char  *p, *end;
555
0
    size_t  length;
556
0
    u_char  temp[NJS_DOUBLE_LEN];
557
558
0
    p = temp + NJS_DOUBLE_LEN;
559
560
0
    do {
561
0
        *(--p) = (u_char) (fmod(n, 10) + '0');
562
0
        n = trunc(n / 10);
563
0
    } while (!njs_double_is_zero(n));
564
565
    /* Zero or space padding. */
566
567
0
    if (spf->width != 0) {
568
0
        length = (temp + NJS_DOUBLE_LEN) - p;
569
0
        end = buf + (spf->width - length);
570
0
        end = njs_min(end, spf->end);
571
572
0
        while (buf < end) {
573
0
            *buf++ = spf->padding;
574
0
        }
575
0
    }
576
577
    /* Number copying. */
578
579
0
    length = (temp + NJS_DOUBLE_LEN) - p;
580
581
0
    end = buf + length;
582
0
    end = njs_min(end, spf->end);
583
584
0
    while (buf < end) {
585
0
        *buf++ = *p++;
586
0
    }
587
588
0
    return buf;
589
0
}
590
591
592
NJS_EXPORT
593
int njs_dprint(int fd, u_char *buf, size_t size)
594
21.3k
{
595
21.3k
    return write(fd, buf, size);
596
21.3k
}
597
598
599
int
600
njs_dprintf(int fd, const char *fmt, ...)
601
0
{
602
0
    size_t   size;
603
0
    u_char   text[16384], *p;
604
0
    va_list  args;
605
606
0
    va_start(args, fmt);
607
0
    p = njs_vsprintf(text, text + sizeof(text), fmt, args);
608
0
    va_end(args);
609
610
0
    size = p - text;
611
612
0
    return write(fd, text, size);
613
0
}