xref: /aosp_15_r20/external/cronet/third_party/abseil-cpp/absl/strings/str_cat.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 <initializer_list>
97 #include <limits>
98 #include <string>
99 #include <type_traits>
100 #include <utility>
101 #include <vector>
102 
103 #include "absl/base/attributes.h"
104 #include "absl/base/nullability.h"
105 #include "absl/base/port.h"
106 #include "absl/meta/type_traits.h"
107 #include "absl/strings/has_absl_stringify.h"
108 #include "absl/strings/internal/resize_uninitialized.h"
109 #include "absl/strings/internal/stringify_sink.h"
110 #include "absl/strings/numbers.h"
111 #include "absl/strings/string_view.h"
112 
113 namespace absl {
114 ABSL_NAMESPACE_BEGIN
115 
116 namespace strings_internal {
117 // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
118 // memory allocation.  It is simply a pair of a fixed-size character array, and
119 // a size.  Please don't use outside of absl, yet.
120 template <size_t max_size>
121 struct AlphaNumBuffer {
122   std::array<char, max_size> data;
123   size_t size;
124 };
125 
126 }  // namespace strings_internal
127 
128 // Enum that specifies the number of significant digits to return in a `Hex` or
129 // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
130 // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
131 // would produce hexadecimal strings such as "    a","    f".
132 enum PadSpec : uint8_t {
133   kNoPad = 1,
134   kZeroPad2,
135   kZeroPad3,
136   kZeroPad4,
137   kZeroPad5,
138   kZeroPad6,
139   kZeroPad7,
140   kZeroPad8,
141   kZeroPad9,
142   kZeroPad10,
143   kZeroPad11,
144   kZeroPad12,
145   kZeroPad13,
146   kZeroPad14,
147   kZeroPad15,
148   kZeroPad16,
149   kZeroPad17,
150   kZeroPad18,
151   kZeroPad19,
152   kZeroPad20,
153 
154   kSpacePad2 = kZeroPad2 + 64,
155   kSpacePad3,
156   kSpacePad4,
157   kSpacePad5,
158   kSpacePad6,
159   kSpacePad7,
160   kSpacePad8,
161   kSpacePad9,
162   kSpacePad10,
163   kSpacePad11,
164   kSpacePad12,
165   kSpacePad13,
166   kSpacePad14,
167   kSpacePad15,
168   kSpacePad16,
169   kSpacePad17,
170   kSpacePad18,
171   kSpacePad19,
172   kSpacePad20,
173 };
174 
175 // -----------------------------------------------------------------------------
176 // Hex
177 // -----------------------------------------------------------------------------
178 //
179 // `Hex` stores a set of hexadecimal string conversion parameters for use
180 // within `AlphaNum` string conversions.
181 struct Hex {
182   uint64_t value;
183   uint8_t width;
184   char fill;
185 
186   template <typename Int>
187   explicit Hex(
188       Int v, PadSpec spec = absl::kNoPad,
189       typename std::enable_if<sizeof(Int) == 1 &&
190                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex191       : Hex(spec, static_cast<uint8_t>(v)) {}
192   template <typename Int>
193   explicit Hex(
194       Int v, PadSpec spec = absl::kNoPad,
195       typename std::enable_if<sizeof(Int) == 2 &&
196                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex197       : Hex(spec, static_cast<uint16_t>(v)) {}
198   template <typename Int>
199   explicit Hex(
200       Int v, PadSpec spec = absl::kNoPad,
201       typename std::enable_if<sizeof(Int) == 4 &&
202                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex203       : Hex(spec, static_cast<uint32_t>(v)) {}
204   template <typename Int>
205   explicit Hex(
206       Int v, PadSpec spec = absl::kNoPad,
207       typename std::enable_if<sizeof(Int) == 8 &&
208                               !std::is_pointer<Int>::value>::type* = nullptr)
HexHex209       : Hex(spec, static_cast<uint64_t>(v)) {}
210   template <typename Pointee>
211   explicit Hex(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad)
HexHex212       : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
213 
214   template <typename S>
AbslStringifyHex215   friend void AbslStringify(S& sink, Hex hex) {
216     static_assert(
217         numbers_internal::kFastToBufferSize >= 32,
218         "This function only works when output buffer >= 32 bytes long");
219     char buffer[numbers_internal::kFastToBufferSize];
220     char* const end = &buffer[numbers_internal::kFastToBufferSize];
221     auto real_width =
222         absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
223     if (real_width >= hex.width) {
224       sink.Append(absl::string_view(end - real_width, real_width));
225     } else {
226       // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
227       // max pad width can be up to 20.
228       std::memset(end - 32, hex.fill, 16);
229       // Patch up everything else up to the real_width.
230       std::memset(end - real_width - 16, hex.fill, 16);
231       sink.Append(absl::string_view(end - hex.width, hex.width));
232     }
233   }
234 
235  private:
HexHex236   Hex(PadSpec spec, uint64_t v)
237       : value(v),
238         width(spec == absl::kNoPad
239                   ? 1
240                   : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
241                                              : spec - absl::kZeroPad2 + 2),
242         fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
243 };
244 
245 // -----------------------------------------------------------------------------
246 // Dec
247 // -----------------------------------------------------------------------------
248 //
249 // `Dec` stores a set of decimal string conversion parameters for use
250 // within `AlphaNum` string conversions.  Dec is slower than the default
251 // integer conversion, so use it only if you need padding.
252 struct Dec {
253   uint64_t value;
254   uint8_t width;
255   char fill;
256   bool neg;
257 
258   template <typename Int>
259   explicit Dec(Int v, PadSpec spec = absl::kNoPad,
260                typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
261       : value(v >= 0 ? static_cast<uint64_t>(v)
262                      : uint64_t{0} - static_cast<uint64_t>(v)),
263         width(spec == absl::kNoPad       ? 1
264               : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
265                                          : spec - absl::kZeroPad2 + 2),
266         fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
267         neg(v < 0) {}
268 
269   template <typename S>
AbslStringifyDec270   friend void AbslStringify(S& sink, Dec dec) {
271     assert(dec.width <= numbers_internal::kFastToBufferSize);
272     char buffer[numbers_internal::kFastToBufferSize];
273     char* const end = &buffer[numbers_internal::kFastToBufferSize];
274     char* const minfill = end - dec.width;
275     char* writer = end;
276     uint64_t val = dec.value;
277     while (val > 9) {
278       *--writer = '0' + (val % 10);
279       val /= 10;
280     }
281     *--writer = '0' + static_cast<char>(val);
282     if (dec.neg) *--writer = '-';
283 
284     ptrdiff_t fillers = writer - minfill;
285     if (fillers > 0) {
286       // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
287       // But...: if the fill character is '0', then it's <+/-><fill><digits>
288       bool add_sign_again = false;
289       if (dec.neg && dec.fill == '0') {  // If filling with '0',
290         ++writer;                    // ignore the sign we just added
291         add_sign_again = true;       // and re-add the sign later.
292       }
293       writer -= fillers;
294       std::fill_n(writer, fillers, dec.fill);
295       if (add_sign_again) *--writer = '-';
296     }
297 
298     sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
299   }
300 };
301 
302 // -----------------------------------------------------------------------------
303 // AlphaNum
304 // -----------------------------------------------------------------------------
305 //
306 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
307 // `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
308 // and hexadecimal values (through the `Dec` and `Hex` types) into strings.
309 // `AlphaNum` should only be used as a function parameter. Do not instantiate
310 //  `AlphaNum` directly as a stack variable.
311 
312 class AlphaNum {
313  public:
314   // No bool ctor -- bools convert to an integral type.
315   // A bool ctor would also convert incoming pointers (bletch).
316 
317   // Prevent brace initialization
318   template <typename T>
319   AlphaNum(std::initializer_list<T>) = delete;  // NOLINT(runtime/explicit)
320 
AlphaNum(int x)321   AlphaNum(int x)  // NOLINT(runtime/explicit)
322       : piece_(digits_, static_cast<size_t>(
323                             numbers_internal::FastIntToBuffer(x, digits_) -
324                             &digits_[0])) {}
AlphaNum(unsigned int x)325   AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
326       : piece_(digits_, static_cast<size_t>(
327                             numbers_internal::FastIntToBuffer(x, digits_) -
328                             &digits_[0])) {}
AlphaNum(long x)329   AlphaNum(long x)  // NOLINT(*)
330       : piece_(digits_, static_cast<size_t>(
331                             numbers_internal::FastIntToBuffer(x, digits_) -
332                             &digits_[0])) {}
AlphaNum(unsigned long x)333   AlphaNum(unsigned long x)  // NOLINT(*)
334       : piece_(digits_, static_cast<size_t>(
335                             numbers_internal::FastIntToBuffer(x, digits_) -
336                             &digits_[0])) {}
AlphaNum(long long x)337   AlphaNum(long long x)  // NOLINT(*)
338       : piece_(digits_, static_cast<size_t>(
339                             numbers_internal::FastIntToBuffer(x, digits_) -
340                             &digits_[0])) {}
AlphaNum(unsigned long long x)341   AlphaNum(unsigned long long x)  // NOLINT(*)
342       : piece_(digits_, static_cast<size_t>(
343                             numbers_internal::FastIntToBuffer(x, digits_) -
344                             &digits_[0])) {}
345 
AlphaNum(float f)346   AlphaNum(float f)  // NOLINT(runtime/explicit)
347       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
AlphaNum(double f)348   AlphaNum(double f)  // NOLINT(runtime/explicit)
349       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
350 
351   template <size_t size>
AlphaNum(const strings_internal::AlphaNumBuffer<size> & buf ABSL_ATTRIBUTE_LIFETIME_BOUND)352   AlphaNum(  // NOLINT(runtime/explicit)
353       const strings_internal::AlphaNumBuffer<size>& buf
354           ABSL_ATTRIBUTE_LIFETIME_BOUND)
355       : piece_(&buf.data[0], buf.size) {}
356 
AlphaNum(absl::Nullable<const char * > c_str ABSL_ATTRIBUTE_LIFETIME_BOUND)357   AlphaNum(absl::Nullable<const char*> c_str  // NOLINT(runtime/explicit)
358                ABSL_ATTRIBUTE_LIFETIME_BOUND)
359       : piece_(NullSafeStringView(c_str)) {}
AlphaNum(absl::string_view pc ABSL_ATTRIBUTE_LIFETIME_BOUND)360   AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
361                ABSL_ATTRIBUTE_LIFETIME_BOUND)
362       : piece_(pc) {}
363 
364   template <typename T, typename = typename std::enable_if<
365                             HasAbslStringify<T>::value>::type>
366   AlphaNum(  // NOLINT(runtime/explicit)
367       const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
368       strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
piece_(strings_internal::ExtractStringification (sink,v))369       : piece_(strings_internal::ExtractStringification(sink, v)) {}
370 
371   template <typename Allocator>
AlphaNum(const std::basic_string<char,std::char_traits<char>,Allocator> & str ABSL_ATTRIBUTE_LIFETIME_BOUND)372   AlphaNum(  // NOLINT(runtime/explicit)
373       const std::basic_string<char, std::char_traits<char>, Allocator>& str
374           ABSL_ATTRIBUTE_LIFETIME_BOUND)
375       : piece_(str) {}
376 
377   // Use string literals ":" instead of character literals ':'.
378   AlphaNum(char c) = delete;  // NOLINT(runtime/explicit)
379 
380   AlphaNum(const AlphaNum&) = delete;
381   AlphaNum& operator=(const AlphaNum&) = delete;
382 
size()383   absl::string_view::size_type size() const { return piece_.size(); }
data()384   absl::Nullable<const char*> data() const { return piece_.data(); }
Piece()385   absl::string_view Piece() const { return piece_; }
386 
387   // Match unscoped enums.  Use integral promotion so that a `char`-backed
388   // enum becomes a wider integral type AlphaNum will accept.
389   template <typename T,
390             typename = typename std::enable_if<
391                 std::is_enum<T>{} && std::is_convertible<T, int>{} &&
392                 !HasAbslStringify<T>::value>::type>
393   AlphaNum(T e)  // NOLINT(runtime/explicit)
394       : AlphaNum(+e) {}
395 
396   // This overload matches scoped enums.  We must explicitly cast to the
397   // underlying type, but use integral promotion for the same reason as above.
398   template <typename T,
399             typename std::enable_if<std::is_enum<T>{} &&
400                                         !std::is_convertible<T, int>{} &&
401                                         !HasAbslStringify<T>::value,
402                                     char*>::type = nullptr>
403   AlphaNum(T e)  // NOLINT(runtime/explicit)
404       : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
405 
406   // vector<bool>::reference and const_reference require special help to
407   // convert to `AlphaNum` because it requires two user defined conversions.
408   template <
409       typename T,
410       typename std::enable_if<
411           std::is_class<T>::value &&
412           (std::is_same<T, std::vector<bool>::reference>::value ||
413            std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
414           nullptr>
AlphaNum(T e)415   AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
416 
417  private:
418   absl::string_view piece_;
419   char digits_[numbers_internal::kFastToBufferSize];
420 };
421 
422 // -----------------------------------------------------------------------------
423 // StrCat()
424 // -----------------------------------------------------------------------------
425 //
426 // Merges given strings or numbers, using no delimiter(s), returning the merged
427 // result as a string.
428 //
429 // `StrCat()` is designed to be the fastest possible way to construct a string
430 // out of a mix of raw C strings, string_views, strings, bool values,
431 // and numeric values.
432 //
433 // Don't use `StrCat()` for user-visible strings. The localization process
434 // works poorly on strings built up out of fragments.
435 //
436 // For clarity and performance, don't use `StrCat()` when appending to a
437 // string. Use `StrAppend()` instead. In particular, avoid using any of these
438 // (anti-)patterns:
439 //
440 //   str.append(StrCat(...))
441 //   str += StrCat(...)
442 //   str = StrCat(str, ...)
443 //
444 // The last case is the worst, with a potential to change a loop
445 // from a linear time operation with O(1) dynamic allocations into a
446 // quadratic time operation with O(n) dynamic allocations.
447 //
448 // See `StrAppend()` below for more information.
449 
450 namespace strings_internal {
451 
452 // Do not call directly - this is not part of the public API.
453 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
454 void AppendPieces(absl::Nonnull<std::string*> dest,
455                   std::initializer_list<absl::string_view> pieces);
456 
457 template <typename Integer>
IntegerToString(Integer i)458 std::string IntegerToString(Integer i) {
459   // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
460   // with 22 bytes (including NULL at the end).
461   constexpr size_t kMaxDigits10 = 22;
462   std::string result;
463   strings_internal::STLStringResizeUninitialized(&result, kMaxDigits10);
464   char* start = &result[0];
465   // note: this can be optimized to not write last zero.
466   char* end = numbers_internal::FastIntToBuffer(i, start);
467   auto size = static_cast<size_t>(end - start);
468   assert((size < result.size()) &&
469          "StrCat(Integer) does not fit into kMaxDigits10");
470   result.erase(size);
471   return result;
472 }
473 template <typename Float>
FloatToString(Float f)474 std::string FloatToString(Float f) {
475   std::string result;
476   strings_internal::STLStringResizeUninitialized(
477       &result, numbers_internal::kSixDigitsToBufferSize);
478   char* start = &result[0];
479   result.erase(numbers_internal::SixDigitsToBuffer(f, start));
480   return result;
481 }
482 
483 // `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
484 // (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
485 // and int64_t, then at least one of the three (`int` / `long` / `long long`)
486 // would have been ambiguous when passed to `SingleArgStrCat`.
SingleArgStrCat(int x)487 inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
SingleArgStrCat(unsigned int x)488 inline std::string SingleArgStrCat(unsigned int x) {
489   return IntegerToString(x);
490 }
491 // NOLINTNEXTLINE
SingleArgStrCat(long x)492 inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
493 // NOLINTNEXTLINE
SingleArgStrCat(unsigned long x)494 inline std::string SingleArgStrCat(unsigned long x) {
495   return IntegerToString(x);
496 }
497 // NOLINTNEXTLINE
SingleArgStrCat(long long x)498 inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
499 // NOLINTNEXTLINE
SingleArgStrCat(unsigned long long x)500 inline std::string SingleArgStrCat(unsigned long long x) {
501   return IntegerToString(x);
502 }
SingleArgStrCat(float x)503 inline std::string SingleArgStrCat(float x) { return FloatToString(x); }
SingleArgStrCat(double x)504 inline std::string SingleArgStrCat(double x) { return FloatToString(x); }
505 
506 // As of September 2023, the SingleArgStrCat() optimization is only enabled for
507 // libc++. The reasons for this are:
508 // 1) The SSO size for libc++ is 23, while libstdc++ and MSSTL have an SSO size
509 // of 15. Since IntegerToString unconditionally resizes the string to 22 bytes,
510 // this causes both libstdc++ and MSSTL to allocate.
511 // 2) strings_internal::STLStringResizeUninitialized() only has an
512 // implementation that avoids initialization when using libc++. This isn't as
513 // relevant as (1), and the cost should be benchmarked if (1) ever changes on
514 // libstc++ or MSSTL.
515 #ifdef _LIBCPP_VERSION
516 #define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE true
517 #else
518 #define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE false
519 #endif
520 
521 template <typename T, typename = std::enable_if_t<
522                           ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE &&
523                           std::is_arithmetic<T>{} && !std::is_same<T, char>{}>>
524 using EnableIfFastCase = T;
525 
526 #undef ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE
527 
528 }  // namespace strings_internal
529 
StrCat()530 ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
531 
532 template <typename T>
StrCat(strings_internal::EnableIfFastCase<T> a)533 ABSL_MUST_USE_RESULT inline std::string StrCat(
534     strings_internal::EnableIfFastCase<T> a) {
535   return strings_internal::SingleArgStrCat(a);
536 }
StrCat(const AlphaNum & a)537 ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
538   return std::string(a.data(), a.size());
539 }
540 
541 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
542 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
543                                         const AlphaNum& c);
544 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
545                                         const AlphaNum& c, const AlphaNum& d);
546 
547 // Support 5 or more arguments
548 template <typename... AV>
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)549 ABSL_MUST_USE_RESULT inline std::string StrCat(
550     const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
551     const AlphaNum& e, const AV&... args) {
552   return strings_internal::CatPieces(
553       {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
554        static_cast<const AlphaNum&>(args).Piece()...});
555 }
556 
557 // -----------------------------------------------------------------------------
558 // StrAppend()
559 // -----------------------------------------------------------------------------
560 //
561 // Appends a string or set of strings to an existing string, in a similar
562 // fashion to `StrCat()`.
563 //
564 // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
565 // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
566 // not try to check each of its input arguments to be sure that they are not
567 // a subset of the string being appended to. That is, while this will work:
568 //
569 //   std::string s = "foo";
570 //   s += s;
571 //
572 // This output is undefined:
573 //
574 //   std::string s = "foo";
575 //   StrAppend(&s, s);
576 //
577 // This output is undefined as well, since `absl::string_view` does not own its
578 // data:
579 //
580 //   std::string s = "foobar";
581 //   absl::string_view p = s;
582 //   StrAppend(&s, p);
583 
StrAppend(absl::Nonnull<std::string * >)584 inline void StrAppend(absl::Nonnull<std::string*>) {}
585 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a);
586 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
587                const AlphaNum& b);
588 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
589                const AlphaNum& b, const AlphaNum& c);
590 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
591                const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
592 
593 // Support 5 or more arguments
594 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)595 inline void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
596                       const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
597                       const AlphaNum& e, const AV&... args) {
598   strings_internal::AppendPieces(
599       dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
600              static_cast<const AlphaNum&>(args).Piece()...});
601 }
602 
603 // Helper function for the future StrCat default floating-point format, %.6g
604 // This is fast.
605 inline strings_internal::AlphaNumBuffer<
606     numbers_internal::kSixDigitsToBufferSize>
SixDigits(double d)607 SixDigits(double d) {
608   strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
609       result;
610   result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
611   return result;
612 }
613 
614 ABSL_NAMESPACE_END
615 }  // namespace absl
616 
617 #endif  // ABSL_STRINGS_STR_CAT_H_
618