Coverage Report

Created: 2025-07-11 06:37

/src/abseil-cpp/absl/strings/str_cat.h
Line
Count
Source (jump to first uncovered line)
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_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
      std::enable_if_t<sizeof(Int) == 1 && !std::is_pointer<Int>::value, bool> =
195
          true)
196
      : Hex(spec, static_cast<uint8_t>(v)) {}
197
  template <typename Int>
198
  explicit Hex(
199
      Int v, PadSpec spec = absl::kNoPad,
200
      std::enable_if_t<sizeof(Int) == 2 && !std::is_pointer<Int>::value, bool> =
201
          true)
202
      : Hex(spec, static_cast<uint16_t>(v)) {}
203
  template <typename Int>
204
  explicit Hex(
205
      Int v, PadSpec spec = absl::kNoPad,
206
      std::enable_if_t<sizeof(Int) == 4 && !std::is_pointer<Int>::value, bool> =
207
          true)
208
      : Hex(spec, static_cast<uint32_t>(v)) {}
209
  template <typename Int>
210
  explicit Hex(
211
      Int v, PadSpec spec = absl::kNoPad,
212
      std::enable_if_t<sizeof(Int) == 8 && !std::is_pointer<Int>::value, bool> =
213
          true)
214
      : Hex(spec, static_cast<uint64_t>(v)) {}
215
  template <typename Pointee>
216
  explicit Hex(Pointee* absl_nullable v, PadSpec spec = absl::kNoPad)
217
      : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
218
219
  template <typename S>
220
  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:
241
  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
0
        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
               std::enable_if_t<sizeof(Int) <= 8, bool> = true)
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>
275
  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
326
  AlphaNum(int x)  // NOLINT(runtime/explicit)
327
      : piece_(digits_, static_cast<size_t>(
328
                            numbers_internal::FastIntToBuffer(x, digits_) -
329
0
                            &digits_[0])) {}
330
  AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
331
      : piece_(digits_, static_cast<size_t>(
332
                            numbers_internal::FastIntToBuffer(x, digits_) -
333
0
                            &digits_[0])) {}
334
  AlphaNum(long x)  // NOLINT(*)
335
      : piece_(digits_, static_cast<size_t>(
336
                            numbers_internal::FastIntToBuffer(x, digits_) -
337
0
                            &digits_[0])) {}
338
  AlphaNum(unsigned long x)  // NOLINT(*)
339
      : piece_(digits_, static_cast<size_t>(
340
                            numbers_internal::FastIntToBuffer(x, digits_) -
341
0
                            &digits_[0])) {}
342
  AlphaNum(long long x)  // NOLINT(*)
343
      : piece_(digits_, static_cast<size_t>(
344
                            numbers_internal::FastIntToBuffer(x, digits_) -
345
0
                            &digits_[0])) {}
346
  AlphaNum(unsigned long long x)  // NOLINT(*)
347
      : piece_(digits_, static_cast<size_t>(
348
                            numbers_internal::FastIntToBuffer(x, digits_) -
349
0
                            &digits_[0])) {}
350
351
  AlphaNum(float f)  // NOLINT(runtime/explicit)
352
0
      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
353
  AlphaNum(double f)  // NOLINT(runtime/explicit)
354
0
      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
355
356
  template <size_t size>
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
362
  AlphaNum(const char* absl_nullable c_str  // NOLINT(runtime/explicit)
363
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
364
0
      : piece_(NullSafeStringView(c_str)) {}
365
  AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
366
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
367
0
      : piece_(pc) {}
368
369
#if !defined(ABSL_USES_STD_STRING_VIEW)
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 = {})
380
      : piece_(strings_internal::ExtractStringification(sink, v)) {}
381
382
  template <typename Allocator>
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
394
0
  absl::string_view::size_type size() const { return piece_.size(); }
395
0
  const char* absl_nullable data() const { return piece_.data(); }
396
0
  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>
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(std::string* absl_nonnull dest,
466
                  std::initializer_list<absl::string_view> pieces);
467
468
template <typename Integer>
469
0
std::string IntegerToString(Integer i) {
470
0
  // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
471
0
  // with 22 bytes (including NULL at the end).
472
0
  constexpr size_t kMaxDigits10 = 22;
473
0
  std::string result;
474
0
  strings_internal::STLStringResizeUninitialized(&result, kMaxDigits10);
475
0
  char* start = &result[0];
476
0
  // note: this can be optimized to not write last zero.
477
0
  char* end = numbers_internal::FastIntToBuffer(i, start);
478
0
  auto size = static_cast<size_t>(end - start);
479
0
  assert((size < result.size()) &&
480
0
         "StrCat(Integer) does not fit into kMaxDigits10");
481
0
  result.erase(size);
482
0
  return result;
483
0
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::IntegerToString<int>(int)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::IntegerToString<unsigned int>(unsigned int)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::IntegerToString<long>(long)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::IntegerToString<unsigned long>(unsigned long)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::IntegerToString<long long>(long long)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::IntegerToString<unsigned long long>(unsigned long long)
484
template <typename Float>
485
0
std::string FloatToString(Float f) {
486
0
  std::string result;
487
0
  strings_internal::STLStringResizeUninitialized(
488
0
      &result, numbers_internal::kSixDigitsToBufferSize);
489
0
  char* start = &result[0];
490
0
  result.erase(numbers_internal::SixDigitsToBuffer(f, start));
491
0
  return result;
492
0
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::FloatToString<float>(float)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > absl::strings_internal::FloatToString<double>(double)
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`.
498
0
inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
499
0
inline std::string SingleArgStrCat(unsigned int x) {
500
0
  return IntegerToString(x);
501
0
}
502
// NOLINTNEXTLINE
503
0
inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
504
// NOLINTNEXTLINE
505
0
inline std::string SingleArgStrCat(unsigned long x) {
506
0
  return IntegerToString(x);
507
0
}
508
// NOLINTNEXTLINE
509
0
inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
510
// NOLINTNEXTLINE
511
0
inline std::string SingleArgStrCat(unsigned long long x) {
512
0
  return IntegerToString(x);
513
0
}
514
0
inline std::string SingleArgStrCat(float x) { return FloatToString(x); }
515
0
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
541
0
[[nodiscard]] inline std::string StrCat() { return std::string(); }
542
543
template <typename T>
544
[[nodiscard]] inline std::string StrCat(
545
    strings_internal::EnableIfFastCase<T> a) {
546
  return strings_internal::SingleArgStrCat(a);
547
}
548
0
[[nodiscard]] inline std::string StrCat(const AlphaNum& a) {
549
0
  return std::string(a.data(), a.size());
550
0
}
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>
560
[[nodiscard]] inline std::string StrCat(const AlphaNum& a, const AlphaNum& b,
561
                                        const AlphaNum& c, const AlphaNum& d,
562
0
                                        const AlphaNum& e, const AV&... args) {
563
0
  return strings_internal::CatPieces(
564
0
      {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
565
0
       static_cast<const AlphaNum&>(args).Piece()...});
566
0
}
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
595
0
inline void StrAppend(std::string* absl_nonnull) {}
596
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a);
597
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
598
               const AlphaNum& b);
599
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
600
               const AlphaNum& b, const AlphaNum& c);
601
void StrAppend(std::string* absl_nonnull 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>
606
inline void StrAppend(std::string* absl_nonnull 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>
618
0
SixDigits(double d) {
619
0
  strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
620
0
      result;
621
0
  result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
622
0
  return result;
623
0
}
624
625
ABSL_NAMESPACE_END
626
}  // namespace absl
627
628
#endif  // ABSL_STRINGS_STR_CAT_H_