diff --git a/include/fmt/format.h b/include/fmt/format.h index ede50013..11002e4c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2137,25 +2137,62 @@ struct id_adapter { Handler &handler; }; -template +template +FMT_CONSTEXPR InputIt find(InputIt first, InputIt last, const T &value) { + for (; first != last; ++first) { + if (*first == value) + return first; + } + return last; +} + +template <> +inline const char *find( + const char *first, const char *last, const char &value) { + auto result = static_cast( + std::memchr(first, value, last - first)); + return result ? result : last; +} + +template FMT_CONSTEXPR void parse_format_string( basic_string_view format_str, Handler &&handler) { + struct writer { + FMT_CONSTEXPR void operator()(const Char *begin, const Char *end) { + for (;;) { + auto p = find(begin, end, '}'); + if (p == end) { + handler_.on_text(begin, end); + return; + } + ++p; + if (p == end || *p != '}') { + handler_.on_error("unmatched '}' in format string"); + return; + } + handler_.on_text(begin, p); + begin = p + 1; + } + } + Handler &handler_; + } write{handler}; auto begin = format_str.data(); auto end = begin + format_str.size(); - auto p = begin; - while (p != end) { - Char ch = *p++; - if (ch != '{' && ch != '}') continue; - if (p != end && *p == ch) { - handler.on_text(begin, p); - begin = ++p; - continue; - } - if (ch == '}') { - handler.on_error("unmatched '}' in format string"); + for (;;) { + // Doing two passes with memchr (one for '{' and another for '}') is up to + // 2.5x faster than the naive one-pass implementation on long format strings. + auto p = find(begin, end, '{'); + if (p == end) { + write(begin, end); return; } - handler.on_text(begin, p - 1); + write(begin, p); + ++p; + if (p != end && *p == '{') { + handler.on_text(p, p + 1); + begin = p + 1; + continue; + } internal::null_terminating_iterator it(p, end); it = parse_arg_id(it, id_adapter(handler)); @@ -2172,11 +2209,8 @@ FMT_CONSTEXPR void parse_format_string( handler.on_error("missing '}' in format string"); return; } - p = pointer_from(it); - - begin = ++p; + begin = pointer_from(it) + 1; } - handler.on_text(begin, p); } template @@ -2243,7 +2277,7 @@ template FMT_CONSTEXPR bool check_format_string( basic_string_view s, ErrorHandler eh = ErrorHandler()) { format_string_checker checker(s, eh); - parse_format_string(s, checker); + parse_format_string(s, checker); return true; } @@ -3354,7 +3388,7 @@ typename Context::iterator vformat_to(typename ArgFormatter::range out, basic_string_view format_str, basic_format_args args) { format_handler h(out, format_str, args); - parse_format_string(format_str, h); + internal::parse_format_string(format_str, h); return h.context.out(); } diff --git a/test/format-test.cc b/test/format-test.cc index 548b215f..d283d913 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1773,7 +1773,7 @@ struct test_format_string_handler { FMT_CONSTEXPR bool parse_string(fmt::string_view s) { test_format_string_handler h; - fmt::internal::parse_format_string(s, h); + fmt::internal::parse_format_string(s, h); return !h.error; }