/src/serenity/AK/Format.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, the SerenityOS developers. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/CharacterTypes.h> |
8 | | #include <AK/Format.h> |
9 | | #include <AK/FormatParser.h> |
10 | | #include <AK/GenericLexer.h> |
11 | | #include <AK/IntegralMath.h> |
12 | | #include <AK/StringBuilder.h> |
13 | | #include <AK/kstdio.h> |
14 | | |
15 | | #if defined(AK_OS_SERENITY) && !defined(KERNEL) |
16 | | # include <serenity.h> |
17 | | #endif |
18 | | |
19 | | #if defined(PREKERNEL) |
20 | | #elif defined(KERNEL) |
21 | | # include <Kernel/Tasks/Process.h> |
22 | | # include <Kernel/Tasks/Thread.h> |
23 | | # include <Kernel/Time/TimeManagement.h> |
24 | | #else |
25 | | # include <AK/LexicalPath.h> |
26 | | # include <AK/String.h> |
27 | | # include <AK/StringFloatingPointConversions.h> |
28 | | # include <math.h> |
29 | | # include <stdio.h> |
30 | | # include <string.h> |
31 | | # include <time.h> |
32 | | #endif |
33 | | |
34 | | namespace AK { |
35 | | |
36 | | namespace { |
37 | | |
38 | | static constexpr size_t use_next_index = NumericLimits<size_t>::max(); |
39 | | |
40 | | // The worst case is that we have the largest 64-bit value formatted as binary number, this would take |
41 | | // 65 bytes (85 bytes with separators). Choosing a larger power of two won't hurt and is a bit of mitigation against out-of-bounds accesses. |
42 | | static constexpr size_t convert_unsigned_to_string(u64 value, Array<u8, 128>& buffer, u8 base, bool upper_case, bool use_separator) |
43 | 107M | { |
44 | 107M | VERIFY(base >= 2 && base <= 16); |
45 | | |
46 | 107M | constexpr char const* lowercase_lookup = "0123456789abcdef"; |
47 | 107M | constexpr char const* uppercase_lookup = "0123456789ABCDEF"; |
48 | | |
49 | 107M | if (value == 0) { |
50 | 667k | buffer[0] = '0'; |
51 | 667k | return 1; |
52 | 667k | } |
53 | | |
54 | 106M | size_t used = 0; |
55 | 106M | size_t digit_count = 0; |
56 | 320M | while (value > 0) { |
57 | 213M | if (upper_case) |
58 | 211M | buffer[used++] = uppercase_lookup[value % base]; |
59 | 1.87M | else |
60 | 1.87M | buffer[used++] = lowercase_lookup[value % base]; |
61 | | |
62 | 213M | digit_count++; |
63 | 213M | value /= base; |
64 | | |
65 | 213M | if (use_separator && value > 0 && digit_count % 3 == 0) |
66 | 0 | buffer[used++] = ','; |
67 | 213M | } |
68 | | |
69 | 213M | for (size_t i = 0; i < used / 2; ++i) |
70 | 106M | swap(buffer[i], buffer[used - i - 1]); |
71 | | |
72 | 106M | return used; |
73 | 107M | } |
74 | | |
75 | | ErrorOr<void> vformat_impl(TypeErasedFormatParams& params, FormatBuilder& builder, FormatParser& parser) |
76 | 274M | { |
77 | 274M | auto const literal = parser.consume_literal(); |
78 | 274M | TRY(builder.put_literal(literal)); |
79 | | |
80 | 274M | FormatParser::FormatSpecifier specifier; |
81 | 274M | if (!parser.consume_specifier(specifier)) { |
82 | 110M | VERIFY(parser.is_eof()); |
83 | 110M | return {}; |
84 | 110M | } |
85 | | |
86 | 164M | if (specifier.index == use_next_index) |
87 | 164M | specifier.index = params.take_next_index(); |
88 | | |
89 | 164M | auto& parameter = params.parameters().at(specifier.index); |
90 | | |
91 | 164M | FormatParser argparser { specifier.flags }; |
92 | 164M | TRY(parameter.visit([&]<typename T>(T const& value) { |
93 | 164M | if constexpr (IsSame<T, TypeErasedParameter::CustomType>) { |
94 | 164M | return value.formatter(params, builder, argparser, value.value); |
95 | 164M | } else { |
96 | 164M | return __format_value<T>(params, builder, argparser, &value); |
97 | 164M | } |
98 | 164M | })); |
99 | 164M | TRY(vformat_impl(params, builder, parser)); |
100 | 164M | return {}; |
101 | 164M | } |
102 | | |
103 | | } // namespace AK::{anonymous} |
104 | | |
105 | | FormatParser::FormatParser(StringView input) |
106 | 274M | : GenericLexer(input) |
107 | 274M | { |
108 | 274M | } |
109 | | StringView FormatParser::consume_literal() |
110 | 274M | { |
111 | 274M | auto const begin = tell(); |
112 | | |
113 | 1.02G | while (!is_eof()) { |
114 | 919M | if (consume_specific("{{"sv)) |
115 | 0 | continue; |
116 | | |
117 | 919M | if (consume_specific("}}"sv)) |
118 | 79.7k | continue; |
119 | | |
120 | 919M | if (next_is(is_any_of("{}"sv))) |
121 | 164M | return m_input.substring_view(begin, tell() - begin); |
122 | | |
123 | 755M | consume(); |
124 | 755M | } |
125 | | |
126 | 110M | return m_input.substring_view(begin); |
127 | 274M | } |
128 | | bool FormatParser::consume_number(size_t& value) |
129 | 328M | { |
130 | 328M | value = 0; |
131 | | |
132 | 328M | bool consumed_at_least_one = false; |
133 | 434M | while (next_is(is_ascii_digit)) { |
134 | 106M | value *= 10; |
135 | 106M | value += parse_ascii_digit(consume()); |
136 | 106M | consumed_at_least_one = true; |
137 | 106M | } |
138 | | |
139 | 328M | return consumed_at_least_one; |
140 | 328M | } |
141 | | bool FormatParser::consume_specifier(FormatSpecifier& specifier) |
142 | 274M | { |
143 | 274M | VERIFY(!next_is('}')); |
144 | | |
145 | 274M | if (!consume_specific('{')) |
146 | 110M | return false; |
147 | | |
148 | 164M | if (!consume_number(specifier.index)) |
149 | 164M | specifier.index = use_next_index; |
150 | | |
151 | 164M | if (consume_specific(':')) { |
152 | 106M | auto const begin = tell(); |
153 | | |
154 | 106M | size_t level = 1; |
155 | 530M | while (level > 0) { |
156 | 424M | VERIFY(!is_eof()); |
157 | | |
158 | 424M | if (consume_specific('{')) { |
159 | 0 | ++level; |
160 | 0 | continue; |
161 | 0 | } |
162 | | |
163 | 424M | if (consume_specific('}')) { |
164 | 106M | --level; |
165 | 106M | continue; |
166 | 106M | } |
167 | | |
168 | 318M | consume(); |
169 | 318M | } |
170 | | |
171 | 106M | specifier.flags = m_input.substring_view(begin, tell() - begin - 1); |
172 | 106M | } else { |
173 | 58.0M | if (!consume_specific('}')) |
174 | 58.0M | VERIFY_NOT_REACHED(); |
175 | | |
176 | 58.0M | specifier.flags = ""sv; |
177 | 58.0M | } |
178 | | |
179 | 164M | return true; |
180 | 164M | } |
181 | | bool FormatParser::consume_replacement_field(size_t& index) |
182 | 164M | { |
183 | 164M | if (!consume_specific('{')) |
184 | 164M | return false; |
185 | | |
186 | 0 | if (!consume_number(index)) |
187 | 0 | index = use_next_index; |
188 | |
|
189 | 0 | if (!consume_specific('}')) |
190 | 0 | VERIFY_NOT_REACHED(); |
191 | | |
192 | 0 | return true; |
193 | 0 | } |
194 | | |
195 | | ErrorOr<void> FormatBuilder::put_padding(char fill, size_t amount) |
196 | 164M | { |
197 | 165M | for (size_t i = 0; i < amount; ++i) |
198 | 186k | TRY(m_builder.try_append(fill)); |
199 | 164M | return {}; |
200 | 164M | } |
201 | | ErrorOr<void> FormatBuilder::put_literal(StringView value) |
202 | 274M | { |
203 | 1.03G | for (size_t i = 0; i < value.length(); ++i) { |
204 | 756M | TRY(m_builder.try_append(value[i])); |
205 | 756M | if (value[i] == '{' || value[i] == '}') |
206 | 79.7k | ++i; |
207 | 756M | } |
208 | 274M | return {}; |
209 | 274M | } |
210 | | |
211 | | ErrorOr<void> FormatBuilder::put_string( |
212 | | StringView value, |
213 | | Align align, |
214 | | size_t min_width, |
215 | | size_t max_width, |
216 | | char fill) |
217 | 57.6M | { |
218 | 57.6M | auto const used_by_string = min(max_width, value.length()); |
219 | 57.6M | auto const used_by_padding = max(min_width, used_by_string) - used_by_string; |
220 | | |
221 | 57.6M | if (used_by_string < value.length()) |
222 | 0 | value = value.substring_view(0, used_by_string); |
223 | | |
224 | 57.6M | if (align == Align::Left || align == Align::Default) { |
225 | 57.6M | TRY(m_builder.try_append(value)); |
226 | 57.6M | TRY(put_padding(fill, used_by_padding)); |
227 | 57.6M | } else if (align == Align::Center) { |
228 | 0 | auto const used_by_left_padding = used_by_padding / 2; |
229 | 0 | auto const used_by_right_padding = ceil_div<size_t, size_t>(used_by_padding, 2); |
230 | |
|
231 | 0 | TRY(put_padding(fill, used_by_left_padding)); |
232 | 0 | TRY(m_builder.try_append(value)); |
233 | 0 | TRY(put_padding(fill, used_by_right_padding)); |
234 | 0 | } else if (align == Align::Right) { |
235 | 0 | TRY(put_padding(fill, used_by_padding)); |
236 | 0 | TRY(m_builder.try_append(value)); |
237 | 0 | } |
238 | 57.6M | return {}; |
239 | 57.6M | } |
240 | | |
241 | | ErrorOr<void> FormatBuilder::put_u64( |
242 | | u64 value, |
243 | | u8 base, |
244 | | bool prefix, |
245 | | bool upper_case, |
246 | | bool zero_pad, |
247 | | bool use_separator, |
248 | | Align align, |
249 | | size_t min_width, |
250 | | char fill, |
251 | | SignMode sign_mode, |
252 | | bool is_negative) |
253 | 107M | { |
254 | 107M | if (align == Align::Default) |
255 | 107M | align = Align::Right; |
256 | | |
257 | 107M | Array<u8, 128> buffer; |
258 | | |
259 | 107M | auto const used_by_digits = convert_unsigned_to_string(value, buffer, base, upper_case, use_separator); |
260 | | |
261 | 107M | size_t used_by_prefix = 0; |
262 | 107M | if (align == Align::Right && zero_pad) { |
263 | | // We want ByteString::formatted("{:#08x}", 32) to produce '0x00000020' instead of '0x000020'. This |
264 | | // behavior differs from both fmtlib and printf, but is more intuitive. |
265 | 106M | used_by_prefix = 0; |
266 | 106M | } else { |
267 | 1.13M | if (is_negative || sign_mode != SignMode::OnlyIfNeeded) |
268 | 1.27k | used_by_prefix += 1; |
269 | | |
270 | 1.13M | if (prefix) { |
271 | 0 | if (base == 8) |
272 | 0 | used_by_prefix += 1; |
273 | 0 | else if (base == 16) |
274 | 0 | used_by_prefix += 2; |
275 | 0 | else if (base == 2) |
276 | 0 | used_by_prefix += 2; |
277 | 0 | } |
278 | 1.13M | } |
279 | | |
280 | 107M | auto const used_by_field = used_by_prefix + used_by_digits; |
281 | 107M | auto const used_by_padding = max(used_by_field, min_width) - used_by_field; |
282 | | |
283 | 107M | auto const put_prefix = [&]() -> ErrorOr<void> { |
284 | 107M | if (is_negative) |
285 | 1.27k | TRY(m_builder.try_append('-')); |
286 | 107M | else if (sign_mode == SignMode::Always) |
287 | 0 | TRY(m_builder.try_append('+')); |
288 | 107M | else if (sign_mode == SignMode::Reserved) |
289 | 0 | TRY(m_builder.try_append(' ')); |
290 | | |
291 | 107M | if (prefix) { |
292 | 0 | if (base == 2) { |
293 | 0 | if (upper_case) |
294 | 0 | TRY(m_builder.try_append("0B"sv)); |
295 | 0 | else |
296 | 0 | TRY(m_builder.try_append("0b"sv)); |
297 | 0 | } else if (base == 8) { |
298 | 0 | TRY(m_builder.try_append("0"sv)); |
299 | 0 | } else if (base == 16) { |
300 | 0 | if (upper_case) |
301 | 0 | TRY(m_builder.try_append("0X"sv)); |
302 | 0 | else |
303 | 0 | TRY(m_builder.try_append("0x"sv)); |
304 | 0 | } |
305 | 0 | } |
306 | 107M | return {}; |
307 | 107M | }; |
308 | | |
309 | 107M | auto const put_digits = [&]() -> ErrorOr<void> { |
310 | 321M | for (size_t i = 0; i < used_by_digits; ++i) |
311 | 214M | TRY(m_builder.try_append(buffer[i])); |
312 | 107M | return {}; |
313 | 107M | }; |
314 | | |
315 | 107M | if (align == Align::Left) { |
316 | 0 | auto const used_by_right_padding = used_by_padding; |
317 | |
|
318 | 0 | TRY(put_prefix()); |
319 | 0 | TRY(put_digits()); |
320 | 0 | TRY(put_padding(fill, used_by_right_padding)); |
321 | 107M | } else if (align == Align::Center) { |
322 | 0 | auto const used_by_left_padding = used_by_padding / 2; |
323 | 0 | auto const used_by_right_padding = ceil_div<size_t, size_t>(used_by_padding, 2); |
324 | |
|
325 | 0 | TRY(put_padding(fill, used_by_left_padding)); |
326 | 0 | TRY(put_prefix()); |
327 | 0 | TRY(put_digits()); |
328 | 0 | TRY(put_padding(fill, used_by_right_padding)); |
329 | 107M | } else if (align == Align::Right) { |
330 | 107M | auto const used_by_left_padding = used_by_padding; |
331 | | |
332 | 107M | if (zero_pad) { |
333 | 106M | TRY(put_prefix()); |
334 | 106M | TRY(put_padding('0', used_by_left_padding)); |
335 | 106M | TRY(put_digits()); |
336 | 106M | } else { |
337 | 1.13M | TRY(put_padding(fill, used_by_left_padding)); |
338 | 1.13M | TRY(put_prefix()); |
339 | 1.13M | TRY(put_digits()); |
340 | 1.13M | } |
341 | 107M | } |
342 | 107M | return {}; |
343 | 107M | } |
344 | | |
345 | | ErrorOr<void> FormatBuilder::put_i64( |
346 | | i64 value, |
347 | | u8 base, |
348 | | bool prefix, |
349 | | bool upper_case, |
350 | | bool zero_pad, |
351 | | bool use_separator, |
352 | | Align align, |
353 | | size_t min_width, |
354 | | char fill, |
355 | | SignMode sign_mode) |
356 | 811k | { |
357 | 811k | auto const is_negative = value < 0; |
358 | 811k | u64 positive_value; |
359 | 811k | if (value == NumericLimits<i64>::min()) { |
360 | 0 | positive_value = static_cast<u64>(NumericLimits<i64>::max()) + 1; |
361 | 811k | } else { |
362 | 811k | positive_value = is_negative ? -value : value; |
363 | 811k | } |
364 | | |
365 | 811k | TRY(put_u64(positive_value, base, prefix, upper_case, zero_pad, use_separator, align, min_width, fill, sign_mode, is_negative)); |
366 | 811k | return {}; |
367 | 811k | } |
368 | | |
369 | | ErrorOr<void> FormatBuilder::put_fixed_point( |
370 | | bool is_negative, |
371 | | i64 integer_value, |
372 | | u64 fraction_value, |
373 | | u64 fraction_one, |
374 | | size_t precision, |
375 | | u8 base, |
376 | | bool upper_case, |
377 | | bool zero_pad, |
378 | | bool use_separator, |
379 | | Align align, |
380 | | size_t min_width, |
381 | | size_t fraction_max_width, |
382 | | char fill, |
383 | | SignMode sign_mode) |
384 | 0 | { |
385 | 0 | StringBuilder string_builder; |
386 | 0 | FormatBuilder format_builder { string_builder }; |
387 | |
|
388 | 0 | if (is_negative) |
389 | 0 | integer_value = -integer_value; |
390 | |
|
391 | 0 | TRY(format_builder.put_u64(static_cast<u64>(integer_value), base, false, upper_case, false, use_separator, Align::Right, 0, ' ', sign_mode, is_negative)); |
392 | |
|
393 | 0 | if (fraction_max_width && (zero_pad || fraction_value)) { |
394 | | // FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good |
395 | | // place to start would be the following video from CppCon 2019: |
396 | | // https://youtu.be/4P_kbF0EbZM (Stephan T. Lavavej “Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss”) |
397 | |
|
398 | 0 | if (is_negative && fraction_value) |
399 | 0 | fraction_value = fraction_one - fraction_value; |
400 | |
|
401 | 0 | TRY(string_builder.try_append('.')); |
402 | |
|
403 | 0 | if (base == 10) { |
404 | 0 | u64 scale = pow<u64>(5, precision); |
405 | | // FIXME: overflows (not before: fraction_value = (2^precision - 1) and precision >= 20) (use wider integer type) |
406 | 0 | auto fraction = scale * fraction_value; |
407 | 0 | TRY(format_builder.put_u64(fraction, base, false, upper_case, true, use_separator, Align::Right, precision)); |
408 | 0 | } else if (base == 16 || base == 8 || base == 2) { |
409 | 0 | auto bits_per_character = log2(base); |
410 | 0 | auto fraction = fraction_value << ((bits_per_character - (precision % bits_per_character)) % bits_per_character); |
411 | 0 | TRY(format_builder.put_u64(fraction, base, false, upper_case, false, use_separator, Align::Right, precision / bits_per_character + (precision % bits_per_character != 0), '0')); |
412 | 0 | } else { |
413 | 0 | VERIFY_NOT_REACHED(); |
414 | 0 | } |
415 | 0 | } |
416 | | |
417 | 0 | auto formatted_string = string_builder.string_view(); |
418 | 0 | if (fraction_max_width && (zero_pad || fraction_value)) { |
419 | 0 | auto point_index = formatted_string.find('.').value_or(0); |
420 | 0 | if (!point_index) |
421 | 0 | VERIFY_NOT_REACHED(); |
422 | | |
423 | 0 | if (auto formatted_length = (formatted_string.length() - point_index - 1); formatted_length > fraction_max_width) { |
424 | 0 | formatted_string = formatted_string.substring_view(0, 1 + point_index + fraction_max_width); |
425 | 0 | } else { |
426 | 0 | string_builder.append_repeated('0', fraction_max_width - formatted_length); |
427 | 0 | formatted_string = string_builder.string_view(); |
428 | 0 | } |
429 | |
|
430 | 0 | if (!zero_pad) |
431 | 0 | formatted_string = formatted_string.trim("0"sv, TrimMode::Right); |
432 | |
|
433 | 0 | if (formatted_string.ends_with('.')) |
434 | 0 | formatted_string = formatted_string.trim("."sv, TrimMode::Right); |
435 | 0 | } |
436 | | |
437 | 0 | TRY(put_string(formatted_string, align, min_width, NumericLimits<size_t>::max(), fill)); |
438 | 0 | return {}; |
439 | 0 | } |
440 | | |
441 | | #ifndef KERNEL |
442 | | static ErrorOr<void> round_up_digits(StringBuilder& digits_builder) |
443 | 0 | { |
444 | 0 | auto digits_buffer = TRY(digits_builder.to_byte_buffer()); |
445 | 0 | int current_position = digits_buffer.size() - 1; |
446 | |
|
447 | 0 | while (current_position >= 0) { |
448 | 0 | if (digits_buffer[current_position] == '.') { |
449 | 0 | --current_position; |
450 | 0 | continue; |
451 | 0 | } |
452 | 0 | ++digits_buffer[current_position]; |
453 | 0 | if (digits_buffer[current_position] <= '9') |
454 | 0 | break; |
455 | 0 | digits_buffer[current_position] = '0'; |
456 | 0 | --current_position; |
457 | 0 | } |
458 | |
|
459 | 0 | digits_builder.clear(); |
460 | 0 | if (current_position < 0) |
461 | 0 | TRY(digits_builder.try_append('1')); |
462 | 0 | return digits_builder.try_append(digits_buffer); |
463 | 0 | } |
464 | | |
465 | | ErrorOr<void> FormatBuilder::put_f64_with_precision( |
466 | | double value, |
467 | | u8 base, |
468 | | bool upper_case, |
469 | | bool zero_pad, |
470 | | bool use_separator, |
471 | | Align align, |
472 | | size_t min_width, |
473 | | size_t precision, |
474 | | char fill, |
475 | | SignMode sign_mode, |
476 | | RealNumberDisplayMode display_mode) |
477 | 0 | { |
478 | 0 | StringBuilder string_builder; |
479 | 0 | FormatBuilder format_builder { string_builder }; |
480 | |
|
481 | 0 | if (isnan(value) || isinf(value)) [[unlikely]] { |
482 | 0 | if (value < 0.0) |
483 | 0 | TRY(string_builder.try_append('-')); |
484 | 0 | else if (sign_mode == SignMode::Always) |
485 | 0 | TRY(string_builder.try_append('+')); |
486 | 0 | else if (sign_mode == SignMode::Reserved) |
487 | 0 | TRY(string_builder.try_append(' ')); |
488 | |
|
489 | 0 | if (isnan(value)) |
490 | 0 | TRY(string_builder.try_append(upper_case ? "NAN"sv : "nan"sv)); |
491 | 0 | else |
492 | 0 | TRY(string_builder.try_append(upper_case ? "INF"sv : "inf"sv)); |
493 | 0 | TRY(put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill)); |
494 | 0 | return {}; |
495 | 0 | } |
496 | | |
497 | 0 | bool is_negative = value < 0.0; |
498 | 0 | if (is_negative) |
499 | 0 | value = -value; |
500 | |
|
501 | 0 | TRY(format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, false, use_separator, Align::Right, 0, ' ', sign_mode, is_negative)); |
502 | 0 | value -= static_cast<i64>(value); |
503 | |
|
504 | 0 | if (precision > 0) { |
505 | | // FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good |
506 | | // place to start would be the following video from CppCon 2019: |
507 | | // https://youtu.be/4P_kbF0EbZM (Stephan T. Lavavej “Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss”) |
508 | 0 | double epsilon = 0.5; |
509 | 0 | if (!zero_pad && display_mode != RealNumberDisplayMode::FixedPoint) { |
510 | 0 | for (size_t i = 0; i < precision; ++i) |
511 | 0 | epsilon /= 10.0; |
512 | 0 | } |
513 | |
|
514 | 0 | for (size_t digit = 0; digit < precision; ++digit) { |
515 | 0 | if (!zero_pad && display_mode != RealNumberDisplayMode::FixedPoint && value - static_cast<i64>(value) < epsilon) |
516 | 0 | break; |
517 | | |
518 | 0 | value *= 10.0; |
519 | 0 | epsilon *= 10.0; |
520 | |
|
521 | 0 | if (value > NumericLimits<u32>::max()) |
522 | 0 | value -= static_cast<u64>(value) - (static_cast<u64>(value) % 10); |
523 | |
|
524 | 0 | if (digit == 0) |
525 | 0 | TRY(string_builder.try_append('.')); |
526 | |
|
527 | 0 | TRY(string_builder.try_append('0' + (static_cast<u32>(value) % 10))); |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | // Round up if the following decimal is 5 or higher |
532 | 0 | if (static_cast<u64>(value * 10.0) % 10 >= 5) |
533 | 0 | TRY(round_up_digits(string_builder)); |
534 | |
|
535 | 0 | return put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); |
536 | 0 | } |
537 | | |
538 | | template<OneOf<f32, f64> T> |
539 | | ErrorOr<void> FormatBuilder::put_f32_or_f64( |
540 | | T value, |
541 | | u8 base, |
542 | | bool upper_case, |
543 | | bool zero_pad, |
544 | | bool use_separator, |
545 | | Align align, |
546 | | size_t min_width, |
547 | | Optional<size_t> precision, |
548 | | char fill, |
549 | | SignMode sign_mode, |
550 | | RealNumberDisplayMode display_mode) |
551 | 0 | { |
552 | 0 | if (precision.has_value() || base != 10) |
553 | 0 | return put_f64_with_precision(value, base, upper_case, zero_pad, use_separator, align, min_width, precision.value_or(6), fill, sign_mode, display_mode); |
554 | | |
555 | | // No precision specified, so pick the best precision with roundtrip guarantees. |
556 | 0 | StringBuilder builder; |
557 | | |
558 | | // Special cases: NaN, inf, -inf, 0 and -0. |
559 | 0 | auto const is_nan = isnan(value); |
560 | 0 | auto const is_inf = isinf(value); |
561 | 0 | auto const is_zero = value == static_cast<T>(0.0) || value == static_cast<T>(-0.0); |
562 | 0 | if (is_nan || is_inf || is_zero) { |
563 | 0 | if (value < 0) |
564 | 0 | TRY(builder.try_append('-')); |
565 | 0 | else if (sign_mode == SignMode::Always) |
566 | 0 | TRY(builder.try_append('+')); |
567 | 0 | else if (sign_mode == SignMode::Reserved) |
568 | 0 | TRY(builder.try_append(' ')); |
569 | |
|
570 | 0 | if (is_nan) |
571 | 0 | TRY(builder.try_append(upper_case ? "NAN"sv : "nan"sv)); |
572 | 0 | else if (is_inf) |
573 | 0 | TRY(builder.try_append(upper_case ? "INF"sv : "inf"sv)); |
574 | 0 | else |
575 | 0 | TRY(builder.try_append('0')); |
576 | |
|
577 | 0 | return put_string(builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); |
578 | 0 | } |
579 | | |
580 | 0 | auto const [sign, mantissa, exponent] = convert_floating_point_to_decimal_exponential_form(value); |
581 | |
|
582 | 0 | auto convert_to_decimal_digits_array = [](auto x, auto& digits) -> size_t { |
583 | 0 | size_t length = 0; |
584 | 0 | for (; x; x /= 10) |
585 | 0 | digits[length++] = x % 10 | '0'; |
586 | 0 | for (size_t i = 0; 2 * i + 1 < length; ++i) |
587 | 0 | swap(digits[i], digits[length - i - 1]); |
588 | 0 | return length; |
589 | 0 | }; Unexecuted instantiation: _ZZN2AK13FormatBuilder14put_f32_or_f64ITkNS_8Concepts5OneOfIfdEEfEENS_7ErrorOrIvNS_5ErrorEEET_hbbbNS0_5AlignEmNS_8OptionalImEEcNS0_8SignModeENS0_21RealNumberDisplayModeEENKUlS7_RT0_E_clImNS_5ArrayIhLm20EEEEEmS7_SE_ Unexecuted instantiation: _ZZN2AK13FormatBuilder14put_f32_or_f64ITkNS_8Concepts5OneOfIfdEEfEENS_7ErrorOrIvNS_5ErrorEEET_hbbbNS0_5AlignEmNS_8OptionalImEEcNS0_8SignModeENS0_21RealNumberDisplayModeEENKUlS7_RT0_E_clIiNS_5ArrayIhLm5EEEEEmS7_SE_ Unexecuted instantiation: _ZZN2AK13FormatBuilder14put_f32_or_f64ITkNS_8Concepts5OneOfIfdEEdEENS_7ErrorOrIvNS_5ErrorEEET_hbbbNS0_5AlignEmNS_8OptionalImEEcNS0_8SignModeENS0_21RealNumberDisplayModeEENKUlS7_RT0_E_clImNS_5ArrayIhLm20EEEEEmS7_SE_ Unexecuted instantiation: _ZZN2AK13FormatBuilder14put_f32_or_f64ITkNS_8Concepts5OneOfIfdEEdEENS_7ErrorOrIvNS_5ErrorEEET_hbbbNS0_5AlignEmNS_8OptionalImEEcNS0_8SignModeENS0_21RealNumberDisplayModeEENKUlS7_RT0_E_clIiNS_5ArrayIhLm5EEEEEmS7_SE_ |
590 | |
|
591 | 0 | Array<u8, 20> mantissa_digits; |
592 | 0 | auto mantissa_length = convert_to_decimal_digits_array(mantissa, mantissa_digits); |
593 | |
|
594 | 0 | if (sign) |
595 | 0 | TRY(builder.try_append('-')); |
596 | 0 | else if (sign_mode == SignMode::Always) |
597 | 0 | TRY(builder.try_append('+')); |
598 | 0 | else if (sign_mode == SignMode::Reserved) |
599 | 0 | TRY(builder.try_append(' ')); |
600 | |
|
601 | 0 | auto const n = exponent + static_cast<i32>(mantissa_length); |
602 | 0 | auto const mantissa_text = StringView { mantissa_digits.span().slice(0, mantissa_length) }; |
603 | 0 | size_t integral_part_end = 0; |
604 | | // NOTE: Range from ECMA262, seems like an okay default. |
605 | 0 | if (n >= -5 && n <= 21) { |
606 | 0 | if (exponent >= 0) { |
607 | 0 | TRY(builder.try_append(mantissa_text)); |
608 | 0 | TRY(builder.try_append_repeated('0', exponent)); |
609 | 0 | integral_part_end = builder.length(); |
610 | 0 | } else if (n > 0) { |
611 | 0 | TRY(builder.try_append(mantissa_text.substring_view(0, n))); |
612 | 0 | integral_part_end = builder.length(); |
613 | 0 | TRY(builder.try_append('.')); |
614 | 0 | TRY(builder.try_append(mantissa_text.substring_view(n))); |
615 | 0 | } else { |
616 | 0 | TRY(builder.try_append("0."sv)); |
617 | 0 | TRY(builder.try_append_repeated('0', -n)); |
618 | 0 | TRY(builder.try_append(mantissa_text)); |
619 | 0 | integral_part_end = 1; |
620 | 0 | } |
621 | 0 | } else { |
622 | 0 | auto const exponent_sign = n < 0 ? '-' : '+'; |
623 | 0 | Array<u8, 5> exponent_digits; |
624 | 0 | auto const exponent_length = convert_to_decimal_digits_array(abs(n - 1), exponent_digits); |
625 | 0 | auto const exponent_text = StringView { exponent_digits.span().slice(0, exponent_length) }; |
626 | 0 | integral_part_end = 1; |
627 | |
|
628 | 0 | if (mantissa_length == 1) { |
629 | | // <mantissa>e<exponent> |
630 | 0 | TRY(builder.try_append(mantissa_text)); |
631 | 0 | TRY(builder.try_append('e')); |
632 | 0 | TRY(builder.try_append(exponent_sign)); |
633 | 0 | TRY(builder.try_append(exponent_text)); |
634 | 0 | } else { |
635 | | // <mantissa>.<mantissa[1..]>e<exponent> |
636 | 0 | TRY(builder.try_append(mantissa_text.substring_view(0, 1))); |
637 | 0 | TRY(builder.try_append('.')); |
638 | 0 | TRY(builder.try_append(mantissa_text.substring_view(1))); |
639 | 0 | TRY(builder.try_append('e')); |
640 | 0 | TRY(builder.try_append(exponent_sign)); |
641 | 0 | TRY(builder.try_append(exponent_text)); |
642 | 0 | } |
643 | 0 | } |
644 | |
|
645 | 0 | if (use_separator && integral_part_end > 3) { |
646 | | // Go backwards from the end of the integral part, inserting commas every 3 consecutive digits. |
647 | 0 | StringBuilder separated_builder; |
648 | 0 | auto const string_view = builder.string_view(); |
649 | 0 | for (size_t i = 0; i < integral_part_end; ++i) { |
650 | 0 | auto const index_from_end = integral_part_end - i - 1; |
651 | 0 | if (index_from_end > 0 && index_from_end != integral_part_end - 1 && index_from_end % 3 == 2) |
652 | 0 | TRY(separated_builder.try_append(',')); |
653 | 0 | TRY(separated_builder.try_append(string_view[i])); |
654 | 0 | } |
655 | 0 | TRY(separated_builder.try_append(string_view.substring_view(integral_part_end))); |
656 | 0 | builder = move(separated_builder); |
657 | 0 | } |
658 | |
|
659 | 0 | return put_string(builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); |
660 | 0 | } Unexecuted instantiation: _ZN2AK13FormatBuilder14put_f32_or_f64ITkNS_8Concepts5OneOfIfdEEfEENS_7ErrorOrIvNS_5ErrorEEET_hbbbNS0_5AlignEmNS_8OptionalImEEcNS0_8SignModeENS0_21RealNumberDisplayModeE Unexecuted instantiation: _ZN2AK13FormatBuilder14put_f32_or_f64ITkNS_8Concepts5OneOfIfdEEdEENS_7ErrorOrIvNS_5ErrorEEET_hbbbNS0_5AlignEmNS_8OptionalImEEcNS0_8SignModeENS0_21RealNumberDisplayModeE |
661 | | |
662 | | ErrorOr<void> FormatBuilder::put_f80( |
663 | | long double value, |
664 | | u8 base, |
665 | | bool upper_case, |
666 | | bool use_separator, |
667 | | Align align, |
668 | | size_t min_width, |
669 | | size_t precision, |
670 | | char fill, |
671 | | SignMode sign_mode, |
672 | | RealNumberDisplayMode display_mode) |
673 | 0 | { |
674 | 0 | StringBuilder string_builder; |
675 | 0 | FormatBuilder format_builder { string_builder }; |
676 | |
|
677 | 0 | if (isnan(value) || isinf(value)) [[unlikely]] { |
678 | 0 | if (value < 0.0l) |
679 | 0 | TRY(string_builder.try_append('-')); |
680 | 0 | else if (sign_mode == SignMode::Always) |
681 | 0 | TRY(string_builder.try_append('+')); |
682 | 0 | else if (sign_mode == SignMode::Reserved) |
683 | 0 | TRY(string_builder.try_append(' ')); |
684 | |
|
685 | 0 | if (isnan(value)) |
686 | 0 | TRY(string_builder.try_append(upper_case ? "NAN"sv : "nan"sv)); |
687 | 0 | else |
688 | 0 | TRY(string_builder.try_append(upper_case ? "INF"sv : "inf"sv)); |
689 | 0 | TRY(put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill)); |
690 | 0 | return {}; |
691 | 0 | } |
692 | | |
693 | 0 | bool is_negative = value < 0.0l; |
694 | 0 | if (is_negative) |
695 | 0 | value = -value; |
696 | |
|
697 | 0 | TRY(format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, false, use_separator, Align::Right, 0, ' ', sign_mode, is_negative)); |
698 | 0 | value -= static_cast<i64>(value); |
699 | |
|
700 | 0 | if (precision > 0) { |
701 | | // FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good |
702 | | // place to start would be the following video from CppCon 2019: |
703 | | // https://youtu.be/4P_kbF0EbZM (Stephan T. Lavavej “Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss”) |
704 | 0 | long double epsilon = 0.5l; |
705 | 0 | if (display_mode != RealNumberDisplayMode::FixedPoint) { |
706 | 0 | for (size_t i = 0; i < precision; ++i) |
707 | 0 | epsilon /= 10.0l; |
708 | 0 | } |
709 | |
|
710 | 0 | for (size_t digit = 0; digit < precision; ++digit) { |
711 | 0 | if (display_mode != RealNumberDisplayMode::FixedPoint && value - static_cast<i64>(value) < epsilon) |
712 | 0 | break; |
713 | | |
714 | 0 | value *= 10.0l; |
715 | 0 | epsilon *= 10.0l; |
716 | |
|
717 | 0 | if (value > NumericLimits<u32>::max()) |
718 | 0 | value -= static_cast<u64>(value) - (static_cast<u64>(value) % 10); |
719 | |
|
720 | 0 | if (digit == 0) |
721 | 0 | TRY(string_builder.try_append('.')); |
722 | |
|
723 | 0 | TRY(string_builder.try_append('0' + (static_cast<u32>(value) % 10))); |
724 | 0 | } |
725 | 0 | } |
726 | | |
727 | | // Round up if the following decimal is 5 or higher |
728 | 0 | if (static_cast<u64>(value * 10.0l) % 10 >= 5) |
729 | 0 | TRY(round_up_digits(string_builder)); |
730 | |
|
731 | 0 | TRY(put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill)); |
732 | 0 | return {}; |
733 | 0 | } |
734 | | |
735 | | #endif |
736 | | |
737 | | ErrorOr<void> FormatBuilder::put_hexdump(ReadonlyBytes bytes, size_t width, char fill) |
738 | 0 | { |
739 | 0 | auto put_char_view = [&](auto i) -> ErrorOr<void> { |
740 | 0 | TRY(put_padding(fill, 4)); |
741 | 0 | for (size_t j = i - min(i, width); j < i; ++j) { |
742 | 0 | auto ch = bytes[j]; |
743 | 0 | TRY(m_builder.try_append(ch >= 32 && ch <= 127 ? ch : '.')); // silly hack |
744 | 0 | } |
745 | 0 | return {}; |
746 | 0 | }; |
747 | |
|
748 | 0 | for (size_t i = 0; i < bytes.size(); ++i) { |
749 | 0 | if (width > 0) { |
750 | 0 | if (i % width == 0 && i) { |
751 | 0 | TRY(put_char_view(i)); |
752 | 0 | TRY(put_literal("\n"sv)); |
753 | 0 | } |
754 | 0 | } |
755 | 0 | TRY(put_u64(bytes[i], 16, false, false, true, false, Align::Right, 2)); |
756 | 0 | } |
757 | |
|
758 | 0 | if (width > 0) |
759 | 0 | TRY(put_char_view(bytes.size())); |
760 | |
|
761 | 0 | return {}; |
762 | 0 | } |
763 | | |
764 | | ErrorOr<void> vformat(StringBuilder& builder, StringView fmtstr, TypeErasedFormatParams& params) |
765 | 110M | { |
766 | 110M | FormatBuilder fmtbuilder { builder }; |
767 | 110M | FormatParser parser { fmtstr }; |
768 | | |
769 | 110M | TRY(vformat_impl(params, fmtbuilder, parser)); |
770 | 110M | return {}; |
771 | 110M | } |
772 | | |
773 | | void StandardFormatter::parse(TypeErasedFormatParams& params, FormatParser& parser) |
774 | 164M | { |
775 | 164M | if ("<^>"sv.contains(parser.peek(1))) { |
776 | 0 | VERIFY(!parser.next_is(is_any_of("{}"sv))); |
777 | 0 | m_fill = parser.consume(); |
778 | 0 | } |
779 | | |
780 | 164M | if (parser.consume_specific('<')) |
781 | 0 | m_align = FormatBuilder::Align::Left; |
782 | 164M | else if (parser.consume_specific('^')) |
783 | 0 | m_align = FormatBuilder::Align::Center; |
784 | 164M | else if (parser.consume_specific('>')) |
785 | 0 | m_align = FormatBuilder::Align::Right; |
786 | | |
787 | 164M | if (parser.consume_specific('-')) |
788 | 0 | m_sign_mode = FormatBuilder::SignMode::OnlyIfNeeded; |
789 | 164M | else if (parser.consume_specific('+')) |
790 | 0 | m_sign_mode = FormatBuilder::SignMode::Always; |
791 | 164M | else if (parser.consume_specific(' ')) |
792 | 0 | m_sign_mode = FormatBuilder::SignMode::Reserved; |
793 | | |
794 | 164M | if (parser.consume_specific('#')) |
795 | 0 | m_alternative_form = true; |
796 | | |
797 | 164M | if (parser.consume_specific('\'')) |
798 | 0 | m_use_separator = true; |
799 | | |
800 | 164M | if (parser.consume_specific('0')) |
801 | 106M | m_zero_pad = true; |
802 | | |
803 | 164M | if (size_t index = 0; parser.consume_replacement_field(index)) { |
804 | 0 | if (index == use_next_index) |
805 | 0 | index = params.take_next_index(); |
806 | |
|
807 | 0 | m_width = params.parameters().at(index).to_size(); |
808 | 164M | } else if (size_t width = 0; parser.consume_number(width)) { |
809 | 106M | m_width = width; |
810 | 106M | } |
811 | | |
812 | 164M | if (parser.consume_specific('.')) { |
813 | 0 | if (size_t index = 0; parser.consume_replacement_field(index)) { |
814 | 0 | if (index == use_next_index) |
815 | 0 | index = params.take_next_index(); |
816 | |
|
817 | 0 | m_precision = params.parameters().at(index).to_size(); |
818 | 0 | } else if (size_t precision = 0; parser.consume_number(precision)) { |
819 | 0 | m_precision = precision; |
820 | 0 | } |
821 | 0 | } |
822 | | |
823 | 164M | if (parser.consume_specific('b')) |
824 | 0 | m_mode = Mode::Binary; |
825 | 164M | else if (parser.consume_specific('B')) |
826 | 0 | m_mode = Mode::BinaryUppercase; |
827 | 164M | else if (parser.consume_specific('d')) |
828 | 0 | m_mode = Mode::Decimal; |
829 | 164M | else if (parser.consume_specific('o')) |
830 | 0 | m_mode = Mode::Octal; |
831 | 164M | else if (parser.consume_specific('x')) |
832 | 44 | m_mode = Mode::Hexadecimal; |
833 | 164M | else if (parser.consume_specific('X')) |
834 | 106M | m_mode = Mode::HexadecimalUppercase; |
835 | 58.0M | else if (parser.consume_specific('c')) |
836 | 0 | m_mode = Mode::Character; |
837 | 58.0M | else if (parser.consume_specific('s')) |
838 | 0 | m_mode = Mode::String; |
839 | 58.0M | else if (parser.consume_specific('p')) |
840 | 0 | m_mode = Mode::Pointer; |
841 | 58.0M | else if (parser.consume_specific('f')) |
842 | 0 | m_mode = Mode::FixedPoint; |
843 | 58.0M | else if (parser.consume_specific('a')) |
844 | 0 | m_mode = Mode::Hexfloat; |
845 | 58.0M | else if (parser.consume_specific('A')) |
846 | 0 | m_mode = Mode::HexfloatUppercase; |
847 | 58.0M | else if (parser.consume_specific("hex-dump"sv)) |
848 | 0 | m_mode = Mode::HexDump; |
849 | | |
850 | 164M | if (!parser.is_eof()) |
851 | 0 | dbgln("{} did not consume '{}'", __PRETTY_FUNCTION__, parser.remaining()); |
852 | | |
853 | 164M | VERIFY(parser.is_eof()); |
854 | 164M | } |
855 | | |
856 | | ErrorOr<void> Formatter<StringView>::format(FormatBuilder& builder, StringView value) |
857 | 57.6M | { |
858 | 57.6M | if (m_sign_mode != FormatBuilder::SignMode::Default) |
859 | 57.6M | VERIFY_NOT_REACHED(); |
860 | 57.6M | if (m_zero_pad) |
861 | 57.6M | VERIFY_NOT_REACHED(); |
862 | 57.6M | if (m_mode != Mode::Default && m_mode != Mode::String && m_mode != Mode::Character && m_mode != Mode::HexDump) |
863 | 57.6M | VERIFY_NOT_REACHED(); |
864 | | |
865 | 57.6M | m_width = m_width.value_or(0); |
866 | 57.6M | m_precision = m_precision.value_or(NumericLimits<size_t>::max()); |
867 | | |
868 | 57.6M | if (m_mode == Mode::HexDump) |
869 | 0 | return builder.put_hexdump(value.bytes(), m_width.value(), m_fill); |
870 | 57.6M | return builder.put_string(value, m_align, m_width.value(), m_precision.value(), m_fill); |
871 | 57.6M | } |
872 | | |
873 | | ErrorOr<void> Formatter<FormatString>::vformat(FormatBuilder& builder, StringView fmtstr, TypeErasedFormatParams& params) |
874 | 141k | { |
875 | 141k | StringBuilder string_builder; |
876 | 141k | TRY(AK::vformat(string_builder, fmtstr, params)); |
877 | 141k | TRY(Formatter<StringView>::format(builder, string_builder.string_view())); |
878 | 141k | return {}; |
879 | 141k | } |
880 | | |
881 | | template<Integral T> |
882 | | ErrorOr<void> Formatter<T>::format(FormatBuilder& builder, T value) |
883 | 107M | { |
884 | 107M | if (m_mode == Mode::Character) { |
885 | 0 | m_mode = Mode::String; |
886 | |
|
887 | 0 | Formatter<StringView> formatter { *this }; |
888 | | |
889 | | // FIXME: We just support ASCII for now, in the future maybe unicode? |
890 | 0 | VERIFY(value >= 0 && value <= 127); |
891 | | |
892 | | // Convert value to a single byte. This is important for big-endian systems, because the LSB is stored in the last byte. |
893 | 0 | char const c = (value & 0x7f); |
894 | |
|
895 | 0 | return formatter.format(builder, StringView { &c, 1 }); |
896 | 0 | } |
897 | | |
898 | 107M | if (m_precision.has_value()) |
899 | 107M | VERIFY_NOT_REACHED(); |
900 | | |
901 | 107M | if (m_mode == Mode::Pointer) { |
902 | 0 | if (m_sign_mode != FormatBuilder::SignMode::Default) |
903 | 0 | VERIFY_NOT_REACHED(); |
904 | 0 | if (m_align != FormatBuilder::Align::Default) |
905 | 0 | VERIFY_NOT_REACHED(); |
906 | 0 | if (m_alternative_form) |
907 | 0 | VERIFY_NOT_REACHED(); |
908 | 0 | if (m_width.has_value()) |
909 | 0 | VERIFY_NOT_REACHED(); |
910 | | |
911 | 0 | m_mode = Mode::Hexadecimal; |
912 | 0 | m_alternative_form = true; |
913 | 0 | m_width = 2 * sizeof(void*); |
914 | 0 | m_zero_pad = true; |
915 | 0 | } |
916 | | |
917 | 107M | u8 base = 0; |
918 | 107M | bool upper_case = false; |
919 | 107M | if (m_mode == Mode::Binary) { |
920 | 0 | base = 2; |
921 | 107M | } else if (m_mode == Mode::BinaryUppercase) { |
922 | 0 | base = 2; |
923 | 0 | upper_case = true; |
924 | 107M | } else if (m_mode == Mode::Octal) { |
925 | 0 | base = 8; |
926 | 107M | } else if (m_mode == Mode::Decimal || m_mode == Mode::Default) { |
927 | 1.13M | base = 10; |
928 | 106M | } else if (m_mode == Mode::Hexadecimal) { |
929 | 44 | base = 16; |
930 | 106M | } else if (m_mode == Mode::HexadecimalUppercase) { |
931 | 106M | base = 16; |
932 | 106M | upper_case = true; |
933 | 106M | } else if (m_mode == Mode::HexDump) { |
934 | 0 | m_width = m_width.value_or(32); |
935 | 0 | return builder.put_hexdump({ &value, sizeof(value) }, m_width.value(), m_fill); |
936 | 0 | } else { |
937 | 0 | VERIFY_NOT_REACHED(); |
938 | 0 | } |
939 | | |
940 | 107M | m_width = m_width.value_or(0); |
941 | | |
942 | | if constexpr (IsSame<MakeUnsigned<T>, T>) |
943 | 106M | return builder.put_u64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); |
944 | | else |
945 | 811k | return builder.put_i64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); |
946 | 107M | } Unexecuted instantiation: AK::Formatter<unsigned char, void>::format(AK::FormatBuilder&, unsigned char) Unexecuted instantiation: AK::Formatter<unsigned short, void>::format(AK::FormatBuilder&, unsigned short) Unexecuted instantiation: AK::Formatter<unsigned int, void>::format(AK::FormatBuilder&, unsigned int) AK::Formatter<unsigned long, void>::format(AK::FormatBuilder&, unsigned long) Line | Count | Source | 883 | 106M | { | 884 | 106M | if (m_mode == Mode::Character) { | 885 | 0 | m_mode = Mode::String; | 886 | |
| 887 | 0 | Formatter<StringView> formatter { *this }; | 888 | | | 889 | | // FIXME: We just support ASCII for now, in the future maybe unicode? | 890 | 0 | VERIFY(value >= 0 && value <= 127); | 891 | | | 892 | | // Convert value to a single byte. This is important for big-endian systems, because the LSB is stored in the last byte. | 893 | 0 | char const c = (value & 0x7f); | 894 | |
| 895 | 0 | return formatter.format(builder, StringView { &c, 1 }); | 896 | 0 | } | 897 | | | 898 | 106M | if (m_precision.has_value()) | 899 | 106M | VERIFY_NOT_REACHED(); | 900 | | | 901 | 106M | if (m_mode == Mode::Pointer) { | 902 | 0 | if (m_sign_mode != FormatBuilder::SignMode::Default) | 903 | 0 | VERIFY_NOT_REACHED(); | 904 | 0 | if (m_align != FormatBuilder::Align::Default) | 905 | 0 | VERIFY_NOT_REACHED(); | 906 | 0 | if (m_alternative_form) | 907 | 0 | VERIFY_NOT_REACHED(); | 908 | 0 | if (m_width.has_value()) | 909 | 0 | VERIFY_NOT_REACHED(); | 910 | | | 911 | 0 | m_mode = Mode::Hexadecimal; | 912 | 0 | m_alternative_form = true; | 913 | 0 | m_width = 2 * sizeof(void*); | 914 | 0 | m_zero_pad = true; | 915 | 0 | } | 916 | | | 917 | 106M | u8 base = 0; | 918 | 106M | bool upper_case = false; | 919 | 106M | if (m_mode == Mode::Binary) { | 920 | 0 | base = 2; | 921 | 106M | } else if (m_mode == Mode::BinaryUppercase) { | 922 | 0 | base = 2; | 923 | 0 | upper_case = true; | 924 | 106M | } else if (m_mode == Mode::Octal) { | 925 | 0 | base = 8; | 926 | 106M | } else if (m_mode == Mode::Decimal || m_mode == Mode::Default) { | 927 | 323k | base = 10; | 928 | 106M | } else if (m_mode == Mode::Hexadecimal) { | 929 | 44 | base = 16; | 930 | 106M | } else if (m_mode == Mode::HexadecimalUppercase) { | 931 | 106M | base = 16; | 932 | 106M | upper_case = true; | 933 | 106M | } else if (m_mode == Mode::HexDump) { | 934 | 0 | m_width = m_width.value_or(32); | 935 | 0 | return builder.put_hexdump({ &value, sizeof(value) }, m_width.value(), m_fill); | 936 | 0 | } else { | 937 | 0 | VERIFY_NOT_REACHED(); | 938 | 0 | } | 939 | | | 940 | 106M | m_width = m_width.value_or(0); | 941 | | | 942 | | if constexpr (IsSame<MakeUnsigned<T>, T>) | 943 | 106M | return builder.put_u64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); | 944 | | else | 945 | | return builder.put_i64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); | 946 | 106M | } |
Unexecuted instantiation: AK::Formatter<unsigned long long, void>::format(AK::FormatBuilder&, unsigned long long) Unexecuted instantiation: AK::Formatter<short, void>::format(AK::FormatBuilder&, short) AK::Formatter<int, void>::format(AK::FormatBuilder&, int) Line | Count | Source | 883 | 742k | { | 884 | 742k | if (m_mode == Mode::Character) { | 885 | 0 | m_mode = Mode::String; | 886 | |
| 887 | 0 | Formatter<StringView> formatter { *this }; | 888 | | | 889 | | // FIXME: We just support ASCII for now, in the future maybe unicode? | 890 | 0 | VERIFY(value >= 0 && value <= 127); | 891 | | | 892 | | // Convert value to a single byte. This is important for big-endian systems, because the LSB is stored in the last byte. | 893 | 0 | char const c = (value & 0x7f); | 894 | |
| 895 | 0 | return formatter.format(builder, StringView { &c, 1 }); | 896 | 0 | } | 897 | | | 898 | 742k | if (m_precision.has_value()) | 899 | 742k | VERIFY_NOT_REACHED(); | 900 | | | 901 | 742k | if (m_mode == Mode::Pointer) { | 902 | 0 | if (m_sign_mode != FormatBuilder::SignMode::Default) | 903 | 0 | VERIFY_NOT_REACHED(); | 904 | 0 | if (m_align != FormatBuilder::Align::Default) | 905 | 0 | VERIFY_NOT_REACHED(); | 906 | 0 | if (m_alternative_form) | 907 | 0 | VERIFY_NOT_REACHED(); | 908 | 0 | if (m_width.has_value()) | 909 | 0 | VERIFY_NOT_REACHED(); | 910 | | | 911 | 0 | m_mode = Mode::Hexadecimal; | 912 | 0 | m_alternative_form = true; | 913 | 0 | m_width = 2 * sizeof(void*); | 914 | 0 | m_zero_pad = true; | 915 | 0 | } | 916 | | | 917 | 742k | u8 base = 0; | 918 | 742k | bool upper_case = false; | 919 | 742k | if (m_mode == Mode::Binary) { | 920 | 0 | base = 2; | 921 | 742k | } else if (m_mode == Mode::BinaryUppercase) { | 922 | 0 | base = 2; | 923 | 0 | upper_case = true; | 924 | 742k | } else if (m_mode == Mode::Octal) { | 925 | 0 | base = 8; | 926 | 742k | } else if (m_mode == Mode::Decimal || m_mode == Mode::Default) { | 927 | 742k | base = 10; | 928 | 742k | } else if (m_mode == Mode::Hexadecimal) { | 929 | 0 | base = 16; | 930 | 0 | } else if (m_mode == Mode::HexadecimalUppercase) { | 931 | 0 | base = 16; | 932 | 0 | upper_case = true; | 933 | 0 | } else if (m_mode == Mode::HexDump) { | 934 | 0 | m_width = m_width.value_or(32); | 935 | 0 | return builder.put_hexdump({ &value, sizeof(value) }, m_width.value(), m_fill); | 936 | 0 | } else { | 937 | 0 | VERIFY_NOT_REACHED(); | 938 | 0 | } | 939 | | | 940 | 742k | m_width = m_width.value_or(0); | 941 | | | 942 | | if constexpr (IsSame<MakeUnsigned<T>, T>) | 943 | | return builder.put_u64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); | 944 | | else | 945 | 742k | return builder.put_i64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); | 946 | 742k | } |
AK::Formatter<long, void>::format(AK::FormatBuilder&, long) Line | Count | Source | 883 | 68.9k | { | 884 | 68.9k | if (m_mode == Mode::Character) { | 885 | 0 | m_mode = Mode::String; | 886 | |
| 887 | 0 | Formatter<StringView> formatter { *this }; | 888 | | | 889 | | // FIXME: We just support ASCII for now, in the future maybe unicode? | 890 | 0 | VERIFY(value >= 0 && value <= 127); | 891 | | | 892 | | // Convert value to a single byte. This is important for big-endian systems, because the LSB is stored in the last byte. | 893 | 0 | char const c = (value & 0x7f); | 894 | |
| 895 | 0 | return formatter.format(builder, StringView { &c, 1 }); | 896 | 0 | } | 897 | | | 898 | 68.9k | if (m_precision.has_value()) | 899 | 68.9k | VERIFY_NOT_REACHED(); | 900 | | | 901 | 68.9k | if (m_mode == Mode::Pointer) { | 902 | 0 | if (m_sign_mode != FormatBuilder::SignMode::Default) | 903 | 0 | VERIFY_NOT_REACHED(); | 904 | 0 | if (m_align != FormatBuilder::Align::Default) | 905 | 0 | VERIFY_NOT_REACHED(); | 906 | 0 | if (m_alternative_form) | 907 | 0 | VERIFY_NOT_REACHED(); | 908 | 0 | if (m_width.has_value()) | 909 | 0 | VERIFY_NOT_REACHED(); | 910 | | | 911 | 0 | m_mode = Mode::Hexadecimal; | 912 | 0 | m_alternative_form = true; | 913 | 0 | m_width = 2 * sizeof(void*); | 914 | 0 | m_zero_pad = true; | 915 | 0 | } | 916 | | | 917 | 68.9k | u8 base = 0; | 918 | 68.9k | bool upper_case = false; | 919 | 68.9k | if (m_mode == Mode::Binary) { | 920 | 0 | base = 2; | 921 | 68.9k | } else if (m_mode == Mode::BinaryUppercase) { | 922 | 0 | base = 2; | 923 | 0 | upper_case = true; | 924 | 68.9k | } else if (m_mode == Mode::Octal) { | 925 | 0 | base = 8; | 926 | 68.9k | } else if (m_mode == Mode::Decimal || m_mode == Mode::Default) { | 927 | 68.9k | base = 10; | 928 | 68.9k | } else if (m_mode == Mode::Hexadecimal) { | 929 | 0 | base = 16; | 930 | 0 | } else if (m_mode == Mode::HexadecimalUppercase) { | 931 | 0 | base = 16; | 932 | 0 | upper_case = true; | 933 | 0 | } else if (m_mode == Mode::HexDump) { | 934 | 0 | m_width = m_width.value_or(32); | 935 | 0 | return builder.put_hexdump({ &value, sizeof(value) }, m_width.value(), m_fill); | 936 | 0 | } else { | 937 | 0 | VERIFY_NOT_REACHED(); | 938 | 0 | } | 939 | | | 940 | 68.9k | m_width = m_width.value_or(0); | 941 | | | 942 | | if constexpr (IsSame<MakeUnsigned<T>, T>) | 943 | | return builder.put_u64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); | 944 | | else | 945 | 68.9k | return builder.put_i64(value, base, m_alternative_form, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_fill, m_sign_mode); | 946 | 68.9k | } |
Unexecuted instantiation: AK::Formatter<long long, void>::format(AK::FormatBuilder&, long long) Unexecuted instantiation: AK::Formatter<signed char, void>::format(AK::FormatBuilder&, signed char) |
947 | | |
948 | | ErrorOr<void> Formatter<char>::format(FormatBuilder& builder, char value) |
949 | 0 | { |
950 | 0 | if (m_mode == Mode::Binary || m_mode == Mode::BinaryUppercase || m_mode == Mode::Decimal || m_mode == Mode::Octal || m_mode == Mode::Hexadecimal || m_mode == Mode::HexadecimalUppercase) { |
951 | | // Trick: signed char != char. (Sometimes weird features are actually helpful.) |
952 | 0 | if constexpr (IsSigned<char>) { |
953 | 0 | Formatter<signed char> formatter { *this }; |
954 | 0 | return formatter.format(builder, static_cast<signed char>(value)); |
955 | | } else { |
956 | | Formatter<unsigned char> formatter { *this }; |
957 | | return formatter.format(builder, static_cast<unsigned char>(value)); |
958 | | } |
959 | 0 | } else { |
960 | 0 | Formatter<StringView> formatter { *this }; |
961 | 0 | return formatter.format(builder, { &value, 1 }); |
962 | 0 | } |
963 | 0 | } |
964 | | ErrorOr<void> Formatter<wchar_t>::format(FormatBuilder& builder, wchar_t value) |
965 | 0 | { |
966 | 0 | if (m_mode == Mode::Binary || m_mode == Mode::BinaryUppercase || m_mode == Mode::Decimal || m_mode == Mode::Octal || m_mode == Mode::Hexadecimal || m_mode == Mode::HexadecimalUppercase) { |
967 | 0 | Formatter<u32> formatter { *this }; |
968 | 0 | return formatter.format(builder, static_cast<u32>(value)); |
969 | 0 | } else { |
970 | 0 | StringBuilder codepoint; |
971 | 0 | codepoint.append_code_point(value); |
972 | |
|
973 | 0 | Formatter<StringView> formatter { *this }; |
974 | 0 | return formatter.format(builder, codepoint.string_view()); |
975 | 0 | } |
976 | 0 | } |
977 | | ErrorOr<void> Formatter<bool>::format(FormatBuilder& builder, bool value) |
978 | 0 | { |
979 | 0 | if (m_mode == Mode::Binary || m_mode == Mode::BinaryUppercase || m_mode == Mode::Decimal || m_mode == Mode::Octal || m_mode == Mode::Hexadecimal || m_mode == Mode::HexadecimalUppercase) { |
980 | 0 | Formatter<u8> formatter { *this }; |
981 | 0 | return formatter.format(builder, static_cast<u8>(value)); |
982 | 0 | } else if (m_mode == Mode::HexDump) { |
983 | 0 | return builder.put_hexdump({ &value, sizeof(value) }, m_width.value_or(32), m_fill); |
984 | 0 | } else { |
985 | 0 | Formatter<StringView> formatter { *this }; |
986 | 0 | return formatter.format(builder, value ? "true"sv : "false"sv); |
987 | 0 | } |
988 | 0 | } |
989 | | #ifndef KERNEL |
990 | | ErrorOr<void> Formatter<long double>::format(FormatBuilder& builder, long double value) |
991 | 0 | { |
992 | 0 | u8 base; |
993 | 0 | bool upper_case; |
994 | 0 | FormatBuilder::RealNumberDisplayMode real_number_display_mode = FormatBuilder::RealNumberDisplayMode::General; |
995 | 0 | if (m_mode == Mode::Default || m_mode == Mode::FixedPoint) { |
996 | 0 | base = 10; |
997 | 0 | upper_case = false; |
998 | 0 | if (m_mode == Mode::FixedPoint) |
999 | 0 | real_number_display_mode = FormatBuilder::RealNumberDisplayMode::FixedPoint; |
1000 | 0 | } else if (m_mode == Mode::Hexfloat) { |
1001 | 0 | base = 16; |
1002 | 0 | upper_case = false; |
1003 | 0 | } else if (m_mode == Mode::HexfloatUppercase) { |
1004 | 0 | base = 16; |
1005 | 0 | upper_case = true; |
1006 | 0 | } else { |
1007 | 0 | VERIFY_NOT_REACHED(); |
1008 | 0 | } |
1009 | | |
1010 | 0 | m_width = m_width.value_or(0); |
1011 | 0 | m_precision = m_precision.value_or(6); |
1012 | |
|
1013 | 0 | return builder.put_f80(value, base, upper_case, m_use_separator, m_align, m_width.value(), m_precision.value(), m_fill, m_sign_mode, real_number_display_mode); |
1014 | 0 | } |
1015 | | |
1016 | | ErrorOr<void> Formatter<double>::format(FormatBuilder& builder, double value) |
1017 | 0 | { |
1018 | 0 | u8 base; |
1019 | 0 | bool upper_case; |
1020 | 0 | FormatBuilder::RealNumberDisplayMode real_number_display_mode = FormatBuilder::RealNumberDisplayMode::General; |
1021 | 0 | if (m_mode == Mode::Default || m_mode == Mode::FixedPoint) { |
1022 | 0 | base = 10; |
1023 | 0 | upper_case = false; |
1024 | 0 | if (m_mode == Mode::FixedPoint) |
1025 | 0 | real_number_display_mode = FormatBuilder::RealNumberDisplayMode::FixedPoint; |
1026 | 0 | } else if (m_mode == Mode::Hexfloat) { |
1027 | 0 | base = 16; |
1028 | 0 | upper_case = false; |
1029 | 0 | } else if (m_mode == Mode::HexfloatUppercase) { |
1030 | 0 | base = 16; |
1031 | 0 | upper_case = true; |
1032 | 0 | } else { |
1033 | 0 | VERIFY_NOT_REACHED(); |
1034 | 0 | } |
1035 | | |
1036 | 0 | m_width = m_width.value_or(0); |
1037 | |
|
1038 | 0 | return builder.put_f32_or_f64(value, base, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_precision, m_fill, m_sign_mode, real_number_display_mode); |
1039 | 0 | } |
1040 | | |
1041 | | ErrorOr<void> Formatter<float>::format(FormatBuilder& builder, float value) |
1042 | 0 | { |
1043 | 0 | u8 base; |
1044 | 0 | bool upper_case; |
1045 | 0 | FormatBuilder::RealNumberDisplayMode real_number_display_mode = FormatBuilder::RealNumberDisplayMode::General; |
1046 | 0 | if (m_mode == Mode::Default || m_mode == Mode::FixedPoint) { |
1047 | 0 | base = 10; |
1048 | 0 | upper_case = false; |
1049 | 0 | if (m_mode == Mode::FixedPoint) |
1050 | 0 | real_number_display_mode = FormatBuilder::RealNumberDisplayMode::FixedPoint; |
1051 | 0 | } else if (m_mode == Mode::Hexfloat) { |
1052 | 0 | base = 16; |
1053 | 0 | upper_case = false; |
1054 | 0 | } else if (m_mode == Mode::HexfloatUppercase) { |
1055 | 0 | base = 16; |
1056 | 0 | upper_case = true; |
1057 | 0 | } else { |
1058 | 0 | VERIFY_NOT_REACHED(); |
1059 | 0 | } |
1060 | | |
1061 | 0 | m_width = m_width.value_or(0); |
1062 | |
|
1063 | 0 | return builder.put_f32_or_f64(value, base, upper_case, m_zero_pad, m_use_separator, m_align, m_width.value(), m_precision, m_fill, m_sign_mode, real_number_display_mode); |
1064 | 0 | } |
1065 | | |
1066 | | template ErrorOr<void> FormatBuilder::put_f32_or_f64<float>(float, u8, bool, bool, bool, Align, size_t, Optional<size_t>, char, SignMode, RealNumberDisplayMode); |
1067 | | template ErrorOr<void> FormatBuilder::put_f32_or_f64<double>(double, u8, bool, bool, bool, Align, size_t, Optional<size_t>, char, SignMode, RealNumberDisplayMode); |
1068 | | #endif |
1069 | | |
1070 | | #ifndef KERNEL |
1071 | | void vout(FILE* file, StringView fmtstr, TypeErasedFormatParams& params, bool newline) |
1072 | 222 | { |
1073 | 222 | StringBuilder builder; |
1074 | 222 | MUST(vformat(builder, fmtstr, params)); |
1075 | | |
1076 | 222 | if (newline) |
1077 | 222 | builder.append('\n'); |
1078 | | |
1079 | 222 | auto const string = builder.string_view(); |
1080 | 222 | auto const retval = ::fwrite(string.characters_without_null_termination(), 1, string.length(), file); |
1081 | 222 | if (static_cast<size_t>(retval) != string.length()) { |
1082 | 0 | auto error = ferror(file); |
1083 | 0 | dbgln("vout() failed ({} written out of {}), error was {} ({})", retval, string.length(), error, strerror(error)); |
1084 | 0 | } |
1085 | 222 | } |
1086 | | #endif |
1087 | | |
1088 | | #ifndef KERNEL |
1089 | | // FIXME: Deduplicate with Core::Process:get_name() |
1090 | | [[gnu::used]] static ByteString process_name_helper() |
1091 | 0 | { |
1092 | | # if defined(AK_OS_SERENITY) |
1093 | | char buffer[BUFSIZ] = {}; |
1094 | | int rc = get_process_name(buffer, BUFSIZ); |
1095 | | if (rc != 0) |
1096 | | return ByteString {}; |
1097 | | return StringView { buffer, strlen(buffer) }; |
1098 | | # elif defined(AK_LIBC_GLIBC) || defined(AK_OS_LINUX) |
1099 | | return StringView { program_invocation_name, strlen(program_invocation_name) }; |
1100 | | # elif defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU) |
1101 | | auto const* progname = getprogname(); |
1102 | | return StringView { progname, strlen(progname) }; |
1103 | | # else |
1104 | | // FIXME: Implement process_name_helper() for other platforms. |
1105 | | return StringView {}; |
1106 | | # endif |
1107 | 0 | } |
1108 | | |
1109 | | static StringView process_name_for_logging() |
1110 | 0 | { |
1111 | | // NOTE: We use AK::Format in the DynamicLoader and LibC, which cannot use thread-safe statics |
1112 | | // Also go to extraordinary lengths here to avoid strlen() on the process name every call to dbgln |
1113 | 0 | static char process_name_buf[256] = {}; |
1114 | 0 | static StringView process_name; |
1115 | 0 | static bool process_name_retrieved = false; |
1116 | 0 | if (!process_name_retrieved) { |
1117 | 0 | auto path = LexicalPath(process_name_helper()); |
1118 | 0 | process_name_retrieved = true; |
1119 | 0 | (void)path.basename().copy_characters_to_buffer(process_name_buf, sizeof(process_name_buf)); |
1120 | 0 | process_name = { process_name_buf, strlen(process_name_buf) }; |
1121 | 0 | } |
1122 | 0 | return process_name; |
1123 | 0 | } |
1124 | | #endif |
1125 | | |
1126 | | static bool is_debug_enabled = true; |
1127 | | |
1128 | | void set_debug_enabled(bool value) |
1129 | 131k | { |
1130 | 131k | is_debug_enabled = value; |
1131 | 131k | } |
1132 | | |
1133 | | // On Serenity, dbgln goes to a non-stderr output |
1134 | | static bool is_rich_debug_enabled = |
1135 | | #if defined(AK_OS_SERENITY) |
1136 | | true; |
1137 | | #else |
1138 | | false; |
1139 | | #endif |
1140 | | |
1141 | | void set_rich_debug_enabled(bool value) |
1142 | 0 | { |
1143 | 0 | is_rich_debug_enabled = value; |
1144 | 0 | } |
1145 | | |
1146 | | void vdbg(StringView fmtstr, TypeErasedFormatParams& params, bool newline) |
1147 | 14.9M | { |
1148 | 14.9M | if (!is_debug_enabled) |
1149 | 14.9M | return; |
1150 | | |
1151 | 0 | StringBuilder builder; |
1152 | |
|
1153 | 0 | if (is_rich_debug_enabled) { |
1154 | | #if defined(PREKERNEL) |
1155 | | ; |
1156 | | #elif defined(KERNEL) |
1157 | | if (Kernel::Processor::is_initialized() && TimeManagement::is_initialized()) { |
1158 | | auto time = TimeManagement::the().monotonic_time(TimePrecision::Coarse); |
1159 | | if (Kernel::Thread::current()) { |
1160 | | auto& thread = *Kernel::Thread::current(); |
1161 | | thread.process().name().with([&](auto& process_name) { |
1162 | | builder.appendff("{}.{:03} \033[34;1m[#{} {}({}:{})]\033[0m: ", time.truncated_seconds(), time.nanoseconds_within_second() / 1000000, Kernel::Processor::current_id(), process_name.representable_view(), thread.pid().value(), thread.tid().value()); |
1163 | | }); |
1164 | | } else { |
1165 | | builder.appendff("{}.{:03} \033[34;1m[#{} Kernel]\033[0m: ", time.truncated_seconds(), time.nanoseconds_within_second() / 1000000, Kernel::Processor::current_id()); |
1166 | | } |
1167 | | } else { |
1168 | | builder.appendff("\033[34;1m[Kernel]\033[0m: "); |
1169 | | } |
1170 | | #elif !defined(AK_OS_WINDOWS) |
1171 | | auto process_name = process_name_for_logging(); |
1172 | 0 | if (!process_name.is_empty()) { |
1173 | 0 | struct timespec ts = {}; |
1174 | 0 | clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); |
1175 | 0 | auto pid = getpid(); |
1176 | 0 | # if defined(AK_OS_SERENITY) || defined(AK_OS_LINUX) |
1177 | | // Linux and Serenity handle thread IDs as if they are related to process ids |
1178 | 0 | auto tid = gettid(); |
1179 | 0 | if (pid == tid) |
1180 | 0 | # endif |
1181 | 0 | { |
1182 | 0 | builder.appendff("{}.{:03} \033[33;1m{}({})\033[0m: ", ts.tv_sec, ts.tv_nsec / 1000000, process_name, pid); |
1183 | 0 | } |
1184 | 0 | # if defined(AK_OS_SERENITY) || defined(AK_OS_LINUX) |
1185 | 0 | else { |
1186 | 0 | builder.appendff("{}.{:03} \033[33;1m{}({}:{})\033[0m: ", ts.tv_sec, ts.tv_nsec / 1000000, process_name, pid, tid); |
1187 | 0 | } |
1188 | 0 | # endif |
1189 | 0 | } |
1190 | 0 | #endif |
1191 | 0 | } |
1192 | |
|
1193 | 0 | MUST(vformat(builder, fmtstr, params)); |
1194 | 0 | if (newline) |
1195 | 0 | builder.append('\n'); |
1196 | 0 | auto const string = builder.string_view(); |
1197 | |
|
1198 | | #ifdef AK_OS_SERENITY |
1199 | | # if defined(KERNEL) && !defined(PREKERNEL) |
1200 | | if (!Kernel::Processor::is_initialized()) { |
1201 | | kernelearlyputstr(string.characters_without_null_termination(), string.length()); |
1202 | | return; |
1203 | | } |
1204 | | # endif |
1205 | | #endif |
1206 | 0 | dbgputstr(string.characters_without_null_termination(), string.length()); |
1207 | 0 | } |
1208 | | |
1209 | | #if defined(KERNEL) && !defined(PREKERNEL) |
1210 | | void vdmesgln(StringView fmtstr, TypeErasedFormatParams& params) |
1211 | | { |
1212 | | StringBuilder builder; |
1213 | | |
1214 | | # ifdef AK_OS_SERENITY |
1215 | | if (TimeManagement::is_initialized()) { |
1216 | | auto time = TimeManagement::the().monotonic_time(TimePrecision::Coarse); |
1217 | | |
1218 | | if (Kernel::Processor::is_initialized() && Kernel::Thread::current()) { |
1219 | | auto& thread = *Kernel::Thread::current(); |
1220 | | thread.process().name().with([&](auto& process_name) { |
1221 | | builder.appendff("{}.{:03} \033[34;1m[{}({}:{})]\033[0m: ", time.truncated_seconds(), time.nanoseconds_within_second() / 1000000, process_name.representable_view(), thread.pid().value(), thread.tid().value()); |
1222 | | }); |
1223 | | } else { |
1224 | | builder.appendff("{}.{:03} \033[34;1m[Kernel]\033[0m: ", time.truncated_seconds(), time.nanoseconds_within_second() / 1000000); |
1225 | | } |
1226 | | } else { |
1227 | | builder.appendff("\033[34;1m[Kernel]\033[0m: "); |
1228 | | } |
1229 | | # endif |
1230 | | |
1231 | | MUST(vformat(builder, fmtstr, params)); |
1232 | | builder.append('\n'); |
1233 | | |
1234 | | auto const string = builder.string_view(); |
1235 | | kernelputstr(string.characters_without_null_termination(), string.length()); |
1236 | | } |
1237 | | |
1238 | | void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams& params) |
1239 | | { |
1240 | | // FIXME: Try to avoid memory allocations further to prevent faulting |
1241 | | // at OOM conditions. |
1242 | | |
1243 | | StringBuilder builder; |
1244 | | # ifdef AK_OS_SERENITY |
1245 | | if (Kernel::Processor::is_initialized() && Kernel::Thread::current()) { |
1246 | | auto& thread = *Kernel::Thread::current(); |
1247 | | thread.process().name().with([&](auto& process_name) { |
1248 | | builder.appendff("[{}({}:{})]: ", process_name.representable_view(), thread.pid().value(), thread.tid().value()); |
1249 | | }); |
1250 | | } else { |
1251 | | builder.appendff("[Kernel]: "); |
1252 | | } |
1253 | | # endif |
1254 | | |
1255 | | MUST(vformat(builder, fmtstr, params)); |
1256 | | builder.append('\n'); |
1257 | | |
1258 | | auto const string = builder.string_view(); |
1259 | | kernelcriticalputstr(string.characters_without_null_termination(), string.length()); |
1260 | | } |
1261 | | |
1262 | | #endif |
1263 | | |
1264 | | template struct Formatter<unsigned char, void>; |
1265 | | template struct Formatter<unsigned short, void>; |
1266 | | template struct Formatter<unsigned int, void>; |
1267 | | template struct Formatter<unsigned long, void>; |
1268 | | template struct Formatter<unsigned long long, void>; |
1269 | | template struct Formatter<short, void>; |
1270 | | template struct Formatter<int, void>; |
1271 | | template struct Formatter<long, void>; |
1272 | | template struct Formatter<long long, void>; |
1273 | | template struct Formatter<signed char, void>; |
1274 | | |
1275 | | } // namespace AK |