| // Copyright 2020 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. | |
| // | |
| // POSIX spec: | |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html | |
| // | |
| #include "absl/strings/internal/str_format/arg.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <cwchar> | |
| #include <limits> | |
| #include <string> | |
| #include <string_view> | |
| #include <type_traits> | |
| #include "absl/base/config.h" | |
| #include "absl/base/optimization.h" | |
| #include "absl/container/fixed_array.h" | |
| #include "absl/numeric/int128.h" | |
| #include "absl/strings/internal/str_format/extension.h" | |
| #include "absl/strings/internal/str_format/float_conversion.h" | |
| #include "absl/strings/internal/utf8.h" | |
| #include "absl/strings/numbers.h" | |
| #include "absl/strings/string_view.h" | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| namespace str_format_internal { | |
| namespace { | |
| // Reduce *capacity by s.size(), clipped to a 0 minimum. | |
| void ReducePadding(string_view s, size_t *capacity) { | |
| *capacity = Excess(s.size(), *capacity); | |
| } | |
| // Reduce *capacity by n, clipped to a 0 minimum. | |
| void ReducePadding(size_t n, size_t *capacity) { | |
| *capacity = Excess(n, *capacity); | |
| } | |
| template <typename T> | |
| struct MakeUnsigned : std::make_unsigned<T> {}; | |
| template <> | |
| struct MakeUnsigned<absl::int128> { | |
| using type = absl::uint128; | |
| }; | |
| template <> | |
| struct MakeUnsigned<absl::uint128> { | |
| using type = absl::uint128; | |
| }; | |
| template <typename T> | |
| struct IsSigned : std::is_signed<T> {}; | |
| template <> | |
| struct IsSigned<absl::int128> : std::true_type {}; | |
| template <> | |
| struct IsSigned<absl::uint128> : std::false_type {}; | |
| // Integral digit printer. | |
| // Call one of the PrintAs* routines after construction once. | |
| // Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results. | |
| class IntDigits { | |
| public: | |
| // Print the unsigned integer as octal. | |
| // Supports unsigned integral types and uint128. | |
| template <typename T> | |
| void PrintAsOct(T v) { | |
| static_assert(!IsSigned<T>::value); | |
| char *p = storage_ + sizeof(storage_); | |
| do { | |
| *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7)); | |
| v >>= 3; | |
| } while (v); | |
| start_ = p; | |
| size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); | |
| } | |
| // Print the signed or unsigned integer as decimal. | |
| // Supports all integral types. | |
| template <typename T> | |
| void PrintAsDec(T v) { | |
| static_assert(std::is_integral_v<T>); | |
| start_ = storage_; | |
| size_ = static_cast<size_t>(numbers_internal::FastIntToBuffer(v, storage_) - | |
| storage_); | |
| } | |
| void PrintAsDec(int128 v) { | |
| auto u = static_cast<uint128>(v); | |
| bool add_neg = false; | |
| if (v < 0) { | |
| add_neg = true; | |
| u = uint128{} - u; | |
| } | |
| PrintAsDec(u, add_neg); | |
| } | |
| void PrintAsDec(uint128 v, bool add_neg = false) { | |
| // This function can be sped up if needed. We can call FastIntToBuffer | |
| // twice, or fix FastIntToBuffer to support uint128. | |
| char *p = storage_ + sizeof(storage_); | |
| do { | |
| p -= 2; | |
| numbers_internal::PutTwoDigits(static_cast<uint32_t>(v % 100), p); | |
| v /= 100; | |
| } while (v); | |
| if (p[0] == '0') { | |
| // We printed one too many hexits. | |
| ++p; | |
| } | |
| if (add_neg) { | |
| *--p = '-'; | |
| } | |
| size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); | |
| start_ = p; | |
| } | |
| // Print the unsigned integer as hex using lowercase. | |
| // Supports unsigned integral types and uint128. | |
| template <typename T> | |
| void PrintAsHexLower(T v) { | |
| static_assert(!IsSigned<T>::value); | |
| char *p = storage_ + sizeof(storage_); | |
| do { | |
| p -= 2; | |
| constexpr const char* table = numbers_internal::kHexTable; | |
| std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2); | |
| if (sizeof(T) == 1) break; | |
| v >>= 8; | |
| } while (v); | |
| if (p[0] == '0') { | |
| // We printed one too many digits. | |
| ++p; | |
| } | |
| start_ = p; | |
| size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); | |
| } | |
| // Print the unsigned integer as hex using uppercase. | |
| // Supports unsigned integral types and uint128. | |
| template <typename T> | |
| void PrintAsHexUpper(T v) { | |
| static_assert(!IsSigned<T>::value); | |
| char *p = storage_ + sizeof(storage_); | |
| // kHexTable is only lowercase, so do it manually for uppercase. | |
| do { | |
| *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15]; | |
| v >>= 4; | |
| } while (v); | |
| start_ = p; | |
| size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p); | |
| } | |
| // The printed value including the '-' sign if available. | |
| // For inputs of value `0`, this will return "0" | |
| string_view with_neg_and_zero() const { return {start_, size_}; } | |
| // The printed value not including the '-' sign. | |
| // For inputs of value `0`, this will return "". | |
| string_view without_neg_or_zero() const { | |
| static_assert('-' < '0', "The check below verifies both."); | |
| size_t advance = start_[0] <= '0' ? 1 : 0; | |
| return {start_ + advance, size_ - advance}; | |
| } | |
| bool is_negative() const { return start_[0] == '-'; } | |
| private: | |
| const char *start_; | |
| size_t size_; | |
| // Max size: 128 bit value as octal -> 43 digits, plus sign char | |
| char storage_[128 / 3 + 1 + 1]; | |
| }; | |
| // Note: 'o' conversions do not have a base indicator, it's just that | |
| // the '#' flag is specified to modify the precision for 'o' conversions. | |
| string_view BaseIndicator(const IntDigits &as_digits, | |
| const FormatConversionSpecImpl conv) { | |
| // always show 0x for %p. | |
| bool alt = conv.has_alt_flag() || | |
| conv.conversion_char() == FormatConversionCharInternal::p; | |
| bool hex = (conv.conversion_char() == FormatConversionCharInternal::x || | |
| conv.conversion_char() == FormatConversionCharInternal::X || | |
| conv.conversion_char() == FormatConversionCharInternal::p); | |
| // From the POSIX description of '#' flag: | |
| // "For x or X conversion specifiers, a non-zero result shall have | |
| // 0x (or 0X) prefixed to it." | |
| if (alt && hex && !as_digits.without_neg_or_zero().empty()) { | |
| return conv.conversion_char() == FormatConversionCharInternal::X ? "0X" | |
| : "0x"; | |
| } | |
| return {}; | |
| } | |
| string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { | |
| if (conv.conversion_char() == FormatConversionCharInternal::d || | |
| conv.conversion_char() == FormatConversionCharInternal::i) { | |
| if (neg) return "-"; | |
| if (conv.has_show_pos_flag()) return "+"; | |
| if (conv.has_sign_col_flag()) return " "; | |
| } | |
| return {}; | |
| } | |
| bool ConvertCharImpl(char v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl* sink) { | |
| size_t fill = 0; | |
| if (conv.width() >= 0) | |
| fill = static_cast<size_t>(conv.width()); | |
| ReducePadding(1, &fill); | |
| if (!conv.has_left_flag()) sink->Append(fill, ' '); | |
| sink->Append(1, v); | |
| if (conv.has_left_flag()) sink->Append(fill, ' '); | |
| return true; | |
| } | |
| bool ConvertIntImplInnerSlow(const IntDigits &as_digits, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| // Print as a sequence of Substrings: | |
| // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] | |
| size_t fill = 0; | |
| if (conv.width() >= 0) | |
| fill = static_cast<size_t>(conv.width()); | |
| string_view formatted = as_digits.without_neg_or_zero(); | |
| ReducePadding(formatted, &fill); | |
| string_view sign = SignColumn(as_digits.is_negative(), conv); | |
| ReducePadding(sign, &fill); | |
| string_view base_indicator = BaseIndicator(as_digits, conv); | |
| ReducePadding(base_indicator, &fill); | |
| bool precision_specified = conv.precision() >= 0; | |
| size_t precision = | |
| precision_specified ? static_cast<size_t>(conv.precision()) : size_t{1}; | |
| if (conv.has_alt_flag() && | |
| conv.conversion_char() == FormatConversionCharInternal::o) { | |
| // From POSIX description of the '#' (alt) flag: | |
| // "For o conversion, it increases the precision (if necessary) to | |
| // force the first digit of the result to be zero." | |
| if (formatted.empty() || *formatted.begin() != '0') { | |
| size_t needed = formatted.size() + 1; | |
| precision = std::max(precision, needed); | |
| } | |
| } | |
| size_t num_zeroes = Excess(formatted.size(), precision); | |
| ReducePadding(num_zeroes, &fill); | |
| size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; | |
| size_t num_right_spaces = conv.has_left_flag() ? fill : 0; | |
| // From POSIX description of the '0' (zero) flag: | |
| // "For d, i, o, u, x, and X conversion specifiers, if a precision | |
| // is specified, the '0' flag is ignored." | |
| if (!precision_specified && conv.has_zero_flag()) { | |
| num_zeroes += num_left_spaces; | |
| num_left_spaces = 0; | |
| } | |
| sink->Append(num_left_spaces, ' '); | |
| sink->Append(sign); | |
| sink->Append(base_indicator); | |
| sink->Append(num_zeroes, '0'); | |
| sink->Append(formatted); | |
| sink->Append(num_right_spaces, ' '); | |
| return true; | |
| } | |
| template <typename T> | |
| bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) { | |
| if (conv.conversion_char() == FormatConversionCharInternal::v) { | |
| conv.set_conversion_char(FormatConversionCharInternal::g); | |
| } | |
| return FormatConversionCharIsFloat(conv.conversion_char()) && | |
| ConvertFloatImpl(v, conv, sink); | |
| } | |
| inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| if (conv.is_basic()) { | |
| sink->Append(v); | |
| return true; | |
| } | |
| return sink->PutPaddedString(v, conv.width(), conv.precision(), | |
| conv.has_left_flag()); | |
| } | |
| inline bool IsLowSurrogate(uint32_t c) { return c >= 0xDC00 && c <= 0xDFFF; } | |
| inline bool ConvertStringArg(const wchar_t *v, | |
| size_t len, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| // Each wide character may result in up to 4 bytes (UTF-8 code units). | |
| constexpr size_t kMaxUtf8CodeUnitsPerWideChar = 4; | |
| if (len > (std::numeric_limits<decltype(len)>::max)() / | |
| kMaxUtf8CodeUnitsPerWideChar) { | |
| // Size too large; we can't handle this. | |
| return false; | |
| } | |
| FixedArray<char> mb(len * kMaxUtf8CodeUnitsPerWideChar); | |
| strings_internal::ShiftState s; | |
| size_t chars_written = 0; | |
| for (size_t i = 0; i < len; ++i) { | |
| // A high surrogate must be immediately followed by a low surrogate. If it | |
| // isn't, the UTF-16 input is malformed and WideToUtf8() would otherwise | |
| // leave a partial sequence in the buffer. The single wchar_t path already | |
| // rejects an unpaired surrogate, so reject it here too. | |
| if (s.saw_high_surrogate) { | |
| const uint32_t cu = static_cast<uint32_t>(v[i]); | |
| if (!IsLowSurrogate(cu)) return false; | |
| } | |
| const size_t chars = | |
| strings_internal::WideToUtf8(v[i], &mb[chars_written], s); | |
| if (chars == static_cast<size_t>(-1)) { return false; } | |
| chars_written += chars; | |
| } | |
| // A trailing high surrogate has no low surrogate to complete it. | |
| if (s.saw_high_surrogate) return false; | |
| return ConvertStringArg(string_view(mb.data(), chars_written), conv, sink); | |
| } | |
| bool ConvertWCharTImpl(wchar_t v, const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| char mb[4]; | |
| strings_internal::ShiftState s; | |
| const size_t chars_written = strings_internal::WideToUtf8(v, mb, s); | |
| return chars_written != static_cast<size_t>(-1) && !s.saw_high_surrogate && | |
| ConvertStringArg(string_view(mb, chars_written), conv, sink); | |
| } | |
| } // namespace | |
| bool ConvertBoolArg(bool v, FormatSinkImpl *sink) { | |
| if (v) { | |
| sink->Append("true"); | |
| } else { | |
| sink->Append("false"); | |
| } | |
| return true; | |
| } | |
| template <typename T> | |
| bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) { | |
| using U = typename MakeUnsigned<T>::type; | |
| IntDigits as_digits; | |
| // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes | |
| // it to complain about a switch/case type mismatch, even though both are | |
| // FormatConversionChar. Likely this is because at this point | |
| // FormatConversionChar is declared, but not defined. | |
| switch (static_cast<uint8_t>(conv.conversion_char())) { | |
| case static_cast<uint8_t>(FormatConversionCharInternal::c): | |
| return (std::is_same_v<T, wchar_t> || (conv.length_mod() == LengthMod::l)) | |
| ? ConvertWCharTImpl(static_cast<wchar_t>(v), conv, sink) | |
| : ConvertCharImpl(static_cast<char>(v), conv, sink); | |
| case static_cast<uint8_t>(FormatConversionCharInternal::o): | |
| as_digits.PrintAsOct(static_cast<U>(v)); | |
| break; | |
| case static_cast<uint8_t>(FormatConversionCharInternal::x): | |
| as_digits.PrintAsHexLower(static_cast<U>(v)); | |
| break; | |
| case static_cast<uint8_t>(FormatConversionCharInternal::X): | |
| as_digits.PrintAsHexUpper(static_cast<U>(v)); | |
| break; | |
| case static_cast<uint8_t>(FormatConversionCharInternal::u): | |
| as_digits.PrintAsDec(static_cast<U>(v)); | |
| break; | |
| case static_cast<uint8_t>(FormatConversionCharInternal::d): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::i): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::v): | |
| as_digits.PrintAsDec(v); | |
| break; | |
| case static_cast<uint8_t>(FormatConversionCharInternal::a): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::e): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::f): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::g): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::A): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::E): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::F): | |
| case static_cast<uint8_t>(FormatConversionCharInternal::G): | |
| return ConvertFloatImpl(static_cast<double>(v), conv, sink); | |
| default: | |
| ABSL_ASSUME(false); | |
| } | |
| if (conv.is_basic()) { | |
| sink->Append(as_digits.with_neg_and_zero()); | |
| return true; | |
| } | |
| return ConvertIntImplInnerSlow(as_digits, conv, sink); | |
| } | |
| template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<signed char>(signed char v, | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<unsigned char>(unsigned char v, | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<wchar_t>(wchar_t v, FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<short>(short v, // NOLINT | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<unsigned short>(unsigned short v, // NOLINT | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<unsigned int>(unsigned int v, | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<long>(long v, // NOLINT | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<unsigned long>(unsigned long v, // NOLINT | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<long long>(long long v, // NOLINT | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| template bool ConvertIntArg<unsigned long long>(unsigned long long v, // NOLINT | |
| FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink); | |
| // ==================== Strings ==================== | |
| StringConvertResult FormatConvertImpl(const std::string &v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertStringArg(v, conv, sink)}; | |
| } | |
| StringConvertResult FormatConvertImpl(const std::wstring &v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertStringArg(v.data(), v.size(), conv, sink)}; | |
| } | |
| StringConvertResult FormatConvertImpl(string_view v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertStringArg(v, conv, sink)}; | |
| } | |
| StringConvertResult FormatConvertImpl(std::wstring_view v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl* sink) { | |
| return {ConvertStringArg(v.data(), v.size(), conv, sink)}; | |
| } | |
| StringPtrConvertResult FormatConvertImpl(const char* v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl* sink) { | |
| if (conv.conversion_char() == FormatConversionCharInternal::p) | |
| return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; | |
| size_t len; | |
| if (v == nullptr) { | |
| len = 0; | |
| } else if (conv.precision() < 0) { | |
| len = std::strlen(v); | |
| } else { | |
| // If precision is set, we look for the NUL-terminator on the valid range. | |
| len = static_cast<size_t>(std::find(v, v + conv.precision(), '\0') - v); | |
| } | |
| return {ConvertStringArg(string_view(v, len), conv, sink)}; | |
| } | |
| StringPtrConvertResult FormatConvertImpl(const wchar_t* v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl* sink) { | |
| if (conv.conversion_char() == FormatConversionCharInternal::p) { | |
| return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; | |
| } | |
| size_t len; | |
| if (v == nullptr) { | |
| len = 0; | |
| } else if (conv.precision() < 0) { | |
| len = std::wcslen(v); | |
| } else { | |
| // If precision is set, we look for the NUL-terminator on the valid range. | |
| len = static_cast<size_t>(std::find(v, v + conv.precision(), L'\0') - v); | |
| } | |
| return {ConvertStringArg(v, len, conv, sink)}; | |
| } | |
| StringPtrConvertResult FormatConvertImpl(std::nullptr_t, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl* sink) { | |
| return FormatConvertImpl(static_cast<const char*>(nullptr), conv, sink); | |
| } | |
| // ==================== Raw pointers ==================== | |
| ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( | |
| VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { | |
| if (!v.value) { | |
| sink->Append("(nil)"); | |
| return {true}; | |
| } | |
| IntDigits as_digits; | |
| as_digits.PrintAsHexLower(v.value); | |
| return {ConvertIntImplInnerSlow(as_digits, conv, sink)}; | |
| } | |
| // ==================== Floats ==================== | |
| FloatingConvertResult FormatConvertImpl(float v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertFloatArg(v, conv, sink)}; | |
| } | |
| FloatingConvertResult FormatConvertImpl(double v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertFloatArg(v, conv, sink)}; | |
| } | |
| FloatingConvertResult FormatConvertImpl(long double v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertFloatArg(v, conv, sink)}; | |
| } | |
| // ==================== Chars ==================== | |
| CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| CharConvertResult FormatConvertImpl(wchar_t v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl* sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| // ==================== Ints ==================== | |
| IntegralConvertResult FormatConvertImpl(signed char v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(unsigned char v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(short v, // NOLINT | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(int v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(unsigned v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(long v, // NOLINT | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(long long v, // NOLINT | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(absl::int128 v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| IntegralConvertResult FormatConvertImpl(absl::uint128 v, | |
| const FormatConversionSpecImpl conv, | |
| FormatSinkImpl *sink) { | |
| return {ConvertIntArg(v, conv, sink)}; | |
| } | |
| ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); | |
| } // namespace str_format_internal | |
| ABSL_NAMESPACE_END | |
| } // namespace absl |