diff --git a/include/fmt/base.h b/include/fmt/base.h index 401cbd12..14369ca2 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2387,23 +2387,6 @@ FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1; } -// Return the result via the out param to workaround gcc bug 77539. -template -FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { - for (out = first; out != last; ++out) { - if (*out == value) return true; - } - return false; -} - -template <> -inline auto find(const char* first, const char* last, char value, - const char*& out) -> bool { - out = - static_cast(memchr(first, value, to_unsigned(last - first))); - return out != nullptr; -} - // Parses the range [begin, end) as an unsigned integer. This function assumes // that the range is non-empty and the first character is a digit. template @@ -2775,54 +2758,24 @@ FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, return begin + 1; } -template -FMT_CONSTEXPR void parse_format_string(basic_string_view format_str, +template +FMT_CONSTEXPR void parse_format_string(basic_string_view fmt, Handler&& handler) { - auto begin = format_str.data(); - auto end = begin + format_str.size(); - if (FMT_OPTIMIZE_SIZE || end - begin < 32) { - // Use a simple loop instead of memchr for small strings. - const Char* p = begin; - while (p != end) { - auto c = *p++; - if (c == '{') { - handler.on_text(begin, p - 1); - begin = p = parse_replacement_field(p - 1, end, handler); - } else if (c == '}') { - if (p == end || *p != '}') - return handler.on_error("unmatched '}' in format string"); - handler.on_text(begin, p); - begin = ++p; - } + auto begin = fmt.data(), end = begin + fmt.size(); + auto p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; } - handler.on_text(begin, end); - return; - } - struct writer { - FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { - if (from == to) return; - for (;;) { - const Char* p = nullptr; - if (!find(from, to, Char('}'), p)) - return handler_.on_text(from, to); - ++p; - if (p == to || *p != '}') - return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(from, p); - from = p + 1; - } - } - Handler& handler_; - } write = {handler}; - while (begin != end) { - // Doing two passes with memchr (one for '{' and another for '}') is up to - // 2.5x faster than the naive one-pass implementation on big format strings. - const Char* p = begin; - if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) - return write(begin, end); - write(begin, p); - begin = parse_replacement_field(p, end, handler); } + handler.on_text(begin, end); } // Checks char specs and returns true iff the presentation type is char-like. @@ -2841,8 +2794,7 @@ FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { template FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). -FMT_CONSTEXPR auto parse_format_specs(parse_context& ctx) - -> decltype(ctx.begin()) { +FMT_CONSTEXPR auto invoke_parse(parse_context& ctx) -> const Char* { using mapper = arg_mapper>; using mapped_type = remove_cvref_t()))>; #if defined(__cpp_if_constexpr) @@ -2863,7 +2815,7 @@ class format_string_checker { named_arg_info named_args_[NUM_NAMED_ARGS > 0 ? NUM_NAMED_ARGS : 1]; compile_parse_context context_; - using parse_func = const Char* (*)(parse_context&); + using parse_func = auto (*)(parse_context&) -> const Char*; parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1]; public: @@ -2873,7 +2825,7 @@ class format_string_checker { : types_{mapped_type_constant>::value...}, named_args_{}, context_(fmt, NUM_ARGS, types_), - parse_funcs_{&parse_format_specs...} { + parse_funcs_{&invoke_parse...} { using ignore = int[]; int arg_index = 0, named_arg_index = 0; (void)ignore{0, (init_statically_named_arg(named_args_, arg_index, @@ -3011,8 +2963,7 @@ template class basic_format_string { detail::count<(std::is_base_of>::value && std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); - if (FMT_USE_CONSTEVAL) - parse_format_string(s, checker(s, arg_pack())); + if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); #ifdef FMT_ENFORCE_COMPILE_STRING static_assert( FMT_USE_CONSTEVAL && sizeof(S) != 0, diff --git a/include/fmt/format.h b/include/fmt/format.h index fe592fb7..1c156ba6 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4229,7 +4229,7 @@ void vformat_to(buffer& buf, basic_string_view fmt, auto out = basic_appender(buf); if (fmt.size() == 2 && equal2(fmt.data(), "{}")) return args.get(0).visit(default_arg_formatter{out}); - parse_format_string( + parse_format_string( fmt, format_handler{parse_context(fmt), {out, args, loc}}); } diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 09da6481..8a130118 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -55,6 +55,23 @@ template class basic_printf_context { namespace detail { +// Return the result via the out param to workaround gcc bug 77539. +template +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find(const char* first, const char* last, char value, + const char*& out) -> bool { + out = + static_cast(memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template struct int_checker { diff --git a/test/base-test.cc b/test/base-test.cc index edfb96bd..1a70c23e 100644 --- a/test/base-test.cc +++ b/test/base-test.cc @@ -561,7 +561,7 @@ struct test_format_string_handler { template constexpr bool parse_string(const char (&s)[N]) { auto h = test_format_string_handler(); - fmt::detail::parse_format_string(fmt::string_view(s, N - 1), h); + fmt::detail::parse_format_string(fmt::string_view(s, N - 1), h); return !h.error; } diff --git a/test/scan.h b/test/scan.h index 304e692e..f6aaf4e3 100644 --- a/test/scan.h +++ b/test/scan.h @@ -558,7 +558,7 @@ struct scan_handler { void vscan(detail::scan_buffer& buf, string_view fmt, scan_args args) { auto h = detail::scan_handler(fmt, buf, args); - detail::parse_format_string(fmt, h); + detail::parse_format_string(fmt, h); } template