text: //
  // Copyright 2017 The Abseil Authors.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      https://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  //
  #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
  #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
  
  #include <limits.h>
  
  #include <cstddef>
  #include <cstring>
  #include <ostream>
  
  #include "absl/base/config.h"
  #include "absl/base/port.h"
  #include "absl/strings/internal/str_format/output.h"
  #include "absl/strings/string_view.h"
  
  namespace absl {
  ABSL_NAMESPACE_BEGIN
  namespace str_format_internal {
  
  class FormatRawSinkImpl {
   public:
    // Implicitly convert from any type that provides the hook function as
    // described above.
    template <typename T, decltype(str_format_internal::InvokeFlush(
                              std::declval<T*>(), string_view()))* = nullptr>
    FormatRawSinkImpl(T* raw)  // NOLINT
        : sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {}
  
    void Write(string_view s) { write_(sink_, s); }
  
    template <typename T>
    static FormatRawSinkImpl Extract(T s) {
      return s.sink_;
    }
  
   private:
    template <typename T>
    static void Flush(void* r, string_view s) {
      str_format_internal::InvokeFlush(static_cast<T*>(r), s);
    }
  
    void* sink_;
    void (*write_)(void*, string_view);
  };
  
  // An abstraction to which conversions write their string data.
  class FormatSinkImpl {
   public:
    explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {}
  
    ~FormatSinkImpl() { Flush(); }
  
    void Flush() {
      raw_.Write(string_view(buf_, pos_ - buf_));
      pos_ = buf_;
    }
  
    void Append(size_t n, char c) {
      if (n == 0) return;
      size_ += n;
      auto raw_append = [&](size_t count) {
        memset(pos_, c, count);
        pos_ += count;
      };
      while (n > Avail()) {
        n -= Avail();
        if (Avail() > 0) {
          raw_append(Avail());
        }
        Flush();
      }
      raw_append(n);
    }
  
    void Append(string_view v) {
      size_t n = v.size();
      if (n == 0) return;
      size_ += n;
      if (n >= Avail()) {
        Flush();
        raw_.Write(v);
        return;
      }
      memcpy(pos_, v.data(), n);
      pos_ += n;
    }
  
    size_t size() const { return size_; }
  
    // Put 'v' to 'sink' with specified width, precision, and left flag.
    bool PutPaddedString(string_view v, int w, int p, bool l);
  
    template <typename T>
    T Wrap() {
      return T(this);
    }
  
    template <typename T>
    static FormatSinkImpl* Extract(T* s) {
      return s->sink_;
    }
  
   private:
    size_t Avail() const { return buf_ + sizeof(buf_) - pos_; }
  
    FormatRawSinkImpl raw_;
    size_t size_ = 0;
    char* pos_ = buf_;
    char buf_[1024];
  };
  
  struct Flags {
    bool basic : 1;     // fastest conversion: no flags, width, or precision
    bool left : 1;      // "-"
    bool show_pos : 1;  // "+"
    bool sign_col : 1;  // " "
    bool alt : 1;       // "#"
    bool zero : 1;      // "0"
    std::string ToString() const;
    friend std::ostream& operator<<(std::ostream& os, const Flags& v) {
      return os << v.ToString();
    }
  };
  
  // clang-format off
  #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
    /* text */ \
    X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \
    /* ints */ \
    X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \
    X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \
    /* floats */ \
    X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \
    X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
    /* misc */ \
    X_VAL(n) X_SEP X_VAL(p)
  
  enum class FormatConversionChar : uint8_t {
      c, C, s, S,              // text
      d, i, o, u, x, X,        // int
      f, F, e, E, g, G, a, A,  // float
      n, p,                    // misc
      kNone,
      none = kNone
  };
  // clang-format on
  
  inline FormatConversionChar FormatConversionCharFromChar(char c) {
    switch (c) {
  #define ABSL_INTERNAL_X_VAL(id) \
    case #id[0]:                  \
      return FormatConversionChar::id;
      ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
  #undef ABSL_INTERNAL_X_VAL
    }
    return FormatConversionChar::kNone;
  }
  
  inline int FormatConversionCharRadix(FormatConversionChar c) {
    switch (c) {
      case FormatConversionChar::x:
      case FormatConversionChar::X:
      case FormatConversionChar::a:
      case FormatConversionChar::A:
      case FormatConversionChar::p:
        return 16;
      case FormatConversionChar::o:
        return 8;
      default:
        return 10;
    }
  }
  
  inline bool FormatConversionCharIsUpper(FormatConversionChar c) {
    switch (c) {
      case FormatConversionChar::X:
      case FormatConversionChar::F:
      case FormatConversionChar::E:
      case FormatConversionChar::G:
      case FormatConversionChar::A:
        return true;
      default:
        return false;
    }
  }
  
  inline bool FormatConversionCharIsSigned(FormatConversionChar c) {
    switch (c) {
      case FormatConversionChar::d:
      case FormatConversionChar::i:
        return true;
      default:
        return false;
    }
  }
  
  inline bool FormatConversionCharIsIntegral(FormatConversionChar c) {
    switch (c) {
      case FormatConversionChar::d:
      case FormatConversionChar::i:
      case FormatConversionChar::u:
      case FormatConversionChar::o:
      case FormatConversionChar::x:
      case FormatConversionChar::X:
        return true;
      default:
        return false;
    }
  }
  
  inline bool FormatConversionCharIsFloat(FormatConversionChar c) {
    switch (c) {
      case FormatConversionChar::a:
      case FormatConversionChar::e:
      case FormatConversionChar::f:
      case FormatConversionChar::g:
      case FormatConversionChar::A:
      case FormatConversionChar::E:
      case FormatConversionChar::F:
      case FormatConversionChar::G:
        return true;
      default:
        return false;
    }
  }
  
  inline char FormatConversionCharToChar(FormatConversionChar c) {
    switch (c) {
  #define ABSL_INTERNAL_X_VAL(e)  \
    case FormatConversionChar::e: \
      return #e[0];
  #define ABSL_INTERNAL_X_SEP
      ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL,
                                             ABSL_INTERNAL_X_SEP)
      case FormatConversionChar::kNone:
        return '\0';
  #undef ABSL_INTERNAL_X_VAL
  #undef ABSL_INTERNAL_X_SEP
    }
    return '\0';
  }
  
  // The associated char.
  inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) {
    char c = FormatConversionCharToChar(v);
    if (!c) c = '?';
    return os << c;
  }
  
  struct FormatConversionSpecImplFriend;
  
  class FormatConversionSpec {
   public:
    // Width and precison are not specified, no flags are set.
    bool is_basic() const { return flags_.basic; }
    bool has_left_flag() const { return flags_.left; }
    bool has_show_pos_flag() const { return flags_.show_pos; }
    bool has_sign_col_flag() const { return flags_.sign_col; }
    bool has_alt_flag() const { return flags_.alt; }
    bool has_zero_flag() const { return flags_.zero; }
  
    FormatConversionChar conversion_char() const {
      // Keep this field first in the struct . It generates better code when
      // accessing it when ConversionSpec is passed by value in registers.
      static_assert(offsetof(FormatConversionSpec, conv_) == 0, "");
      return conv_;
    }
  
    // Returns the specified width. If width is unspecfied, it returns a negative
    // value.
    int width() const { return width_; }
    // Returns the specified precision. If precision is unspecfied, it returns a
    // negative value.
    int precision() const { return precision_; }
  
    // Deprecated (use has_x_flag() instead).
    Flags flags() const { return flags_; }
    // Deprecated
    FormatConversionChar conv() const { return conversion_char(); }
  
   private:
    friend struct str_format_internal::FormatConversionSpecImplFriend;
    FormatConversionChar conv_ = FormatConversionChar::kNone;
    Flags flags_;
    int width_;
    int precision_;
  };
  
  struct FormatConversionSpecImplFriend final {
    static void SetFlags(Flags f, FormatConversionSpec* conv) {
      conv->flags_ = f;
    }
    static void SetConversionChar(FormatConversionChar c,
                                  FormatConversionSpec* conv) {
      conv->conv_ = c;
    }
    static void SetWidth(int w, FormatConversionSpec* conv) { conv->width_ = w; }
    static void SetPrecision(int p, FormatConversionSpec* conv) {
      conv->precision_ = p;
    }
    static std::string FlagsToString(const FormatConversionSpec& spec) {
      return spec.flags_.ToString();
    }
  };
  
  constexpr uint64_t FormatConversionCharToConvValue(char conv) {
    return
  #define ABSL_INTERNAL_CHAR_SET_CASE(c)                                       \
    conv == #c[0]                                                              \
        ? (uint64_t{1} << (1 + static_cast<uint8_t>(FormatConversionChar::c))) \
        :
        ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
  #undef ABSL_INTERNAL_CHAR_SET_CASE
                    conv == '*'
            ? 1
            : 0;
  }
  
  enum class FormatConversionCharSet : uint64_t {
  #define ABSL_INTERNAL_CHAR_SET_CASE(c) \
    c = FormatConversionCharToConvValue(#c[0]),
    ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
  #undef ABSL_INTERNAL_CHAR_SET_CASE
  
    // Used for width/precision '*' specification.
    kStar = FormatConversionCharToConvValue('*'),
    // Some predefined values:
    kIntegral = d | i | u | o | x | X,
    kFloating = a | e | f | g | A | E | F | G,
    kNumeric = kIntegral | kFloating,
    kString = s,
    kPointer = p,
  
    // The following are deprecated
    star = kStar,
    integral = kIntegral,
    floating = kFloating,
    numeric = kNumeric,
    string = kString,
    pointer = kPointer
  };
  
  // Type safe OR operator.
  // We need this for two reasons:
  //  1. operator| on enums makes them decay to integers and the result is an
  //     integer. We need the result to stay as an enum.
  //  2. We use "enum class" which would not work even if we accepted the decay.
  constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
                                              FormatConversionCharSet b) {
    return FormatConversionCharSet(static_cast<uint64_t>(a) |
                                   static_cast<uint64_t>(b));
  }
  
  // Get a conversion with a single character in it.
  constexpr FormatConversionCharSet ConversionCharToConv(char c) {
    return FormatConversionCharSet(FormatConversionCharToConvValue(c));
  }
  
  // Checks whether `c` exists in `set`.
  constexpr bool Contains(FormatConversionCharSet set, char c) {
    return (static_cast<uint64_t>(set) & FormatConversionCharToConvValue(c)) != 0;
  }
  
  // Checks whether all the characters in `c` are contained in `set`
  constexpr bool Contains(FormatConversionCharSet set,
                          FormatConversionCharSet c) {
    return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
           static_cast<uint64_t>(c);
  }
  
  // Return type of the AbslFormatConvert() functions.
  // The FormatConversionCharSet template parameter is used to inform the
  // framework of what conversion characters are supported by that
  // AbslFormatConvert routine.
  template <FormatConversionCharSet C>
  struct FormatConvertResult {
    static constexpr FormatConversionCharSet kConv = C;
    bool value;
  };
  
  template <FormatConversionCharSet C>
  constexpr FormatConversionCharSet FormatConvertResult<C>::kConv;
  
  // Return capacity - used, clipped to a minimum of 0.
  inline size_t Excess(size_t used, size_t capacity) {
    return used < capacity ? capacity - used : 0;
  }
  
  // Type alias for use during migration.
  using ConversionChar = FormatConversionChar;
  using ConversionSpec = FormatConversionSpec;
  using Conv = FormatConversionCharSet;
  template <FormatConversionCharSet C>
  using ConvertResult = FormatConvertResult<C>;
  
  }  // namespace str_format_internal
  
  ABSL_NAMESPACE_END
  }  // namespace absl
  
  #endif  // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
  