mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:18:11 +00:00
AK+Format: Keep type information for integers in TypeErasedParameter.
It's now save to pass a signed integer as parameter and then use it as replacement field (previously, this would just cast it to size_t which would be bad.)
This commit is contained in:
parent
6a2f5f4522
commit
56bfefabb6
3 changed files with 81 additions and 10 deletions
|
@ -168,6 +168,43 @@ void vformat_impl(StringBuilder& builder, FormatStringParser& parser, Span<const
|
||||||
vformat_impl(builder, parser, parameters, argument_index);
|
vformat_impl(builder, parser, parameters, argument_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t decode_value(size_t value, Span<const AK::TypeErasedParameter> 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<i64> svalue;
|
||||||
|
if (parameter.type == AK::TypeErasedParameter::Type::UInt8)
|
||||||
|
value = *reinterpret_cast<const u8*>(parameter.value);
|
||||||
|
else if (parameter.type == AK::TypeErasedParameter::Type::UInt16)
|
||||||
|
value = *reinterpret_cast<const u16*>(parameter.value);
|
||||||
|
else if (parameter.type == AK::TypeErasedParameter::Type::UInt32)
|
||||||
|
value = *reinterpret_cast<const u32*>(parameter.value);
|
||||||
|
else if (parameter.type == AK::TypeErasedParameter::Type::UInt64)
|
||||||
|
value = *reinterpret_cast<const u64*>(parameter.value);
|
||||||
|
else if (parameter.type == AK::TypeErasedParameter::Type::Int8)
|
||||||
|
svalue = *reinterpret_cast<const i8*>(parameter.value);
|
||||||
|
else if (parameter.type == AK::TypeErasedParameter::Type::Int16)
|
||||||
|
svalue = *reinterpret_cast<const i16*>(parameter.value);
|
||||||
|
else if (parameter.type == AK::TypeErasedParameter::Type::Int32)
|
||||||
|
svalue = *reinterpret_cast<const i32*>(parameter.value);
|
||||||
|
else if (parameter.type == AK::TypeErasedParameter::Type::Int64)
|
||||||
|
svalue = *reinterpret_cast<const i64*>(parameter.value);
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
|
||||||
|
if (svalue.has_value()) {
|
||||||
|
ASSERT(svalue.value() >= 0);
|
||||||
|
value = static_cast<size_t>(svalue.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
@ -307,14 +344,7 @@ void Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type>::format(StringB
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t width = m_width;
|
auto width = decode_value(m_width, parameters);
|
||||||
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<const size_t*>(parameter.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintfImplementation::Align align;
|
PrintfImplementation::Align align;
|
||||||
if (m_align == Align::Left)
|
if (m_align == Align::Left)
|
||||||
|
|
42
AK/Format.h
42
AK/Format.h
|
@ -39,7 +39,43 @@ template<typename T, typename = void>
|
||||||
struct Formatter;
|
struct Formatter;
|
||||||
|
|
||||||
struct TypeErasedParameter {
|
struct TypeErasedParameter {
|
||||||
|
enum class Type {
|
||||||
|
UInt8,
|
||||||
|
UInt16,
|
||||||
|
UInt32,
|
||||||
|
UInt64,
|
||||||
|
Int8,
|
||||||
|
Int16,
|
||||||
|
Int32,
|
||||||
|
Int64,
|
||||||
|
Custom
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static Type get_type()
|
||||||
|
{
|
||||||
|
if (IsSame<T, u8>::value)
|
||||||
|
return Type::UInt8;
|
||||||
|
if (IsSame<T, u16>::value)
|
||||||
|
return Type::UInt16;
|
||||||
|
if (IsSame<T, u32>::value)
|
||||||
|
return Type::UInt32;
|
||||||
|
if (IsSame<T, u64>::value)
|
||||||
|
return Type::UInt64;
|
||||||
|
if (IsSame<T, i8>::value)
|
||||||
|
return Type::Int8;
|
||||||
|
if (IsSame<T, i16>::value)
|
||||||
|
return Type::Int16;
|
||||||
|
if (IsSame<T, i32>::value)
|
||||||
|
return Type::Int32;
|
||||||
|
if (IsSame<T, i64>::value)
|
||||||
|
return Type::Int64;
|
||||||
|
|
||||||
|
return Type::Custom;
|
||||||
|
}
|
||||||
|
|
||||||
const void* value;
|
const void* value;
|
||||||
|
Type type;
|
||||||
void (*formatter)(StringBuilder& builder, const void* value, StringView flags, Span<const TypeErasedParameter> parameters);
|
void (*formatter)(StringBuilder& builder, const void* value, StringView flags, Span<const TypeErasedParameter> parameters);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,7 +128,8 @@ struct StandardFormatter {
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr size_t value_not_set = 0;
|
static constexpr size_t value_not_set = 0;
|
||||||
static constexpr size_t value_from_arg = NumericLimits<size_t>::max() - max_format_arguments;
|
static constexpr size_t value_from_next_arg = NumericLimits<size_t>::max();
|
||||||
|
static constexpr size_t value_from_arg = NumericLimits<size_t>::max() - max_format_arguments - 1;
|
||||||
|
|
||||||
Align m_align = Align::Default;
|
Align m_align = Align::Default;
|
||||||
Sign m_sign = Sign::NegativeOnly;
|
Sign m_sign = Sign::NegativeOnly;
|
||||||
|
@ -132,7 +169,8 @@ template<typename... Parameters>
|
||||||
Array<TypeErasedParameter, sizeof...(Parameters)> make_type_erased_parameters(const Parameters&... parameters)
|
Array<TypeErasedParameter, sizeof...(Parameters)> make_type_erased_parameters(const Parameters&... parameters)
|
||||||
{
|
{
|
||||||
static_assert(sizeof...(Parameters) <= max_format_arguments);
|
static_assert(sizeof...(Parameters) <= max_format_arguments);
|
||||||
return { TypeErasedParameter { ¶meters, Detail::Format::format_value<Parameters> }... };
|
|
||||||
|
return { TypeErasedParameter { ¶meters, TypeErasedParameter::get_type<Parameters>(), Detail::Format::format_value<Parameters> }... };
|
||||||
}
|
}
|
||||||
|
|
||||||
void vformat(StringBuilder& builder, StringView fmtstr, Span<const TypeErasedParameter>);
|
void vformat(StringBuilder& builder, StringView fmtstr, Span<const TypeErasedParameter>);
|
||||||
|
|
|
@ -116,6 +116,9 @@ TEST_CASE(zero_pad)
|
||||||
TEST_CASE(replacement_field)
|
TEST_CASE(replacement_field)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(String::formatted("{:*>{1}}", 13, static_cast<size_t>(10)), "********13");
|
EXPECT_EQ(String::formatted("{:*>{1}}", 13, static_cast<size_t>(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)
|
TEST_MAIN(Format)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue