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