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