Coverage Report

Created: 2026-05-23 07:02

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