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