abseil-cpp/absl/strings/internal/str_format/checker.h
Go to the documentation of this file.
1 // Copyright 2020 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
16 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
17 
18 #include "absl/base/attributes.h"
19 #include "absl/strings/internal/str_format/arg.h"
20 #include "absl/strings/internal/str_format/extension.h"
21 
22 // Compile time check support for entry points.
23 
24 #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
25 // We disable format checker under vscode intellisense compilation.
26 // See https://github.com/microsoft/vscode-cpptools/issues/3683 for
27 // more details.
28 #if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && \
29  !defined(__INTELLISENSE__)
30 #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
31 #endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) &&
32  // !defined(__INTELLISENSE__)
33 #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
34 
35 namespace absl {
37 namespace str_format_internal {
38 
39 constexpr bool AllOf() { return true; }
40 
41 template <typename... T>
42 constexpr bool AllOf(bool b, T... t) {
43  return b && AllOf(t...);
44 }
45 
46 #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
47 
48 constexpr bool ContainsChar(const char* chars, char c) {
49  return *chars == c || (*chars && ContainsChar(chars + 1, c));
50 }
51 
52 // A constexpr compatible list of Convs.
53 struct ConvList {
55  int count;
56 
57  // We do the bound check here to avoid having to do it on the callers.
58  // Returning an empty FormatConversionCharSet has the same effect as
59  // short circuiting because it will never match any conversion.
60  constexpr FormatConversionCharSet operator[](int i) const {
61  return i < count ? array[i] : FormatConversionCharSet{};
62  }
63 
64  constexpr ConvList without_front() const {
65  return count != 0 ? ConvList{array + 1, count - 1} : *this;
66  }
67 };
68 
69 template <size_t count>
70 struct ConvListT {
71  // Make sure the array has size > 0.
73 };
74 
75 constexpr char GetChar(string_view str, size_t index) {
76  return index < str.size() ? str[index] : char{};
77 }
78 
79 constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
80  return len <= str.size() ? string_view(str.data() + len, str.size() - len)
81  : string_view();
82 }
83 
84 constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
85  return ContainsChar(chars, GetChar(format, 0))
86  ? ConsumeAnyOf(ConsumeFront(format), chars)
87  : format;
88 }
89 
90 constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
91 
92 // Helper class for the ParseDigits function.
93 // It encapsulates the two return values we need there.
94 struct Integer {
96  int value;
97 
98  // If the next character is a '$', consume it.
99  // Otherwise, make `this` an invalid positional argument.
100  constexpr Integer ConsumePositionalDollar() const {
101  return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
102  : Integer{format, 0};
103  }
104 };
105 
106 constexpr Integer ParseDigits(string_view format, int value = 0) {
107  return IsDigit(GetChar(format, 0))
108  ? ParseDigits(ConsumeFront(format),
109  10 * value + GetChar(format, 0) - '0')
110  : Integer{format, value};
111 }
112 
113 // Parse digits for a positional argument.
114 // The parsing also consumes the '$'.
115 constexpr Integer ParsePositional(string_view format) {
116  return ParseDigits(format).ConsumePositionalDollar();
117 }
118 
119 // Parses a single conversion specifier.
120 // See ConvParser::Run() for post conditions.
121 class ConvParser {
122  constexpr ConvParser SetFormat(string_view format) const {
123  return ConvParser(format, args_, error_, arg_position_, is_positional_);
124  }
125 
126  constexpr ConvParser SetArgs(ConvList args) const {
127  return ConvParser(format_, args, error_, arg_position_, is_positional_);
128  }
129 
130  constexpr ConvParser SetError(bool error) const {
131  return ConvParser(format_, args_, error_ || error, arg_position_,
132  is_positional_);
133  }
134 
135  constexpr ConvParser SetArgPosition(int arg_position) const {
136  return ConvParser(format_, args_, error_, arg_position, is_positional_);
137  }
138 
139  // Consumes the next arg and verifies that it matches `conv`.
140  // `error_` is set if there is no next arg or if it doesn't match `conv`.
141  constexpr ConvParser ConsumeNextArg(char conv) const {
142  return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
143  }
144 
145  // Verify that positional argument `i.value` matches `conv`.
146  // `error_` is set if `i.value` is not a valid argument or if it doesn't
147  // match.
148  constexpr ConvParser VerifyPositional(Integer i, char conv) const {
149  return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
150  }
151 
152  // Parse the position of the arg and store it in `arg_position_`.
153  constexpr ConvParser ParseArgPosition(Integer arg) const {
154  return SetFormat(arg.format).SetArgPosition(arg.value);
155  }
156 
157  // Consume the flags.
158  constexpr ConvParser ParseFlags() const {
159  return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
160  }
161 
162  // Consume the width.
163  // If it is '*', we verify that it matches `args_`. `error_` is set if it
164  // doesn't match.
165  constexpr ConvParser ParseWidth() const {
166  return IsDigit(GetChar(format_, 0))
167  ? SetFormat(ParseDigits(format_).format)
168  : GetChar(format_, 0) == '*'
169  ? is_positional_
170  ? VerifyPositional(
171  ParsePositional(ConsumeFront(format_)), '*')
172  : SetFormat(ConsumeFront(format_))
173  .ConsumeNextArg('*')
174  : *this;
175  }
176 
177  // Consume the precision.
178  // If it is '*', we verify that it matches `args_`. `error_` is set if it
179  // doesn't match.
180  constexpr ConvParser ParsePrecision() const {
181  return GetChar(format_, 0) != '.'
182  ? *this
183  : GetChar(format_, 1) == '*'
184  ? is_positional_
185  ? VerifyPositional(
186  ParsePositional(ConsumeFront(format_, 2)), '*')
187  : SetFormat(ConsumeFront(format_, 2))
188  .ConsumeNextArg('*')
189  : SetFormat(ParseDigits(ConsumeFront(format_)).format);
190  }
191 
192  // Consume the length characters.
193  constexpr ConvParser ParseLength() const {
194  return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
195  }
196 
197  // Consume the conversion character and verify that it matches `args_`.
198  // `error_` is set if it doesn't match.
199  constexpr ConvParser ParseConversion() const {
200  return is_positional_
201  ? VerifyPositional({ConsumeFront(format_), arg_position_},
202  GetChar(format_, 0))
203  : ConsumeNextArg(GetChar(format_, 0))
204  .SetFormat(ConsumeFront(format_));
205  }
206 
207  constexpr ConvParser(string_view format, ConvList args, bool error,
208  int arg_position, bool is_positional)
209  : format_(format),
210  args_(args),
211  error_(error),
212  arg_position_(arg_position),
213  is_positional_(is_positional) {}
214 
215  public:
216  constexpr ConvParser(string_view format, ConvList args, bool is_positional)
217  : format_(format),
218  args_(args),
219  error_(false),
220  arg_position_(0),
221  is_positional_(is_positional) {}
222 
223  // Consume the whole conversion specifier.
224  // `format()` will be set to the character after the conversion character.
225  // `error()` will be set if any of the arguments do not match.
226  constexpr ConvParser Run() const {
227  return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
228  .ParseFlags()
229  .ParseWidth()
230  .ParsePrecision()
231  .ParseLength()
232  .ParseConversion();
233  }
234 
235  constexpr string_view format() const { return format_; }
236  constexpr ConvList args() const { return args_; }
237  constexpr bool error() const { return error_; }
238  constexpr bool is_positional() const { return is_positional_; }
239 
240  private:
241  string_view format_;
242  // Current list of arguments. If we are not in positional mode we will consume
243  // from the front.
244  ConvList args_;
245  bool error_;
246  // Holds the argument position of the conversion character, if we are in
247  // positional mode. Otherwise, it is unspecified.
248  int arg_position_;
249  // Whether we are in positional mode.
250  // It changes the behavior of '*' and where to find the converted argument.
251  bool is_positional_;
252 };
253 
254 // Parses a whole format expression.
255 // See FormatParser::Run().
256 class FormatParser {
257  static constexpr bool FoundPercent(string_view format) {
258  return format.empty() ||
259  (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
260  }
261 
262  // We use an inner function to increase the recursion limit.
263  // The inner function consumes up to `limit` characters on every run.
264  // This increases the limit from 512 to ~512*limit.
265  static constexpr string_view ConsumeNonPercentInner(string_view format,
266  int limit = 20) {
267  return FoundPercent(format) || !limit
268  ? format
269  : ConsumeNonPercentInner(
270  ConsumeFront(format, GetChar(format, 0) == '%' &&
271  GetChar(format, 1) == '%'
272  ? 2
273  : 1),
274  limit - 1);
275  }
276 
277  // Consume characters until the next conversion spec %.
278  // It skips %%.
279  static constexpr string_view ConsumeNonPercent(string_view format) {
280  return FoundPercent(format)
281  ? format
282  : ConsumeNonPercent(ConsumeNonPercentInner(format));
283  }
284 
285  static constexpr bool IsPositional(string_view format) {
286  return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
287  : GetChar(format, 0) == '$';
288  }
289 
290  constexpr bool RunImpl(bool is_positional) const {
291  // In non-positional mode we require all arguments to be consumed.
292  // In positional mode just reaching the end of the format without errors is
293  // enough.
294  return (format_.empty() && (is_positional || args_.count == 0)) ||
295  (!format_.empty() &&
296  ValidateArg(
297  ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
298  }
299 
300  constexpr bool ValidateArg(ConvParser conv) const {
301  return !conv.error() && FormatParser(conv.format(), conv.args())
302  .RunImpl(conv.is_positional());
303  }
304 
305  public:
306  constexpr FormatParser(string_view format, ConvList args)
307  : format_(ConsumeNonPercent(format)), args_(args) {}
308 
309  // Runs the parser for `format` and `args`.
310  // It verifies that the format is valid and that all conversion specifiers
311  // match the arguments passed.
312  // In non-positional mode it also verfies that all arguments are consumed.
313  constexpr bool Run() const {
314  return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
315  }
316 
317  private:
318  string_view format_;
319  // Current list of arguments.
320  // If we are not in positional mode we will consume from the front and will
321  // have to be empty in the end.
322  ConvList args_;
323 };
324 
325 template <FormatConversionCharSet... C>
326 constexpr bool ValidFormatImpl(string_view format) {
327  return FormatParser(format,
328  {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
329  .Run();
330 }
331 
332 #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
333 
334 } // namespace str_format_internal
336 } // namespace absl
337 
338 #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
xds_interop_client.str
str
Definition: xds_interop_client.py:487
http2_test_server.format
format
Definition: http2_test_server.py:118
absl::str_format_internal::Contains
constexpr bool Contains(FormatConversionCharSet set, char c)
Definition: abseil-cpp/absl/strings/internal/str_format/extension.h:421
fix_build_deps.c
list c
Definition: fix_build_deps.py:490
false
#define false
Definition: setup_once.h:323
C
#define C(x)
Definition: abseil-cpp/absl/hash/internal/city_test.cc:49
absl::string_view
Definition: abseil-cpp/absl/strings/string_view.h:167
array
PHP_PROTO_OBJECT_FREE_END PHP_PROTO_OBJECT_DTOR_END intern array
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/array.c:111
absl::str_format_internal::AllOf
constexpr bool AllOf()
Definition: abseil-cpp/absl/strings/internal/str_format/checker.h:39
error
grpc_error_handle error
Definition: retry_filter.cc:499
arg::value
void * value
Definition: cmdline.cc:44
args_
grpc_channel_args * args_
Definition: grpclb.cc:513
ABSL_NAMESPACE_END
#define ABSL_NAMESPACE_END
Definition: third_party/abseil-cpp/absl/base/config.h:171
T
#define T(upbtypeconst, upbtype, ctype, default_value)
absl::debugging_internal::IsDigit
static bool IsDigit(char c)
Definition: abseil-cpp/absl/debugging/internal/demangle.cc:383
ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_BEGIN
Definition: third_party/abseil-cpp/absl/base/config.h:170
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
array
Definition: undname.c:101
conv
const FormatConversionSpecImpl & conv
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:449
arg
Definition: cmdline.cc:40
b
uint64_t b
Definition: abseil-cpp/absl/container/internal/layout_test.cc:53
value
const char * value
Definition: hpack_parser_table.cc:165
count
int * count
Definition: bloaty/third_party/googletest/googlemock/test/gmock_stress_test.cc:96
absl::str_format_internal::LengthMod::t
@ t
index
int index
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/protobuf.h:1184
string_view
absl::string_view string_view
Definition: attr.cc:22
absl::FormatConversionCharSet
FormatConversionCharSet
Definition: abseil-cpp/absl/strings/str_format.h:737
absl
Definition: abseil-cpp/absl/algorithm/algorithm.h:31
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
error_
grpc_error_handle error_
Definition: message_decompress_filter.cc:112
googletest-break-on-failure-unittest.Run
def Run(command)
Definition: bloaty/third_party/googletest/googletest/test/googletest-break-on-failure-unittest.py:76
str_format_internal
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230


grpc
Author(s):
autogenerated on Fri May 16 2025 02:57:53