/* * Copyright (c) 2020, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include namespace AK { class TypeErasedFormatParams; class FormatParser; class FormatBuilder; template struct Formatter { using __no_formatter_defined = void; }; enum AllowDebugOnlyFormatters { No, Yes }; template inline constexpr bool HasFormatter = true; template inline constexpr bool HasFormatter::__no_formatter_defined> = false; template inline constexpr bool is_debug_only_formatter() { constexpr bool has_is_debug_only = requires(Formatter const& formatter) { formatter.is_debug_only(); }; if constexpr (has_is_debug_only) return Formatter::is_debug_only(); return false; } template concept Formattable = HasFormatter; constexpr size_t max_format_arguments = 256; template ErrorOr __format_value(TypeErasedFormatParams& params, FormatBuilder& builder, FormatParser& parser, void const* value) { Formatter formatter; formatter.parse(params, parser); return formatter.format(builder, *static_cast(value)); } struct TypeErasedParameter { enum class Type { UnsignedInteger, SignedInteger, Boolean, Character, Float, Double, StringView, CString, CustomType }; struct CustomType { void const* value; ErrorOr (*formatter)(TypeErasedFormatParams&, FormatBuilder&, FormatParser&, void const* value); }; template static bool const IsChar = IsOneOf; template explicit constexpr TypeErasedParameter(U const& value) requires(!IsChar && sizeof(U) <= sizeof(u64)) : value { .as_unsigned = value } , type { Type::UnsignedInteger } { } template explicit constexpr TypeErasedParameter(I const& value) requires(!IsChar && sizeof(I) <= sizeof(i64)) : value { .as_signed = value } , type { Type::SignedInteger } { } explicit constexpr TypeErasedParameter(bool const& value) : value { .as_bool = value } , type { Type::Boolean } { } explicit constexpr TypeErasedParameter(char const& value) : value { .as_char = value } , type { Type::Character } { } explicit constexpr TypeErasedParameter(float const& value) : value { .as_float = value } , type { Type::Float } { } explicit constexpr TypeErasedParameter(double const& value) : value { .as_double = value } , type { Type::Double } { } explicit constexpr TypeErasedParameter(StringView const& value) : value { .as_string_view = value } , type { Type::StringView } { } explicit constexpr TypeErasedParameter(char const* value) : value { .as_c_string = value } , type { Type::CString } { } template explicit constexpr TypeErasedParameter(T const& value) : value { .as_custom_type = { &value, __format_value } } , type { Type::CustomType } { } template constexpr auto visit(Visitor&& visitor) const { switch (type) { case Type::UnsignedInteger: return visitor(value.as_unsigned); case Type::SignedInteger: return visitor(value.as_signed); case Type::Boolean: return visitor(value.as_bool); case Type::Character: return visitor(value.as_char); case Type::Float: return visitor(value.as_float); case Type::Double: return visitor(value.as_double); case Type::StringView: return visitor(value.as_string_view); case Type::CString: return visitor(value.as_c_string); case Type::CustomType: return visitor(value.as_custom_type); } VERIFY_NOT_REACHED(); } constexpr size_t to_size() const { return visit([](T value) -> size_t { if constexpr (IsSame) return static_cast(value); if constexpr (IsSame) { VERIFY(value >= 0); return static_cast(value); } TODO(); }); } union { u64 as_unsigned; i64 as_signed; bool as_bool; char as_char; float as_float; double as_double; StringView as_string_view; char const* as_c_string; CustomType as_custom_type; } value; Type type; }; class FormatBuilder { public: enum class Align { Default, Left, Center, Right, }; enum class SignMode { OnlyIfNeeded, Always, Reserved, Default = OnlyIfNeeded, }; enum class RealNumberDisplayMode { FixedPoint, General, Default = General, }; explicit FormatBuilder(StringBuilder& builder) : m_builder(builder) { } ErrorOr put_padding(char fill, size_t amount); ErrorOr put_literal(StringView value); ErrorOr put_string( StringView value, Align align = Align::Left, size_t min_width = 0, size_t max_width = NumericLimits::max(), char fill = ' '); ErrorOr put_u64( u64 value, u8 base = 10, bool prefix = false, bool upper_case = false, bool zero_pad = false, bool use_separator = false, Align align = Align::Right, size_t min_width = 0, char fill = ' ', SignMode sign_mode = SignMode::OnlyIfNeeded, bool is_negative = false); ErrorOr put_i64( i64 value, u8 base = 10, bool prefix = false, bool upper_case = false, bool zero_pad = false, bool use_separator = false, Align align = Align::Right, size_t min_width = 0, char fill = ' ', SignMode sign_mode = SignMode::OnlyIfNeeded); ErrorOr put_fixed_point( bool is_negative, i64 integer_value, u64 fraction_value, u64 fraction_one, size_t precision, u8 base = 10, bool upper_case = false, bool zero_pad = false, bool use_separator = false, Align align = Align::Right, size_t min_width = 0, size_t fraction_max_width = 6, char fill = ' ', SignMode sign_mode = SignMode::OnlyIfNeeded); ErrorOr put_f80( long double value, u8 base = 10, bool upper_case = false, bool use_separator = false, Align align = Align::Right, size_t min_width = 0, size_t precision = 6, char fill = ' ', SignMode sign_mode = SignMode::OnlyIfNeeded, RealNumberDisplayMode = RealNumberDisplayMode::Default); template T> ErrorOr put_f32_or_f64( T value, u8 base = 10, bool upper_case = false, bool zero_pad = false, bool use_separator = false, Align align = Align::Right, size_t min_width = 0, Optional precision = {}, char fill = ' ', SignMode sign_mode = SignMode::OnlyIfNeeded, RealNumberDisplayMode = RealNumberDisplayMode::Default); ErrorOr put_hexdump( ReadonlyBytes, size_t width, char fill = ' '); StringBuilder const& builder() const { return m_builder; } StringBuilder& builder() { return m_builder; } private: StringBuilder& m_builder; ErrorOr put_f64_with_precision( double value, u8 base, bool upper_case, bool zero_pad, bool use_separator, Align align, size_t min_width, size_t precision, char fill, SignMode sign_mode, RealNumberDisplayMode); }; class TypeErasedFormatParams { public: TypeErasedFormatParams(u32 size) : m_size(size) { } ReadonlySpan parameters() const { return { m_parameters, m_size }; } size_t take_next_index() { return m_next_index++; } private: u32 m_size { 0 }; u32 m_next_index { 0 }; TypeErasedParameter m_parameters[0]; }; template class VariadicFormatParams : public TypeErasedFormatParams { public: static_assert(sizeof...(Parameters) <= max_format_arguments); explicit VariadicFormatParams(Parameters const&... parameters) : TypeErasedFormatParams(sizeof...(Parameters)) , m_parameter_storage { TypeErasedParameter { parameters }... } { constexpr bool any_debug_formatters = (is_debug_only_formatter>() || ...); static_assert(!any_debug_formatters || allow_debug_formatters == AllowDebugOnlyFormatters::Yes, "You are attempting to use a debug-only formatter outside of a debug log! Maybe one of your format values is an ErrorOr?"); } private: TypeErasedParameter m_parameter_storage[sizeof...(Parameters)]; }; // 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, FixedPoint, 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; bool m_use_separator = false; char m_fill = ' '; bool m_zero_pad = false; Optional m_width; Optional m_precision; void parse(TypeErasedFormatParams&, FormatParser&); }; template struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(move(formatter)) { } ErrorOr format(FormatBuilder&, T); }; template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(move(formatter)) { } ErrorOr format(FormatBuilder&, StringView); }; template requires(HasFormatter) struct Formatter> : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(move(formatter)) { } ErrorOr format(FormatBuilder& builder, ReadonlySpan value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; TRY(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; TRY(builder.put_literal("[ "sv)); bool first = true; for (auto& content : value) { if (!first) { TRY(builder.put_literal(", "sv)); content_fmt = Formatter {}; } first = false; TRY(content_fmt.format(builder, content)); } TRY(builder.put_literal(" ]"sv)); return {}; } }; template requires(HasFormatter) struct Formatter> : Formatter> { ErrorOr format(FormatBuilder& builder, Span value) { return Formatter>::format(builder, value); } }; template requires(HasFormatter) struct Formatter> : Formatter> { ErrorOr format(FormatBuilder& builder, Vector const& value) { return Formatter>::format(builder, value.span()); } }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, ReadonlyBytes value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; return formatter.format(builder, reinterpret_cast(value.data())); } if (m_mode == Mode::Default || m_mode == Mode::HexDump) { m_mode = Mode::HexDump; return Formatter::format(builder, value); } return Formatter::format(builder, value); } }; template<> struct Formatter : Formatter { }; // FIXME: Printing raw char pointers is inherently dangerous. Remove this and // its users and prefer StringView over it. template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, char const* value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; return formatter.format(builder, reinterpret_cast(value)); } return Formatter::format(builder, value != nullptr ? StringView { value, __builtin_strlen(value) } : "(null)"sv); } }; template<> struct Formatter : Formatter { }; template struct Formatter : Formatter { }; template<> struct Formatter : Formatter { }; template struct Formatter : StandardFormatter { ErrorOr format(FormatBuilder& builder, T* value) { if (m_mode == Mode::Default) m_mode = Mode::Pointer; Formatter formatter { *this }; return formatter.format(builder, reinterpret_cast(value)); } }; template<> struct Formatter : StandardFormatter { ErrorOr format(FormatBuilder&, char); }; template<> struct Formatter : StandardFormatter { ErrorOr format(FormatBuilder& builder, char32_t); }; template<> struct Formatter : StandardFormatter { ErrorOr format(FormatBuilder&, bool); }; template<> struct Formatter : StandardFormatter { ErrorOr format(FormatBuilder&, float value); }; template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } ErrorOr format(FormatBuilder&, double); }; template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } ErrorOr format(FormatBuilder&, long double value); }; template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } ErrorOr format(FormatBuilder&, f16 value); }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, nullptr_t) { if (m_mode == Mode::Default) m_mode = Mode::Pointer; return Formatter::format(builder, 0); } }; ErrorOr vformat(StringBuilder&, StringView fmtstr, TypeErasedFormatParams&); void vout(FILE*, StringView fmtstr, TypeErasedFormatParams&, bool newline = false); template void out(FILE* file, CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(file, fmtstr.view(), variadic_format_params); } template void outln(FILE* file, CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(file, fmtstr.view(), variadic_format_params, true); } inline void outln(FILE* file) { fputc('\n', file); } #ifndef AK_OS_ANDROID template void out(CheckedFormatString&& fmtstr, Parameters const&... parameters) { out(stdout, move(fmtstr), parameters...); } template void outln(CheckedFormatString&& fmtstr, Parameters const&... parameters) { outln(stdout, move(fmtstr), parameters...); } inline void outln() { outln(stdout); } template void warn(CheckedFormatString&& fmtstr, Parameters const&... parameters) { out(stderr, move(fmtstr), parameters...); } template void warnln(CheckedFormatString&& fmtstr, Parameters const&... parameters) { outln(stderr, move(fmtstr), parameters...); } inline void warnln() { outln(stderr); } #else // v Android ^ No Android enum class LogLevel { Debug, Info, Warning, }; void vout(LogLevel, StringView fmtstr, TypeErasedFormatParams&, bool newline = false); template void out(CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(LogLevel::Info, fmtstr.view(), variadic_format_params); } template void outln(CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(LogLevel::Info, fmtstr.view(), variadic_format_params, true); } inline void outln() { outln(""); } template void warn(CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(LogLevel::Warning, fmtstr.view(), variadic_format_params); } template void warnln(CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vout(LogLevel::Warning, fmtstr.view(), variadic_format_params, true); } inline void warnln() { warnln(""); } void set_log_tag_name(char const*); #endif // AK_OS_ANDROID #define outln_if(flag, fmt, ...) \ do { \ if constexpr (flag) \ outln(fmt, ##__VA_ARGS__); \ } while (0) #define warnln_if(flag, fmt, ...) \ do { \ if constexpr (flag) \ warnln(fmt, ##__VA_ARGS__); \ } while (0) void vdbg(StringView fmtstr, TypeErasedFormatParams&, bool newline = false); template void dbg(CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vdbg(fmtstr.view(), variadic_format_params, false); } template void dbgln(CheckedFormatString&& fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; vdbg(fmtstr.view(), variadic_format_params, true); } inline void dbgln() { dbgln(""); } void set_debug_enabled(bool); void set_rich_debug_enabled(bool); template class FormatIfSupported { public: explicit FormatIfSupported(T const& value) : m_value(value) { } T const& value() const { return m_value; } private: T const& m_value; }; template struct __FormatIfSupported : Formatter { ErrorOr format(FormatBuilder& builder, FormatIfSupported const&) { return Formatter::format(builder, "?"sv); } }; template struct __FormatIfSupported : Formatter { ErrorOr format(FormatBuilder& builder, FormatIfSupported const& value) { return 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 ErrorOr format(FormatBuilder& builder, StringView fmtstr, Parameters const&... parameters) { VariadicFormatParams variadic_format_params { parameters... }; return vformat(builder, fmtstr, variadic_format_params); } ErrorOr vformat(FormatBuilder& builder, StringView fmtstr, TypeErasedFormatParams& params); }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Error const& error); private: ErrorOr format_windows_error(FormatBuilder& builder, Error const& error); }; template struct Formatter> : Formatter { static constexpr bool is_debug_only() { return true; } ErrorOr format(FormatBuilder& builder, ErrorOr const& error_or) { if (error_or.is_error()) return Formatter::format(builder, "{}"sv, error_or.error()); return Formatter::format(builder, "{{{}}}"sv, error_or.value()); } }; template struct Formatter> : Formatter { ErrorOr format(FormatBuilder& builder, Optional const& optional) { if (optional.has_value()) return Formatter::format(builder, "{}"sv, *optional); return builder.put_literal("None"sv); } }; } // namespace AK #undef AK_HANDLE_UNEXPECTED_ERROR #define AK_HANDLE_UNEXPECTED_ERROR(result) \ if (result.is_error()) [[unlikely]] { \ if (ak_colorize_output()) { \ ::AK::warn("\033[31;1mUNEXPECTED ERROR\033[0m"); \ } else { \ ::AK::warn("UNEXPECTED ERROR"); \ } \ if constexpr (::AK::HasFormatter) { \ ::AK::warn(": {}", result.release_error()); \ } \ ::AK::warnln(" at {}:{}", __FILE__, __LINE__); \ ak_trap(); \ } #if USING_AK_GLOBALLY using AK::out; using AK::outln; using AK::warn; using AK::warnln; using AK::dbg; 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) #endif