tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

arg.cc (25203B)


      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 //
     16 // POSIX spec:
     17 //   http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html
     18 //
     19 #include "absl/strings/internal/str_format/arg.h"
     20 
     21 #include <algorithm>
     22 #include <cassert>
     23 #include <cstddef>
     24 #include <cstdint>
     25 #include <cstdlib>
     26 #include <cstring>
     27 #include <cwchar>
     28 #include <string>
     29 #include <type_traits>
     30 
     31 #include "absl/base/config.h"
     32 #include "absl/base/optimization.h"
     33 #include "absl/container/fixed_array.h"
     34 #include "absl/numeric/int128.h"
     35 #include "absl/strings/internal/str_format/extension.h"
     36 #include "absl/strings/internal/str_format/float_conversion.h"
     37 #include "absl/strings/numbers.h"
     38 #include "absl/strings/string_view.h"
     39 
     40 #if defined(ABSL_HAVE_STD_STRING_VIEW)
     41 #include <string_view>
     42 #endif
     43 
     44 namespace absl {
     45 ABSL_NAMESPACE_BEGIN
     46 namespace str_format_internal {
     47 namespace {
     48 
     49 // Reduce *capacity by s.size(), clipped to a 0 minimum.
     50 void ReducePadding(string_view s, size_t *capacity) {
     51  *capacity = Excess(s.size(), *capacity);
     52 }
     53 
     54 // Reduce *capacity by n, clipped to a 0 minimum.
     55 void ReducePadding(size_t n, size_t *capacity) {
     56  *capacity = Excess(n, *capacity);
     57 }
     58 
     59 template <typename T>
     60 struct MakeUnsigned : std::make_unsigned<T> {};
     61 template <>
     62 struct MakeUnsigned<absl::int128> {
     63  using type = absl::uint128;
     64 };
     65 template <>
     66 struct MakeUnsigned<absl::uint128> {
     67  using type = absl::uint128;
     68 };
     69 
     70 template <typename T>
     71 struct IsSigned : std::is_signed<T> {};
     72 template <>
     73 struct IsSigned<absl::int128> : std::true_type {};
     74 template <>
     75 struct IsSigned<absl::uint128> : std::false_type {};
     76 
     77 // Integral digit printer.
     78 // Call one of the PrintAs* routines after construction once.
     79 // Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results.
     80 class IntDigits {
     81 public:
     82  // Print the unsigned integer as octal.
     83  // Supports unsigned integral types and uint128.
     84  template <typename T>
     85  void PrintAsOct(T v) {
     86    static_assert(!IsSigned<T>::value, "");
     87    char *p = storage_ + sizeof(storage_);
     88    do {
     89      *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7));
     90      v >>= 3;
     91    } while (v);
     92    start_ = p;
     93    size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
     94  }
     95 
     96  // Print the signed or unsigned integer as decimal.
     97  // Supports all integral types.
     98  template <typename T>
     99  void PrintAsDec(T v) {
    100    static_assert(std::is_integral<T>::value, "");
    101    start_ = storage_;
    102    size_ = static_cast<size_t>(numbers_internal::FastIntToBuffer(v, storage_) -
    103                                storage_);
    104  }
    105 
    106  void PrintAsDec(int128 v) {
    107    auto u = static_cast<uint128>(v);
    108    bool add_neg = false;
    109    if (v < 0) {
    110      add_neg = true;
    111      u = uint128{} - u;
    112    }
    113    PrintAsDec(u, add_neg);
    114  }
    115 
    116  void PrintAsDec(uint128 v, bool add_neg = false) {
    117    // This function can be sped up if needed. We can call FastIntToBuffer
    118    // twice, or fix FastIntToBuffer to support uint128.
    119    char *p = storage_ + sizeof(storage_);
    120    do {
    121      p -= 2;
    122      numbers_internal::PutTwoDigits(static_cast<uint32_t>(v % 100), p);
    123      v /= 100;
    124    } while (v);
    125    if (p[0] == '0') {
    126      // We printed one too many hexits.
    127      ++p;
    128    }
    129    if (add_neg) {
    130      *--p = '-';
    131    }
    132    size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
    133    start_ = p;
    134  }
    135 
    136  // Print the unsigned integer as hex using lowercase.
    137  // Supports unsigned integral types and uint128.
    138  template <typename T>
    139  void PrintAsHexLower(T v) {
    140    static_assert(!IsSigned<T>::value, "");
    141    char *p = storage_ + sizeof(storage_);
    142 
    143    do {
    144      p -= 2;
    145      constexpr const char* table = numbers_internal::kHexTable;
    146      std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2);
    147      if (sizeof(T) == 1) break;
    148      v >>= 8;
    149    } while (v);
    150    if (p[0] == '0') {
    151      // We printed one too many digits.
    152      ++p;
    153    }
    154    start_ = p;
    155    size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
    156  }
    157 
    158  // Print the unsigned integer as hex using uppercase.
    159  // Supports unsigned integral types and uint128.
    160  template <typename T>
    161  void PrintAsHexUpper(T v) {
    162    static_assert(!IsSigned<T>::value, "");
    163    char *p = storage_ + sizeof(storage_);
    164 
    165    // kHexTable is only lowercase, so do it manually for uppercase.
    166    do {
    167      *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15];
    168      v >>= 4;
    169    } while (v);
    170    start_ = p;
    171    size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
    172  }
    173 
    174  // The printed value including the '-' sign if available.
    175  // For inputs of value `0`, this will return "0"
    176  string_view with_neg_and_zero() const { return {start_, size_}; }
    177 
    178  // The printed value not including the '-' sign.
    179  // For inputs of value `0`, this will return "".
    180  string_view without_neg_or_zero() const {
    181    static_assert('-' < '0', "The check below verifies both.");
    182    size_t advance = start_[0] <= '0' ? 1 : 0;
    183    return {start_ + advance, size_ - advance};
    184  }
    185 
    186  bool is_negative() const { return start_[0] == '-'; }
    187 
    188 private:
    189  const char *start_;
    190  size_t size_;
    191  // Max size: 128 bit value as octal -> 43 digits, plus sign char
    192  char storage_[128 / 3 + 1 + 1];
    193 };
    194 
    195 // Note: 'o' conversions do not have a base indicator, it's just that
    196 // the '#' flag is specified to modify the precision for 'o' conversions.
    197 string_view BaseIndicator(const IntDigits &as_digits,
    198                          const FormatConversionSpecImpl conv) {
    199  // always show 0x for %p.
    200  bool alt = conv.has_alt_flag() ||
    201             conv.conversion_char() == FormatConversionCharInternal::p;
    202  bool hex = (conv.conversion_char() == FormatConversionCharInternal::x ||
    203              conv.conversion_char() == FormatConversionCharInternal::X ||
    204              conv.conversion_char() == FormatConversionCharInternal::p);
    205  // From the POSIX description of '#' flag:
    206  //   "For x or X conversion specifiers, a non-zero result shall have
    207  //   0x (or 0X) prefixed to it."
    208  if (alt && hex && !as_digits.without_neg_or_zero().empty()) {
    209    return conv.conversion_char() == FormatConversionCharInternal::X ? "0X"
    210                                                                     : "0x";
    211  }
    212  return {};
    213 }
    214 
    215 string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) {
    216  if (conv.conversion_char() == FormatConversionCharInternal::d ||
    217      conv.conversion_char() == FormatConversionCharInternal::i) {
    218    if (neg) return "-";
    219    if (conv.has_show_pos_flag()) return "+";
    220    if (conv.has_sign_col_flag()) return " ";
    221  }
    222  return {};
    223 }
    224 
    225 bool ConvertCharImpl(char v,
    226                     const FormatConversionSpecImpl conv,
    227                     FormatSinkImpl* sink) {
    228  size_t fill = 0;
    229  if (conv.width() >= 0)
    230    fill = static_cast<size_t>(conv.width());
    231  ReducePadding(1, &fill);
    232  if (!conv.has_left_flag()) sink->Append(fill, ' ');
    233  sink->Append(1, v);
    234  if (conv.has_left_flag()) sink->Append(fill, ' ');
    235  return true;
    236 }
    237 
    238 bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
    239                             const FormatConversionSpecImpl conv,
    240                             FormatSinkImpl *sink) {
    241  // Print as a sequence of Substrings:
    242  //   [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
    243  size_t fill = 0;
    244  if (conv.width() >= 0)
    245    fill = static_cast<size_t>(conv.width());
    246 
    247  string_view formatted = as_digits.without_neg_or_zero();
    248  ReducePadding(formatted, &fill);
    249 
    250  string_view sign = SignColumn(as_digits.is_negative(), conv);
    251  ReducePadding(sign, &fill);
    252 
    253  string_view base_indicator = BaseIndicator(as_digits, conv);
    254  ReducePadding(base_indicator, &fill);
    255 
    256  bool precision_specified = conv.precision() >= 0;
    257  size_t precision =
    258      precision_specified ? static_cast<size_t>(conv.precision()) : size_t{1};
    259 
    260  if (conv.has_alt_flag() &&
    261      conv.conversion_char() == FormatConversionCharInternal::o) {
    262    // From POSIX description of the '#' (alt) flag:
    263    //   "For o conversion, it increases the precision (if necessary) to
    264    //   force the first digit of the result to be zero."
    265    if (formatted.empty() || *formatted.begin() != '0') {
    266      size_t needed = formatted.size() + 1;
    267      precision = std::max(precision, needed);
    268    }
    269  }
    270 
    271  size_t num_zeroes = Excess(formatted.size(), precision);
    272  ReducePadding(num_zeroes, &fill);
    273 
    274  size_t num_left_spaces = !conv.has_left_flag() ? fill : 0;
    275  size_t num_right_spaces = conv.has_left_flag() ? fill : 0;
    276 
    277  // From POSIX description of the '0' (zero) flag:
    278  //   "For d, i, o, u, x, and X conversion specifiers, if a precision
    279  //   is specified, the '0' flag is ignored."
    280  if (!precision_specified && conv.has_zero_flag()) {
    281    num_zeroes += num_left_spaces;
    282    num_left_spaces = 0;
    283  }
    284 
    285  sink->Append(num_left_spaces, ' ');
    286  sink->Append(sign);
    287  sink->Append(base_indicator);
    288  sink->Append(num_zeroes, '0');
    289  sink->Append(formatted);
    290  sink->Append(num_right_spaces, ' ');
    291  return true;
    292 }
    293 
    294 template <typename T>
    295 bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
    296  if (conv.conversion_char() == FormatConversionCharInternal::v) {
    297    conv.set_conversion_char(FormatConversionCharInternal::g);
    298  }
    299 
    300  return FormatConversionCharIsFloat(conv.conversion_char()) &&
    301         ConvertFloatImpl(v, conv, sink);
    302 }
    303 
    304 inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
    305                             FormatSinkImpl *sink) {
    306  if (conv.is_basic()) {
    307    sink->Append(v);
    308    return true;
    309  }
    310  return sink->PutPaddedString(v, conv.width(), conv.precision(),
    311                               conv.has_left_flag());
    312 }
    313 
    314 struct ShiftState {
    315  bool saw_high_surrogate = false;
    316  uint8_t bits = 0;
    317 };
    318 
    319 // Converts `v` from UTF-16 or UTF-32 to UTF-8 and writes to `buf`. `buf` is
    320 // assumed to have enough space for the output. `s` is used to carry state
    321 // between successive calls with a UTF-16 surrogate pair. Returns the number of
    322 // chars written, or `static_cast<size_t>(-1)` on failure.
    323 //
    324 // This is basically std::wcrtomb(), but always outputting UTF-8 instead of
    325 // respecting the current locale.
    326 inline size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s) {
    327  const auto v = static_cast<uint32_t>(wc);
    328  if (v < 0x80) {
    329    *buf = static_cast<char>(v);
    330    return 1;
    331  } else if (v < 0x800) {
    332    *buf++ = static_cast<char>(0xc0 | (v >> 6));
    333    *buf = static_cast<char>(0x80 | (v & 0x3f));
    334    return 2;
    335  } else if (v < 0xd800 || (v - 0xe000) < 0x2000) {
    336    *buf++ = static_cast<char>(0xe0 | (v >> 12));
    337    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
    338    *buf = static_cast<char>(0x80 | (v & 0x3f));
    339    return 3;
    340  } else if ((v - 0x10000) < 0x100000) {
    341    *buf++ = static_cast<char>(0xf0 | (v >> 18));
    342    *buf++ = static_cast<char>(0x80 | ((v >> 12) & 0x3f));
    343    *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
    344    *buf = static_cast<char>(0x80 | (v & 0x3f));
    345    return 4;
    346  } else if (v < 0xdc00) {
    347    s.saw_high_surrogate = true;
    348    s.bits = static_cast<uint8_t>(v & 0x3);
    349    const uint8_t high_bits = ((v >> 6) & 0xf) + 1;
    350    *buf++ = static_cast<char>(0xf0 | (high_bits >> 2));
    351    *buf =
    352        static_cast<char>(0x80 | static_cast<uint8_t>((high_bits & 0x3) << 4) |
    353                          static_cast<uint8_t>((v >> 2) & 0xf));
    354    return 2;
    355  } else if (v < 0xe000 && s.saw_high_surrogate) {
    356    *buf++ = static_cast<char>(0x80 | static_cast<uint8_t>(s.bits << 4) |
    357                               static_cast<uint8_t>((v >> 6) & 0xf));
    358    *buf = static_cast<char>(0x80 | (v & 0x3f));
    359    s.saw_high_surrogate = false;
    360    s.bits = 0;
    361    return 2;
    362  } else {
    363    return static_cast<size_t>(-1);
    364  }
    365 }
    366 
    367 inline bool ConvertStringArg(const wchar_t *v,
    368                             size_t len,
    369                             const FormatConversionSpecImpl conv,
    370                             FormatSinkImpl *sink) {
    371  FixedArray<char> mb(len * 4);
    372  ShiftState s;
    373  size_t chars_written = 0;
    374  for (size_t i = 0; i < len; ++i) {
    375    const size_t chars = WideToUtf8(v[i], &mb[chars_written], s);
    376    if (chars == static_cast<size_t>(-1)) { return false; }
    377    chars_written += chars;
    378  }
    379  return ConvertStringArg(string_view(mb.data(), chars_written), conv, sink);
    380 }
    381 
    382 bool ConvertWCharTImpl(wchar_t v, const FormatConversionSpecImpl conv,
    383                       FormatSinkImpl *sink) {
    384  char mb[4];
    385  ShiftState s;
    386  const size_t chars_written = WideToUtf8(v, mb, s);
    387  return chars_written != static_cast<size_t>(-1) && !s.saw_high_surrogate &&
    388         ConvertStringArg(string_view(mb, chars_written), conv, sink);
    389 }
    390 
    391 }  // namespace
    392 
    393 bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
    394  if (v) {
    395    sink->Append("true");
    396  } else {
    397    sink->Append("false");
    398  }
    399  return true;
    400 }
    401 
    402 template <typename T>
    403 bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
    404  using U = typename MakeUnsigned<T>::type;
    405  IntDigits as_digits;
    406 
    407  // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes
    408  // it to complain about a switch/case type mismatch, even though both are
    409  // FormatConversionChar.  Likely this is because at this point
    410  // FormatConversionChar is declared, but not defined.
    411  switch (static_cast<uint8_t>(conv.conversion_char())) {
    412    case static_cast<uint8_t>(FormatConversionCharInternal::c):
    413      return (std::is_same<T, wchar_t>::value ||
    414              (conv.length_mod() == LengthMod::l))
    415                 ? ConvertWCharTImpl(static_cast<wchar_t>(v), conv, sink)
    416                 : ConvertCharImpl(static_cast<char>(v), conv, sink);
    417 
    418    case static_cast<uint8_t>(FormatConversionCharInternal::o):
    419      as_digits.PrintAsOct(static_cast<U>(v));
    420      break;
    421 
    422    case static_cast<uint8_t>(FormatConversionCharInternal::x):
    423      as_digits.PrintAsHexLower(static_cast<U>(v));
    424      break;
    425    case static_cast<uint8_t>(FormatConversionCharInternal::X):
    426      as_digits.PrintAsHexUpper(static_cast<U>(v));
    427      break;
    428 
    429    case static_cast<uint8_t>(FormatConversionCharInternal::u):
    430      as_digits.PrintAsDec(static_cast<U>(v));
    431      break;
    432 
    433    case static_cast<uint8_t>(FormatConversionCharInternal::d):
    434    case static_cast<uint8_t>(FormatConversionCharInternal::i):
    435    case static_cast<uint8_t>(FormatConversionCharInternal::v):
    436      as_digits.PrintAsDec(v);
    437      break;
    438 
    439    case static_cast<uint8_t>(FormatConversionCharInternal::a):
    440    case static_cast<uint8_t>(FormatConversionCharInternal::e):
    441    case static_cast<uint8_t>(FormatConversionCharInternal::f):
    442    case static_cast<uint8_t>(FormatConversionCharInternal::g):
    443    case static_cast<uint8_t>(FormatConversionCharInternal::A):
    444    case static_cast<uint8_t>(FormatConversionCharInternal::E):
    445    case static_cast<uint8_t>(FormatConversionCharInternal::F):
    446    case static_cast<uint8_t>(FormatConversionCharInternal::G):
    447      return ConvertFloatImpl(static_cast<double>(v), conv, sink);
    448 
    449    default:
    450      ABSL_ASSUME(false);
    451  }
    452 
    453  if (conv.is_basic()) {
    454    sink->Append(as_digits.with_neg_and_zero());
    455    return true;
    456  }
    457  return ConvertIntImplInnerSlow(as_digits, conv, sink);
    458 }
    459 
    460 template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
    461                                  FormatSinkImpl *sink);
    462 template bool ConvertIntArg<signed char>(signed char v,
    463                                         FormatConversionSpecImpl conv,
    464                                         FormatSinkImpl *sink);
    465 template bool ConvertIntArg<unsigned char>(unsigned char v,
    466                                           FormatConversionSpecImpl conv,
    467                                           FormatSinkImpl *sink);
    468 template bool ConvertIntArg<wchar_t>(wchar_t v, FormatConversionSpecImpl conv,
    469                                     FormatSinkImpl *sink);
    470 template bool ConvertIntArg<short>(short v,  // NOLINT
    471                                   FormatConversionSpecImpl conv,
    472                                   FormatSinkImpl *sink);
    473 template bool ConvertIntArg<unsigned short>(unsigned short v,  // NOLINT
    474                                            FormatConversionSpecImpl conv,
    475                                            FormatSinkImpl *sink);
    476 template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
    477                                 FormatSinkImpl *sink);
    478 template bool ConvertIntArg<unsigned int>(unsigned int v,
    479                                          FormatConversionSpecImpl conv,
    480                                          FormatSinkImpl *sink);
    481 template bool ConvertIntArg<long>(long v,  // NOLINT
    482                                  FormatConversionSpecImpl conv,
    483                                  FormatSinkImpl *sink);
    484 template bool ConvertIntArg<unsigned long>(unsigned long v,  // NOLINT
    485                                           FormatConversionSpecImpl conv,
    486                                           FormatSinkImpl *sink);
    487 template bool ConvertIntArg<long long>(long long v,  // NOLINT
    488                                       FormatConversionSpecImpl conv,
    489                                       FormatSinkImpl *sink);
    490 template bool ConvertIntArg<unsigned long long>(unsigned long long v,  // NOLINT
    491                                                FormatConversionSpecImpl conv,
    492                                                FormatSinkImpl *sink);
    493 
    494 // ==================== Strings ====================
    495 StringConvertResult FormatConvertImpl(const std::string &v,
    496                                      const FormatConversionSpecImpl conv,
    497                                      FormatSinkImpl *sink) {
    498  return {ConvertStringArg(v, conv, sink)};
    499 }
    500 
    501 StringConvertResult FormatConvertImpl(const std::wstring &v,
    502                                      const FormatConversionSpecImpl conv,
    503                                      FormatSinkImpl *sink) {
    504  return {ConvertStringArg(v.data(), v.size(), conv, sink)};
    505 }
    506 
    507 StringConvertResult FormatConvertImpl(string_view v,
    508                                      const FormatConversionSpecImpl conv,
    509                                      FormatSinkImpl *sink) {
    510  return {ConvertStringArg(v, conv, sink)};
    511 }
    512 
    513 #if defined(ABSL_HAVE_STD_STRING_VIEW)
    514 StringConvertResult FormatConvertImpl(std::wstring_view v,
    515                                      const FormatConversionSpecImpl conv,
    516                                      FormatSinkImpl* sink) {
    517  return {ConvertStringArg(v.data(), v.size(), conv, sink)};
    518 }
    519 #endif
    520 
    521 StringPtrConvertResult FormatConvertImpl(const char* v,
    522                                         const FormatConversionSpecImpl conv,
    523                                         FormatSinkImpl* sink) {
    524  if (conv.conversion_char() == FormatConversionCharInternal::p)
    525    return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
    526  size_t len;
    527  if (v == nullptr) {
    528    len = 0;
    529  } else if (conv.precision() < 0) {
    530    len = std::strlen(v);
    531  } else {
    532    // If precision is set, we look for the NUL-terminator on the valid range.
    533    len = static_cast<size_t>(std::find(v, v + conv.precision(), '\0') - v);
    534  }
    535  return {ConvertStringArg(string_view(v, len), conv, sink)};
    536 }
    537 
    538 StringPtrConvertResult FormatConvertImpl(const wchar_t* v,
    539                                         const FormatConversionSpecImpl conv,
    540                                         FormatSinkImpl* sink) {
    541  if (conv.conversion_char() == FormatConversionCharInternal::p) {
    542    return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
    543  }
    544  size_t len;
    545  if (v == nullptr) {
    546    len = 0;
    547  } else if (conv.precision() < 0) {
    548    len = std::wcslen(v);
    549  } else {
    550    // If precision is set, we look for the NUL-terminator on the valid range.
    551    len = static_cast<size_t>(std::find(v, v + conv.precision(), L'\0') - v);
    552  }
    553  return {ConvertStringArg(v, len, conv, sink)};
    554 }
    555 
    556 StringPtrConvertResult FormatConvertImpl(std::nullptr_t,
    557                                         const FormatConversionSpecImpl conv,
    558                                         FormatSinkImpl* sink) {
    559  return FormatConvertImpl(static_cast<const char*>(nullptr), conv, sink);
    560 }
    561 
    562 // ==================== Raw pointers ====================
    563 ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
    564    VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
    565  if (!v.value) {
    566    sink->Append("(nil)");
    567    return {true};
    568  }
    569  IntDigits as_digits;
    570  as_digits.PrintAsHexLower(v.value);
    571  return {ConvertIntImplInnerSlow(as_digits, conv, sink)};
    572 }
    573 
    574 // ==================== Floats ====================
    575 FloatingConvertResult FormatConvertImpl(float v,
    576                                        const FormatConversionSpecImpl conv,
    577                                        FormatSinkImpl *sink) {
    578  return {ConvertFloatArg(v, conv, sink)};
    579 }
    580 FloatingConvertResult FormatConvertImpl(double v,
    581                                        const FormatConversionSpecImpl conv,
    582                                        FormatSinkImpl *sink) {
    583  return {ConvertFloatArg(v, conv, sink)};
    584 }
    585 FloatingConvertResult FormatConvertImpl(long double v,
    586                                        const FormatConversionSpecImpl conv,
    587                                        FormatSinkImpl *sink) {
    588  return {ConvertFloatArg(v, conv, sink)};
    589 }
    590 
    591 // ==================== Chars ====================
    592 CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv,
    593                                    FormatSinkImpl *sink) {
    594  return {ConvertIntArg(v, conv, sink)};
    595 }
    596 CharConvertResult FormatConvertImpl(wchar_t v,
    597                                    const FormatConversionSpecImpl conv,
    598                                    FormatSinkImpl* sink) {
    599  return {ConvertIntArg(v, conv, sink)};
    600 }
    601 
    602 // ==================== Ints ====================
    603 IntegralConvertResult FormatConvertImpl(signed char v,
    604                                        const FormatConversionSpecImpl conv,
    605                                        FormatSinkImpl *sink) {
    606  return {ConvertIntArg(v, conv, sink)};
    607 }
    608 IntegralConvertResult FormatConvertImpl(unsigned char v,
    609                                        const FormatConversionSpecImpl conv,
    610                                        FormatSinkImpl *sink) {
    611  return {ConvertIntArg(v, conv, sink)};
    612 }
    613 IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
    614                                        const FormatConversionSpecImpl conv,
    615                                        FormatSinkImpl *sink) {
    616  return {ConvertIntArg(v, conv, sink)};
    617 }
    618 IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
    619                                        const FormatConversionSpecImpl conv,
    620                                        FormatSinkImpl *sink) {
    621  return {ConvertIntArg(v, conv, sink)};
    622 }
    623 IntegralConvertResult FormatConvertImpl(int v,
    624                                        const FormatConversionSpecImpl conv,
    625                                        FormatSinkImpl *sink) {
    626  return {ConvertIntArg(v, conv, sink)};
    627 }
    628 IntegralConvertResult FormatConvertImpl(unsigned v,
    629                                        const FormatConversionSpecImpl conv,
    630                                        FormatSinkImpl *sink) {
    631  return {ConvertIntArg(v, conv, sink)};
    632 }
    633 IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
    634                                        const FormatConversionSpecImpl conv,
    635                                        FormatSinkImpl *sink) {
    636  return {ConvertIntArg(v, conv, sink)};
    637 }
    638 IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
    639                                        const FormatConversionSpecImpl conv,
    640                                        FormatSinkImpl *sink) {
    641  return {ConvertIntArg(v, conv, sink)};
    642 }
    643 IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
    644                                        const FormatConversionSpecImpl conv,
    645                                        FormatSinkImpl *sink) {
    646  return {ConvertIntArg(v, conv, sink)};
    647 }
    648 IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
    649                                        const FormatConversionSpecImpl conv,
    650                                        FormatSinkImpl *sink) {
    651  return {ConvertIntArg(v, conv, sink)};
    652 }
    653 IntegralConvertResult FormatConvertImpl(absl::int128 v,
    654                                        const FormatConversionSpecImpl conv,
    655                                        FormatSinkImpl *sink) {
    656  return {ConvertIntArg(v, conv, sink)};
    657 }
    658 IntegralConvertResult FormatConvertImpl(absl::uint128 v,
    659                                        const FormatConversionSpecImpl conv,
    660                                        FormatSinkImpl *sink) {
    661  return {ConvertIntArg(v, conv, sink)};
    662 }
    663 
    664 ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
    665 
    666 
    667 
    668 }  // namespace str_format_internal
    669 
    670 ABSL_NAMESPACE_END
    671 }  // namespace absl