checker.h
Go to the documentation of this file.
00001 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
00002 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
00003 
00004 #include "absl/strings/internal/str_format/arg.h"
00005 #include "absl/strings/internal/str_format/extension.h"
00006 
00007 // Compile time check support for entry points.
00008 
00009 #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
00010 #if defined(__clang__) && !defined(__native_client__)
00011 #if __has_attribute(enable_if)
00012 #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
00013 #endif  // __has_attribute(enable_if)
00014 #endif  // defined(__clang__) && !defined(__native_client__)
00015 #endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
00016 
00017 namespace absl {
00018 namespace str_format_internal {
00019 
00020 constexpr bool AllOf() { return true; }
00021 
00022 template <typename... T>
00023 constexpr bool AllOf(bool b, T... t) {
00024   return b && AllOf(t...);
00025 }
00026 
00027 template <typename Arg>
00028 constexpr Conv ArgumentToConv() {
00029   return decltype(str_format_internal::FormatConvertImpl(
00030       std::declval<const Arg&>(), std::declval<const ConversionSpec&>(),
00031       std::declval<FormatSinkImpl*>()))::kConv;
00032 }
00033 
00034 #if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
00035 
00036 constexpr bool ContainsChar(const char* chars, char c) {
00037   return *chars == c || (*chars && ContainsChar(chars + 1, c));
00038 }
00039 
00040 // A constexpr compatible list of Convs.
00041 struct ConvList {
00042   const Conv* array;
00043   int count;
00044 
00045   // We do the bound check here to avoid having to do it on the callers.
00046   // Returning an empty Conv has the same effect as short circuiting because it
00047   // will never match any conversion.
00048   constexpr Conv operator[](int i) const {
00049     return i < count ? array[i] : Conv{};
00050   }
00051 
00052   constexpr ConvList without_front() const {
00053     return count != 0 ? ConvList{array + 1, count - 1} : *this;
00054   }
00055 };
00056 
00057 template <size_t count>
00058 struct ConvListT {
00059   // Make sure the array has size > 0.
00060   Conv list[count ? count : 1];
00061 };
00062 
00063 constexpr char GetChar(string_view str, size_t index) {
00064   return index < str.size() ? str[index] : char{};
00065 }
00066 
00067 constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
00068   return len <= str.size() ? string_view(str.data() + len, str.size() - len)
00069                            : string_view();
00070 }
00071 
00072 constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
00073   return ContainsChar(chars, GetChar(format, 0))
00074              ? ConsumeAnyOf(ConsumeFront(format), chars)
00075              : format;
00076 }
00077 
00078 constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
00079 
00080 // Helper class for the ParseDigits function.
00081 // It encapsulates the two return values we need there.
00082 struct Integer {
00083   string_view format;
00084   int value;
00085 
00086   // If the next character is a '$', consume it.
00087   // Otherwise, make `this` an invalid positional argument.
00088   constexpr Integer ConsumePositionalDollar() const {
00089     return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
00090                                      : Integer{format, 0};
00091   }
00092 };
00093 
00094 constexpr Integer ParseDigits(string_view format, int value = 0) {
00095   return IsDigit(GetChar(format, 0))
00096              ? ParseDigits(ConsumeFront(format),
00097                            10 * value + GetChar(format, 0) - '0')
00098              : Integer{format, value};
00099 }
00100 
00101 // Parse digits for a positional argument.
00102 // The parsing also consumes the '$'.
00103 constexpr Integer ParsePositional(string_view format) {
00104   return ParseDigits(format).ConsumePositionalDollar();
00105 }
00106 
00107 // Parses a single conversion specifier.
00108 // See ConvParser::Run() for post conditions.
00109 class ConvParser {
00110   constexpr ConvParser SetFormat(string_view format) const {
00111     return ConvParser(format, args_, error_, arg_position_, is_positional_);
00112   }
00113 
00114   constexpr ConvParser SetArgs(ConvList args) const {
00115     return ConvParser(format_, args, error_, arg_position_, is_positional_);
00116   }
00117 
00118   constexpr ConvParser SetError(bool error) const {
00119     return ConvParser(format_, args_, error_ || error, arg_position_,
00120                       is_positional_);
00121   }
00122 
00123   constexpr ConvParser SetArgPosition(int arg_position) const {
00124     return ConvParser(format_, args_, error_, arg_position, is_positional_);
00125   }
00126 
00127   // Consumes the next arg and verifies that it matches `conv`.
00128   // `error_` is set if there is no next arg or if it doesn't match `conv`.
00129   constexpr ConvParser ConsumeNextArg(char conv) const {
00130     return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
00131   }
00132 
00133   // Verify that positional argument `i.value` matches `conv`.
00134   // `error_` is set if `i.value` is not a valid argument or if it doesn't
00135   // match.
00136   constexpr ConvParser VerifyPositional(Integer i, char conv) const {
00137     return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
00138   }
00139 
00140   // Parse the position of the arg and store it in `arg_position_`.
00141   constexpr ConvParser ParseArgPosition(Integer arg) const {
00142     return SetFormat(arg.format).SetArgPosition(arg.value);
00143   }
00144 
00145   // Consume the flags.
00146   constexpr ConvParser ParseFlags() const {
00147     return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
00148   }
00149 
00150   // Consume the width.
00151   // If it is '*', we verify that it matches `args_`. `error_` is set if it
00152   // doesn't match.
00153   constexpr ConvParser ParseWidth() const {
00154     return IsDigit(GetChar(format_, 0))
00155                ? SetFormat(ParseDigits(format_).format)
00156                : GetChar(format_, 0) == '*'
00157                      ? is_positional_
00158                            ? VerifyPositional(
00159                                  ParsePositional(ConsumeFront(format_)), '*')
00160                            : SetFormat(ConsumeFront(format_))
00161                                  .ConsumeNextArg('*')
00162                      : *this;
00163   }
00164 
00165   // Consume the precision.
00166   // If it is '*', we verify that it matches `args_`. `error_` is set if it
00167   // doesn't match.
00168   constexpr ConvParser ParsePrecision() const {
00169     return GetChar(format_, 0) != '.'
00170                ? *this
00171                : GetChar(format_, 1) == '*'
00172                      ? is_positional_
00173                            ? VerifyPositional(
00174                                  ParsePositional(ConsumeFront(format_, 2)), '*')
00175                            : SetFormat(ConsumeFront(format_, 2))
00176                                  .ConsumeNextArg('*')
00177                      : SetFormat(ParseDigits(ConsumeFront(format_)).format);
00178   }
00179 
00180   // Consume the length characters.
00181   constexpr ConvParser ParseLength() const {
00182     return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
00183   }
00184 
00185   // Consume the conversion character and verify that it matches `args_`.
00186   // `error_` is set if it doesn't match.
00187   constexpr ConvParser ParseConversion() const {
00188     return is_positional_
00189                ? VerifyPositional({ConsumeFront(format_), arg_position_},
00190                                   GetChar(format_, 0))
00191                : ConsumeNextArg(GetChar(format_, 0))
00192                      .SetFormat(ConsumeFront(format_));
00193   }
00194 
00195   constexpr ConvParser(string_view format, ConvList args, bool error,
00196                        int arg_position, bool is_positional)
00197       : format_(format),
00198         args_(args),
00199         error_(error),
00200         arg_position_(arg_position),
00201         is_positional_(is_positional) {}
00202 
00203  public:
00204   constexpr ConvParser(string_view format, ConvList args, bool is_positional)
00205       : format_(format),
00206         args_(args),
00207         error_(false),
00208         arg_position_(0),
00209         is_positional_(is_positional) {}
00210 
00211   // Consume the whole conversion specifier.
00212   // `format()` will be set to the character after the conversion character.
00213   // `error()` will be set if any of the arguments do not match.
00214   constexpr ConvParser Run() const {
00215     return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
00216         .ParseFlags()
00217         .ParseWidth()
00218         .ParsePrecision()
00219         .ParseLength()
00220         .ParseConversion();
00221   }
00222 
00223   constexpr string_view format() const { return format_; }
00224   constexpr ConvList args() const { return args_; }
00225   constexpr bool error() const { return error_; }
00226   constexpr bool is_positional() const { return is_positional_; }
00227 
00228  private:
00229   string_view format_;
00230   // Current list of arguments. If we are not in positional mode we will consume
00231   // from the front.
00232   ConvList args_;
00233   bool error_;
00234   // Holds the argument position of the conversion character, if we are in
00235   // positional mode. Otherwise, it is unspecified.
00236   int arg_position_;
00237   // Whether we are in positional mode.
00238   // It changes the behavior of '*' and where to find the converted argument.
00239   bool is_positional_;
00240 };
00241 
00242 // Parses a whole format expression.
00243 // See FormatParser::Run().
00244 class FormatParser {
00245   static constexpr bool FoundPercent(string_view format) {
00246     return format.empty() ||
00247            (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
00248   }
00249 
00250   // We use an inner function to increase the recursion limit.
00251   // The inner function consumes up to `limit` characters on every run.
00252   // This increases the limit from 512 to ~512*limit.
00253   static constexpr string_view ConsumeNonPercentInner(string_view format,
00254                                                       int limit = 20) {
00255     return FoundPercent(format) || !limit
00256                ? format
00257                : ConsumeNonPercentInner(
00258                      ConsumeFront(format, GetChar(format, 0) == '%' &&
00259                                                   GetChar(format, 1) == '%'
00260                                               ? 2
00261                                               : 1),
00262                      limit - 1);
00263   }
00264 
00265   // Consume characters until the next conversion spec %.
00266   // It skips %%.
00267   static constexpr string_view ConsumeNonPercent(string_view format) {
00268     return FoundPercent(format)
00269                ? format
00270                : ConsumeNonPercent(ConsumeNonPercentInner(format));
00271   }
00272 
00273   static constexpr bool IsPositional(string_view format) {
00274     return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
00275                                        : GetChar(format, 0) == '$';
00276   }
00277 
00278   constexpr bool RunImpl(bool is_positional) const {
00279     // In non-positional mode we require all arguments to be consumed.
00280     // In positional mode just reaching the end of the format without errors is
00281     // enough.
00282     return (format_.empty() && (is_positional || args_.count == 0)) ||
00283            (!format_.empty() &&
00284             ValidateArg(
00285                 ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
00286   }
00287 
00288   constexpr bool ValidateArg(ConvParser conv) const {
00289     return !conv.error() && FormatParser(conv.format(), conv.args())
00290                                 .RunImpl(conv.is_positional());
00291   }
00292 
00293  public:
00294   constexpr FormatParser(string_view format, ConvList args)
00295       : format_(ConsumeNonPercent(format)), args_(args) {}
00296 
00297   // Runs the parser for `format` and `args`.
00298   // It verifies that the format is valid and that all conversion specifiers
00299   // match the arguments passed.
00300   // In non-positional mode it also verfies that all arguments are consumed.
00301   constexpr bool Run() const {
00302     return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
00303   }
00304 
00305  private:
00306   string_view format_;
00307   // Current list of arguments.
00308   // If we are not in positional mode we will consume from the front and will
00309   // have to be empty in the end.
00310   ConvList args_;
00311 };
00312 
00313 template <Conv... C>
00314 constexpr bool ValidFormatImpl(string_view format) {
00315   return FormatParser(format,
00316                       {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
00317       .Run();
00318 }
00319 
00320 #endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
00321 
00322 }  // namespace str_format_internal
00323 }  // namespace absl
00324 
00325 #endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:14