| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 | 
							- #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 
- #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 
- #include "absl/base/attributes.h"
 
- #include "absl/strings/internal/str_format/arg.h"
 
- #include "absl/strings/internal/str_format/extension.h"
 
- // Compile time check support for entry points.
 
- #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 
- #if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__)
 
- #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
 
- #endif  // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__)
 
- #endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 
- namespace absl {
 
- ABSL_NAMESPACE_BEGIN
 
- namespace str_format_internal {
 
- constexpr bool AllOf() { return true; }
 
- template <typename... T>
 
- constexpr bool AllOf(bool b, T... t) {
 
-   return b && AllOf(t...);
 
- }
 
- #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 
- constexpr bool ContainsChar(const char* chars, char c) {
 
-   return *chars == c || (*chars && ContainsChar(chars + 1, c));
 
- }
 
- // A constexpr compatible list of Convs.
 
- struct ConvList {
 
-   const FormatConversionCharSet* array;
 
-   int count;
 
-   // We do the bound check here to avoid having to do it on the callers.
 
-   // Returning an empty FormatConversionCharSet has the same effect as
 
-   // short circuiting because it will never match any conversion.
 
-   constexpr FormatConversionCharSet operator[](int i) const {
 
-     return i < count ? array[i] : FormatConversionCharSet{};
 
-   }
 
-   constexpr ConvList without_front() const {
 
-     return count != 0 ? ConvList{array + 1, count - 1} : *this;
 
-   }
 
- };
 
- template <size_t count>
 
- struct ConvListT {
 
-   // Make sure the array has size > 0.
 
-   FormatConversionCharSet list[count ? count : 1];
 
- };
 
- constexpr char GetChar(string_view str, size_t index) {
 
-   return index < str.size() ? str[index] : char{};
 
- }
 
- constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
 
-   return len <= str.size() ? string_view(str.data() + len, str.size() - len)
 
-                            : string_view();
 
- }
 
- constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
 
-   return ContainsChar(chars, GetChar(format, 0))
 
-              ? ConsumeAnyOf(ConsumeFront(format), chars)
 
-              : format;
 
- }
 
- constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
 
- // Helper class for the ParseDigits function.
 
- // It encapsulates the two return values we need there.
 
- struct Integer {
 
-   string_view format;
 
-   int value;
 
-   // If the next character is a '$', consume it.
 
-   // Otherwise, make `this` an invalid positional argument.
 
-   constexpr Integer ConsumePositionalDollar() const {
 
-     return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
 
-                                      : Integer{format, 0};
 
-   }
 
- };
 
- constexpr Integer ParseDigits(string_view format, int value = 0) {
 
-   return IsDigit(GetChar(format, 0))
 
-              ? ParseDigits(ConsumeFront(format),
 
-                            10 * value + GetChar(format, 0) - '0')
 
-              : Integer{format, value};
 
- }
 
- // Parse digits for a positional argument.
 
- // The parsing also consumes the '$'.
 
- constexpr Integer ParsePositional(string_view format) {
 
-   return ParseDigits(format).ConsumePositionalDollar();
 
- }
 
- // Parses a single conversion specifier.
 
- // See ConvParser::Run() for post conditions.
 
- class ConvParser {
 
-   constexpr ConvParser SetFormat(string_view format) const {
 
-     return ConvParser(format, args_, error_, arg_position_, is_positional_);
 
-   }
 
-   constexpr ConvParser SetArgs(ConvList args) const {
 
-     return ConvParser(format_, args, error_, arg_position_, is_positional_);
 
-   }
 
-   constexpr ConvParser SetError(bool error) const {
 
-     return ConvParser(format_, args_, error_ || error, arg_position_,
 
-                       is_positional_);
 
-   }
 
-   constexpr ConvParser SetArgPosition(int arg_position) const {
 
-     return ConvParser(format_, args_, error_, arg_position, is_positional_);
 
-   }
 
-   // Consumes the next arg and verifies that it matches `conv`.
 
-   // `error_` is set if there is no next arg or if it doesn't match `conv`.
 
-   constexpr ConvParser ConsumeNextArg(char conv) const {
 
-     return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
 
-   }
 
-   // Verify that positional argument `i.value` matches `conv`.
 
-   // `error_` is set if `i.value` is not a valid argument or if it doesn't
 
-   // match.
 
-   constexpr ConvParser VerifyPositional(Integer i, char conv) const {
 
-     return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
 
-   }
 
-   // Parse the position of the arg and store it in `arg_position_`.
 
-   constexpr ConvParser ParseArgPosition(Integer arg) const {
 
-     return SetFormat(arg.format).SetArgPosition(arg.value);
 
-   }
 
-   // Consume the flags.
 
-   constexpr ConvParser ParseFlags() const {
 
-     return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
 
-   }
 
-   // Consume the width.
 
-   // If it is '*', we verify that it matches `args_`. `error_` is set if it
 
-   // doesn't match.
 
-   constexpr ConvParser ParseWidth() const {
 
-     return IsDigit(GetChar(format_, 0))
 
-                ? SetFormat(ParseDigits(format_).format)
 
-                : GetChar(format_, 0) == '*'
 
-                      ? is_positional_
 
-                            ? VerifyPositional(
 
-                                  ParsePositional(ConsumeFront(format_)), '*')
 
-                            : SetFormat(ConsumeFront(format_))
 
-                                  .ConsumeNextArg('*')
 
-                      : *this;
 
-   }
 
-   // Consume the precision.
 
-   // If it is '*', we verify that it matches `args_`. `error_` is set if it
 
-   // doesn't match.
 
-   constexpr ConvParser ParsePrecision() const {
 
-     return GetChar(format_, 0) != '.'
 
-                ? *this
 
-                : GetChar(format_, 1) == '*'
 
-                      ? is_positional_
 
-                            ? VerifyPositional(
 
-                                  ParsePositional(ConsumeFront(format_, 2)), '*')
 
-                            : SetFormat(ConsumeFront(format_, 2))
 
-                                  .ConsumeNextArg('*')
 
-                      : SetFormat(ParseDigits(ConsumeFront(format_)).format);
 
-   }
 
-   // Consume the length characters.
 
-   constexpr ConvParser ParseLength() const {
 
-     return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
 
-   }
 
-   // Consume the conversion character and verify that it matches `args_`.
 
-   // `error_` is set if it doesn't match.
 
-   constexpr ConvParser ParseConversion() const {
 
-     return is_positional_
 
-                ? VerifyPositional({ConsumeFront(format_), arg_position_},
 
-                                   GetChar(format_, 0))
 
-                : ConsumeNextArg(GetChar(format_, 0))
 
-                      .SetFormat(ConsumeFront(format_));
 
-   }
 
-   constexpr ConvParser(string_view format, ConvList args, bool error,
 
-                        int arg_position, bool is_positional)
 
-       : format_(format),
 
-         args_(args),
 
-         error_(error),
 
-         arg_position_(arg_position),
 
-         is_positional_(is_positional) {}
 
-  public:
 
-   constexpr ConvParser(string_view format, ConvList args, bool is_positional)
 
-       : format_(format),
 
-         args_(args),
 
-         error_(false),
 
-         arg_position_(0),
 
-         is_positional_(is_positional) {}
 
-   // Consume the whole conversion specifier.
 
-   // `format()` will be set to the character after the conversion character.
 
-   // `error()` will be set if any of the arguments do not match.
 
-   constexpr ConvParser Run() const {
 
-     return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
 
-         .ParseFlags()
 
-         .ParseWidth()
 
-         .ParsePrecision()
 
-         .ParseLength()
 
-         .ParseConversion();
 
-   }
 
-   constexpr string_view format() const { return format_; }
 
-   constexpr ConvList args() const { return args_; }
 
-   constexpr bool error() const { return error_; }
 
-   constexpr bool is_positional() const { return is_positional_; }
 
-  private:
 
-   string_view format_;
 
-   // Current list of arguments. If we are not in positional mode we will consume
 
-   // from the front.
 
-   ConvList args_;
 
-   bool error_;
 
-   // Holds the argument position of the conversion character, if we are in
 
-   // positional mode. Otherwise, it is unspecified.
 
-   int arg_position_;
 
-   // Whether we are in positional mode.
 
-   // It changes the behavior of '*' and where to find the converted argument.
 
-   bool is_positional_;
 
- };
 
- // Parses a whole format expression.
 
- // See FormatParser::Run().
 
- class FormatParser {
 
-   static constexpr bool FoundPercent(string_view format) {
 
-     return format.empty() ||
 
-            (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
 
-   }
 
-   // We use an inner function to increase the recursion limit.
 
-   // The inner function consumes up to `limit` characters on every run.
 
-   // This increases the limit from 512 to ~512*limit.
 
-   static constexpr string_view ConsumeNonPercentInner(string_view format,
 
-                                                       int limit = 20) {
 
-     return FoundPercent(format) || !limit
 
-                ? format
 
-                : ConsumeNonPercentInner(
 
-                      ConsumeFront(format, GetChar(format, 0) == '%' &&
 
-                                                   GetChar(format, 1) == '%'
 
-                                               ? 2
 
-                                               : 1),
 
-                      limit - 1);
 
-   }
 
-   // Consume characters until the next conversion spec %.
 
-   // It skips %%.
 
-   static constexpr string_view ConsumeNonPercent(string_view format) {
 
-     return FoundPercent(format)
 
-                ? format
 
-                : ConsumeNonPercent(ConsumeNonPercentInner(format));
 
-   }
 
-   static constexpr bool IsPositional(string_view format) {
 
-     return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
 
-                                        : GetChar(format, 0) == '$';
 
-   }
 
-   constexpr bool RunImpl(bool is_positional) const {
 
-     // In non-positional mode we require all arguments to be consumed.
 
-     // In positional mode just reaching the end of the format without errors is
 
-     // enough.
 
-     return (format_.empty() && (is_positional || args_.count == 0)) ||
 
-            (!format_.empty() &&
 
-             ValidateArg(
 
-                 ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
 
-   }
 
-   constexpr bool ValidateArg(ConvParser conv) const {
 
-     return !conv.error() && FormatParser(conv.format(), conv.args())
 
-                                 .RunImpl(conv.is_positional());
 
-   }
 
-  public:
 
-   constexpr FormatParser(string_view format, ConvList args)
 
-       : format_(ConsumeNonPercent(format)), args_(args) {}
 
-   // Runs the parser for `format` and `args`.
 
-   // It verifies that the format is valid and that all conversion specifiers
 
-   // match the arguments passed.
 
-   // In non-positional mode it also verfies that all arguments are consumed.
 
-   constexpr bool Run() const {
 
-     return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
 
-   }
 
-  private:
 
-   string_view format_;
 
-   // Current list of arguments.
 
-   // If we are not in positional mode we will consume from the front and will
 
-   // have to be empty in the end.
 
-   ConvList args_;
 
- };
 
- template <FormatConversionCharSet... C>
 
- constexpr bool ValidFormatImpl(string_view format) {
 
-   return FormatParser(format,
 
-                       {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
 
-       .Run();
 
- }
 
- #endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 
- }  // namespace str_format_internal
 
- ABSL_NAMESPACE_END
 
- }  // namespace absl
 
- #endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 
 
  |