checker.h
Go to the documentation of this file.
1 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
2 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
3 
6 
7 // Compile time check support for entry points.
8 
9 #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
10 #if defined(__clang__) && !defined(__native_client__)
11 #if __has_attribute(enable_if)
12 #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
13 #endif // __has_attribute(enable_if)
14 #endif // defined(__clang__) && !defined(__native_client__)
15 #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
16 
17 namespace absl {
18 namespace str_format_internal {
19 
20 constexpr bool AllOf() { return true; }
21 
22 template <typename... T>
23 constexpr bool AllOf(bool b, T... t) {
24  return b && AllOf(t...);
25 }
26 
27 template <typename Arg>
28 constexpr Conv ArgumentToConv() {
30  std::declval<const Arg&>(), std::declval<const ConversionSpec&>(),
31  std::declval<FormatSinkImpl*>()))::kConv;
32 }
33 
34 #if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
35 
36 constexpr bool ContainsChar(const char* chars, char c) {
37  return *chars == c || (*chars && ContainsChar(chars + 1, c));
38 }
39 
40 // A constexpr compatible list of Convs.
41 struct ConvList {
42  const Conv* array;
43  int count;
44 
45  // We do the bound check here to avoid having to do it on the callers.
46  // Returning an empty Conv has the same effect as short circuiting because it
47  // will never match any conversion.
48  constexpr Conv operator[](int i) const {
49  return i < count ? array[i] : Conv{};
50  }
51 
52  constexpr ConvList without_front() const {
53  return count != 0 ? ConvList{array + 1, count - 1} : *this;
54  }
55 };
56 
57 template <size_t count>
58 struct ConvListT {
59  // Make sure the array has size > 0.
60  Conv list[count ? count : 1];
61 };
62 
63 constexpr char GetChar(string_view str, size_t index) {
64  return index < str.size() ? str[index] : char{};
65 }
66 
67 constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
68  return len <= str.size() ? string_view(str.data() + len, str.size() - len)
69  : string_view();
70 }
71 
72 constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
73  return ContainsChar(chars, GetChar(format, 0))
74  ? ConsumeAnyOf(ConsumeFront(format), chars)
75  : format;
76 }
77 
78 constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
79 
80 // Helper class for the ParseDigits function.
81 // It encapsulates the two return values we need there.
82 struct Integer {
84  int value;
85 
86  // If the next character is a '$', consume it.
87  // Otherwise, make `this` an invalid positional argument.
88  constexpr Integer ConsumePositionalDollar() const {
89  return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
90  : Integer{format, 0};
91  }
92 };
93 
94 constexpr Integer ParseDigits(string_view format, int value = 0) {
95  return IsDigit(GetChar(format, 0))
96  ? ParseDigits(ConsumeFront(format),
97  10 * value + GetChar(format, 0) - '0')
98  : Integer{format, value};
99 }
100 
101 // Parse digits for a positional argument.
102 // The parsing also consumes the '$'.
103 constexpr Integer ParsePositional(string_view format) {
104  return ParseDigits(format).ConsumePositionalDollar();
105 }
106 
107 // Parses a single conversion specifier.
108 // See ConvParser::Run() for post conditions.
109 class ConvParser {
110  constexpr ConvParser SetFormat(string_view format) const {
111  return ConvParser(format, args_, error_, arg_position_, is_positional_);
112  }
113 
114  constexpr ConvParser SetArgs(ConvList args) const {
115  return ConvParser(format_, args, error_, arg_position_, is_positional_);
116  }
117 
118  constexpr ConvParser SetError(bool error) const {
119  return ConvParser(format_, args_, error_ || error, arg_position_,
120  is_positional_);
121  }
122 
123  constexpr ConvParser SetArgPosition(int arg_position) const {
124  return ConvParser(format_, args_, error_, arg_position, is_positional_);
125  }
126 
127  // Consumes the next arg and verifies that it matches `conv`.
128  // `error_` is set if there is no next arg or if it doesn't match `conv`.
129  constexpr ConvParser ConsumeNextArg(char conv) const {
130  return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
131  }
132 
133  // Verify that positional argument `i.value` matches `conv`.
134  // `error_` is set if `i.value` is not a valid argument or if it doesn't
135  // match.
136  constexpr ConvParser VerifyPositional(Integer i, char conv) const {
137  return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
138  }
139 
140  // Parse the position of the arg and store it in `arg_position_`.
141  constexpr ConvParser ParseArgPosition(Integer arg) const {
142  return SetFormat(arg.format).SetArgPosition(arg.value);
143  }
144 
145  // Consume the flags.
146  constexpr ConvParser ParseFlags() const {
147  return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
148  }
149 
150  // Consume the width.
151  // If it is '*', we verify that it matches `args_`. `error_` is set if it
152  // doesn't match.
153  constexpr ConvParser ParseWidth() const {
154  return IsDigit(GetChar(format_, 0))
155  ? SetFormat(ParseDigits(format_).format)
156  : GetChar(format_, 0) == '*'
157  ? is_positional_
158  ? VerifyPositional(
159  ParsePositional(ConsumeFront(format_)), '*')
160  : SetFormat(ConsumeFront(format_))
161  .ConsumeNextArg('*')
162  : *this;
163  }
164 
165  // Consume the precision.
166  // If it is '*', we verify that it matches `args_`. `error_` is set if it
167  // doesn't match.
168  constexpr ConvParser ParsePrecision() const {
169  return GetChar(format_, 0) != '.'
170  ? *this
171  : GetChar(format_, 1) == '*'
172  ? is_positional_
173  ? VerifyPositional(
174  ParsePositional(ConsumeFront(format_, 2)), '*')
175  : SetFormat(ConsumeFront(format_, 2))
176  .ConsumeNextArg('*')
177  : SetFormat(ParseDigits(ConsumeFront(format_)).format);
178  }
179 
180  // Consume the length characters.
181  constexpr ConvParser ParseLength() const {
182  return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
183  }
184 
185  // Consume the conversion character and verify that it matches `args_`.
186  // `error_` is set if it doesn't match.
187  constexpr ConvParser ParseConversion() const {
188  return is_positional_
189  ? VerifyPositional({ConsumeFront(format_), arg_position_},
190  GetChar(format_, 0))
191  : ConsumeNextArg(GetChar(format_, 0))
192  .SetFormat(ConsumeFront(format_));
193  }
194 
195  constexpr ConvParser(string_view format, ConvList args, bool error,
196  int arg_position, bool is_positional)
197  : format_(format),
198  args_(args),
199  error_(error),
200  arg_position_(arg_position),
201  is_positional_(is_positional) {}
202 
203  public:
204  constexpr ConvParser(string_view format, ConvList args, bool is_positional)
205  : format_(format),
206  args_(args),
207  error_(false),
208  arg_position_(0),
209  is_positional_(is_positional) {}
210 
211  // Consume the whole conversion specifier.
212  // `format()` will be set to the character after the conversion character.
213  // `error()` will be set if any of the arguments do not match.
214  constexpr ConvParser Run() const {
215  return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
216  .ParseFlags()
217  .ParseWidth()
218  .ParsePrecision()
219  .ParseLength()
220  .ParseConversion();
221  }
222 
223  constexpr string_view format() const { return format_; }
224  constexpr ConvList args() const { return args_; }
225  constexpr bool error() const { return error_; }
226  constexpr bool is_positional() const { return is_positional_; }
227 
228  private:
229  string_view format_;
230  // Current list of arguments. If we are not in positional mode we will consume
231  // from the front.
232  ConvList args_;
233  bool error_;
234  // Holds the argument position of the conversion character, if we are in
235  // positional mode. Otherwise, it is unspecified.
236  int arg_position_;
237  // Whether we are in positional mode.
238  // It changes the behavior of '*' and where to find the converted argument.
239  bool is_positional_;
240 };
241 
242 // Parses a whole format expression.
243 // See FormatParser::Run().
244 class FormatParser {
245  static constexpr bool FoundPercent(string_view format) {
246  return format.empty() ||
247  (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
248  }
249 
250  // We use an inner function to increase the recursion limit.
251  // The inner function consumes up to `limit` characters on every run.
252  // This increases the limit from 512 to ~512*limit.
253  static constexpr string_view ConsumeNonPercentInner(string_view format,
254  int limit = 20) {
255  return FoundPercent(format) || !limit
256  ? format
257  : ConsumeNonPercentInner(
258  ConsumeFront(format, GetChar(format, 0) == '%' &&
259  GetChar(format, 1) == '%'
260  ? 2
261  : 1),
262  limit - 1);
263  }
264 
265  // Consume characters until the next conversion spec %.
266  // It skips %%.
267  static constexpr string_view ConsumeNonPercent(string_view format) {
268  return FoundPercent(format)
269  ? format
270  : ConsumeNonPercent(ConsumeNonPercentInner(format));
271  }
272 
273  static constexpr bool IsPositional(string_view format) {
274  return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
275  : GetChar(format, 0) == '$';
276  }
277 
278  constexpr bool RunImpl(bool is_positional) const {
279  // In non-positional mode we require all arguments to be consumed.
280  // In positional mode just reaching the end of the format without errors is
281  // enough.
282  return (format_.empty() && (is_positional || args_.count == 0)) ||
283  (!format_.empty() &&
284  ValidateArg(
285  ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
286  }
287 
288  constexpr bool ValidateArg(ConvParser conv) const {
289  return !conv.error() && FormatParser(conv.format(), conv.args())
290  .RunImpl(conv.is_positional());
291  }
292 
293  public:
294  constexpr FormatParser(string_view format, ConvList args)
295  : format_(ConsumeNonPercent(format)), args_(args) {}
296 
297  // Runs the parser for `format` and `args`.
298  // It verifies that the format is valid and that all conversion specifiers
299  // match the arguments passed.
300  // In non-positional mode it also verfies that all arguments are consumed.
301  constexpr bool Run() const {
302  return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
303  }
304 
305  private:
306  string_view format_;
307  // Current list of arguments.
308  // If we are not in positional mode we will consume from the front and will
309  // have to be empty in the end.
310  ConvList args_;
311 };
312 
313 template <Conv... C>
314 constexpr bool ValidFormatImpl(string_view format) {
315  return FormatParser(format,
316  {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
317  .Run();
318 }
319 
320 #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
321 
322 } // namespace str_format_internal
323 } // namespace absl
324 
325 #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
std::vector< std::string > args_
Definition: parse.cc:130
Definition: algorithm.h:29
constexpr size_type size() const noexcept
Definition: string_view.h:260
size_t value
std::string format(const std::string &, const time_point< seconds > &, const femtoseconds &, const time_zone &)
static bool IsDigit(char c)
Definition: demangle.cc:369
constexpr Conv ArgumentToConv()
Definition: checker.h:28
ConvertResult< Conv::s > FormatConvertImpl(const std::string &v, const ConversionSpec conv, FormatSinkImpl *sink)
Definition: arg.cc:255
constexpr bool empty() const noexcept
Definition: string_view.h:277
constexpr bool Contains(Conv set, char c)
Definition: extension.h:381
constexpr bool AllOf()
Definition: checker.h:20
void * arg
Definition: mutex.cc:292
constexpr const_pointer data() const noexcept
Definition: string_view.h:302
uint64_t b
Definition: layout_test.cc:50
#define C(x)
Definition: city_test.cc:47


abseil_cpp
Author(s):
autogenerated on Mon Feb 28 2022 21:31:17