mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 13:07:46 +00:00
AK+Format: Do some housekeeping in the format implementation.
This commit is contained in:
parent
ac5e08a541
commit
6351a56d27
13 changed files with 476 additions and 640 deletions
169
AK/Format.h
169
AK/Format.h
|
@ -27,6 +27,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/StringView.h>
|
||||
|
||||
// FIXME: I would really love to merge the format_value and make_type_erased_parameters functions,
|
||||
|
@ -35,9 +36,15 @@
|
|||
|
||||
namespace AK {
|
||||
|
||||
class TypeErasedFormatParams;
|
||||
class FormatParser;
|
||||
class FormatBuilder;
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct Formatter;
|
||||
|
||||
constexpr size_t max_format_arguments = 256;
|
||||
|
||||
struct TypeErasedParameter {
|
||||
enum class Type {
|
||||
UInt8,
|
||||
|
@ -76,65 +83,131 @@ struct TypeErasedParameter {
|
|||
|
||||
const void* value;
|
||||
Type type;
|
||||
void (*formatter)(StringBuilder& builder, const void* value, class FormatterContext&);
|
||||
void (*formatter)(TypeErasedFormatParams&, FormatBuilder&, FormatParser&, const void* value);
|
||||
};
|
||||
|
||||
class FormatterContext {
|
||||
class FormatParser : public GenericLexer {
|
||||
public:
|
||||
FormatterContext(Span<const TypeErasedParameter> parameters)
|
||||
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);
|
||||
};
|
||||
|
||||
class FormatBuilder {
|
||||
public:
|
||||
enum class Align {
|
||||
Default,
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
};
|
||||
enum class SignMode {
|
||||
OnlyIfNeeded,
|
||||
Always,
|
||||
Reserved,
|
||||
Default = OnlyIfNeeded,
|
||||
};
|
||||
|
||||
explicit FormatBuilder(StringBuilder& builder)
|
||||
: m_builder(builder)
|
||||
{
|
||||
}
|
||||
|
||||
void put_padding(char fill, size_t amount);
|
||||
|
||||
void put_literal(StringView value);
|
||||
|
||||
void put_string(
|
||||
StringView value,
|
||||
Align align = Align::Left,
|
||||
size_t min_width = 0,
|
||||
size_t max_width = NumericLimits<size_t>::max(),
|
||||
char fill = ' ');
|
||||
|
||||
void put_u64(
|
||||
u64 value,
|
||||
u8 base = 10,
|
||||
bool prefix = false,
|
||||
bool upper_case = false,
|
||||
bool zero_pad = false,
|
||||
Align align = Align::Right,
|
||||
size_t min_width = 0,
|
||||
char fill = ' ',
|
||||
SignMode sign_mode = SignMode::OnlyIfNeeded,
|
||||
bool is_negative = false);
|
||||
|
||||
void put_i64(
|
||||
i64 value,
|
||||
u8 base = 10,
|
||||
bool prefix = false,
|
||||
bool upper_case = false,
|
||||
bool zero_pad = false,
|
||||
Align align = Align::Right,
|
||||
size_t min_width = 0,
|
||||
char fill = ' ',
|
||||
SignMode sign_mode = SignMode::OnlyIfNeeded);
|
||||
|
||||
const StringBuilder& builder() const { return m_builder; }
|
||||
StringBuilder& builder() { return m_builder; }
|
||||
|
||||
private:
|
||||
StringBuilder& m_builder;
|
||||
};
|
||||
|
||||
class TypeErasedFormatParams {
|
||||
public:
|
||||
explicit TypeErasedFormatParams(Span<const TypeErasedParameter> parameters)
|
||||
: m_parameters(parameters)
|
||||
{
|
||||
}
|
||||
|
||||
const TypeErasedParameter& parameter_at(size_t index) const { return m_parameters.at(index); }
|
||||
size_t parameter_count() const { return m_parameters.size(); }
|
||||
|
||||
StringView flags() const { return m_flags; }
|
||||
void set_flags(StringView value) { m_flags = value; }
|
||||
Span<const TypeErasedParameter> parameters() const { return m_parameters; }
|
||||
|
||||
size_t take_next_index() { return m_next_index++; }
|
||||
|
||||
size_t decode(size_t value, size_t default_value = 0);
|
||||
|
||||
private:
|
||||
Span<const TypeErasedParameter> m_parameters;
|
||||
StringView m_flags;
|
||||
size_t m_next_index { 0 };
|
||||
};
|
||||
|
||||
} // namespace AK
|
||||
|
||||
namespace AK::Detail::Format {
|
||||
|
||||
template<typename T>
|
||||
void format_value(StringBuilder& builder, const void* value, FormatterContext& context)
|
||||
void __format_value(TypeErasedFormatParams& params, FormatBuilder& builder, FormatParser& parser, const void* value)
|
||||
{
|
||||
Formatter<T> formatter;
|
||||
|
||||
formatter.parse(context);
|
||||
formatter.format(builder, *static_cast<const T*>(value), context);
|
||||
formatter.parse(params, parser);
|
||||
formatter.format(params, builder, *static_cast<const T*>(value));
|
||||
}
|
||||
|
||||
} // namespace AK::Detail::Format
|
||||
template<typename... Parameters>
|
||||
class VariadicFormatParams : public TypeErasedFormatParams {
|
||||
public:
|
||||
static_assert(sizeof...(Parameters) <= max_format_arguments);
|
||||
|
||||
namespace AK {
|
||||
explicit VariadicFormatParams(const Parameters&... parameters)
|
||||
: TypeErasedFormatParams(m_data)
|
||||
, m_data({ TypeErasedParameter { ¶meters, TypeErasedParameter::get_type<Parameters>(), __format_value<Parameters> }... })
|
||||
{
|
||||
}
|
||||
|
||||
constexpr size_t max_format_arguments = 256;
|
||||
private:
|
||||
Array<TypeErasedParameter, sizeof...(Parameters)> m_data;
|
||||
};
|
||||
|
||||
// We use the same format for most types for consistency. This is taken directly from std::format.
|
||||
// Not all valid options do anything yet.
|
||||
// https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification
|
||||
struct StandardFormatter {
|
||||
enum class Align {
|
||||
Default,
|
||||
Left,
|
||||
Right,
|
||||
Center,
|
||||
};
|
||||
enum class Sign {
|
||||
NegativeOnly,
|
||||
PositiveAndNegative,
|
||||
ReserveSpace,
|
||||
Default = NegativeOnly
|
||||
};
|
||||
enum class Mode {
|
||||
Default,
|
||||
Binary,
|
||||
|
@ -148,12 +221,12 @@ struct StandardFormatter {
|
|||
Pointer,
|
||||
};
|
||||
|
||||
static constexpr size_t value_not_set = 0;
|
||||
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;
|
||||
static constexpr size_t value_not_set = NumericLimits<size_t>::max();
|
||||
static constexpr size_t value_from_next_arg = NumericLimits<size_t>::max() - 1;
|
||||
static constexpr size_t value_from_arg = NumericLimits<size_t>::max() - max_format_arguments - 2;
|
||||
|
||||
Align m_align = Align::Default;
|
||||
Sign m_sign = Sign::NegativeOnly;
|
||||
FormatBuilder::Align m_align = FormatBuilder::Align::Default;
|
||||
FormatBuilder::SignMode m_sign_mode = FormatBuilder::SignMode::OnlyIfNeeded;
|
||||
Mode m_mode = Mode::Default;
|
||||
bool m_alternative_form = false;
|
||||
char m_fill = ' ';
|
||||
|
@ -161,7 +234,7 @@ struct StandardFormatter {
|
|||
size_t m_width = value_not_set;
|
||||
size_t m_precision = value_not_set;
|
||||
|
||||
void parse(FormatterContext&);
|
||||
void parse(TypeErasedFormatParams&, FormatParser&);
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -172,7 +245,7 @@ struct Formatter<StringView> : StandardFormatter {
|
|||
{
|
||||
}
|
||||
|
||||
void format(StringBuilder& builder, StringView value, FormatterContext&);
|
||||
void format(TypeErasedFormatParams&, FormatBuilder&, StringView value);
|
||||
};
|
||||
template<>
|
||||
struct Formatter<const char*> : Formatter<StringView> {
|
||||
|
@ -195,32 +268,24 @@ struct Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type> : StandardFor
|
|||
{
|
||||
}
|
||||
|
||||
void format(StringBuilder&, T value, FormatterContext&);
|
||||
void format(TypeErasedFormatParams&, FormatBuilder&, T value);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Formatter<T*> : StandardFormatter {
|
||||
void format(StringBuilder& builder, T* value, FormatterContext& context)
|
||||
void format(TypeErasedFormatParams& params, FormatBuilder& builder, T* value)
|
||||
{
|
||||
Formatter<FlatPtr> formatter { *this };
|
||||
formatter.format(builder, reinterpret_cast<FlatPtr>(value), context);
|
||||
formatter.format(params, builder, reinterpret_cast<FlatPtr>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Formatter<bool> : StandardFormatter {
|
||||
void format(StringBuilder&, bool value, FormatterContext&);
|
||||
void format(TypeErasedFormatParams&, FormatBuilder&, bool value);
|
||||
};
|
||||
|
||||
template<typename... Parameters>
|
||||
Array<TypeErasedParameter, sizeof...(Parameters)> make_type_erased_parameters(const Parameters&... parameters)
|
||||
{
|
||||
static_assert(sizeof...(Parameters) <= max_format_arguments);
|
||||
|
||||
return { TypeErasedParameter { ¶meters, TypeErasedParameter::get_type<Parameters>(), Detail::Format::format_value<Parameters> }... };
|
||||
}
|
||||
|
||||
void vformat(StringBuilder& builder, StringView fmtstr, Span<const TypeErasedParameter>);
|
||||
void vformat(const LogStream& stream, StringView fmtstr, Span<const TypeErasedParameter>);
|
||||
void vformat(StringBuilder& builder, StringView fmtstr, TypeErasedFormatParams);
|
||||
void vformat(const LogStream& stream, StringView fmtstr, TypeErasedFormatParams);
|
||||
|
||||
} // namespace AK
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue