/* * Copyright (c) 2020, the SerenityOS developers. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #ifndef KERNEL # include #endif namespace AK { class TypeErasedFormatParams; class FormatParser; class FormatBuilder; template struct Formatter { using __no_formatter_defined = void; }; constexpr size_t max_format_arguments = 256; 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)(TypeErasedFormatParams&, FormatBuilder&, FormatParser&, const void* value); }; class FormatParser : public GenericLexer { public: 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::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_f64( 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); #endif 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++; } size_t decode(size_t value, size_t default_value = 0); 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(params, 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, }; static constexpr size_t value_not_set = NumericLimits::max(); static constexpr size_t value_from_next_arg = NumericLimits::max() - 1; static constexpr size_t value_from_arg = NumericLimits::max() - max_format_arguments - 2; 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; size_t m_width = value_not_set; size_t m_precision = value_not_set; void parse(TypeErasedFormatParams&, FormatParser&); }; template struct Formatter::value>::Type> : StandardFormatter { Formatter() { } explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(TypeErasedFormatParams&, FormatBuilder&, T value); }; template<> struct Formatter : StandardFormatter { Formatter() { } explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(TypeErasedFormatParams&, FormatBuilder&, StringView value); }; template<> struct Formatter : Formatter { void format(TypeErasedFormatParams& params, FormatBuilder& builder, const char* value) { if (m_mode == Mode::Pointer) { Formatter formatter { *this }; formatter.format(params, builder, reinterpret_cast(value)); } else { Formatter::format(params, builder, value); } } }; template<> struct Formatter : Formatter { }; template struct Formatter : Formatter { }; template<> struct Formatter : Formatter { }; template<> struct Formatter : Formatter { }; template struct Formatter : StandardFormatter { void format(TypeErasedFormatParams& params, FormatBuilder& builder, T* value) { if (m_mode == Mode::Default) m_mode = Mode::Pointer; Formatter formatter { *this }; formatter.format(params, builder, reinterpret_cast(value)); } }; template<> struct Formatter : StandardFormatter { void format(TypeErasedFormatParams&, FormatBuilder&, char value); }; template<> struct Formatter : StandardFormatter { void format(TypeErasedFormatParams&, FormatBuilder&, bool value); }; #ifndef KERNEL template<> struct Formatter : StandardFormatter { void format(TypeErasedFormatParams&, FormatBuilder&, float value); }; template<> struct Formatter : StandardFormatter { Formatter() { } explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } void format(TypeErasedFormatParams&, FormatBuilder&, double value); }; #endif void vformat(StringBuilder& builder, StringView fmtstr, TypeErasedFormatParams); void vformat(const LogStream& stream, StringView fmtstr, TypeErasedFormatParams); #ifndef KERNEL void vout(FILE*, StringView fmtstr, TypeErasedFormatParams, bool newline = false); template void out(FILE* file, StringView fmtstr, const Parameters&... parameters) { vout(file, fmtstr, VariadicFormatParams { parameters... }); } template void outln(FILE* file, StringView fmtstr, const Parameters&... parameters) { vout(file, fmtstr, VariadicFormatParams { parameters... }, true); } template void outln(FILE* file, const char* fmtstr, const Parameters&... parameters) { vout(file, fmtstr, VariadicFormatParams { parameters... }, true); } inline void outln(FILE* file) { fputc('\n', file); } template void out(StringView fmtstr, const Parameters&... parameters) { out(stdout, fmtstr, parameters...); } template void outln(StringView fmtstr, const Parameters&... parameters) { outln(stdout, fmtstr, parameters...); } template void outln(const char* fmtstr, const Parameters&... parameters) { outln(stdout, fmtstr, parameters...); } inline void outln() { outln(stdout); } template void warn(StringView fmtstr, const Parameters&... parameters) { out(stderr, fmtstr, parameters...); } template void warnln(StringView fmtstr, const Parameters&... parameters) { outln(stderr, fmtstr, parameters...); } template void warnln(const char* fmtstr, const Parameters&... parameters) { outln(stderr, fmtstr, parameters...); } inline void warnln() { outln(stderr); } #endif void vdbgln(StringView fmtstr, TypeErasedFormatParams); template void dbgln(StringView fmtstr, const Parameters&... parameters) { vdbgln(fmtstr, VariadicFormatParams { parameters... }); } template void dbgln(const char* fmtstr, const Parameters&... parameters) { dbgln(StringView { fmtstr }, parameters...); } template struct HasFormatter : TrueType { }; template struct HasFormatter::__no_formatter_defined> : FalseType { }; 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(TypeErasedFormatParams& params, FormatBuilder& builder, const FormatIfSupported&) { Formatter::format(params, builder, "?"); } }; template struct __FormatIfSupported : Formatter { void format(TypeErasedFormatParams& params, FormatBuilder& builder, const FormatIfSupported& value) { Formatter::format(params, builder, value.value()); } }; template struct Formatter> : __FormatIfSupported::value> { }; } // namespace AK #ifndef KERNEL using AK::out; using AK::outln; using AK::warn; using AK::warnln; #endif using AK::dbgln; using AK::FormatIfSupported;