checker.h 12 KB

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