Coverage Report

Created: 2026-10-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/strings/str_cat.h
Line
Count
Source
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
// Floating point values can also be converted to a string which, if passed to
47
// `strtod()`, would produce the exact same original double (except in case of
48
// NaN; all NaNs are considered the same value) by passing the number to
49
// absl::HighPrecision. HighPrecision tries to keep the string short but
50
// it's not guaranteed to be as short as possible.
51
// See http://go/faster-double-strcat
52
//
53
// You can convert to hexadecimal output rather than decimal output using the
54
// `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to
55
// `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using
56
// a `PadSpec` enum.
57
//
58
// User-defined types can be formatted with the `AbslStringify()` customization
59
// point. The API relies on detecting an overload in the user-defined type's
60
// namespace of a free (non-member) `AbslStringify()` function as a definition
61
// (typically declared as a friend and implemented in-line.
62
// with the following signature:
63
//
64
// class MyClass { ... };
65
//
66
// template <typename Sink>
67
// void AbslStringify(Sink& sink, const MyClass& value);
68
//
69
// An `AbslStringify()` overload for a type should only be declared in the same
70
// file and namespace as said type.
71
//
72
// Note that `AbslStringify()` also supports use with `absl::StrFormat()` and
73
// `absl::Substitute()`.
74
//
75
// Example:
76
//
77
// struct Point {
78
//   // To add formatting support to `Point`, we simply need to add a free
79
//   // (non-member) function `AbslStringify()`. This method specifies how
80
//   // Point should be printed when absl::StrCat() is called on it. You can add
81
//   // such a free function using a friend declaration within the body of the
82
//   // class. The sink parameter is a templated type to avoid requiring
83
//   // dependencies.
84
//   template <typename Sink> friend void AbslStringify(Sink&
85
//   sink, const Point& p) {
86
//     absl::Format(&sink, "(%v, %v)", p.x, p.y);
87
//   }
88
//
89
//   int x;
90
//   int y;
91
// };
92
// -----------------------------------------------------------------------------
93
94
#ifndef ABSL_STRINGS_STR_CAT_H_
95
#define ABSL_STRINGS_STR_CAT_H_
96
97
#include <algorithm>
98
#include <array>
99
#include <cassert>
100
#include <cstddef>
101
#include <cstdint>
102
#include <cstring>
103
#include <initializer_list>
104
#include <limits>
105
#include <string>
106
#include <type_traits>
107
#include <utility>
108
#include <vector>
109
110
#include "absl/base/attributes.h"
111
#include "absl/base/config.h"
112
#include "absl/base/macros.h"
113
#include "absl/base/nullability.h"
114
#include "absl/base/port.h"
115
#include "absl/meta/type_traits.h"
116
#include "absl/strings/has_absl_stringify.h"
117
#include "absl/strings/internal/stringify_sink.h"
118
#include "absl/strings/numbers.h"
119
#include "absl/strings/resize_and_overwrite.h"
120
#include "absl/strings/string_view.h"
121
122
#if !defined(ABSL_USES_STD_STRING_VIEW)
123
#include <string_view>
124
#endif
125
126
namespace absl {
127
ABSL_NAMESPACE_BEGIN
128
129
namespace strings_internal {
130
// AlphaNumBuffer allows a way to pass a string to StrCat without having to do
131
// memory allocation.  It is simply a pair of a fixed-size character array, and
132
// a size.  Please don't use outside of absl, yet.
133
template <size_t max_size>
134
struct AlphaNumBuffer {
135
  std::array<char, max_size> data;
136
  size_t size;
137
};
138
139
}  // namespace strings_internal
140
141
// Enum that specifies the number of significant digits to return in a `Hex` or
142
// `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
143
// would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
144
// would produce hexadecimal strings such as "    a","    f".
145
enum PadSpec : uint8_t {
146
  kNoPad = 1,
147
  kZeroPad2,
148
  kZeroPad3,
149
  kZeroPad4,
150
  kZeroPad5,
151
  kZeroPad6,
152
  kZeroPad7,
153
  kZeroPad8,
154
  kZeroPad9,
155
  kZeroPad10,
156
  kZeroPad11,
157
  kZeroPad12,
158
  kZeroPad13,
159
  kZeroPad14,
160
  kZeroPad15,
161
  kZeroPad16,
162
  kZeroPad17,
163
  kZeroPad18,
164
  kZeroPad19,
165
  kZeroPad20,
166
167
  kSpacePad2 = kZeroPad2 + 64,
168
  kSpacePad3,
169
  kSpacePad4,
170
  kSpacePad5,
171
  kSpacePad6,
172
  kSpacePad7,
173
  kSpacePad8,
174
  kSpacePad9,
175
  kSpacePad10,
176
  kSpacePad11,
177
  kSpacePad12,
178
  kSpacePad13,
179
  kSpacePad14,
180
  kSpacePad15,
181
  kSpacePad16,
182
  kSpacePad17,
183
  kSpacePad18,
184
  kSpacePad19,
185
  kSpacePad20,
186
};
187
188
// -----------------------------------------------------------------------------
189
// Hex
190
// -----------------------------------------------------------------------------
191
//
192
// `Hex` stores a set of hexadecimal string conversion parameters for use
193
// within `AlphaNum` string conversions.
194
struct Hex {
195
  uint64_t value;
196
  uint8_t width;
197
  char fill;
198
199
  template <typename Int>
200
  explicit Hex(Int v, PadSpec spec = absl::kNoPad,
201
               std::enable_if_t<sizeof(Int) == 1 && !std::is_pointer_v<Int>,
202
                                bool> = true)
203
      : Hex(spec, static_cast<uint8_t>(v)) {}
204
  template <typename Int>
205
  explicit Hex(Int v, PadSpec spec = absl::kNoPad,
206
               std::enable_if_t<sizeof(Int) == 2 && !std::is_pointer_v<Int>,
207
                                bool> = true)
208
      : Hex(spec, static_cast<uint16_t>(v)) {}
209
  template <typename Int>
210
  explicit Hex(Int v, PadSpec spec = absl::kNoPad,
211
               std::enable_if_t<sizeof(Int) == 4 && !std::is_pointer_v<Int>,
212
                                bool> = true)
213
      : Hex(spec, static_cast<uint32_t>(v)) {}
214
  template <typename Int>
215
  explicit Hex(Int v, PadSpec spec = absl::kNoPad,
216
               std::enable_if_t<sizeof(Int) == 8 && !std::is_pointer_v<Int>,
217
                                bool> = true)
218
      : Hex(spec, static_cast<uint64_t>(v)) {}
219
  template <typename Pointee>
220
  explicit Hex(Pointee* absl_nullable v, PadSpec spec = absl::kNoPad)
221
      : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
222
223
  template <typename S>
224
  friend void AbslStringify(S& sink, Hex hex) {
225
    static_assert(
226
        numbers_internal::kFastToBufferSize >= 32,
227
        "This function only works when output buffer >= 32 bytes long");
228
    char buffer[numbers_internal::kFastToBufferSize];
229
    char* const end = &buffer[numbers_internal::kFastToBufferSize];
230
    auto real_width =
231
        absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
232
    if (real_width >= hex.width) {
233
      sink.Append(absl::string_view(end - real_width, real_width));
234
    } else {
235
      // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
236
      // max pad width can be up to 20.
237
      std::memset(end - 32, hex.fill, 16);
238
      // Patch up everything else up to the real_width.
239
      std::memset(end - real_width - 16, hex.fill, 16);
240
      sink.Append(absl::string_view(end - hex.width, hex.width));
241
    }
242
  }
243
244
 private:
245
  Hex(PadSpec spec, uint64_t v)
246
      : value(v),
247
        width(spec == absl::kNoPad
248
                  ? 1
249
                  : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
250
                                             : spec - absl::kZeroPad2 + 2),
251
0
        fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
252
};
253
254
// -----------------------------------------------------------------------------
255
// Dec
256
// -----------------------------------------------------------------------------
257
//
258
// `Dec` stores a set of decimal string conversion parameters for use
259
// within `AlphaNum` string conversions.  Dec is slower than the default
260
// integer conversion, so use it only if you need padding.
261
struct Dec {
262
  uint64_t value;
263
  uint8_t width;
264
  char fill;
265
  bool neg;
266
267
  template <typename Int>
268
  explicit Dec(Int v, PadSpec spec = absl::kNoPad,
269
               std::enable_if_t<sizeof(Int) <= 8, bool> = true)
270
      : value(v >= 0 ? static_cast<uint64_t>(v)
271
                     : uint64_t{0} - static_cast<uint64_t>(v)),
272
        width(spec == absl::kNoPad       ? 1
273
              : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
274
                                         : spec - absl::kZeroPad2 + 2),
275
        fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
276
        neg(v < 0) {}
277
278
  template <typename S>
279
  friend void AbslStringify(S& sink, Dec dec) {
280
    assert(dec.width <= numbers_internal::kFastToBufferSize);
281
    char buffer[numbers_internal::kFastToBufferSize];
282
    char* const end = &buffer[numbers_internal::kFastToBufferSize];
283
    char* const minfill = end - dec.width;
284
    char* writer = end;
285
    uint64_t val = dec.value;
286
    while (val > 9) {
287
      *--writer = '0' + (val % 10);
288
      val /= 10;
289
    }
290
    *--writer = '0' + static_cast<char>(val);
291
    if (dec.neg) *--writer = '-';
292
293
    ptrdiff_t fillers = writer - minfill;
294
    if (fillers > 0) {
295
      // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
296
      // But...: if the fill character is '0', then it's <+/-><fill><digits>
297
      bool add_sign_again = false;
298
      if (dec.neg && dec.fill == '0') {  // If filling with '0',
299
        ++writer;                    // ignore the sign we just added
300
        add_sign_again = true;       // and re-add the sign later.
301
      }
302
      writer -= fillers;
303
      std::fill_n(writer, fillers, dec.fill);
304
      if (add_sign_again) *--writer = '-';
305
    }
306
307
    sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
308
  }
309
};
310
311
// -----------------------------------------------------------------------------
312
// HighPrecision
313
// -----------------------------------------------------------------------------
314
//
315
// Converts floating point values to a string which, if passed to
316
// `absl::SimpleAtof`/`absl::SimpleAtod`, would produce the exact same original
317
// floating point value (except in case of NaN; all NaNs are considered the same
318
// value). Tries to keep the string short but it's not guaranteed to be as short
319
// as possible.
320
//
321
// HighPrecision is conisderably slower than the default formatting, so only use
322
// it if you need the string to convert back to the same floating-point value.
323
324
strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
325
HighPrecision(float f);
326
327
strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
328
HighPrecision(double d);
329
330
// -----------------------------------------------------------------------------
331
// AlphaNum
332
// -----------------------------------------------------------------------------
333
//
334
// The `AlphaNum` class acts as the main parameter type for `StrCat()` and
335
// `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
336
// and hexadecimal values (through the `Dec` and `Hex` types) into strings.
337
// `AlphaNum` should only be used as a function parameter. Do not instantiate
338
//  `AlphaNum` directly as a stack variable.
339
340
class AlphaNum {
341
 public:
342
  // No bool ctor -- bools convert to an integral type.
343
  // A bool ctor would also convert incoming pointers (bletch).
344
345
  // Prevent brace initialization
346
  template <typename T>
347
  AlphaNum(std::initializer_list<T>) = delete;  // NOLINT(runtime/explicit)
348
349
  AlphaNum(int x)  // NOLINT(runtime/explicit)
350
      : piece_(digits_, static_cast<size_t>(
351
                            numbers_internal::FastIntToBuffer(x, digits_) -
352
0
                            &digits_[0])) {}
353
  AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
354
      : piece_(digits_, static_cast<size_t>(
355
                            numbers_internal::FastIntToBuffer(x, digits_) -
356
0
                            &digits_[0])) {}
357
  AlphaNum(long x)  // NOLINT(*)
358
      : piece_(digits_, static_cast<size_t>(
359
                            numbers_internal::FastIntToBuffer(x, digits_) -
360
0
                            &digits_[0])) {}
361
  AlphaNum(unsigned long x)  // NOLINT(*)
362
      : piece_(digits_, static_cast<size_t>(
363
                            numbers_internal::FastIntToBuffer(x, digits_) -
364
0
                            &digits_[0])) {}
365
  AlphaNum(long long x)  // NOLINT(*)
366
      : piece_(digits_, static_cast<size_t>(
367
                            numbers_internal::FastIntToBuffer(x, digits_) -
368
0
                            &digits_[0])) {}
369
  AlphaNum(unsigned long long x)  // NOLINT(*)
370
      : piece_(digits_, static_cast<size_t>(
371
                            numbers_internal::FastIntToBuffer(x, digits_) -
372
0
                            &digits_[0])) {}
373
374
  AlphaNum(float f)  // NOLINT(runtime/explicit)
375
      : piece_(digits_, numbers_internal::SixDigitsToBuffer(
376
0
                            static_cast<double>(f), digits_)) {}
377
  AlphaNum(double f)  // NOLINT(runtime/explicit)
378
0
      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
379
380
  template <size_t size>
381
  AlphaNum(  // NOLINT(runtime/explicit)
382
      const strings_internal::AlphaNumBuffer<size>& buf
383
          ABSL_ATTRIBUTE_LIFETIME_BOUND)
384
      : piece_(&buf.data[0], buf.size) {}
385
386
  AlphaNum(const char* absl_nullable c_str  // NOLINT(runtime/explicit)
387
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
388
0
      : piece_(NullSafeStringView(c_str)) {}
389
  AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
390
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
391
0
      : piece_(pc) {}
392
393
#if !defined(ABSL_USES_STD_STRING_VIEW)
394
  AlphaNum(std::string_view pc  // NOLINT(runtime/explicit)
395
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
396
      : piece_(pc.data(), pc.size()) {}
397
#endif  // !ABSL_USES_STD_STRING_VIEW
398
399
  template <typename T, typename = std::enable_if_t<HasAbslStringify<T>::value>>
400
  AlphaNum(  // NOLINT(runtime/explicit)
401
      const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
402
      strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
403
      : piece_(strings_internal::ExtractStringification(sink, v)) {}
404
405
  template <typename Allocator>
406
  AlphaNum(  // NOLINT(runtime/explicit)
407
      const std::basic_string<char, std::char_traits<char>, Allocator>& str
408
          ABSL_ATTRIBUTE_LIFETIME_BOUND)
409
      : piece_(str) {}
410
411
  // Use string literals ":" instead of character literals ':'.
412
  AlphaNum(char c) = delete;  // NOLINT(runtime/explicit)
413
414
  AlphaNum(const AlphaNum&) = delete;
415
  AlphaNum& operator=(const AlphaNum&) = delete;
416
417
0
  absl::string_view::size_type size() const { return piece_.size(); }
418
0
  const char* absl_nullable data() const { return piece_.data(); }
419
0
  absl::string_view Piece() const { return piece_; }
420
421
  // Match unscoped enums.  Use integral promotion so that a `char`-backed
422
  // enum becomes a wider integral type AlphaNum will accept.
423
  template <typename T,
424
            typename = std::enable_if_t<std::is_enum<T>{} &&
425
                                        std::is_convertible<T, int>{} &&
426
                                        !HasAbslStringify<T>::value>>
427
  AlphaNum(T e)  // NOLINT(runtime/explicit)
428
      : AlphaNum(+e) {}
429
430
  // This overload matches scoped enums.  We must explicitly cast to the
431
  // underlying type, but use integral promotion for the same reason as above.
432
  template <typename T, std::enable_if_t<std::is_enum<T>{} &&
433
                                             !std::is_convertible<T, int>{} &&
434
                                             !HasAbslStringify<T>::value,
435
                                         char*> = nullptr>
436
  AlphaNum(T e)  // NOLINT(runtime/explicit)
437
      : AlphaNum(+static_cast<std::underlying_type_t<T>>(e)) {}
438
439
  // vector<bool>::reference and const_reference require special help to
440
  // convert to `AlphaNum` because it requires two user defined conversions.
441
  template <
442
      typename T,
443
      std::enable_if_t<
444
          std::is_class_v<T> &&
445
          (std::is_same_v<T, std::vector<bool>::reference> ||
446
           std::is_same_v<T, std::vector<bool>::const_reference>)>* = nullptr>
447
  AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
448
449
 private:
450
  absl::string_view piece_;
451
  char digits_[numbers_internal::kFastToBufferSize];
452
};
453
454
// -----------------------------------------------------------------------------
455
// StrCat()
456
// -----------------------------------------------------------------------------
457
//
458
// Merges given strings or numbers, using no delimiter(s), returning the merged
459
// result as a string.
460
//
461
// `StrCat()` is designed to be the fastest possible way to construct a string
462
// out of a mix of raw C strings, string_views, strings, bool values,
463
// and numeric values.
464
//
465
// Don't use `StrCat()` for user-visible strings. The localization process
466
// works poorly on strings built up out of fragments.
467
//
468
// For clarity and performance, don't use `StrCat()` when appending to a
469
// string. Use `StrAppend()` instead. In particular, avoid using any of these
470
// (anti-)patterns:
471
//
472
//   str.append(StrCat(...))
473
//   str += StrCat(...)
474
//   str = StrCat(str, ...)
475
//
476
// The last case is the worst, with a potential to change a loop
477
// from a linear time operation with O(1) dynamic allocations into a
478
// quadratic time operation with O(n) dynamic allocations.
479
//
480
// See `StrAppend()` below for more information.
481
482
namespace strings_internal {
483
484
// Do not call directly - this is not part of the public API.
485
std::string CatPieces(std::initializer_list<absl::string_view> pieces);
486
void AppendPieces(std::string* absl_nonnull dest,
487
                  std::initializer_list<absl::string_view> pieces);
488
489
// `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
490
// (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
491
// and int64_t, then at least one of the three (`int` / `long` / `long long`)
492
// would have been ambiguous when passed to `SingleArgStrCat`.
493
std::string SingleArgStrCat(int x);
494
std::string SingleArgStrCat(unsigned int x);
495
// NOLINTNEXTLINE
496
std::string SingleArgStrCat(long x);
497
// NOLINTNEXTLINE
498
std::string SingleArgStrCat(unsigned long x);
499
// NOLINTNEXTLINE
500
std::string SingleArgStrCat(long long x);
501
// NOLINTNEXTLINE
502
std::string SingleArgStrCat(unsigned long long x);
503
std::string SingleArgStrCat(float x);
504
std::string SingleArgStrCat(double 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
530
0
[[nodiscard]] inline std::string StrCat() { return std::string(); }
531
532
template <typename T>
533
[[nodiscard]] inline std::string StrCat(
534
    strings_internal::EnableIfFastCase<T> a) {
535
  return strings_internal::SingleArgStrCat(a);
536
}
537
0
[[nodiscard]] inline std::string StrCat(const AlphaNum& a) {
538
0
  return std::string(a.data(), a.size());
539
0
}
540
541
[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b);
542
[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b,
543
                                 const AlphaNum& c);
544
[[nodiscard]] 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>
549
[[nodiscard]] inline std::string StrCat(const AlphaNum& a, const AlphaNum& b,
550
                                        const AlphaNum& c, const AlphaNum& d,
551
0
                                        const AlphaNum& e, const AV&... args) {
552
0
  return strings_internal::CatPieces(
553
0
      {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
554
0
       static_cast<const AlphaNum&>(args).Piece()...});
555
0
}
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
584
0
inline void StrAppend(std::string* absl_nonnull) {}
585
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a);
586
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
587
               const AlphaNum& b);
588
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
589
               const AlphaNum& b, const AlphaNum& c);
590
void StrAppend(std::string* absl_nonnull 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>
595
inline void StrAppend(std::string* absl_nonnull 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
strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
606
SixDigits(double d);
607
608
ABSL_NAMESPACE_END
609
}  // namespace absl
610
611
#endif  // ABSL_STRINGS_STR_CAT_H_