• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2017 The Abseil Authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 // -----------------------------------------------------------------------------
17 // File: str_cat.h
18 // -----------------------------------------------------------------------------
19 //
20 // This package contains functions for efficiently concatenating and appending
21 // strings: `StrCat()` and `StrAppend()`. Most of the work within these routines
22 // is actually handled through use of a special AlphaNum type, which was
23 // designed to be used as a parameter type that efficiently manages conversion
24 // to strings and avoids copies in the above operations.
25 //
26 // Any routine accepting either a string or a number may accept `AlphaNum`.
27 // The basic idea is that by accepting a `const AlphaNum &` as an argument
28 // to your function, your callers will automagically convert bools, integers,
29 // and floating point values to strings for you.
30 //
31 // NOTE: Use of `AlphaNum` outside of the //absl/strings package is unsupported
32 // except for the specific case of function parameters of type `AlphaNum` or
33 // `const AlphaNum &`. In particular, instantiating `AlphaNum` directly as a
34 // stack variable is not supported.
35 //
36 // Conversion from 8-bit values is not accepted because, if it were, then an
37 // attempt to pass ':' instead of ":" might result in a 58 ending up in your
38 // result.
39 //
40 // Bools convert to "0" or "1". Pointers to types other than `char *` are not
41 // valid inputs. No output is generated for null `char *` pointers.
42 //
43 // Floating point numbers are formatted with six-digit precision, which is
44 // the default for "std::cout <<" or printf "%g" (the same as "%.6g").
45 //
46 // You can convert to hexadecimal output rather than decimal output using the
47 // `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to
48 // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using
49 // a `PadSpec` enum.
50 //
51 // User-defined types can be formatted with the `AbslStringify()` customization
52 // point. The API relies on detecting an overload in the user-defined type's
53 // namespace of a free (non-member) `AbslStringify()` function as a definition
54 // (typically declared as a friend and implemented in-line.
55 // with the following signature:
56 //
57 // class MyClass { ... };
58 //
59 // template <typename Sink>
60 // void AbslStringify(Sink& sink, const MyClass& value);
61 //
62 // An `AbslStringify()` overload for a type should only be declared in the same
63 // file and namespace as said type.
64 //
65 // Note that `AbslStringify()` also supports use with `absl::StrFormat()` and
66 // `absl::Substitute()`.
67 //
68 // Example:
69 //
70 // struct Point {
71 //   // To add formatting support to `Point`, we simply need to add a free
72 //   // (non-member) function `AbslStringify()`. This method specifies how
73 //   // Point should be printed when absl::StrCat() is called on it. You can add
74 //   // such a free function using a friend declaration within the body of the
75 //   // class. The sink parameter is a templated type to avoid requiring
76 //   // dependencies.
77 //   template <typename Sink> friend void AbslStringify(Sink&
78 //   sink, const Point& p) {
79 //     absl::Format(&sink, "(%v, %v)", p.x, p.y);
80 //   }
81 //
82 //   int x;
83 //   int y;
84 // };
85 // -----------------------------------------------------------------------------
86 
87 #ifndef ABSL_STRINGS_STR_CAT_H_
88 #define ABSL_STRINGS_STR_CAT_H_
89 
90 #include <algorithm>
91 #include <array>
92 #include <cassert>
93 #include <cstddef>
94 #include <cstdint>
95 #include <cstring>
96 #include <string>
97 #include <type_traits>
98 #include <utility>
99 #include <vector>
100 
101 #include "absl/base/attributes.h"
102 #include "absl/base/nullability.h"
103 #include "absl/base/port.h"
104 #include "absl/meta/type_traits.h"
105 #include "absl/strings/has_absl_stringify.h"
106 #include "absl/strings/internal/resize_uninitialized.h"
107 #include "absl/strings/internal/stringify_sink.h"
108 #include "absl/strings/numbers.h"
109 #include "absl/strings/string_view.h"
110 
111 namespace absl {
112 ABSL_NAMESPACE_BEGIN
113 
114 namespace strings_internal {
115 // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
116 // memory allocation.  It is simply a pair of a fixed-size character array, and
117 // a size.  Please don't use outside of absl, yet.
118 template <size_t max_size>
119 struct AlphaNumBuffer {
120   std::array<char, max_size> data;
121   size_t size;
122 };
123 
124 }  // namespace strings_internal
125 
126 // Enum that specifies the number of significant digits to return in a `Hex` or
127 // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
128 // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
129 // would produce hexadecimal strings such as "    a","    f".
130 enum PadSpec : uint8_t {
131   kNoPad = 1,
132   kZeroPad2,
133   kZeroPad3,
134   kZeroPad4,
135   kZeroPad5,
136   kZeroPad6,
137   kZeroPad7,
138   kZeroPad8,
139   kZeroPad9,
140   kZeroPad10,
141   kZeroPad11,
142   kZeroPad12,
143   kZeroPad13,
144   kZeroPad14,
145   kZeroPad15,
146   kZeroPad16,
147   kZeroPad17,
148   kZeroPad18,
149   kZeroPad19,
150   kZeroPad20,
151 
152   kSpacePad2 = kZeroPad2 + 64,
153   kSpacePad3,
154   kSpacePad4,
155   kSpacePad5,
156   kSpacePad6,
157   kSpacePad7,
158   kSpacePad8,
159   kSpacePad9,
160   kSpacePad10,
161   kSpacePad11,
162   kSpacePad12,
163   kSpacePad13,
164   kSpacePad14,
165   kSpacePad15,
166   kSpacePad16,
167   kSpacePad17,
168   kSpacePad18,
169   kSpacePad19,
170   kSpacePad20,
171 };
172 
173 // -----------------------------------------------------------------------------
174 // Hex
175 // -----------------------------------------------------------------------------
176 //
177 // `Hex` stores a set of hexadecimal string conversion parameters for use
178 // within `AlphaNum` string conversions.
179 struct Hex {
180   uint64_t value;
181   uint8_t width;
182   char fill;
183 
184   template <typename Int>
185   explicit Hex(
186       Int v, PadSpec spec = absl::kNoPad,
187       typename std::enable_if<sizeof(Int) == 1 &&
188                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex189       : Hex(spec, static_cast<uint8_t>(v)) {}
190   template <typename Int>
191   explicit Hex(
192       Int v, PadSpec spec = absl::kNoPad,
193       typename std::enable_if<sizeof(Int) == 2 &&
194                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex195       : Hex(spec, static_cast<uint16_t>(v)) {}
196   template <typename Int>
197   explicit Hex(
198       Int v, PadSpec spec = absl::kNoPad,
199       typename std::enable_if<sizeof(Int) == 4 &&
200                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex201       : Hex(spec, static_cast<uint32_t>(v)) {}
202   template <typename Int>
203   explicit Hex(
204       Int v, PadSpec spec = absl::kNoPad,
205       typename std::enable_if<sizeof(Int) == 8 &&
206                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex207       : Hex(spec, static_cast<uint64_t>(v)) {}
208   template <typename Pointee>
209   explicit Hex(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad)
HexHex210       : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
211 
212   template <typename S>
AbslStringifyHex213   friend void AbslStringify(S& sink, Hex hex) {
214     static_assert(
215         numbers_internal::kFastToBufferSize >= 32,
216         "This function only works when output buffer >= 32 bytes long");
217     char buffer[numbers_internal::kFastToBufferSize];
218     char* const end = &buffer[numbers_internal::kFastToBufferSize];
219     auto real_width =
220         absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
221     if (real_width >= hex.width) {
222       sink.Append(absl::string_view(end - real_width, real_width));
223     } else {
224       // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
225       // max pad width can be up to 20.
226       std::memset(end - 32, hex.fill, 16);
227       // Patch up everything else up to the real_width.
228       std::memset(end - real_width - 16, hex.fill, 16);
229       sink.Append(absl::string_view(end - hex.width, hex.width));
230     }
231   }
232 
233  private:
HexHex234   Hex(PadSpec spec, uint64_t v)
235       : value(v),
236         width(spec == absl::kNoPad
237                   ? 1
238                   : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
239                                              : spec - absl::kZeroPad2 + 2),
240         fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
241 };
242 
243 // -----------------------------------------------------------------------------
244 // Dec
245 // -----------------------------------------------------------------------------
246 //
247 // `Dec` stores a set of decimal string conversion parameters for use
248 // within `AlphaNum` string conversions.  Dec is slower than the default
249 // integer conversion, so use it only if you need padding.
250 struct Dec {
251   uint64_t value;
252   uint8_t width;
253   char fill;
254   bool neg;
255 
256   template <typename Int>
257   explicit Dec(Int v, PadSpec spec = absl::kNoPad,
258                typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
259       : value(v >= 0 ? static_cast<uint64_t>(v)
260                      : uint64_t{0} - static_cast<uint64_t>(v)),
261         width(spec == absl::kNoPad       ? 1
262               : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
263                                          : spec - absl::kZeroPad2 + 2),
264         fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
265         neg(v < 0) {}
266 
267   template <typename S>
AbslStringifyDec268   friend void AbslStringify(S& sink, Dec dec) {
269     assert(dec.width <= numbers_internal::kFastToBufferSize);
270     char buffer[numbers_internal::kFastToBufferSize];
271     char* const end = &buffer[numbers_internal::kFastToBufferSize];
272     char* const minfill = end - dec.width;
273     char* writer = end;
274     uint64_t val = dec.value;
275     while (val > 9) {
276       *--writer = '0' + (val % 10);
277       val /= 10;
278     }
279     *--writer = '0' + static_cast<char>(val);
280     if (dec.neg) *--writer = '-';
281 
282     ptrdiff_t fillers = writer - minfill;
283     if (fillers > 0) {
284       // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
285       // But...: if the fill character is '0', then it's <+/-><fill><digits>
286       bool add_sign_again = false;
287       if (dec.neg && dec.fill == '0') {  // If filling with '0',
288         ++writer;                    // ignore the sign we just added
289         add_sign_again = true;       // and re-add the sign later.
290       }
291       writer -= fillers;
292       std::fill_n(writer, fillers, dec.fill);
293       if (add_sign_again) *--writer = '-';
294     }
295 
296     sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
297   }
298 };
299 
300 // -----------------------------------------------------------------------------
301 // AlphaNum
302 // -----------------------------------------------------------------------------
303 //
304 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
305 // `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
306 // and hexadecimal values (through the `Dec` and `Hex` types) into strings.
307 // `AlphaNum` should only be used as a function parameter. Do not instantiate
308 //  `AlphaNum` directly as a stack variable.
309 
310 class AlphaNum {
311  public:
312   // No bool ctor -- bools convert to an integral type.
313   // A bool ctor would also convert incoming pointers (bletch).
314 
AlphaNum(int x)315   AlphaNum(int x)  // NOLINT(runtime/explicit)
316       : piece_(digits_, static_cast<size_t>(
317                             numbers_internal::FastIntToBuffer(x, digits_) -
318                             &digits_[0])) {}
AlphaNum(unsigned int x)319   AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
320       : piece_(digits_, static_cast<size_t>(
321                             numbers_internal::FastIntToBuffer(x, digits_) -
322                             &digits_[0])) {}
AlphaNum(long x)323   AlphaNum(long x)  // NOLINT(*)
324       : piece_(digits_, static_cast<size_t>(
325                             numbers_internal::FastIntToBuffer(x, digits_) -
326                             &digits_[0])) {}
AlphaNum(unsigned long x)327   AlphaNum(unsigned long x)  // NOLINT(*)
328       : piece_(digits_, static_cast<size_t>(
329                             numbers_internal::FastIntToBuffer(x, digits_) -
330                             &digits_[0])) {}
AlphaNum(long long x)331   AlphaNum(long long x)  // NOLINT(*)
332       : piece_(digits_, static_cast<size_t>(
333                             numbers_internal::FastIntToBuffer(x, digits_) -
334                             &digits_[0])) {}
AlphaNum(unsigned long long x)335   AlphaNum(unsigned long long x)  // NOLINT(*)
336       : piece_(digits_, static_cast<size_t>(
337                             numbers_internal::FastIntToBuffer(x, digits_) -
338                             &digits_[0])) {}
339 
AlphaNum(float f)340   AlphaNum(float f)  // NOLINT(runtime/explicit)
341       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
AlphaNum(double f)342   AlphaNum(double f)  // NOLINT(runtime/explicit)
343       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
344 
345   template <size_t size>
AlphaNum(const strings_internal::AlphaNumBuffer<size> & buf ABSL_ATTRIBUTE_LIFETIME_BOUND)346   AlphaNum(  // NOLINT(runtime/explicit)
347       const strings_internal::AlphaNumBuffer<size>& buf
348           ABSL_ATTRIBUTE_LIFETIME_BOUND)
349       : piece_(&buf.data[0], buf.size) {}
350 
AlphaNum(absl::Nullable<const char * > c_str ABSL_ATTRIBUTE_LIFETIME_BOUND)351   AlphaNum(absl::Nullable<const char*> c_str  // NOLINT(runtime/explicit)
352                ABSL_ATTRIBUTE_LIFETIME_BOUND)
353       : piece_(NullSafeStringView(c_str)) {}
AlphaNum(absl::string_view pc ABSL_ATTRIBUTE_LIFETIME_BOUND)354   AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
355                ABSL_ATTRIBUTE_LIFETIME_BOUND)
356       : piece_(pc) {}
357 
358   template <typename T, typename = typename std::enable_if<
359                             HasAbslStringify<T>::value>::type>
360   AlphaNum(  // NOLINT(runtime/explicit)
361       const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
362       strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
piece_(strings_internal::ExtractStringification (sink,v))363       : piece_(strings_internal::ExtractStringification(sink, v)) {}
364 
365   template <typename Allocator>
AlphaNum(const std::basic_string<char,std::char_traits<char>,Allocator> & str ABSL_ATTRIBUTE_LIFETIME_BOUND)366   AlphaNum(  // NOLINT(runtime/explicit)
367       const std::basic_string<char, std::char_traits<char>, Allocator>& str
368           ABSL_ATTRIBUTE_LIFETIME_BOUND)
369       : piece_(str) {}
370 
371   // Use string literals ":" instead of character literals ':'.
372   AlphaNum(char c) = delete;  // NOLINT(runtime/explicit)
373 
374   AlphaNum(const AlphaNum&) = delete;
375   AlphaNum& operator=(const AlphaNum&) = delete;
376 
size()377   absl::string_view::size_type size() const { return piece_.size(); }
data()378   absl::Nullable<const char*> data() const { return piece_.data(); }
Piece()379   absl::string_view Piece() const { return piece_; }
380 
381   // Match unscoped enums.  Use integral promotion so that a `char`-backed
382   // enum becomes a wider integral type AlphaNum will accept.
383   template <typename T,
384             typename = typename std::enable_if<
385                 std::is_enum<T>{} && std::is_convertible<T, int>{} &&
386                 !HasAbslStringify<T>::value>::type>
387   AlphaNum(T e)  // NOLINT(runtime/explicit)
388       : AlphaNum(+e) {}
389 
390   // This overload matches scoped enums.  We must explicitly cast to the
391   // underlying type, but use integral promotion for the same reason as above.
392   template <typename T,
393             typename std::enable_if<std::is_enum<T>{} &&
394                                         !std::is_convertible<T, int>{} &&
395                                         !HasAbslStringify<T>::value,
396                                     char*>::type = nullptr>
397   AlphaNum(T e)  // NOLINT(runtime/explicit)
398       : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
399 
400   // vector<bool>::reference and const_reference require special help to
401   // convert to `AlphaNum` because it requires two user defined conversions.
402   template <
403       typename T,
404       typename std::enable_if<
405           std::is_class<T>::value &&
406           (std::is_same<T, std::vector<bool>::reference>::value ||
407            std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
408           nullptr>
AlphaNum(T e)409   AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
410 
411  private:
412   absl::string_view piece_;
413   char digits_[numbers_internal::kFastToBufferSize];
414 };
415 
416 // -----------------------------------------------------------------------------
417 // StrCat()
418 // -----------------------------------------------------------------------------
419 //
420 // Merges given strings or numbers, using no delimiter(s), returning the merged
421 // result as a string.
422 //
423 // `StrCat()` is designed to be the fastest possible way to construct a string
424 // out of a mix of raw C strings, string_views, strings, bool values,
425 // and numeric values.
426 //
427 // Don't use `StrCat()` for user-visible strings. The localization process
428 // works poorly on strings built up out of fragments.
429 //
430 // For clarity and performance, don't use `StrCat()` when appending to a
431 // string. Use `StrAppend()` instead. In particular, avoid using any of these
432 // (anti-)patterns:
433 //
434 //   str.append(StrCat(...))
435 //   str += StrCat(...)
436 //   str = StrCat(str, ...)
437 //
438 // The last case is the worst, with a potential to change a loop
439 // from a linear time operation with O(1) dynamic allocations into a
440 // quadratic time operation with O(n) dynamic allocations.
441 //
442 // See `StrAppend()` below for more information.
443 
444 namespace strings_internal {
445 
446 // Do not call directly - this is not part of the public API.
447 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
448 void AppendPieces(absl::Nonnull<std::string*> dest,
449                   std::initializer_list<absl::string_view> pieces);
450 
451 void STLStringAppendUninitializedAmortized(std::string* dest, size_t to_append);
452 
453 // `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
454 // (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
455 // and int64_t, then at least one of the three (`int` / `long` / `long long`)
456 // would have been ambiguous when passed to `SingleArgStrCat`.
457 std::string SingleArgStrCat(int x);
458 std::string SingleArgStrCat(unsigned int x);
459 std::string SingleArgStrCat(long x);                // NOLINT
460 std::string SingleArgStrCat(unsigned long x);       // NOLINT
461 std::string SingleArgStrCat(long long x);           // NOLINT
462 std::string SingleArgStrCat(unsigned long long x);  // NOLINT
463 std::string SingleArgStrCat(float x);
464 std::string SingleArgStrCat(double x);
465 
466 // `SingleArgStrAppend` overloads are defined here for the same reasons as with
467 // `SingleArgStrCat` above.
468 void SingleArgStrAppend(std::string& str, int x);
469 void SingleArgStrAppend(std::string& str, unsigned int x);
470 void SingleArgStrAppend(std::string& str, long x);                // NOLINT
471 void SingleArgStrAppend(std::string& str, unsigned long x);       // NOLINT
472 void SingleArgStrAppend(std::string& str, long long x);           // NOLINT
473 void SingleArgStrAppend(std::string& str, unsigned long long x);  // NOLINT
474 
475 template <typename T,
476           typename = std::enable_if_t<std::is_arithmetic<T>::value &&
477                                       !std::is_same<T, char>::value &&
478                                       !std::is_same<T, bool>::value>>
479 using EnableIfFastCase = T;
480 
481 }  // namespace strings_internal
482 
StrCat()483 ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
484 
485 template <typename T>
StrCat(strings_internal::EnableIfFastCase<T> a)486 ABSL_MUST_USE_RESULT inline std::string StrCat(
487     strings_internal::EnableIfFastCase<T> a) {
488   return strings_internal::SingleArgStrCat(a);
489 }
StrCat(const AlphaNum & a)490 ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
491   return std::string(a.data(), a.size());
492 }
493 
494 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
495 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
496                                         const AlphaNum& c);
497 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
498                                         const AlphaNum& c, const AlphaNum& d);
499 
500 // Support 5 or more arguments
501 template <typename... AV>
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)502 ABSL_MUST_USE_RESULT inline std::string StrCat(
503     const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
504     const AlphaNum& e, const AV&... args) {
505   return strings_internal::CatPieces(
506       {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
507        static_cast<const AlphaNum&>(args).Piece()...});
508 }
509 
510 // -----------------------------------------------------------------------------
511 // StrAppend()
512 // -----------------------------------------------------------------------------
513 //
514 // Appends a string or set of strings to an existing string, in a similar
515 // fashion to `StrCat()`.
516 //
517 // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
518 // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
519 // not try to check each of its input arguments to be sure that they are not
520 // a subset of the string being appended to. That is, while this will work:
521 //
522 //   std::string s = "foo";
523 //   s += s;
524 //
525 // This output is undefined:
526 //
527 //   std::string s = "foo";
528 //   StrAppend(&s, s);
529 //
530 // This output is undefined as well, since `absl::string_view` does not own its
531 // data:
532 //
533 //   std::string s = "foobar";
534 //   absl::string_view p = s;
535 //   StrAppend(&s, p);
536 
StrAppend(absl::Nonnull<std::string * >)537 inline void StrAppend(absl::Nonnull<std::string*>) {}
538 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a);
539 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
540                const AlphaNum& b);
541 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
542                const AlphaNum& b, const AlphaNum& c);
543 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
544                const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
545 
546 // Support 5 or more arguments
547 template <typename... AV>
StrAppend(absl::Nonnull<std::string * > dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)548 inline void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
549                       const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
550                       const AlphaNum& e, const AV&... args) {
551   strings_internal::AppendPieces(
552       dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
553              static_cast<const AlphaNum&>(args).Piece()...});
554 }
555 
556 template <class String, class T>
557 std::enable_if_t<
558     std::is_integral<absl::strings_internal::EnableIfFastCase<T>>::value, void>
StrAppend(absl::Nonnull<String * > result,T i)559 StrAppend(absl::Nonnull<String*> result, T i) {
560   return absl::strings_internal::SingleArgStrAppend(*result, i);
561 }
562 
563 // This overload is only selected if all the parameters are numbers that can be
564 // handled quickly.
565 // Later we can look into how we can extend this to more general argument
566 // mixtures without bloating codegen too much, or copying unnecessarily.
567 template <typename String, typename... T>
568 std::enable_if_t<
569     (sizeof...(T) > 1),
570     std::common_type_t<std::conditional_t<
571         true, void, absl::strings_internal::EnableIfFastCase<T>>...>>
StrAppend(absl::Nonnull<String * > str,T...args)572 StrAppend(absl::Nonnull<String*> str, T... args) {
573   // Do not add unnecessary variables, logic, or even "free" lambdas here.
574   // They can add overhead for the compiler and/or at run time.
575   // Furthermore, assume this function will be inlined.
576   // This function is carefully tailored to be able to be largely optimized away
577   // so that it becomes near-equivalent to the caller handling each argument
578   // individually while minimizing register pressure, so that the compiler
579   // can inline it with minimal overhead.
580 
581   // First, calculate the total length, so we can perform just a single resize.
582   // Save all the lengths for later.
583   size_t total_length = 0;
584   const ptrdiff_t lengths[] = {
585       absl::numbers_internal::GetNumDigitsOrNegativeIfNegative(args)...};
586   for (const ptrdiff_t possibly_negative_length : lengths) {
587     // Lengths are negative for negative numbers. Keep them for later use, but
588     // take their absolute values for calculating total lengths;
589     total_length += possibly_negative_length < 0
590                         ? static_cast<size_t>(-possibly_negative_length)
591                         : static_cast<size_t>(possibly_negative_length);
592   }
593 
594   // Now reserve space for all the arguments.
595   const size_t old_size = str->size();
596   absl::strings_internal::STLStringAppendUninitializedAmortized(str,
597                                                                 total_length);
598 
599   // Finally, output each argument one-by-one, from left to right.
600   size_t i = 0;  // The current argument we're processing
601   ptrdiff_t n;   // The length of the current argument
602   typename String::pointer pos = &(*str)[old_size];
603   using SomeTrivialEmptyType = std::false_type;
604   // Ugly code due to the lack of C++14 fold expression makes us.
605   const SomeTrivialEmptyType dummy1;
606   for (const SomeTrivialEmptyType& dummy2 :
607        {(/* Comma expressions are poor man's C++17 fold expression for C++14 */
608          (void)(n = lengths[i]),
609          (void)(n < 0 ? (void)(*pos++ = '-'), (n = ~n) : 0),
610          (void)absl::numbers_internal::FastIntToBufferBackward(
611              absl::numbers_internal::UnsignedAbsoluteValue(std::move(args)),
612              pos += n, static_cast<uint32_t>(n)),
613          (void)++i, dummy1)...}) {
614     (void)dummy2;  // Remove & migrate to fold expressions in C++17
615   }
616 }
617 
618 // Helper function for the future StrCat default floating-point format, %.6g
619 // This is fast.
620 inline strings_internal::AlphaNumBuffer<
621     numbers_internal::kSixDigitsToBufferSize>
SixDigits(double d)622 SixDigits(double d) {
623   strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
624       result;
625   result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
626   return result;
627 }
628 
629 ABSL_NAMESPACE_END
630 }  // namespace absl
631 
632 #endif  // ABSL_STRINGS_STR_CAT_H_
633