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 <cassert>
22 #include <cerrno>
23 #include <cstdlib>
24 #include <string>
25 #include <type_traits>
26 
27 #include "absl/base/port.h"
28 #include "absl/strings/internal/str_format/float_conversion.h"
29 #include "absl/strings/numbers.h"
30 
31 namespace absl {
32 ABSL_NAMESPACE_BEGIN
33 namespace str_format_internal {
34 namespace {
35 
36 // Reduce *capacity by s.size(), clipped to a 0 minimum.
ReducePadding(string_view s,size_t * capacity)37 void ReducePadding(string_view s, size_t *capacity) {
38   *capacity = Excess(s.size(), *capacity);
39 }
40 
41 // Reduce *capacity by n, clipped to a 0 minimum.
ReducePadding(size_t n,size_t * capacity)42 void ReducePadding(size_t n, size_t *capacity) {
43   *capacity = Excess(n, *capacity);
44 }
45 
46 template <typename T>
47 struct MakeUnsigned : std::make_unsigned<T> {};
48 template <>
49 struct MakeUnsigned<absl::int128> {
50   using type = absl::uint128;
51 };
52 template <>
53 struct MakeUnsigned<absl::uint128> {
54   using type = absl::uint128;
55 };
56 
57 template <typename T>
58 struct IsSigned : std::is_signed<T> {};
59 template <>
60 struct IsSigned<absl::int128> : std::true_type {};
61 template <>
62 struct IsSigned<absl::uint128> : std::false_type {};
63 
64 // Integral digit printer.
65 // Call one of the PrintAs* routines after construction once.
66 // Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results.
67 class IntDigits {
68  public:
69   // Print the unsigned integer as octal.
70   // Supports unsigned integral types and uint128.
71   template <typename T>
PrintAsOct(T v)72   void PrintAsOct(T v) {
73     static_assert(!IsSigned<T>::value, "");
74     char *p = storage_ + sizeof(storage_);
75     do {
76       *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7));
77       v >>= 3;
78     } while (v);
79     start_ = p;
80     size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
81   }
82 
83   // Print the signed or unsigned integer as decimal.
84   // Supports all integral types.
85   template <typename T>
PrintAsDec(T v)86   void PrintAsDec(T v) {
87     static_assert(std::is_integral<T>::value, "");
88     start_ = storage_;
89     size_ = static_cast<size_t>(numbers_internal::FastIntToBuffer(v, storage_) -
90                                 storage_);
91   }
92 
PrintAsDec(int128 v)93   void PrintAsDec(int128 v) {
94     auto u = static_cast<uint128>(v);
95     bool add_neg = false;
96     if (v < 0) {
97       add_neg = true;
98       u = uint128{} - u;
99     }
100     PrintAsDec(u, add_neg);
101   }
102 
PrintAsDec(uint128 v,bool add_neg=false)103   void PrintAsDec(uint128 v, bool add_neg = false) {
104     // This function can be sped up if needed. We can call FastIntToBuffer
105     // twice, or fix FastIntToBuffer to support uint128.
106     char *p = storage_ + sizeof(storage_);
107     do {
108       p -= 2;
109       numbers_internal::PutTwoDigits(static_cast<size_t>(v % 100), p);
110       v /= 100;
111     } while (v);
112     if (p[0] == '0') {
113       // We printed one too many hexits.
114       ++p;
115     }
116     if (add_neg) {
117       *--p = '-';
118     }
119     size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
120     start_ = p;
121   }
122 
123   // Print the unsigned integer as hex using lowercase.
124   // Supports unsigned integral types and uint128.
125   template <typename T>
PrintAsHexLower(T v)126   void PrintAsHexLower(T v) {
127     static_assert(!IsSigned<T>::value, "");
128     char *p = storage_ + sizeof(storage_);
129 
130     do {
131       p -= 2;
132       constexpr const char* table = numbers_internal::kHexTable;
133       std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2);
134       if (sizeof(T) == 1) break;
135       v >>= 8;
136     } while (v);
137     if (p[0] == '0') {
138       // We printed one too many digits.
139       ++p;
140     }
141     start_ = p;
142     size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
143   }
144 
145   // Print the unsigned integer as hex using uppercase.
146   // Supports unsigned integral types and uint128.
147   template <typename T>
PrintAsHexUpper(T v)148   void PrintAsHexUpper(T v) {
149     static_assert(!IsSigned<T>::value, "");
150     char *p = storage_ + sizeof(storage_);
151 
152     // kHexTable is only lowercase, so do it manually for uppercase.
153     do {
154       *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15];
155       v >>= 4;
156     } while (v);
157     start_ = p;
158     size_ = static_cast<size_t>(storage_ + sizeof(storage_) - p);
159   }
160 
161   // The printed value including the '-' sign if available.
162   // For inputs of value `0`, this will return "0"
with_neg_and_zero() const163   string_view with_neg_and_zero() const { return {start_, size_}; }
164 
165   // The printed value not including the '-' sign.
166   // For inputs of value `0`, this will return "".
without_neg_or_zero() const167   string_view without_neg_or_zero() const {
168     static_assert('-' < '0', "The check below verifies both.");
169     size_t advance = start_[0] <= '0' ? 1 : 0;
170     return {start_ + advance, size_ - advance};
171   }
172 
is_negative() const173   bool is_negative() const { return start_[0] == '-'; }
174 
175  private:
176   const char *start_;
177   size_t size_;
178   // Max size: 128 bit value as octal -> 43 digits, plus sign char
179   char storage_[128 / 3 + 1 + 1];
180 };
181 
182 // Note: 'o' conversions do not have a base indicator, it's just that
183 // the '#' flag is specified to modify the precision for 'o' conversions.
BaseIndicator(const IntDigits & as_digits,const FormatConversionSpecImpl conv)184 string_view BaseIndicator(const IntDigits &as_digits,
185                           const FormatConversionSpecImpl conv) {
186   // always show 0x for %p.
187   bool alt = conv.has_alt_flag() ||
188              conv.conversion_char() == FormatConversionCharInternal::p;
189   bool hex = (conv.conversion_char() == FormatConversionCharInternal::x ||
190               conv.conversion_char() == FormatConversionCharInternal::X ||
191               conv.conversion_char() == FormatConversionCharInternal::p);
192   // From the POSIX description of '#' flag:
193   //   "For x or X conversion specifiers, a non-zero result shall have
194   //   0x (or 0X) prefixed to it."
195   if (alt && hex && !as_digits.without_neg_or_zero().empty()) {
196     return conv.conversion_char() == FormatConversionCharInternal::X ? "0X"
197                                                                      : "0x";
198   }
199   return {};
200 }
201 
SignColumn(bool neg,const FormatConversionSpecImpl conv)202 string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) {
203   if (conv.conversion_char() == FormatConversionCharInternal::d ||
204       conv.conversion_char() == FormatConversionCharInternal::i) {
205     if (neg) return "-";
206     if (conv.has_show_pos_flag()) return "+";
207     if (conv.has_sign_col_flag()) return " ";
208   }
209   return {};
210 }
211 
ConvertCharImpl(char v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)212 bool ConvertCharImpl(char v,
213                      const FormatConversionSpecImpl conv,
214                      FormatSinkImpl* sink) {
215   size_t fill = 0;
216   if (conv.width() >= 0)
217     fill = static_cast<size_t>(conv.width());
218   ReducePadding(1, &fill);
219   if (!conv.has_left_flag()) sink->Append(fill, ' ');
220   sink->Append(1, v);
221   if (conv.has_left_flag()) sink->Append(fill, ' ');
222   return true;
223 }
224 
ConvertIntImplInnerSlow(const IntDigits & as_digits,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)225 bool ConvertIntImplInnerSlow(const IntDigits &as_digits,
226                              const FormatConversionSpecImpl conv,
227                              FormatSinkImpl *sink) {
228   // Print as a sequence of Substrings:
229   //   [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
230   size_t fill = 0;
231   if (conv.width() >= 0)
232     fill = static_cast<size_t>(conv.width());
233 
234   string_view formatted = as_digits.without_neg_or_zero();
235   ReducePadding(formatted, &fill);
236 
237   string_view sign = SignColumn(as_digits.is_negative(), conv);
238   ReducePadding(sign, &fill);
239 
240   string_view base_indicator = BaseIndicator(as_digits, conv);
241   ReducePadding(base_indicator, &fill);
242 
243   bool precision_specified = conv.precision() >= 0;
244   size_t precision =
245       precision_specified ? static_cast<size_t>(conv.precision()) : size_t{1};
246 
247   if (conv.has_alt_flag() &&
248       conv.conversion_char() == FormatConversionCharInternal::o) {
249     // From POSIX description of the '#' (alt) flag:
250     //   "For o conversion, it increases the precision (if necessary) to
251     //   force the first digit of the result to be zero."
252     if (formatted.empty() || *formatted.begin() != '0') {
253       size_t needed = formatted.size() + 1;
254       precision = std::max(precision, needed);
255     }
256   }
257 
258   size_t num_zeroes = Excess(formatted.size(), precision);
259   ReducePadding(num_zeroes, &fill);
260 
261   size_t num_left_spaces = !conv.has_left_flag() ? fill : 0;
262   size_t num_right_spaces = conv.has_left_flag() ? fill : 0;
263 
264   // From POSIX description of the '0' (zero) flag:
265   //   "For d, i, o, u, x, and X conversion specifiers, if a precision
266   //   is specified, the '0' flag is ignored."
267   if (!precision_specified && conv.has_zero_flag()) {
268     num_zeroes += num_left_spaces;
269     num_left_spaces = 0;
270   }
271 
272   sink->Append(num_left_spaces, ' ');
273   sink->Append(sign);
274   sink->Append(base_indicator);
275   sink->Append(num_zeroes, '0');
276   sink->Append(formatted);
277   sink->Append(num_right_spaces, ' ');
278   return true;
279 }
280 
281 template <typename T,
282           typename std::enable_if<(std::is_integral<T>::value &&
283                                    std::is_signed<T>::value) ||
284                                       std::is_same<T, int128>::value,
285                                   int>::type = 0>
ConvertV(T)286 constexpr auto ConvertV(T) {
287   return FormatConversionCharInternal::d;
288 }
289 
290 template <typename T,
291           typename std::enable_if<(std::is_integral<T>::value &&
292                                    std::is_unsigned<T>::value) ||
293                                       std::is_same<T, uint128>::value,
294                                   int>::type = 0>
ConvertV(T)295 constexpr auto ConvertV(T) {
296   return FormatConversionCharInternal::u;
297 }
298 
299 template <typename T>
ConvertFloatArg(T v,FormatConversionSpecImpl conv,FormatSinkImpl * sink)300 bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
301   if (conv.conversion_char() == FormatConversionCharInternal::v) {
302     conv.set_conversion_char(FormatConversionCharInternal::g);
303   }
304 
305   return FormatConversionCharIsFloat(conv.conversion_char()) &&
306          ConvertFloatImpl(v, conv, sink);
307 }
308 
ConvertStringArg(string_view v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)309 inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
310                              FormatSinkImpl *sink) {
311   if (conv.is_basic()) {
312     sink->Append(v);
313     return true;
314   }
315   return sink->PutPaddedString(v, conv.width(), conv.precision(),
316                                conv.has_left_flag());
317 }
318 
319 }  // namespace
320 
ConvertBoolArg(bool v,FormatSinkImpl * sink)321 bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
322   if (v) {
323     sink->Append("true");
324   } else {
325     sink->Append("false");
326   }
327   return true;
328 }
329 
330 template <typename T>
ConvertIntArg(T v,FormatConversionSpecImpl conv,FormatSinkImpl * sink)331 bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
332   using U = typename MakeUnsigned<T>::type;
333   IntDigits as_digits;
334 
335   if (conv.conversion_char() == FormatConversionCharInternal::v) {
336     conv.set_conversion_char(ConvertV(T{}));
337   }
338 
339   // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes
340   // it to complain about a switch/case type mismatch, even though both are
341   // FormatConverionChar.  Likely this is because at this point
342   // FormatConversionChar is declared, but not defined.
343   switch (static_cast<uint8_t>(conv.conversion_char())) {
344     case static_cast<uint8_t>(FormatConversionCharInternal::c):
345       return ConvertCharImpl(static_cast<char>(v), conv, sink);
346 
347     case static_cast<uint8_t>(FormatConversionCharInternal::o):
348       as_digits.PrintAsOct(static_cast<U>(v));
349       break;
350 
351     case static_cast<uint8_t>(FormatConversionCharInternal::x):
352       as_digits.PrintAsHexLower(static_cast<U>(v));
353       break;
354     case static_cast<uint8_t>(FormatConversionCharInternal::X):
355       as_digits.PrintAsHexUpper(static_cast<U>(v));
356       break;
357 
358     case static_cast<uint8_t>(FormatConversionCharInternal::u):
359       as_digits.PrintAsDec(static_cast<U>(v));
360       break;
361 
362     case static_cast<uint8_t>(FormatConversionCharInternal::d):
363     case static_cast<uint8_t>(FormatConversionCharInternal::i):
364       as_digits.PrintAsDec(v);
365       break;
366 
367     case static_cast<uint8_t>(FormatConversionCharInternal::a):
368     case static_cast<uint8_t>(FormatConversionCharInternal::e):
369     case static_cast<uint8_t>(FormatConversionCharInternal::f):
370     case static_cast<uint8_t>(FormatConversionCharInternal::g):
371     case static_cast<uint8_t>(FormatConversionCharInternal::A):
372     case static_cast<uint8_t>(FormatConversionCharInternal::E):
373     case static_cast<uint8_t>(FormatConversionCharInternal::F):
374     case static_cast<uint8_t>(FormatConversionCharInternal::G):
375       return ConvertFloatImpl(static_cast<double>(v), conv, sink);
376 
377     default:
378       ABSL_ASSUME(false);
379   }
380 
381   if (conv.is_basic()) {
382     sink->Append(as_digits.with_neg_and_zero());
383     return true;
384   }
385   return ConvertIntImplInnerSlow(as_digits, conv, sink);
386 }
387 
388 template bool ConvertIntArg<char>(char v, FormatConversionSpecImpl conv,
389                                   FormatSinkImpl *sink);
390 template bool ConvertIntArg<signed char>(signed char v,
391                                          FormatConversionSpecImpl conv,
392                                          FormatSinkImpl *sink);
393 template bool ConvertIntArg<unsigned char>(unsigned char v,
394                                            FormatConversionSpecImpl conv,
395                                            FormatSinkImpl *sink);
396 template bool ConvertIntArg<short>(short v,  // NOLINT
397                                    FormatConversionSpecImpl conv,
398                                    FormatSinkImpl *sink);
399 template bool ConvertIntArg<unsigned short>(unsigned short v,  // NOLINT
400                                             FormatConversionSpecImpl conv,
401                                             FormatSinkImpl *sink);
402 template bool ConvertIntArg<int>(int v, FormatConversionSpecImpl conv,
403                                  FormatSinkImpl *sink);
404 template bool ConvertIntArg<unsigned int>(unsigned int v,
405                                           FormatConversionSpecImpl conv,
406                                           FormatSinkImpl *sink);
407 template bool ConvertIntArg<long>(long v,  // NOLINT
408                                   FormatConversionSpecImpl conv,
409                                   FormatSinkImpl *sink);
410 template bool ConvertIntArg<unsigned long>(unsigned long v,  // NOLINT
411                                            FormatConversionSpecImpl conv,
412                                            FormatSinkImpl *sink);
413 template bool ConvertIntArg<long long>(long long v,  // NOLINT
414                                        FormatConversionSpecImpl conv,
415                                        FormatSinkImpl *sink);
416 template bool ConvertIntArg<unsigned long long>(unsigned long long v,  // NOLINT
417                                                 FormatConversionSpecImpl conv,
418                                                 FormatSinkImpl *sink);
419 
420 // ==================== Strings ====================
FormatConvertImpl(const std::string & v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)421 StringConvertResult FormatConvertImpl(const std::string &v,
422                                       const FormatConversionSpecImpl conv,
423                                       FormatSinkImpl *sink) {
424   return {ConvertStringArg(v, conv, sink)};
425 }
426 
FormatConvertImpl(string_view v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)427 StringConvertResult FormatConvertImpl(string_view v,
428                                       const FormatConversionSpecImpl conv,
429                                       FormatSinkImpl *sink) {
430   return {ConvertStringArg(v, conv, sink)};
431 }
432 
433 ArgConvertResult<FormatConversionCharSetUnion(
434     FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
FormatConvertImpl(const char * v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)435 FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv,
436                   FormatSinkImpl *sink) {
437   if (conv.conversion_char() == FormatConversionCharInternal::p)
438     return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
439   size_t len;
440   if (v == nullptr) {
441     len = 0;
442   } else if (conv.precision() < 0) {
443     len = std::strlen(v);
444   } else {
445     // If precision is set, we look for the NUL-terminator on the valid range.
446     len = static_cast<size_t>(std::find(v, v + conv.precision(), '\0') - v);
447   }
448   return {ConvertStringArg(string_view(v, len), conv, sink)};
449 }
450 
451 // ==================== Raw pointers ====================
FormatConvertImpl(VoidPtr v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)452 ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
453     VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
454   if (!v.value) {
455     sink->Append("(nil)");
456     return {true};
457   }
458   IntDigits as_digits;
459   as_digits.PrintAsHexLower(v.value);
460   return {ConvertIntImplInnerSlow(as_digits, conv, sink)};
461 }
462 
463 // ==================== Floats ====================
FormatConvertImpl(float v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)464 FloatingConvertResult FormatConvertImpl(float v,
465                                         const FormatConversionSpecImpl conv,
466                                         FormatSinkImpl *sink) {
467   return {ConvertFloatArg(v, conv, sink)};
468 }
FormatConvertImpl(double v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)469 FloatingConvertResult FormatConvertImpl(double v,
470                                         const FormatConversionSpecImpl conv,
471                                         FormatSinkImpl *sink) {
472   return {ConvertFloatArg(v, conv, sink)};
473 }
FormatConvertImpl(long double v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)474 FloatingConvertResult FormatConvertImpl(long double v,
475                                         const FormatConversionSpecImpl conv,
476                                         FormatSinkImpl *sink) {
477   return {ConvertFloatArg(v, conv, sink)};
478 }
479 
480 // ==================== Chars ====================
FormatConvertImpl(char v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)481 CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv,
482                                     FormatSinkImpl *sink) {
483   return {ConvertIntArg(v, conv, sink)};
484 }
FormatConvertImpl(signed char v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)485 CharConvertResult FormatConvertImpl(signed char v,
486                                     const FormatConversionSpecImpl conv,
487                                     FormatSinkImpl *sink) {
488   return {ConvertIntArg(v, conv, sink)};
489 }
FormatConvertImpl(unsigned char v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)490 CharConvertResult FormatConvertImpl(unsigned char v,
491                                     const FormatConversionSpecImpl conv,
492                                     FormatSinkImpl *sink) {
493   return {ConvertIntArg(v, conv, sink)};
494 }
495 
496 // ==================== Ints ====================
FormatConvertImpl(short v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)497 IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
498                                         const FormatConversionSpecImpl conv,
499                                         FormatSinkImpl *sink) {
500   return {ConvertIntArg(v, conv, sink)};
501 }
FormatConvertImpl(unsigned short v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)502 IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
503                                         const FormatConversionSpecImpl conv,
504                                         FormatSinkImpl *sink) {
505   return {ConvertIntArg(v, conv, sink)};
506 }
FormatConvertImpl(int v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)507 IntegralConvertResult FormatConvertImpl(int v,
508                                         const FormatConversionSpecImpl conv,
509                                         FormatSinkImpl *sink) {
510   return {ConvertIntArg(v, conv, sink)};
511 }
FormatConvertImpl(unsigned v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)512 IntegralConvertResult FormatConvertImpl(unsigned v,
513                                         const FormatConversionSpecImpl conv,
514                                         FormatSinkImpl *sink) {
515   return {ConvertIntArg(v, conv, sink)};
516 }
FormatConvertImpl(long v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)517 IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
518                                         const FormatConversionSpecImpl conv,
519                                         FormatSinkImpl *sink) {
520   return {ConvertIntArg(v, conv, sink)};
521 }
FormatConvertImpl(unsigned long v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)522 IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
523                                         const FormatConversionSpecImpl conv,
524                                         FormatSinkImpl *sink) {
525   return {ConvertIntArg(v, conv, sink)};
526 }
FormatConvertImpl(long long v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)527 IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
528                                         const FormatConversionSpecImpl conv,
529                                         FormatSinkImpl *sink) {
530   return {ConvertIntArg(v, conv, sink)};
531 }
FormatConvertImpl(unsigned long long v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)532 IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
533                                         const FormatConversionSpecImpl conv,
534                                         FormatSinkImpl *sink) {
535   return {ConvertIntArg(v, conv, sink)};
536 }
FormatConvertImpl(absl::int128 v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)537 IntegralConvertResult FormatConvertImpl(absl::int128 v,
538                                         const FormatConversionSpecImpl conv,
539                                         FormatSinkImpl *sink) {
540   return {ConvertIntArg(v, conv, sink)};
541 }
FormatConvertImpl(absl::uint128 v,const FormatConversionSpecImpl conv,FormatSinkImpl * sink)542 IntegralConvertResult FormatConvertImpl(absl::uint128 v,
543                                         const FormatConversionSpecImpl conv,
544                                         FormatSinkImpl *sink) {
545   return {ConvertIntArg(v, conv, sink)};
546 }
547 
548 ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
549 
550 
551 
552 }  // namespace str_format_internal
553 
554 ABSL_NAMESPACE_END
555 }  // namespace absl
556