diff --git a/AK/Format.cpp b/AK/Format.cpp index 1144a42065..79333aa907 100644 --- a/AK/Format.cpp +++ b/AK/Format.cpp @@ -168,6 +168,43 @@ void vformat_impl(StringBuilder& builder, FormatStringParser& parser, Span parameters) +{ + if (value == AK::StandardFormatter::value_from_next_arg) + TODO(); + + if (value >= AK::StandardFormatter::value_from_arg) { + const auto parameter = parameters.at(value - AK::StandardFormatter::value_from_arg); + + Optional svalue; + if (parameter.type == AK::TypeErasedParameter::Type::UInt8) + value = *reinterpret_cast(parameter.value); + else if (parameter.type == AK::TypeErasedParameter::Type::UInt16) + value = *reinterpret_cast(parameter.value); + else if (parameter.type == AK::TypeErasedParameter::Type::UInt32) + value = *reinterpret_cast(parameter.value); + else if (parameter.type == AK::TypeErasedParameter::Type::UInt64) + value = *reinterpret_cast(parameter.value); + else if (parameter.type == AK::TypeErasedParameter::Type::Int8) + svalue = *reinterpret_cast(parameter.value); + else if (parameter.type == AK::TypeErasedParameter::Type::Int16) + svalue = *reinterpret_cast(parameter.value); + else if (parameter.type == AK::TypeErasedParameter::Type::Int32) + svalue = *reinterpret_cast(parameter.value); + else if (parameter.type == AK::TypeErasedParameter::Type::Int64) + svalue = *reinterpret_cast(parameter.value); + else + ASSERT_NOT_REACHED(); + + if (svalue.has_value()) { + ASSERT(svalue.value() >= 0); + value = static_cast(svalue.value()); + } + } + + return value; +} + } // namespace namespace AK { @@ -307,14 +344,7 @@ void Formatter::value>::Type>::format(StringB ASSERT_NOT_REACHED(); } - size_t width = m_width; - if (m_width >= value_from_arg) { - const auto parameter = parameters.at(m_width - value_from_arg); - - // FIXME: Totally unsave cast. We should store the type in TypeErasedParameter. For compactness it could be smart to - // find a few addresses that can not be valid function pointers and encode the type information there? - width = *reinterpret_cast(parameter.value); - } + auto width = decode_value(m_width, parameters); PrintfImplementation::Align align; if (m_align == Align::Left) diff --git a/AK/Format.h b/AK/Format.h index 16ce7d5f33..ab99e13b33 100644 --- a/AK/Format.h +++ b/AK/Format.h @@ -39,7 +39,43 @@ template struct Formatter; struct TypeErasedParameter { + enum class Type { + UInt8, + UInt16, + UInt32, + UInt64, + Int8, + Int16, + Int32, + Int64, + Custom + }; + + template + static Type get_type() + { + if (IsSame::value) + return Type::UInt8; + if (IsSame::value) + return Type::UInt16; + if (IsSame::value) + return Type::UInt32; + if (IsSame::value) + return Type::UInt64; + if (IsSame::value) + return Type::Int8; + if (IsSame::value) + return Type::Int16; + if (IsSame::value) + return Type::Int32; + if (IsSame::value) + return Type::Int64; + + return Type::Custom; + } + const void* value; + Type type; void (*formatter)(StringBuilder& builder, const void* value, StringView flags, Span parameters); }; @@ -92,7 +128,8 @@ struct StandardFormatter { }; static constexpr size_t value_not_set = 0; - static constexpr size_t value_from_arg = NumericLimits::max() - max_format_arguments; + static constexpr size_t value_from_next_arg = NumericLimits::max(); + static constexpr size_t value_from_arg = NumericLimits::max() - max_format_arguments - 1; Align m_align = Align::Default; Sign m_sign = Sign::NegativeOnly; @@ -132,7 +169,8 @@ template Array make_type_erased_parameters(const Parameters&... parameters) { static_assert(sizeof...(Parameters) <= max_format_arguments); - return { TypeErasedParameter { ¶meters, Detail::Format::format_value }... }; + + return { TypeErasedParameter { ¶meters, TypeErasedParameter::get_type(), Detail::Format::format_value }... }; } void vformat(StringBuilder& builder, StringView fmtstr, Span); diff --git a/AK/Tests/TestFormat.cpp b/AK/Tests/TestFormat.cpp index 0336db2c0a..86227f8e16 100644 --- a/AK/Tests/TestFormat.cpp +++ b/AK/Tests/TestFormat.cpp @@ -116,6 +116,9 @@ TEST_CASE(zero_pad) TEST_CASE(replacement_field) { EXPECT_EQ(String::formatted("{:*>{1}}", 13, static_cast(10)), "********13"); + EXPECT_EQ(String::formatted("{:*<{1}}", 7, 4), "7***"); + EXPECT_EQ(String::formatted("{:{2}}", -5, 8, 16), " -5"); + EXPECT_EQ(String::formatted("{{{:*^{1}}}}", 1, 3), "{*1*}"); } TEST_MAIN(Format)