| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2020, the SerenityOS developers. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-04 09:08:13 +02:00
										 |  |  | #include <AK/Assertions.h>
 | 
					
						
							|  |  |  | #include <AK/ByteString.h>
 | 
					
						
							| 
									
										
										
										
											2021-07-06 13:46:46 +02:00
										 |  |  | #include <AK/CharacterTypes.h>
 | 
					
						
							| 
									
										
										
										
											2025-04-04 09:08:13 +02:00
										 |  |  | #include <AK/Error.h>
 | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | #include <AK/Format.h>
 | 
					
						
							|  |  |  | #include <AK/GenericLexer.h>
 | 
					
						
							| 
									
										
										
										
											2025-04-04 09:08:13 +02:00
										 |  |  | #include <AK/HashMap.h>
 | 
					
						
							| 
									
										
										
										
											2022-01-27 13:23:36 +01:00
										 |  |  | #include <AK/IntegralMath.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #include <AK/LexicalPath.h>
 | 
					
						
							| 
									
										
										
										
											2023-09-13 23:19:25 -06:00
										 |  |  | #include <AK/String.h>
 | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #include <AK/StringFloatingPointConversions.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <time.h>
 | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #if defined(AK_OS_SERENITY)
 | 
					
						
							| 
									
										
										
										
											2021-03-12 17:29:37 +01:00
										 |  |  | #    include <serenity.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-13 23:19:25 -06:00
										 |  |  | #if defined(AK_OS_ANDROID)
 | 
					
						
							|  |  |  | #    include <android/log.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-02 12:58:01 -07:00
										 |  |  | #if defined(AK_OS_WINDOWS)
 | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  | #    include <AK/Windows.h>
 | 
					
						
							| 
									
										
										
										
											2024-10-02 12:58:01 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | namespace AK { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-18 07:22:52 -04:00
										 |  |  | class FormatParser : public GenericLexer { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     struct FormatSpecifier { | 
					
						
							|  |  |  |         StringView flags; | 
					
						
							|  |  |  |         size_t index; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     explicit FormatParser(StringView input); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     StringView consume_literal(); | 
					
						
							|  |  |  |     bool consume_number(size_t& value); | 
					
						
							|  |  |  |     bool consume_specifier(FormatSpecifier& specifier); | 
					
						
							|  |  |  |     bool consume_replacement_field(size_t& index); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 13:21:18 +02:00
										 |  |  | namespace { | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-21 13:47:03 -06:00
										 |  |  | static constexpr size_t use_next_index = NumericLimits<size_t>::max(); | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | // The worst case is that we have the largest 64-bit value formatted as binary number, this would take
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  | // 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.
 | 
					
						
							|  |  |  | static constexpr size_t convert_unsigned_to_string(u64 value, Array<u8, 128>& buffer, u8 base, bool upper_case, bool use_separator) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(base >= 2 && base <= 16); | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     constexpr char const* lowercase_lookup = "0123456789abcdef"; | 
					
						
							|  |  |  |     constexpr char const* uppercase_lookup = "0123456789ABCDEF"; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     if (value == 0) { | 
					
						
							|  |  |  |         buffer[0] = '0'; | 
					
						
							|  |  |  |         return 1; | 
					
						
							| 
									
										
										
										
											2020-09-22 13:05:40 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     size_t used = 0; | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     size_t digit_count = 0; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     while (value > 0) { | 
					
						
							|  |  |  |         if (upper_case) | 
					
						
							|  |  |  |             buffer[used++] = uppercase_lookup[value % base]; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             buffer[used++] = lowercase_lookup[value % base]; | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |         digit_count++; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         value /= base; | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (use_separator && value > 0 && digit_count % 3 == 0) | 
					
						
							|  |  |  |             buffer[used++] = ','; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-09-23 13:21:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     for (size_t i = 0; i < used / 2; ++i) | 
					
						
							|  |  |  |         swap(buffer[i], buffer[used - i - 1]); | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     return used; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> vformat_impl(TypeErasedFormatParams& params, FormatBuilder& builder, FormatParser& parser) | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const literal = parser.consume_literal(); | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     TRY(builder.put_literal(literal)); | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     FormatParser::FormatSpecifier specifier; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (!parser.consume_specifier(specifier)) { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY(parser.is_eof()); | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (specifier.index == use_next_index) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         specifier.index = params.take_next_index(); | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     auto& parameter = params.parameters().at(specifier.index); | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     FormatParser argparser { specifier.flags }; | 
					
						
							| 
									
										
										
										
											2025-05-24 09:06:42 +02:00
										 |  |  |     TRY(parameter.visit([&]<typename T>(T const& value) { | 
					
						
							|  |  |  |         if constexpr (IsSame<T, TypeErasedParameter::CustomType>) { | 
					
						
							|  |  |  |             return value.formatter(params, builder, argparser, value.value); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return __format_value<T>(params, builder, argparser, &value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     })); | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     TRY(vformat_impl(params, builder, parser)); | 
					
						
							|  |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | } // namespace AK::{anonymous}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FormatParser::FormatParser(StringView input) | 
					
						
							|  |  |  |     : GenericLexer(input) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | StringView FormatParser::consume_literal() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const begin = tell(); | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     while (!is_eof()) { | 
					
						
							| 
									
										
										
										
											2023-10-10 12:42:20 +02:00
										 |  |  |         if (consume_specific("{{"sv)) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 12:42:20 +02:00
										 |  |  |         if (consume_specific("}}"sv)) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |         if (next_is(is_any_of("{}"sv))) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             return m_input.substring_view(begin, tell() - begin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         consume(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return m_input.substring_view(begin); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | bool FormatParser::consume_number(size_t& value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     value = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool consumed_at_least_one = false; | 
					
						
							| 
									
										
										
										
											2021-07-06 13:46:46 +02:00
										 |  |  |     while (next_is(is_ascii_digit)) { | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         value *= 10; | 
					
						
							| 
									
										
										
										
											2021-07-06 13:46:46 +02:00
										 |  |  |         value += parse_ascii_digit(consume()); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         consumed_at_least_one = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return consumed_at_least_one; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | bool FormatParser::consume_specifier(FormatSpecifier& specifier) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(!next_is('}')); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!consume_specific('{')) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!consume_number(specifier.index)) | 
					
						
							|  |  |  |         specifier.index = use_next_index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (consume_specific(':')) { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |         auto const begin = tell(); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         size_t level = 1; | 
					
						
							|  |  |  |         while (level > 0) { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY(!is_eof()); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (consume_specific('{')) { | 
					
						
							|  |  |  |                 ++level; | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (consume_specific('}')) { | 
					
						
							|  |  |  |                 --level; | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             consume(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         specifier.flags = m_input.substring_view(begin, tell() - begin - 1); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         if (!consume_specific('}')) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |         specifier.flags = ""sv; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | bool FormatParser::consume_replacement_field(size_t& index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!consume_specific('{')) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!consume_number(index)) | 
					
						
							|  |  |  |         index = use_next_index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!consume_specific('}')) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> FormatBuilder::put_padding(char fill, size_t amount) | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     for (size_t i = 0; i < amount; ++i) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(m_builder.try_append(fill)); | 
					
						
							|  |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> FormatBuilder::put_literal(StringView value) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     for (size_t i = 0; i < value.length(); ++i) { | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(m_builder.try_append(value[i])); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         if (value[i] == '{' || value[i] == '}') | 
					
						
							|  |  |  |             ++i; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | ErrorOr<void> FormatBuilder::put_string( | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     StringView value, | 
					
						
							|  |  |  |     Align align, | 
					
						
							|  |  |  |     size_t min_width, | 
					
						
							|  |  |  |     size_t max_width, | 
					
						
							|  |  |  |     char fill) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const used_by_string = min(max_width, value.length()); | 
					
						
							|  |  |  |     auto const used_by_padding = max(min_width, used_by_string) - used_by_string; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (used_by_string < value.length()) | 
					
						
							|  |  |  |         value = value.substring_view(0, used_by_string); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (align == Align::Left || align == Align::Default) { | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(m_builder.try_append(value)); | 
					
						
							|  |  |  |         TRY(put_padding(fill, used_by_padding)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     } else if (align == Align::Center) { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |         auto const used_by_left_padding = used_by_padding / 2; | 
					
						
							|  |  |  |         auto const used_by_right_padding = ceil_div<size_t, size_t>(used_by_padding, 2); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(put_padding(fill, used_by_left_padding)); | 
					
						
							|  |  |  |         TRY(m_builder.try_append(value)); | 
					
						
							|  |  |  |         TRY(put_padding(fill, used_by_right_padding)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     } else if (align == Align::Right) { | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(put_padding(fill, used_by_padding)); | 
					
						
							|  |  |  |         TRY(m_builder.try_append(value)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | ErrorOr<void> FormatBuilder::put_u64( | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     u64 value, | 
					
						
							|  |  |  |     u8 base, | 
					
						
							|  |  |  |     bool prefix, | 
					
						
							|  |  |  |     bool upper_case, | 
					
						
							|  |  |  |     bool zero_pad, | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     bool use_separator, | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     Align align, | 
					
						
							|  |  |  |     size_t min_width, | 
					
						
							|  |  |  |     char fill, | 
					
						
							|  |  |  |     SignMode sign_mode, | 
					
						
							|  |  |  |     bool is_negative) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-10-06 13:32:00 +02:00
										 |  |  |     if (align == Align::Default) | 
					
						
							|  |  |  |         align = Align::Right; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     Array<u8, 128> buffer; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     auto const used_by_digits = convert_unsigned_to_string(value, buffer, base, upper_case, use_separator); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 13:32:00 +02:00
										 |  |  |     size_t used_by_prefix = 0; | 
					
						
							|  |  |  |     if (align == Align::Right && zero_pad) { | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  |         // We want ByteString::formatted("{:#08x}", 32) to produce '0x00000020' instead of '0x000020'. This
 | 
					
						
							| 
									
										
										
										
											2021-09-07 12:56:50 +02:00
										 |  |  |         // behavior differs from both fmtlib and printf, but is more intuitive.
 | 
					
						
							| 
									
										
										
										
											2020-10-06 13:32:00 +02:00
										 |  |  |         used_by_prefix = 0; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         if (is_negative || sign_mode != SignMode::OnlyIfNeeded) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             used_by_prefix += 1; | 
					
						
							| 
									
										
										
										
											2020-10-06 13:32:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (prefix) { | 
					
						
							|  |  |  |             if (base == 8) | 
					
						
							|  |  |  |                 used_by_prefix += 1; | 
					
						
							|  |  |  |             else if (base == 16) | 
					
						
							|  |  |  |                 used_by_prefix += 2; | 
					
						
							|  |  |  |             else if (base == 2) | 
					
						
							|  |  |  |                 used_by_prefix += 2; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const used_by_field = used_by_prefix + used_by_digits; | 
					
						
							|  |  |  |     auto const used_by_padding = max(used_by_field, min_width) - used_by_field; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const put_prefix = [&]() -> ErrorOr<void> { | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         if (is_negative) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(m_builder.try_append('-')); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         else if (sign_mode == SignMode::Always) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(m_builder.try_append('+')); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         else if (sign_mode == SignMode::Reserved) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(m_builder.try_append(' ')); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (prefix) { | 
					
						
							|  |  |  |             if (base == 2) { | 
					
						
							|  |  |  |                 if (upper_case) | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |                     TRY(m_builder.try_append("0B"sv)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |                 else | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |                     TRY(m_builder.try_append("0b"sv)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             } else if (base == 8) { | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |                 TRY(m_builder.try_append("0"sv)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             } else if (base == 16) { | 
					
						
							|  |  |  |                 if (upper_case) | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |                     TRY(m_builder.try_append("0X"sv)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |                 else | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |                     TRY(m_builder.try_append("0x"sv)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const put_digits = [&]() -> ErrorOr<void> { | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         for (size_t i = 0; i < used_by_digits; ++i) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(m_builder.try_append(buffer[i])); | 
					
						
							|  |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (align == Align::Left) { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |         auto const used_by_right_padding = used_by_padding; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(put_prefix()); | 
					
						
							|  |  |  |         TRY(put_digits()); | 
					
						
							|  |  |  |         TRY(put_padding(fill, used_by_right_padding)); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     } else if (align == Align::Center) { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |         auto const used_by_left_padding = used_by_padding / 2; | 
					
						
							|  |  |  |         auto const used_by_right_padding = ceil_div<size_t, size_t>(used_by_padding, 2); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(put_padding(fill, used_by_left_padding)); | 
					
						
							|  |  |  |         TRY(put_prefix()); | 
					
						
							|  |  |  |         TRY(put_digits()); | 
					
						
							|  |  |  |         TRY(put_padding(fill, used_by_right_padding)); | 
					
						
							| 
									
										
										
										
											2020-10-06 13:32:00 +02:00
										 |  |  |     } else if (align == Align::Right) { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |         auto const used_by_left_padding = used_by_padding; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (zero_pad) { | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(put_prefix()); | 
					
						
							|  |  |  |             TRY(put_padding('0', used_by_left_padding)); | 
					
						
							|  |  |  |             TRY(put_digits()); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(put_padding(fill, used_by_left_padding)); | 
					
						
							|  |  |  |             TRY(put_prefix()); | 
					
						
							|  |  |  |             TRY(put_digits()); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | ErrorOr<void> FormatBuilder::put_i64( | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     i64 value, | 
					
						
							|  |  |  |     u8 base, | 
					
						
							|  |  |  |     bool prefix, | 
					
						
							|  |  |  |     bool upper_case, | 
					
						
							|  |  |  |     bool zero_pad, | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     bool use_separator, | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     Align align, | 
					
						
							|  |  |  |     size_t min_width, | 
					
						
							|  |  |  |     char fill, | 
					
						
							|  |  |  |     SignMode sign_mode) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const is_negative = value < 0; | 
					
						
							| 
									
										
										
										
											2023-10-08 17:53:10 +01:00
										 |  |  |     u64 positive_value; | 
					
						
							|  |  |  |     if (value == NumericLimits<i64>::min()) { | 
					
						
							|  |  |  |         positive_value = static_cast<u64>(NumericLimits<i64>::max()) + 1; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         positive_value = is_negative ? -value : value; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 17:53:10 +01:00
										 |  |  |     TRY(put_u64(positive_value, base, prefix, upper_case, zero_pad, use_separator, align, min_width, fill, sign_mode, is_negative)); | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  | ErrorOr<void> FormatBuilder::put_fixed_point( | 
					
						
							| 
									
										
										
										
											2023-02-18 11:31:59 -05:00
										 |  |  |     bool is_negative, | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |     i64 integer_value, | 
					
						
							|  |  |  |     u64 fraction_value, | 
					
						
							|  |  |  |     u64 fraction_one, | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |     size_t precision, | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |     u8 base, | 
					
						
							|  |  |  |     bool upper_case, | 
					
						
							|  |  |  |     bool zero_pad, | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     bool use_separator, | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |     Align align, | 
					
						
							|  |  |  |     size_t min_width, | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |     size_t fraction_max_width, | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |     char fill, | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |     SignMode sign_mode) | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     StringBuilder string_builder; | 
					
						
							|  |  |  |     FormatBuilder format_builder { string_builder }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (is_negative) | 
					
						
							|  |  |  |         integer_value = -integer_value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     TRY(format_builder.put_u64(static_cast<u64>(integer_value), base, false, upper_case, false, use_separator, Align::Right, 0, ' ', sign_mode, is_negative)); | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |     if (fraction_max_width && (zero_pad || fraction_value)) { | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |         // FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good
 | 
					
						
							|  |  |  |         // place to start would be the following video from CppCon 2019:
 | 
					
						
							|  |  |  |         // https://youtu.be/4P_kbF0EbZM (Stephan T. Lavavej “Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss”)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |         if (is_negative && fraction_value) | 
					
						
							|  |  |  |             fraction_value = fraction_one - fraction_value; | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |         TRY(string_builder.try_append('.')); | 
					
						
							| 
									
										
										
										
											2023-01-23 21:28:10 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |         if (base == 10) { | 
					
						
							|  |  |  |             u64 scale = pow<u64>(5, precision); | 
					
						
							|  |  |  |             // FIXME: overflows (not before: fraction_value = (2^precision - 1) and precision >= 20) (use wider integer type)
 | 
					
						
							|  |  |  |             auto fraction = scale * fraction_value; | 
					
						
							|  |  |  |             TRY(format_builder.put_u64(fraction, base, false, upper_case, true, use_separator, Align::Right, precision)); | 
					
						
							| 
									
										
										
										
											2023-08-14 08:58:33 +00:00
										 |  |  |         } else if (base == 16 || base == 8 || base == 2) { | 
					
						
							|  |  |  |             auto bits_per_character = log2(base); | 
					
						
							|  |  |  |             auto fraction = fraction_value << ((bits_per_character - (precision % bits_per_character)) % bits_per_character); | 
					
						
							|  |  |  |             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')); | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2023-01-23 21:28:10 -05:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-23 21:28:10 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |     auto formatted_string = string_builder.string_view(); | 
					
						
							|  |  |  |     if (fraction_max_width && (zero_pad || fraction_value)) { | 
					
						
							|  |  |  |         auto point_index = formatted_string.find('.').value_or(0); | 
					
						
							|  |  |  |         if (!point_index) | 
					
						
							|  |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |         if (auto formatted_length = (formatted_string.length() - point_index - 1); formatted_length > fraction_max_width) { | 
					
						
							|  |  |  |             formatted_string = formatted_string.substring_view(0, 1 + point_index + fraction_max_width); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             string_builder.append_repeated('0', fraction_max_width - formatted_length); | 
					
						
							|  |  |  |             formatted_string = string_builder.string_view(); | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |         if (!zero_pad) | 
					
						
							|  |  |  |             formatted_string = formatted_string.trim("0"sv, TrimMode::Right); | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |         if (formatted_string.ends_with('.')) | 
					
						
							|  |  |  |             formatted_string = formatted_string.trim("."sv, TrimMode::Right); | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-13 15:34:00 +00:00
										 |  |  |     TRY(put_string(formatted_string, align, min_width, NumericLimits<size_t>::max(), fill)); | 
					
						
							| 
									
										
										
										
											2021-12-27 18:23:04 -07:00
										 |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  | static ErrorOr<void> round_up_digits(StringBuilder& digits_builder) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto digits_buffer = TRY(digits_builder.to_byte_buffer()); | 
					
						
							|  |  |  |     int current_position = digits_buffer.size() - 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (current_position >= 0) { | 
					
						
							|  |  |  |         if (digits_buffer[current_position] == '.') { | 
					
						
							|  |  |  |             --current_position; | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         ++digits_buffer[current_position]; | 
					
						
							|  |  |  |         if (digits_buffer[current_position] <= '9') | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         digits_buffer[current_position] = '0'; | 
					
						
							|  |  |  |         --current_position; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     digits_builder.clear(); | 
					
						
							|  |  |  |     if (current_position < 0) | 
					
						
							|  |  |  |         TRY(digits_builder.try_append('1')); | 
					
						
							|  |  |  |     return digits_builder.try_append(digits_buffer); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  | ErrorOr<void> FormatBuilder::put_f64_with_precision( | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     double value, | 
					
						
							|  |  |  |     u8 base, | 
					
						
							|  |  |  |     bool upper_case, | 
					
						
							| 
									
										
										
										
											2021-06-19 17:00:31 +03:00
										 |  |  |     bool zero_pad, | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     bool use_separator, | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     Align align, | 
					
						
							|  |  |  |     size_t min_width, | 
					
						
							|  |  |  |     size_t precision, | 
					
						
							|  |  |  |     char fill, | 
					
						
							| 
									
										
										
										
											2022-12-18 01:32:20 +01:00
										 |  |  |     SignMode sign_mode, | 
					
						
							|  |  |  |     RealNumberDisplayMode display_mode) | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     StringBuilder string_builder; | 
					
						
							|  |  |  |     FormatBuilder format_builder { string_builder }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-15 13:28:02 -07:00
										 |  |  |     bool is_negative = value < 0.0; | 
					
						
							|  |  |  |     if (is_negative) | 
					
						
							|  |  |  |         value = -value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     TRY(format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, false, use_separator, Align::Right, 0, ' ', sign_mode, is_negative)); | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |     value -= static_cast<i64>(value); | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-18 15:56:10 +02:00
										 |  |  |     bool did_emit_decimals = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     if (precision > 0) { | 
					
						
							|  |  |  |         // FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good
 | 
					
						
							|  |  |  |         // place to start would be the following video from CppCon 2019:
 | 
					
						
							|  |  |  |         // https://youtu.be/4P_kbF0EbZM (Stephan T. Lavavej “Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss”)
 | 
					
						
							| 
									
										
										
										
											2021-04-08 18:50:56 +02:00
										 |  |  |         double epsilon = 0.5; | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |         if (!zero_pad && display_mode != RealNumberDisplayMode::FixedPoint) { | 
					
						
							|  |  |  |             for (size_t i = 0; i < precision; ++i) | 
					
						
							|  |  |  |                 epsilon /= 10.0; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |         for (size_t digit = 0; digit < precision; ++digit) { | 
					
						
							|  |  |  |             if (!zero_pad && display_mode != RealNumberDisplayMode::FixedPoint && value - static_cast<i64>(value) < epsilon) | 
					
						
							| 
									
										
										
										
											2021-04-08 18:50:56 +02:00
										 |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-08 18:50:56 +02:00
										 |  |  |             value *= 10.0; | 
					
						
							|  |  |  |             epsilon *= 10.0; | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |             if (value > NumericLimits<u32>::max()) | 
					
						
							|  |  |  |                 value -= static_cast<u64>(value) - (static_cast<u64>(value) % 10); | 
					
						
							| 
									
										
										
										
											2021-06-19 17:00:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |             if (digit == 0) | 
					
						
							|  |  |  |                 TRY(string_builder.try_append('.')); | 
					
						
							| 
									
										
										
										
											2021-06-19 17:00:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |             TRY(string_builder.try_append('0' + (static_cast<u32>(value) % 10))); | 
					
						
							| 
									
										
										
										
											2025-05-18 15:56:10 +02:00
										 |  |  |             did_emit_decimals = true; | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |     // Round up if the following decimal is 5 or higher
 | 
					
						
							|  |  |  |     if (static_cast<u64>(value * 10.0) % 10 >= 5) | 
					
						
							|  |  |  |         TRY(round_up_digits(string_builder)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-18 15:56:10 +02:00
										 |  |  |     if (did_emit_decimals && display_mode == RealNumberDisplayMode::Default) { | 
					
						
							|  |  |  |         while (!string_builder.is_empty()) { | 
					
						
							|  |  |  |             // Strip trailing zero decimals.
 | 
					
						
							|  |  |  |             if (string_builder.string_view().ends_with('0')) { | 
					
						
							|  |  |  |                 string_builder.trim(1); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // Strip trailing decimal point.
 | 
					
						
							|  |  |  |             if (string_builder.string_view().ends_with('.')) { | 
					
						
							|  |  |  |                 string_builder.trim(1); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |     return put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  | template<OneOf<f32, f64> T> | 
					
						
							|  |  |  | ErrorOr<void> FormatBuilder::put_f32_or_f64( | 
					
						
							|  |  |  |     T value, | 
					
						
							|  |  |  |     u8 base, | 
					
						
							|  |  |  |     bool upper_case, | 
					
						
							|  |  |  |     bool zero_pad, | 
					
						
							|  |  |  |     bool use_separator, | 
					
						
							|  |  |  |     Align align, | 
					
						
							|  |  |  |     size_t min_width, | 
					
						
							|  |  |  |     Optional<size_t> precision, | 
					
						
							|  |  |  |     char fill, | 
					
						
							|  |  |  |     SignMode sign_mode, | 
					
						
							|  |  |  |     RealNumberDisplayMode display_mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Special cases: NaN, inf, -inf, 0 and -0.
 | 
					
						
							|  |  |  |     auto const is_nan = isnan(value); | 
					
						
							|  |  |  |     auto const is_inf = isinf(value); | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (is_nan || is_inf) { | 
					
						
							|  |  |  |         StringBuilder special_case_builder; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |         if (value < 0) | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  |             TRY(special_case_builder.try_append('-')); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |         else if (sign_mode == SignMode::Always) | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  |             TRY(special_case_builder.try_append('+')); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |         else if (sign_mode == SignMode::Reserved) | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  |             TRY(special_case_builder.try_append(' ')); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  | 
 | 
					
						
							|  |  |  |         if (is_nan) | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  |             TRY(special_case_builder.try_append(upper_case ? "NAN"sv : "nan"sv)); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |         else if (is_inf) | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  |             TRY(special_case_builder.try_append(upper_case ? "INF"sv : "inf"sv)); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  |         return put_string(special_case_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-06 02:23:33 +12:00
										 |  |  |     auto const [sign, mantissa, exponent] = convert_floating_point_to_decimal_exponential_form(value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto convert_to_decimal_digits_array = [](auto x, auto& digits) -> size_t { | 
					
						
							|  |  |  |         size_t length = 0; | 
					
						
							|  |  |  |         for (; x; x /= 10) | 
					
						
							|  |  |  |             digits[length++] = x % 10 | '0'; | 
					
						
							|  |  |  |         for (size_t i = 0; 2 * i + 1 < length; ++i) | 
					
						
							|  |  |  |             swap(digits[i], digits[length - i - 1]); | 
					
						
							|  |  |  |         return length; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Array<u8, 20> mantissa_digits; | 
					
						
							|  |  |  |     auto mantissa_length = convert_to_decimal_digits_array(mantissa, mantissa_digits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto const n = exponent + static_cast<i32>(mantissa_length); | 
					
						
							|  |  |  |     auto mantissa_text = StringView { mantissa_digits.span().slice(0, mantissa_length) }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // NOTE: Range from ECMA262, seems like an okay default.
 | 
					
						
							|  |  |  |     if (n < -5 || n > 21) { | 
					
						
							|  |  |  |         StringBuilder scientific_notation_builder; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (sign) | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append('-')); | 
					
						
							|  |  |  |         else if (sign_mode == SignMode::Always) | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append('+')); | 
					
						
							|  |  |  |         else if (sign_mode == SignMode::Reserved) | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(' ')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto const exponent_sign = n < 0 ? '-' : '+'; | 
					
						
							|  |  |  |         Array<u8, 5> exponent_digits; | 
					
						
							|  |  |  |         auto const exponent_length = convert_to_decimal_digits_array(abs(n - 1), exponent_digits); | 
					
						
							|  |  |  |         auto const exponent_text = StringView { exponent_digits.span().slice(0, exponent_length) }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (precision.has_value()) | 
					
						
							|  |  |  |             mantissa_text = mantissa_text.substring_view(0, min(*precision + 1, mantissa_text.length())).trim("0"sv, TrimMode::Right); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (mantissa_text.length() == 1) { | 
					
						
							|  |  |  |             // <mantissa>e<exponent>
 | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(mantissa_text)); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append('e')); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(exponent_sign)); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(exponent_text)); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             // <mantissa>.<mantissa[1..]>e<exponent>
 | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(mantissa_text.substring_view(0, 1))); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append('.')); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(mantissa_text.substring_view(1))); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append('e')); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(exponent_sign)); | 
					
						
							|  |  |  |             TRY(scientific_notation_builder.try_append(exponent_text)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return put_string(scientific_notation_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-06 00:35:45 +12:00
										 |  |  |     if (precision.has_value() || base != 10) | 
					
						
							|  |  |  |         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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (value == static_cast<T>(0.0)) { | 
					
						
							|  |  |  |         StringBuilder zero_builder; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (value < 0) | 
					
						
							|  |  |  |             TRY(zero_builder.try_append('-')); | 
					
						
							|  |  |  |         else if (sign_mode == SignMode::Always) | 
					
						
							|  |  |  |             TRY(zero_builder.try_append('+')); | 
					
						
							|  |  |  |         else if (sign_mode == SignMode::Reserved) | 
					
						
							|  |  |  |             TRY(zero_builder.try_append(' ')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         TRY(zero_builder.try_append('0')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return put_string(zero_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // No precision specified, so pick the best precision with roundtrip guarantees.
 | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |     if (sign) | 
					
						
							|  |  |  |         TRY(builder.try_append('-')); | 
					
						
							|  |  |  |     else if (sign_mode == SignMode::Always) | 
					
						
							|  |  |  |         TRY(builder.try_append('+')); | 
					
						
							|  |  |  |     else if (sign_mode == SignMode::Reserved) | 
					
						
							|  |  |  |         TRY(builder.try_append(' ')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     size_t integral_part_end = 0; | 
					
						
							| 
									
										
										
										
											2025-08-06 02:23:33 +12:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (exponent >= 0) { | 
					
						
							|  |  |  |         TRY(builder.try_append(mantissa_text)); | 
					
						
							|  |  |  |         TRY(builder.try_append_repeated('0', exponent)); | 
					
						
							|  |  |  |         integral_part_end = builder.length(); | 
					
						
							|  |  |  |     } else if (n > 0) { | 
					
						
							|  |  |  |         TRY(builder.try_append(mantissa_text.substring_view(0, n))); | 
					
						
							|  |  |  |         integral_part_end = builder.length(); | 
					
						
							|  |  |  |         TRY(builder.try_append('.')); | 
					
						
							|  |  |  |         TRY(builder.try_append(mantissa_text.substring_view(n))); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2025-08-06 02:23:33 +12:00
										 |  |  |         TRY(builder.try_append("0."sv)); | 
					
						
							|  |  |  |         TRY(builder.try_append_repeated('0', -n)); | 
					
						
							|  |  |  |         TRY(builder.try_append(mantissa_text)); | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |         integral_part_end = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (use_separator && integral_part_end > 3) { | 
					
						
							|  |  |  |         // Go backwards from the end of the integral part, inserting commas every 3 consecutive digits.
 | 
					
						
							|  |  |  |         StringBuilder separated_builder; | 
					
						
							|  |  |  |         auto const string_view = builder.string_view(); | 
					
						
							|  |  |  |         for (size_t i = 0; i < integral_part_end; ++i) { | 
					
						
							|  |  |  |             auto const index_from_end = integral_part_end - i - 1; | 
					
						
							|  |  |  |             if (index_from_end > 0 && index_from_end != integral_part_end - 1 && index_from_end % 3 == 2) | 
					
						
							|  |  |  |                 TRY(separated_builder.try_append(',')); | 
					
						
							|  |  |  |             TRY(separated_builder.try_append(string_view[i])); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         TRY(separated_builder.try_append(string_view.substring_view(integral_part_end))); | 
					
						
							|  |  |  |         builder = move(separated_builder); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return put_string(builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> FormatBuilder::put_f80( | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  |     long double value, | 
					
						
							|  |  |  |     u8 base, | 
					
						
							|  |  |  |     bool upper_case, | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     bool use_separator, | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  |     Align align, | 
					
						
							|  |  |  |     size_t min_width, | 
					
						
							|  |  |  |     size_t precision, | 
					
						
							|  |  |  |     char fill, | 
					
						
							| 
									
										
										
										
											2022-12-18 01:32:20 +01:00
										 |  |  |     SignMode sign_mode, | 
					
						
							|  |  |  |     RealNumberDisplayMode display_mode) | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     StringBuilder string_builder; | 
					
						
							|  |  |  |     FormatBuilder format_builder { string_builder }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-06 21:47:49 +02:00
										 |  |  |     if (isnan(value) || isinf(value)) [[unlikely]] { | 
					
						
							|  |  |  |         if (value < 0.0l) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(string_builder.try_append('-')); | 
					
						
							| 
									
										
										
										
											2021-08-06 21:47:49 +02:00
										 |  |  |         else if (sign_mode == SignMode::Always) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(string_builder.try_append('+')); | 
					
						
							| 
									
										
										
										
											2021-10-30 10:43:21 +02:00
										 |  |  |         else if (sign_mode == SignMode::Reserved) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(string_builder.try_append(' ')); | 
					
						
							| 
									
										
										
										
											2021-08-06 21:47:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (isnan(value)) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(string_builder.try_append(upper_case ? "NAN"sv : "nan"sv)); | 
					
						
							| 
									
										
										
										
											2021-08-06 21:47:49 +02:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(string_builder.try_append(upper_case ? "INF"sv : "inf"sv)); | 
					
						
							|  |  |  |         TRY(put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill)); | 
					
						
							|  |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2021-08-06 21:47:49 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-05 22:23:22 +02:00
										 |  |  |     bool is_negative = value < 0.0l; | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  |     if (is_negative) | 
					
						
							|  |  |  |         value = -value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     TRY(format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, false, use_separator, Align::Right, 0, ' ', sign_mode, is_negative)); | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |     value -= static_cast<i64>(value); | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (precision > 0) { | 
					
						
							|  |  |  |         // FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good
 | 
					
						
							|  |  |  |         // place to start would be the following video from CppCon 2019:
 | 
					
						
							|  |  |  |         // https://youtu.be/4P_kbF0EbZM (Stephan T. Lavavej “Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss”)
 | 
					
						
							| 
									
										
										
										
											2021-07-05 22:23:22 +02:00
										 |  |  |         long double epsilon = 0.5l; | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |         if (display_mode != RealNumberDisplayMode::FixedPoint) { | 
					
						
							|  |  |  |             for (size_t i = 0; i < precision; ++i) | 
					
						
							|  |  |  |                 epsilon /= 10.0l; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |         for (size_t digit = 0; digit < precision; ++digit) { | 
					
						
							|  |  |  |             if (display_mode != RealNumberDisplayMode::FixedPoint && value - static_cast<i64>(value) < epsilon) | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-05 22:23:22 +02:00
										 |  |  |             value *= 10.0l; | 
					
						
							|  |  |  |             epsilon *= 10.0l; | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |             if (value > NumericLimits<u32>::max()) | 
					
						
							|  |  |  |                 value -= static_cast<u64>(value) - (static_cast<u64>(value) % 10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (digit == 0) | 
					
						
							|  |  |  |                 TRY(string_builder.try_append('.')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             TRY(string_builder.try_append('0' + (static_cast<u32>(value) % 10))); | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 16:58:30 +02:00
										 |  |  |     // Round up if the following decimal is 5 or higher
 | 
					
						
							|  |  |  |     if (static_cast<u64>(value * 10.0l) % 10 >= 5) | 
					
						
							|  |  |  |         TRY(round_up_digits(string_builder)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     TRY(put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill)); | 
					
						
							|  |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> FormatBuilder::put_hexdump(ReadonlyBytes bytes, size_t width, char fill) | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     auto put_char_view = [&](auto i) -> ErrorOr<void> { | 
					
						
							|  |  |  |         TRY(put_padding(fill, 4)); | 
					
						
							| 
									
										
										
										
											2024-01-18 17:53:46 +03:30
										 |  |  |         for (size_t j = i - min(i, width); j < i; ++j) { | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |             auto ch = bytes[j]; | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |             TRY(m_builder.try_append(ch >= 32 && ch <= 127 ? ch : '.')); // silly hack
 | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |     for (size_t i = 0; i < bytes.size(); ++i) { | 
					
						
							|  |  |  |         if (width > 0) { | 
					
						
							|  |  |  |             if (i % width == 0 && i) { | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |                 TRY(put_char_view(i)); | 
					
						
							|  |  |  |                 TRY(put_literal("\n"sv)); | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |         TRY(put_u64(bytes[i], 16, false, false, true, false, Align::Right, 2)); | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-18 17:53:46 +03:30
										 |  |  |     if (width > 0) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         TRY(put_char_view(bytes.size())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> vformat(StringBuilder& builder, StringView fmtstr, TypeErasedFormatParams& params) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     FormatBuilder fmtbuilder { builder }; | 
					
						
							|  |  |  |     FormatParser parser { fmtstr }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     TRY(vformat_impl(params, fmtbuilder, parser)); | 
					
						
							|  |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | void StandardFormatter::parse(TypeErasedFormatParams& params, FormatParser& parser) | 
					
						
							| 
									
										
										
										
											2020-09-23 13:21:18 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |     if ("<^>"sv.contains(parser.peek(1))) { | 
					
						
							|  |  |  |         VERIFY(!parser.next_is(is_any_of("{}"sv))); | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |         m_fill = parser.consume(); | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (parser.consume_specific('<')) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         m_align = FormatBuilder::Align::Left; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('^')) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         m_align = FormatBuilder::Align::Center; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('>')) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         m_align = FormatBuilder::Align::Right; | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (parser.consume_specific('-')) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         m_sign_mode = FormatBuilder::SignMode::OnlyIfNeeded; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('+')) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         m_sign_mode = FormatBuilder::SignMode::Always; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific(' ')) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         m_sign_mode = FormatBuilder::SignMode::Reserved; | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (parser.consume_specific('#')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_alternative_form = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     if (parser.consume_specific('\'')) | 
					
						
							|  |  |  |         m_use_separator = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (parser.consume_specific('0')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_zero_pad = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (size_t index = 0; parser.consume_replacement_field(index)) { | 
					
						
							|  |  |  |         if (index == use_next_index) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |             index = params.take_next_index(); | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |         m_width = params.parameters().at(index).to_size(); | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     } else if (size_t width = 0; parser.consume_number(width)) { | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_width = width; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (parser.consume_specific('.')) { | 
					
						
							|  |  |  |         if (size_t index = 0; parser.consume_replacement_field(index)) { | 
					
						
							|  |  |  |             if (index == use_next_index) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |                 index = params.take_next_index(); | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |             m_precision = params.parameters().at(index).to_size(); | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |         } else if (size_t precision = 0; parser.consume_number(precision)) { | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |             m_precision = precision; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (parser.consume_specific('b')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_mode = Mode::Binary; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('B')) | 
					
						
							| 
									
										
										
										
											2020-09-26 15:26:14 +02:00
										 |  |  |         m_mode = Mode::BinaryUppercase; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('d')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_mode = Mode::Decimal; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('o')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_mode = Mode::Octal; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('x')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_mode = Mode::Hexadecimal; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('X')) | 
					
						
							| 
									
										
										
										
											2020-09-26 15:26:14 +02:00
										 |  |  |         m_mode = Mode::HexadecimalUppercase; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('c')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_mode = Mode::Character; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('s')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_mode = Mode::String; | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     else if (parser.consume_specific('p')) | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         m_mode = Mode::Pointer; | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     else if (parser.consume_specific('f')) | 
					
						
							| 
									
										
										
										
											2022-12-18 01:04:18 +01:00
										 |  |  |         m_mode = Mode::FixedPoint; | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     else if (parser.consume_specific('a')) | 
					
						
							|  |  |  |         m_mode = Mode::Hexfloat; | 
					
						
							|  |  |  |     else if (parser.consume_specific('A')) | 
					
						
							|  |  |  |         m_mode = Mode::HexfloatUppercase; | 
					
						
							| 
									
										
										
										
											2023-10-10 12:42:20 +02:00
										 |  |  |     else if (parser.consume_specific("hex-dump"sv)) | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |         m_mode = Mode::HexDump; | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-28 09:19:36 +02:00
										 |  |  |     if (!parser.is_eof()) | 
					
						
							| 
									
										
										
										
											2020-10-07 14:02:42 +02:00
										 |  |  |         dbgln("{} did not consume '{}'", __PRETTY_FUNCTION__, parser.remaining()); | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(parser.is_eof()); | 
					
						
							| 
									
										
										
										
											2020-09-23 13:21:18 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> Formatter<StringView>::format(FormatBuilder& builder, StringView value) | 
					
						
							| 
									
										
										
										
											2020-09-23 13:21:18 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |     if (m_sign_mode != FormatBuilder::SignMode::Default) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |     if (m_zero_pad) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |     if (m_mode != Mode::Default && m_mode != Mode::String && m_mode != Mode::Character && m_mode != Mode::HexDump) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-09-29 13:55:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |     m_width = m_width.value_or(0); | 
					
						
							|  |  |  |     m_precision = m_precision.value_or(NumericLimits<size_t>::max()); | 
					
						
							| 
									
										
										
										
											2020-09-29 13:55:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |     if (m_mode == Mode::HexDump) | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         return builder.put_hexdump(value.bytes(), m_width.value(), m_fill); | 
					
						
							|  |  |  |     return builder.put_string(value, m_align, m_width.value(), m_precision.value(), m_fill); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-09-29 13:55:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> Formatter<FormatString>::vformat(FormatBuilder& builder, StringView fmtstr, TypeErasedFormatParams& params) | 
					
						
							| 
									
										
										
										
											2021-01-09 01:00:22 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-10-21 19:09:38 +02:00
										 |  |  |     StringBuilder string_builder; | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     TRY(AK::vformat(string_builder, fmtstr, params)); | 
					
						
							|  |  |  |     TRY(Formatter<StringView>::format(builder, string_builder.string_view())); | 
					
						
							|  |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2021-01-09 01:00:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-17 11:29:46 -06:00
										 |  |  | template<Integral T> | 
					
						
							|  |  |  | ErrorOr<void> Formatter<T>::format(FormatBuilder& builder, T value) | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (m_mode == Mode::Character) { | 
					
						
							|  |  |  |         // FIXME: We just support ASCII for now, in the future maybe unicode?
 | 
					
						
							| 
									
										
										
										
											2022-12-10 19:33:56 +03:30
										 |  |  |         //        VERIFY(value >= 0 && value <= 127);
 | 
					
						
							| 
									
										
										
										
											2020-09-29 13:55:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         m_mode = Mode::String; | 
					
						
							| 
									
										
										
										
											2020-09-29 13:55:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         Formatter<StringView> formatter { *this }; | 
					
						
							| 
									
										
										
										
											2024-07-04 11:18:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // convert value to single byte, important for big-endian because the LSB is the last byte.
 | 
					
						
							|  |  |  |         VERIFY(value >= 0 && value <= 127); | 
					
						
							|  |  |  |         char const c = (value & 0x7f); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return formatter.format(builder, StringView { &c, 1 }); | 
					
						
							| 
									
										
										
										
											2020-09-29 13:55:58 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |     if (m_precision.has_value()) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 14:38:47 +02:00
										 |  |  |     if (m_mode == Mode::Pointer) { | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         if (m_sign_mode != FormatBuilder::SignMode::Default) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-10-02 15:21:30 +02:00
										 |  |  |         if (m_align != FormatBuilder::Align::Default) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-09-30 14:38:47 +02:00
										 |  |  |         if (m_alternative_form) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |         if (m_width.has_value()) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-09-30 14:38:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         m_mode = Mode::Hexadecimal; | 
					
						
							|  |  |  |         m_alternative_form = true; | 
					
						
							| 
									
										
										
										
											2020-10-06 13:32:00 +02:00
										 |  |  |         m_width = 2 * sizeof(void*); | 
					
						
							| 
									
										
										
										
											2020-09-30 14:38:47 +02:00
										 |  |  |         m_zero_pad = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-29 14:07:39 +02:00
										 |  |  |     u8 base = 0; | 
					
						
							| 
									
										
										
										
											2020-09-26 15:26:14 +02:00
										 |  |  |     bool upper_case = false; | 
					
						
							|  |  |  |     if (m_mode == Mode::Binary) { | 
					
						
							|  |  |  |         base = 2; | 
					
						
							|  |  |  |     } else if (m_mode == Mode::BinaryUppercase) { | 
					
						
							|  |  |  |         base = 2; | 
					
						
							|  |  |  |         upper_case = true; | 
					
						
							|  |  |  |     } else if (m_mode == Mode::Octal) { | 
					
						
							|  |  |  |         base = 8; | 
					
						
							|  |  |  |     } else if (m_mode == Mode::Decimal || m_mode == Mode::Default) { | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         base = 10; | 
					
						
							| 
									
										
										
										
											2020-09-26 15:26:14 +02:00
										 |  |  |     } else if (m_mode == Mode::Hexadecimal) { | 
					
						
							| 
									
										
										
										
											2020-09-25 13:11:29 +02:00
										 |  |  |         base = 16; | 
					
						
							| 
									
										
										
										
											2020-09-26 15:26:14 +02:00
										 |  |  |     } else if (m_mode == Mode::HexadecimalUppercase) { | 
					
						
							|  |  |  |         base = 16; | 
					
						
							|  |  |  |         upper_case = true; | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |     } else if (m_mode == Mode::HexDump) { | 
					
						
							|  |  |  |         m_width = m_width.value_or(32); | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |         return builder.put_hexdump({ &value, sizeof(value) }, m_width.value(), m_fill); | 
					
						
							| 
									
										
										
										
											2020-09-26 15:26:14 +02:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-09-26 15:26:14 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |     m_width = m_width.value_or(0); | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-10 18:29:06 +04:30
										 |  |  |     if constexpr (IsSame<MakeUnsigned<T>, T>) | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |         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); | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |         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); | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> Formatter<char>::format(FormatBuilder& builder, char value) | 
					
						
							| 
									
										
										
										
											2020-10-07 14:25:19 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     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) { | 
					
						
							|  |  |  |         // Trick: signed char != char. (Sometimes weird features are actually helpful.)
 | 
					
						
							|  |  |  |         Formatter<signed char> formatter { *this }; | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |         return formatter.format(builder, static_cast<signed char>(value)); | 
					
						
							| 
									
										
										
										
											2020-10-07 14:25:19 +02:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         Formatter<StringView> formatter { *this }; | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |         return formatter.format(builder, { &value, 1 }); | 
					
						
							| 
									
										
										
										
											2020-10-07 14:25:19 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-06-13 10:15:08 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | ErrorOr<void> Formatter<char16_t>::format(FormatBuilder& builder, char16_t value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     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) { | 
					
						
							|  |  |  |         Formatter<u16> formatter { *this }; | 
					
						
							|  |  |  |         return formatter.format(builder, value); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         StringBuilder codepoint; | 
					
						
							|  |  |  |         codepoint.append_code_point(value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Formatter<StringView> formatter { *this }; | 
					
						
							|  |  |  |         return formatter.format(builder, codepoint.string_view()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-18 16:05:41 -07:00
										 |  |  | ErrorOr<void> Formatter<char32_t>::format(FormatBuilder& builder, char32_t value) | 
					
						
							| 
									
										
										
										
											2021-09-21 23:07:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     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) { | 
					
						
							|  |  |  |         Formatter<u32> formatter { *this }; | 
					
						
							| 
									
										
										
										
											2025-05-18 16:05:41 -07:00
										 |  |  |         return formatter.format(builder, value); | 
					
						
							| 
									
										
										
										
											2021-09-21 23:07:03 +02:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         StringBuilder codepoint; | 
					
						
							|  |  |  |         codepoint.append_code_point(value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Formatter<StringView> formatter { *this }; | 
					
						
							| 
									
										
										
										
											2022-02-16 00:03:54 +02:00
										 |  |  |         return formatter.format(builder, codepoint.string_view()); | 
					
						
							| 
									
										
										
										
											2021-09-21 23:07:03 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-06-13 10:15:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> Formatter<bool>::format(FormatBuilder& builder, bool value) | 
					
						
							| 
									
										
										
										
											2020-09-30 13:26:24 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     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) { | 
					
						
							|  |  |  |         Formatter<u8> formatter { *this }; | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |         return formatter.format(builder, static_cast<u8>(value)); | 
					
						
							| 
									
										
										
										
											2021-06-17 13:11:00 +04:30
										 |  |  |     } else if (m_mode == Mode::HexDump) { | 
					
						
							|  |  |  |         return builder.put_hexdump({ &value, sizeof(value) }, m_width.value_or(32), m_fill); | 
					
						
							| 
									
										
										
										
											2020-09-30 13:26:24 +02:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         Formatter<StringView> formatter { *this }; | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |         return formatter.format(builder, value ? "true"sv : "false"sv); | 
					
						
							| 
									
										
										
										
											2020-09-30 13:26:24 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-06-13 10:15:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> Formatter<long double>::format(FormatBuilder& builder, long double value) | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     u8 base; | 
					
						
							|  |  |  |     bool upper_case; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:32:20 +01:00
										 |  |  |     FormatBuilder::RealNumberDisplayMode real_number_display_mode = FormatBuilder::RealNumberDisplayMode::General; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:04:18 +01:00
										 |  |  |     if (m_mode == Mode::Default || m_mode == Mode::FixedPoint) { | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  |         base = 10; | 
					
						
							|  |  |  |         upper_case = false; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:32:20 +01:00
										 |  |  |         if (m_mode == Mode::FixedPoint) | 
					
						
							|  |  |  |             real_number_display_mode = FormatBuilder::RealNumberDisplayMode::FixedPoint; | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  |     } else if (m_mode == Mode::Hexfloat) { | 
					
						
							|  |  |  |         base = 16; | 
					
						
							|  |  |  |         upper_case = false; | 
					
						
							|  |  |  |     } else if (m_mode == Mode::HexfloatUppercase) { | 
					
						
							|  |  |  |         base = 16; | 
					
						
							|  |  |  |         upper_case = true; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_width = m_width.value_or(0); | 
					
						
							|  |  |  |     m_precision = m_precision.value_or(6); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:31:39 +00:00
										 |  |  |     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); | 
					
						
							| 
									
										
										
										
											2021-05-07 11:32:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-09 14:25:05 -06:00
										 |  |  | ErrorOr<void> Formatter<f16>::format(FormatBuilder& builder, f16 value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: Create a proper put_f16() implementation
 | 
					
						
							|  |  |  |     Formatter<double> formatter { *this }; | 
					
						
							|  |  |  |     return TRY(formatter.format(builder, static_cast<double>(value))); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | ErrorOr<void> Formatter<double>::format(FormatBuilder& builder, double value) | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     u8 base; | 
					
						
							|  |  |  |     bool upper_case; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:32:20 +01:00
										 |  |  |     FormatBuilder::RealNumberDisplayMode real_number_display_mode = FormatBuilder::RealNumberDisplayMode::General; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:04:18 +01:00
										 |  |  |     if (m_mode == Mode::Default || m_mode == Mode::FixedPoint) { | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |         base = 10; | 
					
						
							|  |  |  |         upper_case = false; | 
					
						
							| 
									
										
										
										
											2022-12-18 01:32:20 +01:00
										 |  |  |         if (m_mode == Mode::FixedPoint) | 
					
						
							|  |  |  |             real_number_display_mode = FormatBuilder::RealNumberDisplayMode::FixedPoint; | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     } else if (m_mode == Mode::Hexfloat) { | 
					
						
							|  |  |  |         base = 16; | 
					
						
							|  |  |  |         upper_case = false; | 
					
						
							|  |  |  |     } else if (m_mode == Mode::HexfloatUppercase) { | 
					
						
							|  |  |  |         base = 16; | 
					
						
							|  |  |  |         upper_case = true; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-30 12:14:15 +01:00
										 |  |  |     m_width = m_width.value_or(0); | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |     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); | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | ErrorOr<void> Formatter<float>::format(FormatBuilder& builder, float value) | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-10-30 23:31:47 +03:30
										 |  |  |     u8 base; | 
					
						
							|  |  |  |     bool upper_case; | 
					
						
							|  |  |  |     FormatBuilder::RealNumberDisplayMode real_number_display_mode = FormatBuilder::RealNumberDisplayMode::General; | 
					
						
							|  |  |  |     if (m_mode == Mode::Default || m_mode == Mode::FixedPoint) { | 
					
						
							|  |  |  |         base = 10; | 
					
						
							|  |  |  |         upper_case = false; | 
					
						
							|  |  |  |         if (m_mode == Mode::FixedPoint) | 
					
						
							|  |  |  |             real_number_display_mode = FormatBuilder::RealNumberDisplayMode::FixedPoint; | 
					
						
							|  |  |  |     } else if (m_mode == Mode::Hexfloat) { | 
					
						
							|  |  |  |         base = 16; | 
					
						
							|  |  |  |         upper_case = false; | 
					
						
							|  |  |  |     } else if (m_mode == Mode::HexfloatUppercase) { | 
					
						
							|  |  |  |         base = 16; | 
					
						
							|  |  |  |         upper_case = true; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_width = m_width.value_or(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); | 
					
						
							| 
									
										
										
										
											2020-11-09 11:34:44 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-10-31 11:31:17 +03:30
										 |  |  | 
 | 
					
						
							|  |  |  | template ErrorOr<void> FormatBuilder::put_f32_or_f64<float>(float, u8, bool, bool, bool, Align, size_t, Optional<size_t>, char, SignMode, RealNumberDisplayMode); | 
					
						
							|  |  |  | template ErrorOr<void> FormatBuilder::put_f32_or_f64<double>(double, u8, bool, bool, bool, Align, size_t, Optional<size_t>, char, SignMode, RealNumberDisplayMode); | 
					
						
							| 
									
										
										
										
											2020-09-30 13:26:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-30 02:58:22 -07:00
										 |  |  | void vout(FILE* file, StringView fmtstr, TypeErasedFormatParams& params, bool newline) | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     MUST(vformat(builder, fmtstr, params)); | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-15 13:46:00 +02:00
										 |  |  |     if (newline) | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  |         builder.append('\n'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const string = builder.string_view(); | 
					
						
							|  |  |  |     auto const retval = ::fwrite(string.characters_without_null_termination(), 1, string.length(), file); | 
					
						
							| 
									
										
										
										
											2021-05-19 10:18:06 +04:30
										 |  |  |     if (static_cast<size_t>(retval) != string.length()) { | 
					
						
							|  |  |  |         auto error = ferror(file); | 
					
						
							|  |  |  |         dbgln("vout() failed ({} written out of {}), error was {} ({})", retval, string.length(), error, strerror(error)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-04-04 09:08:13 +02:00
										 |  |  | #if defined(AK_OS_WINDOWS)
 | 
					
						
							|  |  |  | ErrorOr<void> Formatter<Error>::format_windows_error(FormatBuilder& builder, Error const& error) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     thread_local HashMap<u32, ByteString> windows_errors; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int code = error.code(); | 
					
						
							|  |  |  |     Optional<ByteString&> string = windows_errors.get(static_cast<u32>(code)); | 
					
						
							|  |  |  |     if (string.has_value()) { | 
					
						
							|  |  |  |         return Formatter<StringView>::format(builder, string->view()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TCHAR* message = nullptr; | 
					
						
							|  |  |  |     u32 size = FormatMessage( | 
					
						
							|  |  |  |         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | 
					
						
							|  |  |  |         nullptr, | 
					
						
							|  |  |  |         static_cast<DWORD>(code), | 
					
						
							|  |  |  |         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | 
					
						
							|  |  |  |         message, | 
					
						
							|  |  |  |         0, | 
					
						
							|  |  |  |         nullptr); | 
					
						
							|  |  |  |     if (size == 0) { | 
					
						
							|  |  |  |         auto format_error = GetLastError(); | 
					
						
							|  |  |  |         return Formatter<FormatString>::format(builder, "Error {:08x} when formatting code {:08x}"sv, format_error, code); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto& string_in_map = windows_errors.ensure(code, [message, size] { return ByteString { message, size }; }); | 
					
						
							|  |  |  |     LocalFree(message); | 
					
						
							|  |  |  |     return Formatter<StringView>::format(builder, string_in_map.view()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | ErrorOr<void> Formatter<Error>::format_windows_error(FormatBuilder&, Error const&) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ErrorOr<void> Formatter<Error>::format(FormatBuilder& builder, Error const& error) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     switch (error.kind()) { | 
					
						
							|  |  |  |     case Error::Kind::Syscall: | 
					
						
							|  |  |  |         return Formatter<FormatString>::format(builder, "{}: {} (errno={})"sv, error.string_literal(), strerror(error.code()), error.code()); | 
					
						
							|  |  |  |     case Error::Kind::Errno: | 
					
						
							|  |  |  |         return Formatter<FormatString>::format(builder, "{} (errno={})"sv, strerror(error.code()), error.code()); | 
					
						
							|  |  |  |     case Error::Kind::Windows: | 
					
						
							|  |  |  |         return Formatter<Error>::format_windows_error(builder, error); | 
					
						
							|  |  |  |     case Error::Kind::StringLiteral: | 
					
						
							|  |  |  |         return Formatter<FormatString>::format(builder, "{}"sv, error.string_literal()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-13 23:19:25 -06:00
										 |  |  | #ifdef AK_OS_ANDROID
 | 
					
						
							|  |  |  | static char const* s_log_tag_name = "Serenity"; | 
					
						
							|  |  |  | void set_log_tag_name(char const* tag_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static String s_log_tag_storage; | 
					
						
							|  |  |  |     // NOTE: Make sure to copy the null terminator
 | 
					
						
							|  |  |  |     s_log_tag_storage = MUST(String::from_utf8({ tag_name, strlen(tag_name) + 1 })); | 
					
						
							|  |  |  |     s_log_tag_name = s_log_tag_storage.bytes_as_string_view().characters_without_null_termination(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void vout(LogLevel log_level, StringView fmtstr, TypeErasedFormatParams& params, bool newline) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							|  |  |  |     MUST(vformat(builder, fmtstr, params)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (newline) | 
					
						
							|  |  |  |         builder.append('\n'); | 
					
						
							|  |  |  |     builder.append('\0'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto const string = builder.string_view(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto ndk_log_level = ANDROID_LOG_UNKNOWN; | 
					
						
							|  |  |  |     switch (log_level) { | 
					
						
							|  |  |  |     case LogLevel ::Debug: | 
					
						
							|  |  |  |         ndk_log_level = ANDROID_LOG_DEBUG; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case LogLevel ::Info: | 
					
						
							|  |  |  |         ndk_log_level = ANDROID_LOG_INFO; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case LogLevel::Warning: | 
					
						
							|  |  |  |         ndk_log_level = ANDROID_LOG_WARN; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __android_log_write(ndk_log_level, s_log_tag_name, string.characters_without_null_termination()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  | // FIXME: Deduplicate with Core::Process:get_name()
 | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  | [[gnu::used]] static ByteString process_name_helper() | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #if defined(AK_OS_SERENITY)
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |     char buffer[BUFSIZ] = {}; | 
					
						
							|  |  |  |     int rc = get_process_name(buffer, BUFSIZ); | 
					
						
							|  |  |  |     if (rc != 0) | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  |         return ByteString {}; | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |     return StringView { buffer, strlen(buffer) }; | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #elif defined(AK_LIBC_GLIBC) || (defined(AK_OS_LINUX) && !defined(AK_OS_ANDROID))
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |     return StringView { program_invocation_name, strlen(program_invocation_name) }; | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #elif defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU)
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |     auto const* progname = getprogname(); | 
					
						
							|  |  |  |     return StringView { progname, strlen(progname) }; | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  | #elif defined AK_OS_WINDOWS
 | 
					
						
							|  |  |  |     char path[MAX_PATH] = {}; | 
					
						
							|  |  |  |     auto length = GetModuleFileName(NULL, path, MAX_PATH); | 
					
						
							|  |  |  |     return { path, length }; | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |     // FIXME: Implement process_name_helper() for other platforms.
 | 
					
						
							|  |  |  |     return StringView {}; | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static StringView process_name_for_logging() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // NOTE: We use AK::Format in the DynamicLoader and LibC, which cannot use thread-safe statics
 | 
					
						
							|  |  |  |     // Also go to extraordinary lengths here to avoid strlen() on the process name every call to dbgln
 | 
					
						
							|  |  |  |     static char process_name_buf[256] = {}; | 
					
						
							|  |  |  |     static StringView process_name; | 
					
						
							|  |  |  |     static bool process_name_retrieved = false; | 
					
						
							|  |  |  |     if (!process_name_retrieved) { | 
					
						
							|  |  |  |         auto path = LexicalPath(process_name_helper()); | 
					
						
							|  |  |  |         process_name_retrieved = true; | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  |         (void)path.title().copy_characters_to_buffer(process_name_buf, sizeof(process_name_buf)); | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |         process_name = { process_name_buf, strlen(process_name_buf) }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return process_name; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-18 17:37:30 +01:00
										 |  |  | static bool is_debug_enabled = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void set_debug_enabled(bool value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     is_debug_enabled = value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  | // On Serenity, dbgln goes to a non-stderr output
 | 
					
						
							|  |  |  | static bool is_rich_debug_enabled = | 
					
						
							|  |  |  | #if defined(AK_OS_SERENITY)
 | 
					
						
							|  |  |  |     true; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     false; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void set_rich_debug_enabled(bool value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     is_rich_debug_enabled = value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  | #ifdef AK_OS_WINDOWS
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #    define YELLOW(str) "\33[93m" str "\33[0m"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int main_thread_id = GetCurrentThreadId(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-23 19:12:50 -06:00
										 |  |  | static int initialize_console_settings() | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  | { | 
					
						
							|  |  |  |     HANDLE console_handle = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); | 
					
						
							|  |  |  |     if (console_handle == INVALID_HANDLE_VALUE) { | 
					
						
							|  |  |  |         dbgln("Unable to get console handle"); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ScopeGuard guard = [&] { CloseHandle(console_handle); }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     DWORD mode = 0; | 
					
						
							|  |  |  |     if (!GetConsoleMode(console_handle, &mode)) { | 
					
						
							|  |  |  |         dbgln("Unable to get console mode"); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-23 19:12:50 -06:00
										 |  |  |     // Enable Virtual Terminal Processing to allow ANSI escape codes
 | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  |     mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; | 
					
						
							| 
									
										
										
										
											2025-06-26 18:29:30 +02:00
										 |  |  |     mode |= ENABLE_PROCESSED_OUTPUT; | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  |     if (!SetConsoleMode(console_handle, mode)) { | 
					
						
							|  |  |  |         dbgln("Unable to set console mode"); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-23 19:12:50 -06:00
										 |  |  |     // Set the output code page to UTF-8 to support Emoji and other Unicode characters
 | 
					
						
							|  |  |  |     (void)SetConsoleOutputCP(CP_UTF8); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-23 19:12:50 -06:00
										 |  |  | static int dummy = initialize_console_settings(); | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-18 09:49:49 -04:00
										 |  |  | void vdbg(StringView fmtstr, TypeErasedFormatParams& params, bool newline) | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-01-18 17:37:30 +01:00
										 |  |  |     if (!is_debug_enabled) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  |     StringBuilder builder; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |     if (is_rich_debug_enabled) { | 
					
						
							| 
									
										
										
										
											2024-06-17 23:12:53 +01:00
										 |  |  | #ifndef AK_OS_WINDOWS
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |         auto process_name = process_name_for_logging(); | 
					
						
							|  |  |  |         if (!process_name.is_empty()) { | 
					
						
							|  |  |  |             struct timespec ts = {}; | 
					
						
							|  |  |  |             clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); | 
					
						
							|  |  |  |             auto pid = getpid(); | 
					
						
							| 
									
										
										
										
											2023-12-28 11:26:53 +01:00
										 |  |  | #    if defined(AK_OS_SERENITY) || defined(AK_OS_LINUX)
 | 
					
						
							|  |  |  |             // Linux and Serenity handle thread IDs as if they are related to process ids
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |             auto tid = gettid(); | 
					
						
							|  |  |  |             if (pid == tid) | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | #    endif
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |             { | 
					
						
							|  |  |  |                 builder.appendff("{}.{:03} \033[33;1m{}({})\033[0m: ", ts.tv_sec, ts.tv_nsec / 1000000, process_name, pid); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-12-28 11:26:53 +01:00
										 |  |  | #    if defined(AK_OS_SERENITY) || defined(AK_OS_LINUX)
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |             else { | 
					
						
							|  |  |  |                 builder.appendff("{}.{:03} \033[33;1m{}({}:{})\033[0m: ", ts.tv_sec, ts.tv_nsec / 1000000, process_name, pid, tid); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | #    endif
 | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-10-10 21:17:02 -06:00
										 |  |  | #else
 | 
					
						
							|  |  |  |         auto process_name = process_name_for_logging(); | 
					
						
							|  |  |  |         if (!process_name.is_empty()) { | 
					
						
							| 
									
										
										
										
											2025-01-02 19:25:55 +05:00
										 |  |  |             int tid = GetCurrentThreadId(); | 
					
						
							|  |  |  |             if (tid == main_thread_id) | 
					
						
							|  |  |  |                 builder.appendff(YELLOW("{}: "), process_name); | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 builder.appendff(YELLOW("{}:{}: "), process_name, tid); | 
					
						
							| 
									
										
										
										
											2024-10-10 21:17:02 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-12-08 13:52:20 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-16 01:15:21 +01:00
										 |  |  |     MUST(vformat(builder, fmtstr, params)); | 
					
						
							| 
									
										
										
										
											2023-05-18 09:49:49 -04:00
										 |  |  |     if (newline) | 
					
						
							|  |  |  |         builder.append('\n'); | 
					
						
							| 
									
										
										
										
											2023-09-13 23:19:25 -06:00
										 |  |  | #ifdef AK_OS_ANDROID
 | 
					
						
							|  |  |  |     builder.append('\0'); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2022-04-01 20:58:27 +03:00
										 |  |  |     auto const string = builder.string_view(); | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-13 23:19:25 -06:00
										 |  |  | #ifdef AK_OS_ANDROID
 | 
					
						
							|  |  |  |     __android_log_write(ANDROID_LOG_DEBUG, s_log_tag_name, string.characters_without_null_termination()); | 
					
						
							| 
									
										
										
										
											2024-10-02 12:58:01 -07:00
										 |  |  | #elif defined(AK_OS_WINDOWS)
 | 
					
						
							|  |  |  |     [[maybe_unused]] auto rc = _write(_fileno(stderr), string.characters_without_null_termination(), string.length()); | 
					
						
							| 
									
										
										
										
											2023-09-13 23:19:25 -06:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2024-06-18 09:42:01 +02:00
										 |  |  |     [[maybe_unused]] auto rc = write(STDERR_FILENO, string.characters_without_null_termination(), string.length()); | 
					
						
							| 
									
										
										
										
											2023-09-13 23:19:25 -06:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2020-10-04 15:35:43 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-22 13:42:30 +02:00
										 |  |  | template struct Formatter<unsigned char, void>; | 
					
						
							|  |  |  | template struct Formatter<unsigned short, void>; | 
					
						
							|  |  |  | template struct Formatter<unsigned int, void>; | 
					
						
							|  |  |  | template struct Formatter<unsigned long, void>; | 
					
						
							|  |  |  | template struct Formatter<unsigned long long, void>; | 
					
						
							|  |  |  | template struct Formatter<short, void>; | 
					
						
							|  |  |  | template struct Formatter<int, void>; | 
					
						
							|  |  |  | template struct Formatter<long, void>; | 
					
						
							|  |  |  | template struct Formatter<long long, void>; | 
					
						
							|  |  |  | template struct Formatter<signed char, void>; | 
					
						
							| 
									
										
										
										
											2020-09-19 13:47:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | } // namespace AK
 |