Coverage Report

Created: 2025-10-27 06:07

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