-- 70f43a482d7d4ae4a255f17ca02b0106653dd600 by Shaindel Schwartz <shaindel@google.com>: Internal change PiperOrigin-RevId: 201571193 -- 93e6e9c2e683158be49d9dd1f5cb1a91d0c0f556 by Abseil Team <absl-team@google.com>: Internal change. PiperOrigin-RevId: 201567108 -- fbd8ee94fbe9f2448e5adf5e88706f9c8216048f by Juemin Yang <jueminyang@google.com>: str_format release PiperOrigin-RevId: 201565129 -- 387faa301555a8a888c4429df52734aa806dca46 by Abseil Team <absl-team@google.com>: Adds a defaulted allocator parameter to the size_type constructor of InlinedVector PiperOrigin-RevId: 201558711 -- 39b15ea2c68d7129d70cbde7e71af900032595ec by Matt Calabrese <calabrese@google.com>: Update the variant implementation to eliminate unnecessary checking on alternative access when the index is known or required to be correct. PiperOrigin-RevId: 201529535 -- adab77f1f7bb363aa534297f22aae2b0f08889ea by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 201458388 -- a701dc0ba62e3cadf0de14203415b91df4ee8151 by Greg Falcon <gfalcon@google.com>: Internal cleanup PiperOrigin-RevId: 201394836 -- 8a7191410b8f440fdfa27f722ff05e451502ab61 by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 201369269 GitOrigin-RevId: 70f43a482d7d4ae4a255f17ca02b0106653dd600 Change-Id: I8ab073b30b4e27405a3b6da2c826bb4f3f0b9af6
		
			
				
	
	
		
			325 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 | 
						|
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 | 
						|
 | 
						|
#include "absl/strings/internal/str_format/arg.h"
 | 
						|
#include "absl/strings/internal/str_format/extension.h"
 | 
						|
 | 
						|
// Compile time check support for entry points.
 | 
						|
 | 
						|
#ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 | 
						|
#if defined(__clang__) && !defined(__native_client__)
 | 
						|
#if __has_attribute(enable_if)
 | 
						|
#define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
 | 
						|
#endif  // __has_attribute(enable_if)
 | 
						|
#endif  // defined(__clang__) && !defined(__native_client__)
 | 
						|
#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 | 
						|
 | 
						|
namespace absl {
 | 
						|
namespace str_format_internal {
 | 
						|
 | 
						|
constexpr bool AllOf() { return true; }
 | 
						|
 | 
						|
template <typename... T>
 | 
						|
constexpr bool AllOf(bool b, T... t) {
 | 
						|
  return b && AllOf(t...);
 | 
						|
}
 | 
						|
 | 
						|
template <typename Arg>
 | 
						|
constexpr Conv ArgumentToConv() {
 | 
						|
  return decltype(str_format_internal::FormatConvertImpl(
 | 
						|
      std::declval<const Arg&>(), std::declval<const ConversionSpec&>(),
 | 
						|
      std::declval<FormatSinkImpl*>()))::kConv;
 | 
						|
}
 | 
						|
 | 
						|
#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 | 
						|
 | 
						|
constexpr bool ContainsChar(const char* chars, char c) {
 | 
						|
  return *chars == c || (*chars && ContainsChar(chars + 1, c));
 | 
						|
}
 | 
						|
 | 
						|
// A constexpr compatible list of Convs.
 | 
						|
struct ConvList {
 | 
						|
  const Conv* array;
 | 
						|
  int count;
 | 
						|
 | 
						|
  // We do the bound check here to avoid having to do it on the callers.
 | 
						|
  // Returning an empty Conv has the same effect as short circuiting because it
 | 
						|
  // will never match any conversion.
 | 
						|
  constexpr Conv operator[](int i) const {
 | 
						|
    return i < count ? array[i] : Conv{};
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr ConvList without_front() const {
 | 
						|
    return count != 0 ? ConvList{array + 1, count - 1} : *this;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
template <size_t count>
 | 
						|
struct ConvListT {
 | 
						|
  // Make sure the array has size > 0.
 | 
						|
  Conv list[count ? count : 1];
 | 
						|
};
 | 
						|
 | 
						|
constexpr char GetChar(string_view str, size_t index) {
 | 
						|
  return index < str.size() ? str[index] : char{};
 | 
						|
}
 | 
						|
 | 
						|
constexpr string_view ConsumeFront(string_view str, size_t len = 1) {
 | 
						|
  return len <= str.size() ? string_view(str.data() + len, str.size() - len)
 | 
						|
                           : string_view();
 | 
						|
}
 | 
						|
 | 
						|
constexpr string_view ConsumeAnyOf(string_view format, const char* chars) {
 | 
						|
  return ContainsChar(chars, GetChar(format, 0))
 | 
						|
             ? ConsumeAnyOf(ConsumeFront(format), chars)
 | 
						|
             : format;
 | 
						|
}
 | 
						|
 | 
						|
constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
 | 
						|
 | 
						|
// Helper class for the ParseDigits function.
 | 
						|
// It encapsulates the two return values we need there.
 | 
						|
struct Integer {
 | 
						|
  string_view format;
 | 
						|
  int value;
 | 
						|
 | 
						|
  // If the next character is a '$', consume it.
 | 
						|
  // Otherwise, make `this` an invalid positional argument.
 | 
						|
  constexpr Integer ConsumePositionalDollar() const {
 | 
						|
    return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value}
 | 
						|
                                     : Integer{format, 0};
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
constexpr Integer ParseDigits(string_view format, int value = 0) {
 | 
						|
  return IsDigit(GetChar(format, 0))
 | 
						|
             ? ParseDigits(ConsumeFront(format),
 | 
						|
                           10 * value + GetChar(format, 0) - '0')
 | 
						|
             : Integer{format, value};
 | 
						|
}
 | 
						|
 | 
						|
// Parse digits for a positional argument.
 | 
						|
// The parsing also consumes the '$'.
 | 
						|
constexpr Integer ParsePositional(string_view format) {
 | 
						|
  return ParseDigits(format).ConsumePositionalDollar();
 | 
						|
}
 | 
						|
 | 
						|
// Parses a single conversion specifier.
 | 
						|
// See ConvParser::Run() for post conditions.
 | 
						|
class ConvParser {
 | 
						|
  constexpr ConvParser SetFormat(string_view format) const {
 | 
						|
    return ConvParser(format, args_, error_, arg_position_, is_positional_);
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr ConvParser SetArgs(ConvList args) const {
 | 
						|
    return ConvParser(format_, args, error_, arg_position_, is_positional_);
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr ConvParser SetError(bool error) const {
 | 
						|
    return ConvParser(format_, args_, error_ || error, arg_position_,
 | 
						|
                      is_positional_);
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr ConvParser SetArgPosition(int arg_position) const {
 | 
						|
    return ConvParser(format_, args_, error_, arg_position, is_positional_);
 | 
						|
  }
 | 
						|
 | 
						|
  // Consumes the next arg and verifies that it matches `conv`.
 | 
						|
  // `error_` is set if there is no next arg or if it doesn't match `conv`.
 | 
						|
  constexpr ConvParser ConsumeNextArg(char conv) const {
 | 
						|
    return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv));
 | 
						|
  }
 | 
						|
 | 
						|
  // Verify that positional argument `i.value` matches `conv`.
 | 
						|
  // `error_` is set if `i.value` is not a valid argument or if it doesn't
 | 
						|
  // match.
 | 
						|
  constexpr ConvParser VerifyPositional(Integer i, char conv) const {
 | 
						|
    return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv));
 | 
						|
  }
 | 
						|
 | 
						|
  // Parse the position of the arg and store it in `arg_position_`.
 | 
						|
  constexpr ConvParser ParseArgPosition(Integer arg) const {
 | 
						|
    return SetFormat(arg.format).SetArgPosition(arg.value);
 | 
						|
  }
 | 
						|
 | 
						|
  // Consume the flags.
 | 
						|
  constexpr ConvParser ParseFlags() const {
 | 
						|
    return SetFormat(ConsumeAnyOf(format_, "-+ #0"));
 | 
						|
  }
 | 
						|
 | 
						|
  // Consume the width.
 | 
						|
  // If it is '*', we verify that it matches `args_`. `error_` is set if it
 | 
						|
  // doesn't match.
 | 
						|
  constexpr ConvParser ParseWidth() const {
 | 
						|
    return IsDigit(GetChar(format_, 0))
 | 
						|
               ? SetFormat(ParseDigits(format_).format)
 | 
						|
               : GetChar(format_, 0) == '*'
 | 
						|
                     ? is_positional_
 | 
						|
                           ? VerifyPositional(
 | 
						|
                                 ParsePositional(ConsumeFront(format_)), '*')
 | 
						|
                           : SetFormat(ConsumeFront(format_))
 | 
						|
                                 .ConsumeNextArg('*')
 | 
						|
                     : *this;
 | 
						|
  }
 | 
						|
 | 
						|
  // Consume the precision.
 | 
						|
  // If it is '*', we verify that it matches `args_`. `error_` is set if it
 | 
						|
  // doesn't match.
 | 
						|
  constexpr ConvParser ParsePrecision() const {
 | 
						|
    return GetChar(format_, 0) != '.'
 | 
						|
               ? *this
 | 
						|
               : GetChar(format_, 1) == '*'
 | 
						|
                     ? is_positional_
 | 
						|
                           ? VerifyPositional(
 | 
						|
                                 ParsePositional(ConsumeFront(format_, 2)), '*')
 | 
						|
                           : SetFormat(ConsumeFront(format_, 2))
 | 
						|
                                 .ConsumeNextArg('*')
 | 
						|
                     : SetFormat(ParseDigits(ConsumeFront(format_)).format);
 | 
						|
  }
 | 
						|
 | 
						|
  // Consume the length characters.
 | 
						|
  constexpr ConvParser ParseLength() const {
 | 
						|
    return SetFormat(ConsumeAnyOf(format_, "lLhjztq"));
 | 
						|
  }
 | 
						|
 | 
						|
  // Consume the conversion character and verify that it matches `args_`.
 | 
						|
  // `error_` is set if it doesn't match.
 | 
						|
  constexpr ConvParser ParseConversion() const {
 | 
						|
    return is_positional_
 | 
						|
               ? VerifyPositional({ConsumeFront(format_), arg_position_},
 | 
						|
                                  GetChar(format_, 0))
 | 
						|
               : ConsumeNextArg(GetChar(format_, 0))
 | 
						|
                     .SetFormat(ConsumeFront(format_));
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr ConvParser(string_view format, ConvList args, bool error,
 | 
						|
                       int arg_position, bool is_positional)
 | 
						|
      : format_(format),
 | 
						|
        args_(args),
 | 
						|
        error_(error),
 | 
						|
        arg_position_(arg_position),
 | 
						|
        is_positional_(is_positional) {}
 | 
						|
 | 
						|
 public:
 | 
						|
  constexpr ConvParser(string_view format, ConvList args, bool is_positional)
 | 
						|
      : format_(format),
 | 
						|
        args_(args),
 | 
						|
        error_(false),
 | 
						|
        arg_position_(0),
 | 
						|
        is_positional_(is_positional) {}
 | 
						|
 | 
						|
  // Consume the whole conversion specifier.
 | 
						|
  // `format()` will be set to the character after the conversion character.
 | 
						|
  // `error()` will be set if any of the arguments do not match.
 | 
						|
  constexpr ConvParser Run() const {
 | 
						|
    return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this)
 | 
						|
        .ParseFlags()
 | 
						|
        .ParseWidth()
 | 
						|
        .ParsePrecision()
 | 
						|
        .ParseLength()
 | 
						|
        .ParseConversion();
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr string_view format() const { return format_; }
 | 
						|
  constexpr ConvList args() const { return args_; }
 | 
						|
  constexpr bool error() const { return error_; }
 | 
						|
  constexpr bool is_positional() const { return is_positional_; }
 | 
						|
 | 
						|
 private:
 | 
						|
  string_view format_;
 | 
						|
  // Current list of arguments. If we are not in positional mode we will consume
 | 
						|
  // from the front.
 | 
						|
  ConvList args_;
 | 
						|
  bool error_;
 | 
						|
  // Holds the argument position of the conversion character, if we are in
 | 
						|
  // positional mode. Otherwise, it is unspecified.
 | 
						|
  int arg_position_;
 | 
						|
  // Whether we are in positional mode.
 | 
						|
  // It changes the behavior of '*' and where to find the converted argument.
 | 
						|
  bool is_positional_;
 | 
						|
};
 | 
						|
 | 
						|
// Parses a whole format expression.
 | 
						|
// See FormatParser::Run().
 | 
						|
class FormatParser {
 | 
						|
  static constexpr bool FoundPercent(string_view format) {
 | 
						|
    return format.empty() ||
 | 
						|
           (GetChar(format, 0) == '%' && GetChar(format, 1) != '%');
 | 
						|
  }
 | 
						|
 | 
						|
  // We use an inner function to increase the recursion limit.
 | 
						|
  // The inner function consumes up to `limit` characters on every run.
 | 
						|
  // This increases the limit from 512 to ~512*limit.
 | 
						|
  static constexpr string_view ConsumeNonPercentInner(string_view format,
 | 
						|
                                                      int limit = 20) {
 | 
						|
    return FoundPercent(format) || !limit
 | 
						|
               ? format
 | 
						|
               : ConsumeNonPercentInner(
 | 
						|
                     ConsumeFront(format, GetChar(format, 0) == '%' &&
 | 
						|
                                                  GetChar(format, 1) == '%'
 | 
						|
                                              ? 2
 | 
						|
                                              : 1),
 | 
						|
                     limit - 1);
 | 
						|
  }
 | 
						|
 | 
						|
  // Consume characters until the next conversion spec %.
 | 
						|
  // It skips %%.
 | 
						|
  static constexpr string_view ConsumeNonPercent(string_view format) {
 | 
						|
    return FoundPercent(format)
 | 
						|
               ? format
 | 
						|
               : ConsumeNonPercent(ConsumeNonPercentInner(format));
 | 
						|
  }
 | 
						|
 | 
						|
  static constexpr bool IsPositional(string_view format) {
 | 
						|
    return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format))
 | 
						|
                                       : GetChar(format, 0) == '$';
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr bool RunImpl(bool is_positional) const {
 | 
						|
    // In non-positional mode we require all arguments to be consumed.
 | 
						|
    // In positional mode just reaching the end of the format without errors is
 | 
						|
    // enough.
 | 
						|
    return (format_.empty() && (is_positional || args_.count == 0)) ||
 | 
						|
           (!format_.empty() &&
 | 
						|
            ValidateArg(
 | 
						|
                ConvParser(ConsumeFront(format_), args_, is_positional).Run()));
 | 
						|
  }
 | 
						|
 | 
						|
  constexpr bool ValidateArg(ConvParser conv) const {
 | 
						|
    return !conv.error() && FormatParser(conv.format(), conv.args())
 | 
						|
                                .RunImpl(conv.is_positional());
 | 
						|
  }
 | 
						|
 | 
						|
 public:
 | 
						|
  constexpr FormatParser(string_view format, ConvList args)
 | 
						|
      : format_(ConsumeNonPercent(format)), args_(args) {}
 | 
						|
 | 
						|
  // Runs the parser for `format` and `args`.
 | 
						|
  // It verifies that the format is valid and that all conversion specifiers
 | 
						|
  // match the arguments passed.
 | 
						|
  // In non-positional mode it also verfies that all arguments are consumed.
 | 
						|
  constexpr bool Run() const {
 | 
						|
    return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_)));
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  string_view format_;
 | 
						|
  // Current list of arguments.
 | 
						|
  // If we are not in positional mode we will consume from the front and will
 | 
						|
  // have to be empty in the end.
 | 
						|
  ConvList args_;
 | 
						|
};
 | 
						|
 | 
						|
template <Conv... C>
 | 
						|
constexpr bool ValidFormatImpl(string_view format) {
 | 
						|
  return FormatParser(format,
 | 
						|
                      {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
 | 
						|
      .Run();
 | 
						|
}
 | 
						|
 | 
						|
#endif  // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 | 
						|
 | 
						|
}  // namespace str_format_internal
 | 
						|
}  // namespace absl
 | 
						|
 | 
						|
#endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
 |