Coverage Report

Created: 2026-10-28 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/PrintfImplementation.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/Format.h>
10
#include <AK/StdLibExtras.h>
11
#include <AK/Types.h>
12
#include <stdarg.h>
13
14
#ifndef KERNEL
15
#    include <AK/FloatingPoint.h>
16
#    include <math.h>
17
#    include <wchar.h>
18
#endif
19
20
#ifdef AK_OS_SERENITY
21
extern "C" size_t strlen(char const*);
22
#else
23
#    include <string.h>
24
#endif
25
26
namespace PrintfImplementation {
27
28
template<typename PutChFunc, typename T, typename CharType>
29
ALWAYS_INLINE int print_hex(PutChFunc putch, CharType*& bufptr, T number, bool upper_case, bool alternate_form, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
30
0
{
31
0
    constexpr char const* printf_hex_digits_lower = "0123456789abcdef";
32
0
    constexpr char const* printf_hex_digits_upper = "0123456789ABCDEF";
33
34
0
    u32 digits = 0;
35
0
    for (T n = number; n > 0; n >>= 4)
36
0
        ++digits;
37
0
    if (digits == 0)
38
0
        digits = 1;
39
40
0
    bool not_zero = number != 0;
41
42
0
    char buf[16];
43
0
    char* p = buf;
44
45
0
    if (!(has_precision && precision == 0 && !not_zero)) {
46
0
        if (number == 0) {
47
0
            (*p++) = '0';
48
0
            if (precision > 0)
49
0
                precision--;
50
0
        } else {
51
0
            u8 shift_count = digits * 4;
52
0
            while (shift_count) {
53
0
                shift_count -= 4;
54
0
                (*p++) = upper_case
55
0
                    ? printf_hex_digits_upper[(number >> shift_count) & 0x0f]
56
0
                    : printf_hex_digits_lower[(number >> shift_count) & 0x0f];
57
0
                if (precision > 0)
58
0
                    precision--;
59
0
            }
60
0
        }
61
0
    }
62
63
0
    size_t numlen = p - buf;
64
65
0
    if (!field_width || field_width < (numlen + has_precision * precision + (alternate_form * 2 * not_zero)))
66
0
        field_width = numlen + has_precision * precision + alternate_form * 2 * not_zero;
67
68
0
    if ((zero_pad && !has_precision) && (alternate_form && not_zero)) {
69
0
        putch(bufptr, '0');
70
0
        putch(bufptr, 'x');
71
0
    }
72
73
0
    if (!left_pad) {
74
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - alternate_form * 2 * not_zero; ++i) {
75
0
            putch(bufptr, (zero_pad && !has_precision) ? '0' : ' ');
76
0
        }
77
0
    }
78
79
0
    if (!(zero_pad && !has_precision) && (alternate_form && not_zero)) {
80
0
        putch(bufptr, '0');
81
0
        putch(bufptr, 'x');
82
0
    }
83
84
0
    if (has_precision) {
85
0
        for (u32 i = 0; i < precision; ++i) {
86
0
            putch(bufptr, '0');
87
0
        }
88
0
    }
89
90
0
    for (unsigned i = 0; i < numlen; ++i) {
91
0
        putch(bufptr, buf[i]);
92
0
    }
93
94
0
    if (left_pad) {
95
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - alternate_form * 2 * not_zero; ++i) {
96
0
            putch(bufptr, ' ');
97
0
        }
98
0
    }
99
100
0
    return field_width;
101
0
}
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_hex<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, unsigned long, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, unsigned long, bool, bool, bool, bool, unsigned int, bool, unsigned int)
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_hex<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, unsigned long long, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, unsigned long long, bool, bool, bool, bool, unsigned int, bool, unsigned int)
102
103
template<typename PutChFunc, typename CharType>
104
ALWAYS_INLINE int print_decimal(PutChFunc putch, CharType*& bufptr, u64 number, bool sign, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
105
0
{
106
0
    u64 divisor = 10000000000000000000LLU;
107
0
    char ch;
108
0
    char padding = 1;
109
0
    char buf[21];
110
0
    char* p = buf;
111
112
0
    if (!(has_precision && precision == 0 && number == 0)) {
113
0
        for (;;) {
114
0
            ch = '0' + (number / divisor);
115
0
            number %= divisor;
116
0
            if (ch != '0')
117
0
                padding = 0;
118
0
            if (!padding || divisor == 1) {
119
0
                *(p++) = ch;
120
0
                if (precision > 0)
121
0
                    precision--;
122
0
            }
123
0
            if (divisor == 1)
124
0
                break;
125
0
            divisor /= 10;
126
0
        }
127
0
    }
128
129
0
    size_t numlen = p - buf;
130
131
0
    if (!field_width || field_width < (numlen + has_precision * precision + (sign || always_sign)))
132
0
        field_width = numlen + has_precision * precision + (sign || always_sign);
133
134
0
    if ((zero_pad && !has_precision) && (sign || always_sign)) {
135
0
        putch(bufptr, sign ? '-' : '+');
136
0
    }
137
138
0
    if (!left_pad) {
139
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - (sign || always_sign); ++i) {
140
0
            putch(bufptr, (zero_pad && !has_precision) ? '0' : ' ');
141
0
        }
142
0
    }
143
144
0
    if (!(zero_pad && !has_precision) && (sign || always_sign)) {
145
0
        putch(bufptr, sign ? '-' : '+');
146
0
    }
147
148
0
    if (has_precision) {
149
0
        for (u32 i = 0; i < precision; ++i) {
150
0
            putch(bufptr, '0');
151
0
        }
152
0
    }
153
154
0
    for (unsigned i = 0; i < numlen; ++i) {
155
0
        putch(bufptr, buf[i]);
156
0
    }
157
158
0
    if (left_pad) {
159
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - (sign || always_sign); ++i) {
160
0
            putch(bufptr, ' ');
161
0
        }
162
0
    }
163
164
0
    return field_width;
165
0
}
166
#ifndef KERNEL
167
template<typename PutChFunc, typename CharType>
168
ALWAYS_INLINE u32 print_nan_and_inf(PutChFunc putch, CharType*& bufptr, long double number, bool sign, bool always_sign, bool left_pad, u32 field_width)
169
0
{
170
0
    u32 length = 0;
171
172
0
    bool nan = isnan(number);
173
0
    bool inf = isinf(number);
174
175
0
    if (nan || inf) {
176
0
        bool include_sign = sign || always_sign;
177
0
        u32 minimum_width = 3 + include_sign;
178
0
        auto padding_width = field_width > minimum_width ? field_width - minimum_width : 0;
179
180
0
        if (!left_pad) {
181
0
            for (unsigned i = 0; i < padding_width; i++)
182
0
                putch(bufptr, ' ');
183
0
            length += padding_width;
184
0
        }
185
186
0
        if (include_sign) {
187
0
            if (sign)
188
0
                putch(bufptr, '-');
189
0
            else
190
0
                putch(bufptr, '+');
191
0
            length++;
192
0
        }
193
0
        if (nan) {
194
0
            putch(bufptr, 'n');
195
0
            putch(bufptr, 'a');
196
0
            putch(bufptr, 'n');
197
0
        } else {
198
0
            putch(bufptr, 'i');
199
0
            putch(bufptr, 'n');
200
0
            putch(bufptr, 'f');
201
0
        }
202
0
        length += 3;
203
204
0
        if (left_pad) {
205
0
            for (unsigned i = 0; i < padding_width; i++)
206
0
                putch(bufptr, ' ');
207
0
            length += padding_width;
208
0
        }
209
0
    }
210
0
    return length;
211
0
}
212
213
template<typename PutChFunc, typename CharType>
214
ALWAYS_INLINE int print_double(PutChFunc putch, CharType*& bufptr, double number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, u32 precision, bool trailing_zeros)
215
0
{
216
0
    int length = 0;
217
218
0
    bool sign = signbit(number);
219
220
0
    length = print_nan_and_inf(putch, bufptr, number, sign, always_sign, left_pad, field_width);
221
0
    if (length > 0)
222
0
        return length;
223
224
0
    if (sign)
225
0
        number = -number;
226
227
0
    u32 whole_width = (field_width >= precision + 1) ? field_width - precision - 1 : 0;
228
0
    length = print_decimal(putch, bufptr, (i64)number, sign, always_sign, left_pad, zero_pad, whole_width, false, 1);
229
0
    if (precision > 0) {
230
0
        double fraction = number - (i64)number;
231
232
0
        for (u32 i = 0; i < precision; ++i)
233
0
            fraction = fraction * 10;
234
235
0
        i64 ifraction = fraction;
236
237
0
        if (trailing_zeros || ifraction > 0) {
238
0
            length++;
239
0
            putch(bufptr, '.');
240
241
0
            while (!trailing_zeros && ifraction % 10 == 0) {
242
0
                ifraction /= 10;
243
0
                precision--;
244
0
            }
245
246
0
            return length + print_decimal(putch, bufptr, ifraction, false, false, false, true, precision, false, 1);
247
0
        }
248
0
    }
249
250
0
    return length;
251
0
}
252
253
template<typename PutChFunc, typename CharType>
254
ALWAYS_INLINE int print_double_as_hex(PutChFunc putch, CharType*& bufptr, double number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, u32 precision)
255
0
{
256
0
    int length = 0;
257
258
0
    bool sign = signbit(number);
259
260
0
    length = print_nan_and_inf(putch, bufptr, number, sign, always_sign, left_pad, field_width);
261
0
    if (length > 0)
262
0
        return length;
263
264
0
    if (sign || always_sign) {
265
0
        if (sign)
266
0
            putch(bufptr, '-');
267
0
        else
268
0
            putch(bufptr, '+');
269
0
        length++;
270
0
    }
271
272
0
    if (left_pad || zero_pad) {
273
0
        dbgln("FIXME: Modifiers are unsupported with %a");
274
0
        (void)zero_pad;
275
0
        (void)precision;
276
0
    }
277
278
0
    auto extractor = FloatExtractor<f64>::from_float(number);
279
280
0
    if (extractor.exponent == 0 && extractor.mantissa == 0) {
281
        // FIXME: Maybe share the modifiers logic with print_nan_and_inf.
282
0
        putch(bufptr, '0');
283
0
        putch(bufptr, 'x');
284
0
        putch(bufptr, '0');
285
0
        putch(bufptr, 'p');
286
0
        putch(bufptr, '+');
287
0
        putch(bufptr, '0');
288
0
        length += 6;
289
290
0
        return length;
291
0
    }
292
293
0
    putch(bufptr, '0');
294
0
    putch(bufptr, 'x');
295
0
    putch(bufptr, '1');
296
0
    length += 3;
297
298
0
    if (extractor.mantissa != 0) {
299
0
        putch(bufptr, '.');
300
0
        length++;
301
302
0
        bool has_seen_non_zero = false;
303
0
        u32 first_trailing_zero = 0;
304
0
        for (u32 i = FloatExtractor<f64>::mantissa_bits; i > 0; i -= 4) {
305
0
            auto extracted = extractor.mantissa & (0xFul << (i - 4));
306
307
0
            if (!has_seen_non_zero) {
308
0
                if (extracted != 0)
309
0
                    has_seen_non_zero = true;
310
0
                continue;
311
0
            }
312
313
0
            if (extracted == 0) {
314
0
                first_trailing_zero = i;
315
0
                break;
316
0
            }
317
0
        }
318
0
        u32 mantissa_precision = (FloatExtractor<f64>::mantissa_bits - first_trailing_zero) / 4;
319
320
0
        auto without_right_zeroes = extractor.mantissa;
321
0
        while ((without_right_zeroes & 0xFu) == 0)
322
0
            without_right_zeroes >>= 4u;
323
324
0
        length += print_hex(putch, bufptr, without_right_zeroes, false, false, false, true, mantissa_precision, false, 0);
325
0
    }
326
327
0
    putch(bufptr, 'p');
328
0
    length++;
329
330
0
    auto unbiased_exponent = static_cast<i32>(extractor.exponent) - FloatExtractor<f64>::exponent_bias;
331
0
    length += print_decimal(putch, bufptr, abs(unbiased_exponent), unbiased_exponent < 0, true, false, false, 0, false, 0);
332
333
0
    return length;
334
0
}
335
#endif
336
template<typename PutChFunc, typename CharType>
337
ALWAYS_INLINE int print_octal_number(PutChFunc putch, CharType*& bufptr, u64 number, bool alternate_form, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
338
0
{
339
0
    u32 divisor = 134217728;
340
0
    char ch;
341
0
    char padding = 1;
342
0
    char buf[32];
343
0
    char* p = buf;
344
345
0
    if (alternate_form) {
346
0
        (*p++) = '0';
347
0
        if (precision > 0)
348
0
            precision--;
349
0
    }
350
351
0
    if (!(has_precision && precision == 0 && number == 0)) {
352
0
        for (;;) {
353
0
            ch = '0' + (number / divisor);
354
0
            number %= divisor;
355
0
            if (ch != '0')
356
0
                padding = 0;
357
0
            if (!padding || divisor == 1) {
358
0
                *(p++) = ch;
359
0
                if (precision > 0)
360
0
                    precision--;
361
0
            }
362
0
            if (divisor == 1)
363
0
                break;
364
0
            divisor /= 8;
365
0
        }
366
0
    }
367
368
0
    size_t numlen = p - buf;
369
370
0
    if (!field_width || field_width < (numlen + has_precision * precision))
371
0
        field_width = numlen + has_precision * precision;
372
373
0
    if (!left_pad) {
374
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision; ++i) {
375
0
            putch(bufptr, (zero_pad && !has_precision) ? '0' : ' ');
376
0
        }
377
0
    }
378
379
0
    if (has_precision) {
380
0
        for (u32 i = 0; i < precision; ++i) {
381
0
            putch(bufptr, '0');
382
0
        }
383
0
    }
384
385
0
    for (unsigned i = 0; i < numlen; ++i) {
386
0
        putch(bufptr, buf[i]);
387
0
    }
388
389
0
    if (left_pad) {
390
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision; ++i) {
391
0
            putch(bufptr, ' ');
392
0
        }
393
0
    }
394
395
0
    return field_width;
396
0
}
397
398
template<typename PutChFunc, typename T, typename CharType>
399
ALWAYS_INLINE int print_string(PutChFunc putch, CharType*& bufptr, T str, size_t len, bool left_pad, size_t field_width, bool dot, size_t precision, bool has_fraction)
400
0
{
401
0
    if (has_fraction)
402
0
        len = min(len, precision);
403
404
0
    if (!dot && (!field_width || field_width < len))
405
0
        field_width = len;
406
407
0
    if (has_fraction && !field_width)
408
0
        field_width = len;
409
410
0
    size_t pad_amount = field_width > len ? field_width - len : 0;
411
412
0
    if (!left_pad) {
413
0
        for (size_t i = 0; i < pad_amount; ++i)
414
0
            putch(bufptr, ' ');
415
0
    }
416
0
    for (size_t i = 0; i < min(len, field_width); ++i) {
417
0
        putch(bufptr, str[i]);
418
0
    }
419
0
    if (left_pad) {
420
0
        for (size_t i = 0; i < pad_amount; ++i)
421
0
            putch(bufptr, ' ');
422
0
    }
423
0
    return field_width;
424
0
}
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_string<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, char*, unsigned long, bool, unsigned long, bool, unsigned long, bool)
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_string<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, wchar_t const*, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, wchar_t const*, unsigned long, bool, unsigned long, bool, unsigned long, bool)
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_string<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char const*, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, char const*, unsigned long, bool, unsigned long, bool, unsigned long, bool)
425
426
template<typename PutChFunc, typename CharType>
427
ALWAYS_INLINE int print_signed_number(PutChFunc putch, CharType*& bufptr, i64 number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
428
0
{
429
    // FIXME: `0 - number` overflows if we are trying to negate the smallest possible value.
430
0
    return print_decimal(putch, bufptr, (number < 0) ? 0 - number : number, number < 0, always_sign, left_pad, zero_pad, field_width, has_precision, precision);
431
0
}
432
433
struct ModifierState {
434
    bool left_pad { false };
435
    bool zero_pad { false };
436
    bool dot { false };
437
    unsigned field_width { 0 };
438
    bool has_precision { false };
439
    unsigned precision { 6 };
440
    unsigned short_qualifiers { 0 }; // TODO: Unimplemented.
441
    unsigned long_qualifiers { 0 };
442
    bool intmax_qualifier { false };  // TODO: Unimplemented.
443
    bool ptrdiff_qualifier { false }; // TODO: Unimplemented.
444
    bool long_double_qualifier { false };
445
    bool size_qualifier { false }; // TODO: Unimplemented.
446
    bool alternate_form { 0 };
447
    bool always_sign { false };
448
};
449
450
template<typename PutChFunc, typename ArgumentListRefT, template<typename T, typename U = ArgumentListRefT> typename NextArgument, typename CharType = char>
451
struct PrintfImpl {
452
    ALWAYS_INLINE PrintfImpl(PutChFunc& putch, CharType*& bufptr, int const& nwritten)
453
0
        : m_bufptr(bufptr)
454
0
        , m_nwritten(nwritten)
455
0
        , m_putch(putch)
456
0
    {
457
0
    }
458
459
    ALWAYS_INLINE int format_s(ModifierState const& state, ArgumentListRefT ap) const
460
0
    {
461
        // FIXME: Narrow characters should be converted to wide characters on the fly and vice versa.
462
        // https://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html
463
        // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wprintf.html
464
0
#ifndef KERNEL
465
0
        if (state.long_qualifiers) {
466
0
            wchar_t const* sp = NextArgument<wchar_t const*>()(ap);
467
0
            if (!sp)
468
0
                sp = L"(null)";
469
0
            return print_string(m_putch, m_bufptr, sp, wcslen(sp), state.left_pad, state.field_width, state.dot, state.precision, state.has_precision);
470
0
        }
471
0
#endif
472
0
        char const* sp = NextArgument<char const*>()(ap);
473
0
        if (!sp)
474
0
            sp = "(null)";
475
0
        return print_string(m_putch, m_bufptr, sp, strlen(sp), state.left_pad, state.field_width, state.dot, state.precision, state.has_precision);
476
0
    }
477
    ALWAYS_INLINE int format_d(ModifierState const& state, ArgumentListRefT ap) const
478
0
    {
479
0
        i64 number = [&]() -> i64 {
480
0
            if (state.long_qualifiers >= 2)
481
0
                return NextArgument<long long int>()(ap);
482
0
            if (state.long_qualifiers == 1)
483
0
                return NextArgument<long int>()(ap);
484
0
            return NextArgument<int>()(ap);
485
0
        }();
486
487
0
        return print_signed_number(m_putch, m_bufptr, number, state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
488
0
    }
489
    ALWAYS_INLINE int format_i(ModifierState const& state, ArgumentListRefT ap) const
490
0
    {
491
0
        return format_d(state, ap);
492
0
    }
493
    ALWAYS_INLINE int format_u(ModifierState const& state, ArgumentListRefT ap) const
494
0
    {
495
0
        u64 number = [&]() -> u64 {
496
0
            if (state.long_qualifiers >= 2)
497
0
                return NextArgument<unsigned long long int>()(ap);
498
0
            if (state.long_qualifiers == 1)
499
0
                return NextArgument<unsigned long int>()(ap);
500
0
            return NextArgument<unsigned int>()(ap);
501
0
        }();
502
503
0
        return print_decimal(m_putch, m_bufptr, number, false, false, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
504
0
    }
505
    ALWAYS_INLINE int format_Q(ModifierState const& state, ArgumentListRefT ap) const
506
0
    {
507
0
        return print_decimal(m_putch, m_bufptr, NextArgument<u64>()(ap), false, false, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
508
0
    }
509
    ALWAYS_INLINE int format_q(ModifierState const& state, ArgumentListRefT ap) const
510
0
    {
511
0
        return print_hex(m_putch, m_bufptr, NextArgument<u64>()(ap), false, false, state.left_pad, state.zero_pad, 16, false, 1);
512
0
    }
513
#ifndef KERNEL
514
    ALWAYS_INLINE int format_g(ModifierState const& state, ArgumentListRefT ap) const
515
0
    {
516
        // FIXME: Exponent notation
517
0
        if (state.long_double_qualifier)
518
0
            return print_double(m_putch, m_bufptr, NextArgument<long double>()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision, false);
519
0
        return print_double(m_putch, m_bufptr, NextArgument<double>()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision, false);
520
0
    }
521
    ALWAYS_INLINE int format_f(ModifierState const& state, ArgumentListRefT ap) const
522
0
    {
523
0
        if (state.long_double_qualifier)
524
0
            return print_double(m_putch, m_bufptr, NextArgument<long double>()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision, true);
525
0
        return print_double(m_putch, m_bufptr, NextArgument<double>()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision, true);
526
0
    }
527
    ALWAYS_INLINE int format_a(ModifierState const& state, ArgumentListRefT ap) const
528
0
    {
529
0
        return print_double_as_hex(m_putch, m_bufptr, NextArgument<double>()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision);
530
0
    }
531
#endif
532
    ALWAYS_INLINE int format_o(ModifierState const& state, ArgumentListRefT ap) const
533
0
    {
534
0
        return print_octal_number(m_putch, m_bufptr, NextArgument<u32>()(ap), state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
535
0
    }
536
    ALWAYS_INLINE int format_unsigned_hex(ModifierState const& state, ArgumentListRefT ap, bool uppercase) const
537
0
    {
538
0
        u64 number = [&]() -> u64 {
539
0
            if (state.long_qualifiers >= 2)
540
0
                return NextArgument<unsigned long long int>()(ap);
541
0
            if (state.long_qualifiers == 1)
542
0
                return NextArgument<unsigned long int>()(ap);
543
0
            return NextArgument<unsigned int>()(ap);
544
0
        }();
545
546
0
        return print_hex(m_putch, m_bufptr, number, uppercase, state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
547
0
    }
548
    ALWAYS_INLINE int format_x(ModifierState const& state, ArgumentListRefT ap) const
549
0
    {
550
0
        return format_unsigned_hex(state, ap, false);
551
0
    }
552
    ALWAYS_INLINE int format_X(ModifierState const& state, ArgumentListRefT ap) const
553
0
    {
554
0
        return format_unsigned_hex(state, ap, true);
555
0
    }
556
    ALWAYS_INLINE int format_n(ModifierState const&, ArgumentListRefT ap) const
557
0
    {
558
0
        *NextArgument<int*>()(ap) = m_nwritten;
559
0
        return 0;
560
0
    }
561
    ALWAYS_INLINE int format_p(ModifierState const&, ArgumentListRefT ap) const
562
0
    {
563
0
        return print_hex(m_putch, m_bufptr, NextArgument<FlatPtr>()(ap), false, true, false, true, 8, false, 1);
564
0
    }
565
    ALWAYS_INLINE int format_P(ModifierState const&, ArgumentListRefT ap) const
566
0
    {
567
0
        return print_hex(m_putch, m_bufptr, NextArgument<FlatPtr>()(ap), true, true, false, true, 8, false, 1);
568
0
    }
569
    ALWAYS_INLINE int format_percent(ModifierState const&, ArgumentListRefT) const
570
0
    {
571
0
        m_putch(m_bufptr, '%');
572
0
        return 1;
573
0
    }
574
    ALWAYS_INLINE int format_c(ModifierState const& state, ArgumentListRefT ap) const
575
0
    {
576
0
        char c = NextArgument<int>()(ap);
577
0
        return print_string(m_putch, m_bufptr, &c, 1, state.left_pad, state.field_width, state.dot, state.precision, state.has_precision);
578
0
    }
579
    ALWAYS_INLINE int format_unrecognized(CharType format_op, CharType const* fmt, ModifierState const&, ArgumentListRefT) const
580
0
    {
581
0
        dbgln("printf_internal: Unimplemented format specifier {} (fmt: {})", format_op, fmt);
582
0
        return 0;
583
0
    }
584
585
protected:
586
    CharType*& m_bufptr;
587
    int const& m_nwritten;
588
    PutChFunc& m_putch;
589
};
590
591
template<typename T, typename V>
592
struct VaArgNextArgument {
593
    ALWAYS_INLINE T operator()(V ap) const
594
0
    {
595
#ifdef AK_OS_WINDOWS
596
        // GCC on msys2 complains about the type of ap,
597
        // so let's force the compiler to believe it's a
598
        // va_list.
599
        return va_arg((va_list&)ap, T);
600
#else
601
0
        return va_arg(ap, T);
602
0
#endif
603
0
    }
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<int, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<unsigned long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<unsigned long long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<unsigned int, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<long long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<long double, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<double, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<int*, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<wchar_t const*, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<char const*, __va_list_tag*&>::operator()(__va_list_tag*&) const
604
};
605
606
#define PRINTF_IMPL_DELEGATE_TO_IMPL(c)    \
607
0
    case *#c:                              \
608
0
        ret += impl.format_##c(state, ap); \
609
0
        break;
610
611
template<typename PutChFunc, template<typename T, typename U, template<typename X, typename Y> typename V, typename C = char> typename Impl = PrintfImpl, typename ArgumentListT = va_list, template<typename T, typename V = decltype(declval<ArgumentListT&>())> typename NextArgument = VaArgNextArgument, typename CharType = char>
612
ALWAYS_INLINE int printf_internal(PutChFunc putch, IdentityType<CharType>* buffer, CharType const*& fmt, ArgumentListT ap)
613
0
{
614
0
    int ret = 0;
615
0
    CharType* bufptr = buffer;
616
617
0
    Impl<PutChFunc, ArgumentListT&, NextArgument, CharType> impl { putch, bufptr, ret };
618
619
0
    for (CharType const* p = fmt; *p; ++p) {
620
0
        ModifierState state;
621
0
        if (*p == '%' && *(p + 1)) {
622
0
        one_more:
623
0
            ++p;
624
0
            if (*p == '.') {
625
0
                state.dot = true;
626
0
                state.has_precision = true;
627
0
                state.precision = 0;
628
0
                if (*(p + 1))
629
0
                    goto one_more;
630
0
            }
631
0
            if (*p == '-') {
632
0
                state.left_pad = true;
633
0
                if (*(p + 1))
634
0
                    goto one_more;
635
0
            }
636
0
            if (*p == '+') {
637
0
                state.always_sign = true;
638
0
                if (*(p + 1))
639
0
                    goto one_more;
640
0
            }
641
0
            if (!state.zero_pad && !state.field_width && !state.dot && *p == '0') {
642
0
                state.zero_pad = true;
643
0
                if (*(p + 1))
644
0
                    goto one_more;
645
0
            }
646
0
            if (*p >= '0' && *p <= '9') {
647
0
                if (!state.dot) {
648
0
                    state.field_width *= 10;
649
0
                    state.field_width += *p - '0';
650
0
                    if (*(p + 1))
651
0
                        goto one_more;
652
0
                } else {
653
0
                    state.precision *= 10;
654
0
                    state.precision += *p - '0';
655
0
                    if (*(p + 1))
656
0
                        goto one_more;
657
0
                }
658
0
            }
659
0
            if (*p == '*') {
660
0
                if (state.dot) {
661
0
                    state.zero_pad = true;
662
0
                    state.precision = NextArgument<int>()(ap);
663
0
                } else {
664
0
                    state.field_width = NextArgument<int>()(ap);
665
0
                }
666
667
0
                if (*(p + 1))
668
0
                    goto one_more;
669
0
            }
670
0
            if (*p == 'h') {
671
0
                ++state.short_qualifiers;
672
0
                if (*(p + 1))
673
0
                    goto one_more;
674
0
            }
675
0
            if (*p == 'l') {
676
0
                ++state.long_qualifiers;
677
0
                if (*(p + 1))
678
0
                    goto one_more;
679
0
            }
680
0
            if (*p == 'j') {
681
0
                state.intmax_qualifier = true;
682
0
                if (*(p + 1))
683
0
                    goto one_more;
684
0
            }
685
0
            if (*p == 't') {
686
0
                state.ptrdiff_qualifier = true;
687
0
                if (*(p + 1))
688
0
                    goto one_more;
689
0
            }
690
0
            if (*p == 'L') {
691
0
                state.long_double_qualifier = true;
692
0
                if (*(p + 1))
693
0
                    goto one_more;
694
0
            }
695
0
            if (*p == 'z') {
696
0
                state.size_qualifier = true;
697
0
                if (*(p + 1))
698
0
                    goto one_more;
699
0
            }
700
0
            if (*p == '#') {
701
0
                state.alternate_form = true;
702
0
                if (*(p + 1))
703
0
                    goto one_more;
704
0
            }
705
0
            switch (*p) {
706
0
            case '%':
707
0
                ret += impl.format_percent(state, ap);
708
0
                break;
709
710
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(P);
711
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(Q);
712
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(X);
713
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(c);
714
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(d);
715
0
#ifndef KERNEL
716
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(f);
717
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(g);
718
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(a);
719
0
#endif
720
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(i);
721
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(n);
722
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(o);
723
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(p);
724
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(q);
725
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(s);
726
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(u);
727
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(x);
728
0
            default:
729
0
                ret += impl.format_unrecognized(*p, fmt, state, ap);
730
0
                break;
731
0
            }
732
0
        } else {
733
0
            putch(bufptr, *p);
734
0
            ++ret;
735
0
        }
736
0
    }
737
0
    return ret;
738
0
}
739
740
#undef PRINTF_IMPL_DELEGATE_TO_IMPL
741
742
}
743
744
using PrintfImplementation::printf_internal;