/* * Copyright (c) 2020, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #ifndef KERNEL # include #endif namespace AK { class TypeErasedFormatParams; class FormatParser; class FormatBuilder; template struct Formatter { using __no_formatter_defined = void; }; template inline constexpr bool HasFormatter = true; template inline constexpr bool HasFormatter::__no_formatter_defined> = false; constexpr size_t max_format_arguments = 256; struct TypeErasedParameter { enum class Type { UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Custom }; template static consteval Type get_type_from_size() { if constexpr (is_unsigned) { if constexpr (size == 1) return Type::UInt8; if constexpr (size == 2) return Type::UInt16; if constexpr (size == 4) return Type::UInt32; if constexpr (size == 8) return Type::UInt64; } else { if constexpr (size == 1) return Type::Int8; if constexpr (size == 2) return Type::Int16; if constexpr (size == 4) return Type::Int32; if constexpr (size == 8) return Type::Int64; } VERIFY_NOT_REACHED(); } template static consteval Type get_type() { if constexpr (IsIntegral) return get_type_from_size>(); else return Type::Custom; } constexpr size_t to_size() const { i64 svalue; if (type == TypeErasedParameter::Type::UInt8) svalue = *static_cast(value); else if (type == TypeErasedParameter::Type::UInt16) svalue = *static_cast(value); else if (type == TypeErasedParameter::Type::UInt32) svalue = *static_cast(value); else if (type == TypeErasedParameter::Type::UInt64) svalue = *static_cast(value); else if (type == TypeErasedParameter::Type::Int8) svalue = *static_cast(value); else if (type == TypeErasedParameter::Type::Int16) svalue = *static_cast(value); else if (type == TypeErasedParameter::Type::Int32) svalue = *static_cast(value); else if (type == TypeErasedParameter::Type::Int64) svalue = *static_cast(value); else VERIFY_NOT_REACHED(); VERIFY(svalue >= 0); return static_cast(svalue); } // FIXME: Getters and setters. const void* value; Type type; void (*formatter)(TypeErasedFormatParams&, FormatBuilder&, FormatParser&, const void* value); }; 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::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); #ifndef KERNEL void put_f80( long double value, u8 base = 10, bool upper_case = false, Align align = Align::Right, size_t min_width = 0, size_t precision = 6, char fill = ' ', SignMode sign_mode = SignMode::OnlyIfNeeded); void put_f64( double value, u8 base = 10, bool upper_case = false, bool zero_pad = false, Align align = Align::Right, size_t min_width = 0, size_t precision = 6, char fill = ' ', SignMode sign_mode = SignMode::OnlyIfNeeded); #endif void put_hexdump( ReadonlyBytes, size_t width, char fill = ' '); const StringBuilder& builder() const { return m_builder; } StringBuilder& builder() { return m_builder; } private: StringBuilder& m_builder; }; class TypeErasedFormatParams { public: Span parameters() const { return m_parameters; } void set_parameters(Span parameters) { m_parameters = parameters; } size_t take_next_index() { return m_next_index++; } private: Span m_parameters; size_t m_next_index { 0 }; }; template void __format_value(TypeErasedFormatParams& params, FormatBuilder& builder, FormatParser& parser, const void* value) { Formatter formatter; formatter.parse(params, parser); formatter.format(builder, *static_cast(value)); } template class VariadicFormatParams : public TypeErasedFormatParams { public: static_assert(sizeof...(Parameters) <= max_format_arguments); explicit VariadicFormatParams(const Parameters&... parameters) : m_data({ TypeErasedParameter { ¶meters, TypeErasedParameter::get_type(), __format_value }... }) { this->set_parameters(m_data); } private: Array m_data; }; // We use the same format for most types for consistency. This is taken directly from // std::format. One difference is that we are not counting the width or sign towards the // total width when calculating zero padding for numbers. // https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification struct StandardFormatter { enum class Mode { Default, Binary, BinaryUppercase, Decimal, Octal, Hexadecimal, HexadecimalUppercase, Character, String, Pointer, Float, Hexfloat, HexfloatUppercase, HexDump, }; 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 = ' '; bool m_zero_pad = false; Optional m_width; Optional m_precision; void parse(TypeErasedFormatParams&, FormatParser&); }; template struct Formatter>::Type> : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(FormatBuilder&, T value); }; template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(FormatBuilder&, StringView value); }; template requires(HasFormatter) struct Formatter> : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(FormatBuilder& builder, Vector value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; formatter.format(builder, reinterpret_cast(value.data())); return; } if (m_sign_mode != FormatBuilder::SignMode::Default) VERIFY_NOT_REACHED(); if (m_alternative_form) VERIFY_NOT_REACHED(); if (m_zero_pad) VERIFY_NOT_REACHED(); if (m_mode != Mode::Default) VERIFY_NOT_REACHED(); if (m_width.has_value() && m_precision.has_value()) VERIFY_NOT_REACHED(); m_width = m_width.value_or(0); m_precision = m_precision.value_or(NumericLimits::max()); Formatter content_fmt; builder.put_literal("[ "sv); bool first = true; for (auto& content : value) { if (!first) { builder.put_literal(", "sv); content_fmt = Formatter {}; } first = false; content_fmt.format(builder, content); } builder.put_literal(" ]"sv); } }; template<> struct Formatter : Formatter { void format(FormatBuilder& builder, ReadonlyBytes const& value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; formatter.format(builder, reinterpret_cast(value.data())); } else if (m_mode == Mode::Default || m_mode == Mode::HexDump) { m_mode = Mode::HexDump; Formatter::format(builder, value); } else { Formatter::format(builder, value); } } }; template<> struct Formatter : Formatter { }; template<> struct Formatter : Formatter { void format(FormatBuilder& builder, const char* value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; formatter.format(builder, reinterpret_cast(value)); } else { Formatter::format(builder, value); } } }; template<> struct Formatter : Formatter { }; template struct Formatter : Formatter { }; template struct Formatter : Formatter { void format(FormatBuilder& builder, const unsigned char* value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; formatter.format(builder, reinterpret_cast(value)); } else { Formatter::format(builder, { value, Size }); } } }; template<> struct Formatter : Formatter { }; template<> struct Formatter : Formatter { }; template struct Formatter : StandardFormatter { void format(FormatBuilder& builder, T* value) { if (m_mode == Mode::Default) m_mode = Mode::Pointer; Formatter formatter { *this }; formatter.format(builder, reinterpret_cast(value)); } }; template<> struct Formatter : StandardFormatter { void format(FormatBuilder&, char value); }; template<> struct Formatter : StandardFormatter { void format(FormatBuilder& builder, wchar_t value); }; template<> struct Formatter : StandardFormatter { void format(FormatBuilder&, bool value); }; #ifndef KERNEL template<> struct Formatter : StandardFormatter { void format(FormatBuilder&, float value); }; template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(FormatBuilder&, double value); }; template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(FormatBuilder&, long double value); }; #endif template<> struct Formatter : Formatter { void format(FormatBuilder& builder, std::nullptr_t) { if (m_mode == Mode::Default) m_mode = Mode::Pointer; return Formatter::format(builder, 0); } }; void vformat(StringBuilder&, StringView fmtstr, TypeErasedFormatParams&); #ifndef KERNEL void vout(FILE*, StringView fmtstr, TypeErasedFormatParams&, bool newline = false); template void out(FILE* file, CheckedFormatString&& fmtstr, const Parameters&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(file, fmtstr.view(), variadic_format_params); } template void outln(FILE* file, CheckedFormatString&& fmtstr, const Parameters&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(file, fmtstr.view(), variadic_format_params, true); } inline void outln(FILE* file) { fputc('\n', file); } template void out(CheckedFormatString&& fmtstr, const Parameters&... parameters) { out(stdout, move(fmtstr), parameters...); } template void outln(CheckedFormatString&& fmtstr, const Parameters&... parameters) { outln(stdout, move(fmtstr), parameters...); } inline void outln() { outln(stdout); } # define outln_if(flag, fmt, ...) \ do { \ if constexpr (flag) \ outln(fmt, ##__VA_ARGS__); \ } while (0) template void warn(CheckedFormatString&& fmtstr, const Parameters&... parameters) { out(stderr, move(fmtstr), parameters...); } template void warnln(CheckedFormatString&& fmtstr, const Parameters&... parameters) { outln(stderr, move(fmtstr), parameters...); } inline void warnln() { outln(stderr); } # define warnln_if(flag, fmt, ...) \ do { \ if constexpr (flag) \ outln(fmt, ##__VA_ARGS__); \ } while (0) #endif void vdbgln(StringView fmtstr, TypeErasedFormatParams&); template void dbgln(CheckedFormatString&& fmtstr, const Parameters&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vdbgln(fmtstr.view(), variadic_format_params); } inline void dbgln() { dbgln(""); } void set_debug_enabled(bool); #ifdef KERNEL void vdmesgln(StringView fmtstr, TypeErasedFormatParams&); template void dmesgln(CheckedFormatString&& fmt, const Parameters&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vdmesgln(fmt.view(), variadic_format_params); } void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams&); // be very careful to not cause any allocations here, since we could be in // a very unstable situation template void critical_dmesgln(CheckedFormatString&& fmt, const Parameters&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; v_critical_dmesgln(fmt.view(), variadic_format_params); } #endif template class FormatIfSupported { public: explicit FormatIfSupported(const T& value) : m_value(value) { } const T& value() const { return m_value; } private: const T& m_value; }; template struct __FormatIfSupported : Formatter { void format(FormatBuilder& builder, const FormatIfSupported&) { Formatter::format(builder, "?"); } }; template struct __FormatIfSupported : Formatter { void format(FormatBuilder& builder, const FormatIfSupported& value) { Formatter::format(builder, value.value()); } }; template struct Formatter> : __FormatIfSupported> { }; // This is a helper class, the idea is that if you want to implement a formatter you can inherit // from this class to "break down" the formatting. struct FormatString { }; template<> struct Formatter : Formatter { template void format(FormatBuilder& builder, StringView fmtstr, const Parameters&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vformat(builder, fmtstr, variadic_format_params); } void vformat(FormatBuilder& builder, StringView fmtstr, TypeErasedFormatParams& params); }; } // namespace AK #ifdef KERNEL using AK::critical_dmesgln; using AK::dmesgln; #else using AK::out; using AK::outln; using AK::warn; using AK::warnln; #endif using AK::dbgln; using AK::CheckedFormatString; using AK::FormatIfSupported; using AK::FormatString; #define dbgln_if(flag, fmt, ...) \ do { \ if constexpr (flag) \ dbgln(fmt, ##__VA_ARGS__); \ } while (0)