• 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 <limits>
97 #include <string>
98 #include <type_traits>
99 #include <utility>
100 #include <vector>
101 
102 #include "absl/base/attributes.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(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
262                   ? 1
263                   : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
264                                              : spec - absl::kZeroPad2 + 2),
265         fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
266         neg(v < 0) {}
267 
268   template <typename S>
AbslStringifyDec269   friend void AbslStringify(S& sink, Dec dec) {
270     assert(dec.width <= numbers_internal::kFastToBufferSize);
271     char buffer[numbers_internal::kFastToBufferSize];
272     char* const end = &buffer[numbers_internal::kFastToBufferSize];
273     char* const minfill = end - dec.width;
274     char* writer = end;
275     uint64_t val = dec.value;
276     while (val > 9) {
277       *--writer = '0' + (val % 10);
278       val /= 10;
279     }
280     *--writer = '0' + static_cast<char>(val);
281     if (dec.neg) *--writer = '-';
282 
283     ptrdiff_t fillers = writer - minfill;
284     if (fillers > 0) {
285       // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
286       // But...: if the fill character is '0', then it's <+/-><fill><digits>
287       bool add_sign_again = false;
288       if (dec.neg && dec.fill == '0') {  // If filling with '0',
289         ++writer;                    // ignore the sign we just added
290         add_sign_again = true;       // and re-add the sign later.
291       }
292       writer -= fillers;
293       std::fill_n(writer, fillers, dec.fill);
294       if (add_sign_again) *--writer = '-';
295     }
296 
297     sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
298   }
299 };
300 
301 // -----------------------------------------------------------------------------
302 // AlphaNum
303 // -----------------------------------------------------------------------------
304 //
305 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
306 // `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
307 // and hexadecimal values (through the `Dec` and `Hex` types) into strings.
308 // `AlphaNum` should only be used as a function parameter. Do not instantiate
309 //  `AlphaNum` directly as a stack variable.
310 
311 class AlphaNum {
312  public:
313   // No bool ctor -- bools convert to an integral type.
314   // A bool ctor would also convert incoming pointers (bletch).
315 
AlphaNum(int x)316   AlphaNum(int x)  // NOLINT(runtime/explicit)
317       : piece_(digits_, static_cast<size_t>(
318                             numbers_internal::FastIntToBuffer(x, digits_) -
319                             &digits_[0])) {}
AlphaNum(unsigned int x)320   AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
321       : piece_(digits_, static_cast<size_t>(
322                             numbers_internal::FastIntToBuffer(x, digits_) -
323                             &digits_[0])) {}
AlphaNum(long x)324   AlphaNum(long x)  // NOLINT(*)
325       : piece_(digits_, static_cast<size_t>(
326                             numbers_internal::FastIntToBuffer(x, digits_) -
327                             &digits_[0])) {}
AlphaNum(unsigned long x)328   AlphaNum(unsigned long x)  // NOLINT(*)
329       : piece_(digits_, static_cast<size_t>(
330                             numbers_internal::FastIntToBuffer(x, digits_) -
331                             &digits_[0])) {}
AlphaNum(long long x)332   AlphaNum(long long x)  // NOLINT(*)
333       : piece_(digits_, static_cast<size_t>(
334                             numbers_internal::FastIntToBuffer(x, digits_) -
335                             &digits_[0])) {}
AlphaNum(unsigned long long x)336   AlphaNum(unsigned long long x)  // NOLINT(*)
337       : piece_(digits_, static_cast<size_t>(
338                             numbers_internal::FastIntToBuffer(x, digits_) -
339                             &digits_[0])) {}
340 
AlphaNum(float f)341   AlphaNum(float f)  // NOLINT(runtime/explicit)
342       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
AlphaNum(double f)343   AlphaNum(double f)  // NOLINT(runtime/explicit)
344       : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
345 
346   template <size_t size>
AlphaNum(const strings_internal::AlphaNumBuffer<size> & buf ABSL_ATTRIBUTE_LIFETIME_BOUND)347   AlphaNum(  // NOLINT(runtime/explicit)
348       const strings_internal::AlphaNumBuffer<size>& buf
349           ABSL_ATTRIBUTE_LIFETIME_BOUND)
350       : piece_(&buf.data[0], buf.size) {}
351 
AlphaNum(const char * c_str ABSL_ATTRIBUTE_LIFETIME_BOUND)352   AlphaNum(const char* c_str  // NOLINT(runtime/explicit)
353                ABSL_ATTRIBUTE_LIFETIME_BOUND)
354       : piece_(NullSafeStringView(c_str)) {}
AlphaNum(absl::string_view pc ABSL_ATTRIBUTE_LIFETIME_BOUND)355   AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
356                ABSL_ATTRIBUTE_LIFETIME_BOUND)
357       : piece_(pc) {}
358 
359   template <typename T, typename = typename std::enable_if<
360                             HasAbslStringify<T>::value>::type>
361   AlphaNum(  // NOLINT(runtime/explicit)
362       const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
363       strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
piece_(strings_internal::ExtractStringification (sink,v))364       : piece_(strings_internal::ExtractStringification(sink, v)) {}
365 
366   template <typename Allocator>
AlphaNum(const std::basic_string<char,std::char_traits<char>,Allocator> & str ABSL_ATTRIBUTE_LIFETIME_BOUND)367   AlphaNum(  // NOLINT(runtime/explicit)
368       const std::basic_string<char, std::char_traits<char>, Allocator>& str
369           ABSL_ATTRIBUTE_LIFETIME_BOUND)
370       : piece_(str) {}
371 
372   // Use string literals ":" instead of character literals ':'.
373   AlphaNum(char c) = delete;  // NOLINT(runtime/explicit)
374 
375   AlphaNum(const AlphaNum&) = delete;
376   AlphaNum& operator=(const AlphaNum&) = delete;
377 
size()378   absl::string_view::size_type size() const { return piece_.size(); }
data()379   const char* data() const { return piece_.data(); }
Piece()380   absl::string_view Piece() const { return piece_; }
381 
382   // Match unscoped enums.  Use integral promotion so that a `char`-backed
383   // enum becomes a wider integral type AlphaNum will accept.
384   template <typename T,
385             typename = typename std::enable_if<
386                 std::is_enum<T>{} && std::is_convertible<T, int>{} &&
387                 !HasAbslStringify<T>::value>::type>
388   AlphaNum(T e)  // NOLINT(runtime/explicit)
389       : AlphaNum(+e) {}
390 
391   // This overload matches scoped enums.  We must explicitly cast to the
392   // underlying type, but use integral promotion for the same reason as above.
393   template <typename T,
394             typename std::enable_if<std::is_enum<T>{} &&
395                                         !std::is_convertible<T, int>{} &&
396                                         !HasAbslStringify<T>::value,
397                                     char*>::type = nullptr>
398   AlphaNum(T e)  // NOLINT(runtime/explicit)
399       : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
400 
401   // vector<bool>::reference and const_reference require special help to
402   // convert to `AlphaNum` because it requires two user defined conversions.
403   template <
404       typename T,
405       typename std::enable_if<
406           std::is_class<T>::value &&
407           (std::is_same<T, std::vector<bool>::reference>::value ||
408            std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
409           nullptr>
AlphaNum(T e)410   AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
411 
412  private:
413   absl::string_view piece_;
414   char digits_[numbers_internal::kFastToBufferSize];
415 };
416 
417 // -----------------------------------------------------------------------------
418 // StrCat()
419 // -----------------------------------------------------------------------------
420 //
421 // Merges given strings or numbers, using no delimiter(s), returning the merged
422 // result as a string.
423 //
424 // `StrCat()` is designed to be the fastest possible way to construct a string
425 // out of a mix of raw C strings, string_views, strings, bool values,
426 // and numeric values.
427 //
428 // Don't use `StrCat()` for user-visible strings. The localization process
429 // works poorly on strings built up out of fragments.
430 //
431 // For clarity and performance, don't use `StrCat()` when appending to a
432 // string. Use `StrAppend()` instead. In particular, avoid using any of these
433 // (anti-)patterns:
434 //
435 //   str.append(StrCat(...))
436 //   str += StrCat(...)
437 //   str = StrCat(str, ...)
438 //
439 // The last case is the worst, with a potential to change a loop
440 // from a linear time operation with O(1) dynamic allocations into a
441 // quadratic time operation with O(n) dynamic allocations.
442 //
443 // See `StrAppend()` below for more information.
444 
445 namespace strings_internal {
446 
447 // Do not call directly - this is not part of the public API.
448 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
449 void AppendPieces(std::string* dest,
450                   std::initializer_list<absl::string_view> pieces);
451 
452 template <typename Integer>
IntegerToString(Integer i)453 std::string IntegerToString(Integer i) {
454   // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
455   // with 22 bytes (including NULL at the end).
456   constexpr size_t kMaxDigits10 = 22;
457   std::string result;
458   strings_internal::STLStringResizeUninitialized(&result, kMaxDigits10);
459   char* start = &result[0];
460   // note: this can be optimized to not write last zero.
461   char* end = numbers_internal::FastIntToBuffer(i, start);
462   auto size = static_cast<size_t>(end - start);
463   assert((size < result.size()) &&
464          "StrCat(Integer) does not fit into kMaxDigits10");
465   result.erase(size);
466   return result;
467 }
468 template <typename Float>
FloatToString(Float f)469 std::string FloatToString(Float f) {
470   std::string result;
471   strings_internal::STLStringResizeUninitialized(
472       &result, numbers_internal::kSixDigitsToBufferSize);
473   char* start = &result[0];
474   result.erase(numbers_internal::SixDigitsToBuffer(f, start));
475   return result;
476 }
477 
478 // `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
479 // (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
480 // and int64_t, then at least one of the three (`int` / `long` / `long long`)
481 // would have been ambiguous when passed to `SingleArgStrCat`.
SingleArgStrCat(int x)482 inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
SingleArgStrCat(unsigned int x)483 inline std::string SingleArgStrCat(unsigned int x) {
484   return IntegerToString(x);
485 }
486 // NOLINTNEXTLINE
SingleArgStrCat(long x)487 inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
488 // NOLINTNEXTLINE
SingleArgStrCat(unsigned long x)489 inline std::string SingleArgStrCat(unsigned long x) {
490   return IntegerToString(x);
491 }
492 // NOLINTNEXTLINE
SingleArgStrCat(long long x)493 inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
494 // NOLINTNEXTLINE
SingleArgStrCat(unsigned long long x)495 inline std::string SingleArgStrCat(unsigned long long x) {
496   return IntegerToString(x);
497 }
SingleArgStrCat(float x)498 inline std::string SingleArgStrCat(float x) { return FloatToString(x); }
SingleArgStrCat(double x)499 inline std::string SingleArgStrCat(double x) { return FloatToString(x); }
500 
501 // As of September 2023, the SingleArgStrCat() optimization is only enabled for
502 // libc++. The reasons for this are:
503 // 1) The SSO size for libc++ is 23, while libstdc++ and MSSTL have an SSO size
504 // of 15. Since IntegerToString unconditionally resizes the string to 22 bytes,
505 // this causes both libstdc++ and MSSTL to allocate.
506 // 2) strings_internal::STLStringResizeUninitialized() only has an
507 // implementation that avoids initialization when using libc++. This isn't as
508 // relevant as (1), and the cost should be benchmarked if (1) ever changes on
509 // libstc++ or MSSTL.
510 #ifdef _LIBCPP_VERSION
511 #define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE true
512 #else
513 #define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE false
514 #endif
515 
516 template <typename T, typename = std::enable_if_t<
517                           ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE &&
518                           std::is_arithmetic<T>{} && !std::is_same<T, char>{}>>
519 using EnableIfFastCase = T;
520 
521 #undef ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE
522 
523 }  // namespace strings_internal
524 
StrCat()525 ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
526 
527 template <typename T>
StrCat(strings_internal::EnableIfFastCase<T> a)528 ABSL_MUST_USE_RESULT inline std::string StrCat(
529     strings_internal::EnableIfFastCase<T> a) {
530   return strings_internal::SingleArgStrCat(a);
531 }
StrCat(const AlphaNum & a)532 ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
533   return std::string(a.data(), a.size());
534 }
535 
536 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b);
537 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
538                                         const AlphaNum& c);
539 ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b,
540                                         const AlphaNum& c, const AlphaNum& d);
541 
542 // Support 5 or more arguments
543 template <typename... AV>
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)544 ABSL_MUST_USE_RESULT inline std::string StrCat(
545     const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
546     const AlphaNum& e, const AV&... args) {
547   return strings_internal::CatPieces(
548       {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
549        static_cast<const AlphaNum&>(args).Piece()...});
550 }
551 
552 // -----------------------------------------------------------------------------
553 // StrAppend()
554 // -----------------------------------------------------------------------------
555 //
556 // Appends a string or set of strings to an existing string, in a similar
557 // fashion to `StrCat()`.
558 //
559 // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
560 // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
561 // not try to check each of its input arguments to be sure that they are not
562 // a subset of the string being appended to. That is, while this will work:
563 //
564 //   std::string s = "foo";
565 //   s += s;
566 //
567 // This output is undefined:
568 //
569 //   std::string s = "foo";
570 //   StrAppend(&s, s);
571 //
572 // This output is undefined as well, since `absl::string_view` does not own its
573 // data:
574 //
575 //   std::string s = "foobar";
576 //   absl::string_view p = s;
577 //   StrAppend(&s, p);
578 
StrAppend(std::string *)579 inline void StrAppend(std::string*) {}
580 void StrAppend(std::string* dest, const AlphaNum& a);
581 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
582 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
583                const AlphaNum& c);
584 void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
585                const AlphaNum& c, const AlphaNum& d);
586 
587 // Support 5 or more arguments
588 template <typename... AV>
StrAppend(std::string * dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)589 inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
590                       const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
591                       const AV&... args) {
592   strings_internal::AppendPieces(
593       dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
594              static_cast<const AlphaNum&>(args).Piece()...});
595 }
596 
597 // Helper function for the future StrCat default floating-point format, %.6g
598 // This is fast.
599 inline strings_internal::AlphaNumBuffer<
600     numbers_internal::kSixDigitsToBufferSize>
SixDigits(double d)601 SixDigits(double d) {
602   strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
603       result;
604   result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
605   return result;
606 }
607 
608 ABSL_NAMESPACE_END
609 }  // namespace absl
610 
611 #endif  // ABSL_STRINGS_STR_CAT_H_
612