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
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
00041 struct ConvList {
00042 const Conv* array;
00043 int count;
00044
00045
00046
00047
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
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
00081
00082 struct Integer {
00083 string_view format;
00084 int value;
00085
00086
00087
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
00102
00103 constexpr Integer ParsePositional(string_view format) {
00104 return ParseDigits(format).ConsumePositionalDollar();
00105 }
00106
00107
00108
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
00128
00129 constexpr ConvParser ConsumeNextArg(char conv) const {
00130 return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
00131 }
00132
00133
00134
00135
00136 constexpr ConvParser VerifyPositional(Integer i, char conv) const {
00137 return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
00138 }
00139
00140
00141 constexpr ConvParser ParseArgPosition(Integer arg) const {
00142 return SetFormat(arg.format).SetArgPosition(arg.value);
00143 }
00144
00145
00146 constexpr ConvParser ParseFlags() const {
00147 return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
00148 }
00149
00150
00151
00152
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
00166
00167
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
00181 constexpr ConvParser ParseLength() const {
00182 return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
00183 }
00184
00185
00186
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
00212
00213
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
00231
00232 ConvList args_;
00233 bool error_;
00234
00235
00236 int arg_position_;
00237
00238
00239 bool is_positional_;
00240 };
00241
00242
00243
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
00251
00252
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
00266
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
00280
00281
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
00298
00299
00300
00301 constexpr bool Run() const {
00302 return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
00303 }
00304
00305 private:
00306 string_view format_;
00307
00308
00309
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 }
00323 }
00324
00325 #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_