Optimize format string parsing

This commit is contained in:
Victor Zverovich 2018-07-14 13:17:40 -07:00
parent c99a259739
commit e7e9578ed4
2 changed files with 54 additions and 20 deletions

View File

@ -2137,25 +2137,62 @@ struct id_adapter {
Handler &handler; Handler &handler;
}; };
template <typename Char, typename Handler> template <bool IS_CONSTEXPR, class InputIt, class T>
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<false, const char*, char>(
const char *first, const char *last, const char &value) {
auto result = static_cast<const char*>(
std::memchr(first, value, last - first));
return result ? result : last;
}
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string( FMT_CONSTEXPR void parse_format_string(
basic_string_view<Char> format_str, Handler &&handler) { basic_string_view<Char> format_str, Handler &&handler) {
auto begin = format_str.data(); struct writer {
auto end = begin + format_str.size(); FMT_CONSTEXPR void operator()(const Char *begin, const Char *end) {
auto p = begin; for (;;) {
while (p != end) { auto p = find<IS_CONSTEXPR>(begin, end, '}');
Char ch = *p++; if (p == end) {
if (ch != '{' && ch != '}') continue; handler_.on_text(begin, end);
if (p != end && *p == ch) {
handler.on_text(begin, p);
begin = ++p;
continue;
}
if (ch == '}') {
handler.on_error("unmatched '}' in format string");
return; return;
} }
handler.on_text(begin, p - 1); ++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();
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<IS_CONSTEXPR>(begin, end, '{');
if (p == end) {
write(begin, end);
return;
}
write(begin, p);
++p;
if (p != end && *p == '{') {
handler.on_text(p, p + 1);
begin = p + 1;
continue;
}
internal::null_terminating_iterator<Char> it(p, end); internal::null_terminating_iterator<Char> it(p, end);
it = parse_arg_id(it, id_adapter<Handler, Char>(handler)); it = parse_arg_id(it, id_adapter<Handler, Char>(handler));
@ -2172,11 +2209,8 @@ FMT_CONSTEXPR void parse_format_string(
handler.on_error("missing '}' in format string"); handler.on_error("missing '}' in format string");
return; return;
} }
p = pointer_from(it); begin = pointer_from(it) + 1;
begin = ++p;
} }
handler.on_text(begin, p);
} }
template <typename T, typename ParseContext> template <typename T, typename ParseContext>
@ -2243,7 +2277,7 @@ template <typename Char, typename ErrorHandler, typename... Args>
FMT_CONSTEXPR bool check_format_string( FMT_CONSTEXPR bool check_format_string(
basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) { basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) {
format_string_checker<Char, ErrorHandler, Args...> checker(s, eh); format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
parse_format_string(s, checker); parse_format_string<true>(s, checker);
return true; return true;
} }
@ -3354,7 +3388,7 @@ typename Context::iterator vformat_to(typename ArgFormatter::range out,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<Context> args) { basic_format_args<Context> args) {
format_handler<ArgFormatter, Char, Context> h(out, format_str, args); format_handler<ArgFormatter, Char, Context> h(out, format_str, args);
parse_format_string(format_str, h); internal::parse_format_string<false>(format_str, h);
return h.context.out(); return h.context.out();
} }

View File

@ -1773,7 +1773,7 @@ struct test_format_string_handler {
FMT_CONSTEXPR bool parse_string(fmt::string_view s) { FMT_CONSTEXPR bool parse_string(fmt::string_view s) {
test_format_string_handler h; test_format_string_handler h;
fmt::internal::parse_format_string(s, h); fmt::internal::parse_format_string<true>(s, h);
return !h.error; return !h.error;
} }