diff options
author | Abseil Team <absl-team@google.com> | 2019-04-16T19·11-0700 |
---|---|---|
committer | Alex Strelnikov <strel@google.com> | 2019-04-16T19·40-0400 |
commit | d902eb869bcfacc1bad14933ed9af4bed006d481 (patch) | |
tree | 2c6b4c121bb5a696657d63db03a166404b3592b8 /absl/strings/internal/str_format/parser.h | |
parent | a02f62f456f2c4a7ecf2be3104fe0c6e16fbad9a (diff) |
Export of internal Abseil changes.
-- babbb6421068af3831870fd5995444437ace6769 by Derek Mauro <dmauro@google.com>: Rollback of: Make raw_hash_set_test less flaky. Split the timing loop into chunks so that we are less suceptible to antogantistic processes. PiperOrigin-RevId: 243854490 -- a2711f17a712f6d09799bf32363d67526737b486 by CJ Johnson <johnsoncj@google.com>: Relocates IsAtLeastForwardIterator to internal/inlined_vector.h PiperOrigin-RevId: 243846090 -- 6c14cdbeb9a61022c27f8957654f930d8abf2fc1 by Matt Kulukundis <kfm@google.com>: Make raw_hash_set_test less flaky. Split the timing loop into chunks so that we are less suceptible to antogantistic processes. PiperOrigin-RevId: 243824289 -- ee6072a6b6e0ac653622524ceb09db3b9e870f96 by Samuel Benzaquen <sbenza@google.com>: Improve format parser performance. Replace the main switch with a lookup in the existing tag table. Improve the ABI of ConsumeUnboundConversion a little. PiperOrigin-RevId: 243824112 -- 24b9e6476dfa4be8d644359eab8ac6816492f187 by Abseil Team <absl-team@google.com>: Fix DR numbers: 3800 ? 3080, 3801 ? 3081. PiperOrigin-RevId: 243804213 -- 0660404074707e197684f07cc0bffe4a9c35cd2f by Abseil Team <absl-team@google.com>: Internal change. PiperOrigin-RevId: 243757359 -- ba0f5bb9b8584d75c4ffc44ff3cb8c691796ffc6 by Xiaoyi Zhang <zhangxy@google.com>: Consolidate ABSL_INTERNAL_UNALIGNED_* implementation into memcpy. The compiler should be good enough to optimize these operations. See https://github.com/abseil/abseil-cpp/issues/269 for background. PiperOrigin-RevId: 243323941 -- 00853a8756548df7217513c562d604b4ee5c6ab9 by Eric Fiselier <ericwf@google.com>: Reexport memory.h from optional.h for compatibility between libc++ and libstdc++. PiperOrigin-RevId: 243313425 GitOrigin-RevId: babbb6421068af3831870fd5995444437ace6769 Change-Id: Ic53c127ad857a431ad60c98b27cc585fed50a3e3
Diffstat (limited to 'absl/strings/internal/str_format/parser.h')
-rw-r--r-- | absl/strings/internal/str_format/parser.h | 128 |
1 files changed, 79 insertions, 49 deletions
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index 1022f06297a7..b7614a09fa4e 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h @@ -63,17 +63,45 @@ struct UnboundConversion { ConversionChar conv; }; -// Consume conversion spec prefix (not including '%') of '*src' if valid. +// Consume conversion spec prefix (not including '%') of [p, end) if valid. // Examples of valid specs would be e.g.: "s", "d", "-12.6f". -// If valid, the front of src is advanced such that src becomes the -// part following the conversion spec, and the spec part is broken down and -// returned in 'conv'. -// If invalid, returns false and leaves 'src' unmodified. -// For example: -// Given "d9", returns "d", and leaves src="9", -// Given "!", returns "" and leaves src="!". -bool ConsumeUnboundConversion(string_view* src, UnboundConversion* conv, - int* next_arg); +// If valid, it returns the first character following the conversion spec, +// and the spec part is broken down and returned in 'conv'. +// If invalid, returns nullptr. +const char* ConsumeUnboundConversion(const char* p, const char* end, + UnboundConversion* conv, int* next_arg); + +// Helper tag class for the table below. +// It allows fast `char -> ConversionChar/LengthMod` checking and conversions. +class ConvTag { + public: + constexpr ConvTag(ConversionChar::Id id) : tag_(id) {} // NOLINT + // We invert the length modifiers to make them negative so that we can easily + // test for them. + constexpr ConvTag(LengthMod::Id id) : tag_(~id) {} // NOLINT + // Everything else is -128, which is negative to make is_conv() simpler. + constexpr ConvTag() : tag_(-128) {} + + bool is_conv() const { return tag_ >= 0; } + bool is_length() const { return tag_ < 0 && tag_ != -128; } + ConversionChar as_conv() const { + assert(is_conv()); + return ConversionChar::FromId(static_cast<ConversionChar::Id>(tag_)); + } + LengthMod as_length() const { + assert(is_length()); + return LengthMod::FromId(static_cast<LengthMod::Id>(~tag_)); + } + + private: + std::int8_t tag_; +}; + +extern const ConvTag kTags[256]; +// Keep a single table for all the conversion chars and length modifiers. +inline ConvTag GetTagForChar(char c) { + return kTags[static_cast<unsigned char>(c)]; +} // Parse the format string provided in 'src' and pass the identified items into // 'consumer'. @@ -88,51 +116,53 @@ bool ConsumeUnboundConversion(string_view* src, UnboundConversion* conv, template <typename Consumer> bool ParseFormatString(string_view src, Consumer consumer) { int next_arg = 0; - while (!src.empty()) { - const char* percent = - static_cast<const char*>(memchr(src.data(), '%', src.size())); + const char* p = src.data(); + const char* const end = p + src.size(); + while (p != end) { + const char* percent = static_cast<const char*>(memchr(p, '%', end - p)); if (!percent) { // We found the last substring. - return consumer.Append(src); + return consumer.Append(string_view(p, end - p)); } // We found a percent, so push the text run then process the percent. - size_t percent_loc = percent - src.data(); - if (!consumer.Append(string_view(src.data(), percent_loc))) return false; - if (percent + 1 >= src.data() + src.size()) return false; - - UnboundConversion conv; - - switch (percent[1]) { - case '%': - if (!consumer.Append("%")) return false; - src.remove_prefix(percent_loc + 2); - continue; - -#define PARSER_CASE(ch) \ - case #ch[0]: \ - src.remove_prefix(percent_loc + 2); \ - conv.conv = ConversionChar::FromId(ConversionChar::ch); \ - conv.arg_position = ++next_arg; \ - break; - ABSL_CONVERSION_CHARS_EXPAND_(PARSER_CASE, ); -#undef PARSER_CASE - - default: - src.remove_prefix(percent_loc + 1); - if (!ConsumeUnboundConversion(&src, &conv, &next_arg)) return false; - break; - } - if (next_arg == 0) { - // This indicates an error in the format std::string. - // The only way to get next_arg == 0 is to have a positional argument - // first which sets next_arg to -1 and then a non-positional argument - // which does ++next_arg. - // Checking here seems to be the cheapeast place to do it. + if (ABSL_PREDICT_FALSE(!consumer.Append(string_view(p, percent - p)))) { return false; } - if (!consumer.ConvertOne( - conv, string_view(percent + 1, src.data() - (percent + 1)))) { - return false; + if (ABSL_PREDICT_FALSE(percent + 1 >= end)) return false; + + auto tag = GetTagForChar(percent[1]); + if (tag.is_conv()) { + if (ABSL_PREDICT_FALSE(next_arg < 0)) { + // This indicates an error in the format std::string. + // The only way to get `next_arg < 0` here is to have a positional + // argument first which sets next_arg to -1 and then a non-positional + // argument. + return false; + } + p = percent + 2; + + // Keep this case separate from the one below. + // ConvertOne is more efficient when the compiler can see that the `basic` + // flag is set. + UnboundConversion conv; + conv.conv = tag.as_conv(); + conv.arg_position = ++next_arg; + if (ABSL_PREDICT_FALSE( + !consumer.ConvertOne(conv, string_view(percent + 1, 1)))) { + return false; + } + } else if (percent[1] != '%') { + UnboundConversion conv; + p = ConsumeUnboundConversion(percent + 1, end, &conv, &next_arg); + if (ABSL_PREDICT_FALSE(p == nullptr)) return false; + if (ABSL_PREDICT_FALSE(!consumer.ConvertOne( + conv, string_view(percent + 1, p - (percent + 1))))) { + return false; + } + } else { + if (ABSL_PREDICT_FALSE(!consumer.Append("%"))) return false; + p = percent + 2; + continue; } } return true; |