Coverage Report

Created: 2026-09-14 06:45

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
inline strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
325
0
HighPrecision(float f) {
326
0
  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
327
0
  result.size =
328
0
      strlen(numbers_internal::RoundTripFloatToBuffer(f, &result.data[0]));
329
0
  return result;
330
0
}
331
332
inline strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
333
0
HighPrecision(double d) {
334
0
  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
335
0
  result.size =
336
0
      strlen(numbers_internal::RoundTripDoubleToBuffer(d, &result.data[0]));
337
0
  return result;
338
0
}
339
340
// -----------------------------------------------------------------------------
341
// AlphaNum
342
// -----------------------------------------------------------------------------
343
//
344
// The `AlphaNum` class acts as the main parameter type for `StrCat()` and
345
// `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
346
// and hexadecimal values (through the `Dec` and `Hex` types) into strings.
347
// `AlphaNum` should only be used as a function parameter. Do not instantiate
348
//  `AlphaNum` directly as a stack variable.
349
350
class AlphaNum {
351
 public:
352
  // No bool ctor -- bools convert to an integral type.
353
  // A bool ctor would also convert incoming pointers (bletch).
354
355
  // Prevent brace initialization
356
  template <typename T>
357
  AlphaNum(std::initializer_list<T>) = delete;  // NOLINT(runtime/explicit)
358
359
  AlphaNum(int x)  // NOLINT(runtime/explicit)
360
      : piece_(digits_, static_cast<size_t>(
361
                            numbers_internal::FastIntToBuffer(x, digits_) -
362
0
                            &digits_[0])) {}
363
  AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
364
      : piece_(digits_, static_cast<size_t>(
365
                            numbers_internal::FastIntToBuffer(x, digits_) -
366
0
                            &digits_[0])) {}
367
  AlphaNum(long x)  // NOLINT(*)
368
      : piece_(digits_, static_cast<size_t>(
369
                            numbers_internal::FastIntToBuffer(x, digits_) -
370
0
                            &digits_[0])) {}
371
  AlphaNum(unsigned long x)  // NOLINT(*)
372
      : piece_(digits_, static_cast<size_t>(
373
                            numbers_internal::FastIntToBuffer(x, digits_) -
374
0
                            &digits_[0])) {}
375
  AlphaNum(long long x)  // NOLINT(*)
376
      : piece_(digits_, static_cast<size_t>(
377
                            numbers_internal::FastIntToBuffer(x, digits_) -
378
0
                            &digits_[0])) {}
379
  AlphaNum(unsigned long long x)  // NOLINT(*)
380
      : piece_(digits_, static_cast<size_t>(
381
                            numbers_internal::FastIntToBuffer(x, digits_) -
382
0
                            &digits_[0])) {}
383
384
  AlphaNum(float f)  // NOLINT(runtime/explicit)
385
0
      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
386
  AlphaNum(double f)  // NOLINT(runtime/explicit)
387
0
      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
388
389
  template <size_t size>
390
  AlphaNum(  // NOLINT(runtime/explicit)
391
      const strings_internal::AlphaNumBuffer<size>& buf
392
          ABSL_ATTRIBUTE_LIFETIME_BOUND)
393
      : piece_(&buf.data[0], buf.size) {}
394
395
  AlphaNum(const char* absl_nullable c_str  // NOLINT(runtime/explicit)
396
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
397
0
      : piece_(NullSafeStringView(c_str)) {}
398
  AlphaNum(absl::string_view pc  // NOLINT(runtime/explicit)
399
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
400
0
      : piece_(pc) {}
401
402
#if !defined(ABSL_USES_STD_STRING_VIEW)
403
  AlphaNum(std::string_view pc  // NOLINT(runtime/explicit)
404
               ABSL_ATTRIBUTE_LIFETIME_BOUND)
405
      : piece_(pc.data(), pc.size()) {}
406
#endif  // !ABSL_USES_STD_STRING_VIEW
407
408
  template <typename T, typename = std::enable_if_t<HasAbslStringify<T>::value>>
409
  AlphaNum(  // NOLINT(runtime/explicit)
410
      const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
411
      strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
412
      : piece_(strings_internal::ExtractStringification(sink, v)) {}
413
414
  template <typename Allocator>
415
  AlphaNum(  // NOLINT(runtime/explicit)
416
      const std::basic_string<char, std::char_traits<char>, Allocator>& str
417
          ABSL_ATTRIBUTE_LIFETIME_BOUND)
418
      : piece_(str) {}
419
420
  // Use string literals ":" instead of character literals ':'.
421
  AlphaNum(char c) = delete;  // NOLINT(runtime/explicit)
422
423
  AlphaNum(const AlphaNum&) = delete;
424
  AlphaNum& operator=(const AlphaNum&) = delete;
425
426
0
  absl::string_view::size_type size() const { return piece_.size(); }
427
0
  const char* absl_nullable data() const { return piece_.data(); }
428
0
  absl::string_view Piece() const { return piece_; }
429
430
  // Match unscoped enums.  Use integral promotion so that a `char`-backed
431
  // enum becomes a wider integral type AlphaNum will accept.
432
  template <typename T,
433
            typename = std::enable_if_t<std::is_enum<T>{} &&
434
                                        std::is_convertible<T, int>{} &&
435
                                        !HasAbslStringify<T>::value>>
436
  AlphaNum(T e)  // NOLINT(runtime/explicit)
437
      : AlphaNum(+e) {}
438
439
  // This overload matches scoped enums.  We must explicitly cast to the
440
  // underlying type, but use integral promotion for the same reason as above.
441
  template <typename T, std::enable_if_t<std::is_enum<T>{} &&
442
                                             !std::is_convertible<T, int>{} &&
443
                                             !HasAbslStringify<T>::value,
444
                                         char*> = nullptr>
445
  AlphaNum(T e)  // NOLINT(runtime/explicit)
446
      : AlphaNum(+static_cast<std::underlying_type_t<T>>(e)) {}
447
448
  // vector<bool>::reference and const_reference require special help to
449
  // convert to `AlphaNum` because it requires two user defined conversions.
450
  template <
451
      typename T,
452
      std::enable_if_t<
453
          std::is_class_v<T> &&
454
          (std::is_same_v<T, std::vector<bool>::reference> ||
455
           std::is_same_v<T, std::vector<bool>::const_reference>)>* = nullptr>
456
  AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
457
458
 private:
459
  absl::string_view piece_;
460
  char digits_[numbers_internal::kFastToBufferSize];
461
};
462
463
// -----------------------------------------------------------------------------
464
// StrCat()
465
// -----------------------------------------------------------------------------
466
//
467
// Merges given strings or numbers, using no delimiter(s), returning the merged
468
// result as a string.
469
//
470
// `StrCat()` is designed to be the fastest possible way to construct a string
471
// out of a mix of raw C strings, string_views, strings, bool values,
472
// and numeric values.
473
//
474
// Don't use `StrCat()` for user-visible strings. The localization process
475
// works poorly on strings built up out of fragments.
476
//
477
// For clarity and performance, don't use `StrCat()` when appending to a
478
// string. Use `StrAppend()` instead. In particular, avoid using any of these
479
// (anti-)patterns:
480
//
481
//   str.append(StrCat(...))
482
//   str += StrCat(...)
483
//   str = StrCat(str, ...)
484
//
485
// The last case is the worst, with a potential to change a loop
486
// from a linear time operation with O(1) dynamic allocations into a
487
// quadratic time operation with O(n) dynamic allocations.
488
//
489
// See `StrAppend()` below for more information.
490
491
namespace strings_internal {
492
493
// Do not call directly - this is not part of the public API.
494
std::string CatPieces(std::initializer_list<absl::string_view> pieces);
495
void AppendPieces(std::string* absl_nonnull dest,
496
                  std::initializer_list<absl::string_view> pieces);
497
498
template <typename Integer>
499
0
std::string IntegerToString(Integer i) {
500
0
  // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
501
0
  // with 22 bytes (including NULL at the end).
502
0
  constexpr size_t kMaxDigits10 = 22;
503
0
  std::string result;
504
0
  StringResizeAndOverwrite(
505
0
      result, kMaxDigits10, [i](char* start, size_t buf_size) {
506
0
        // Note: This can be optimized to not write last zero.
507
0
        char* end = numbers_internal::FastIntToBuffer(i, start);
508
0
        auto size = static_cast<size_t>(end - start);
509
0
        ABSL_ASSERT(size < buf_size);
510
0
        return size;
511
0
      });
512
0
  return result;
513
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)
514
515
template <typename Float>
516
0
std::string FloatToString(Float f) {
517
0
  std::string result;
518
0
  StringResizeAndOverwrite(result, numbers_internal::kSixDigitsToBufferSize,
519
0
                           [f](char* start, size_t buf_size) {
520
0
                             size_t size =
521
0
                                 numbers_internal::SixDigitsToBuffer(f, start);
522
0
                             ABSL_ASSERT(size < buf_size);
523
0
                             return size;
524
0
                           });
525
0
  return result;
526
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)
527
528
// `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
529
// (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
530
// and int64_t, then at least one of the three (`int` / `long` / `long long`)
531
// would have been ambiguous when passed to `SingleArgStrCat`.
532
0
inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
533
0
inline std::string SingleArgStrCat(unsigned int x) {
534
0
  return IntegerToString(x);
535
0
}
536
// NOLINTNEXTLINE
537
0
inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
538
// NOLINTNEXTLINE
539
0
inline std::string SingleArgStrCat(unsigned long x) {
540
0
  return IntegerToString(x);
541
0
}
542
// NOLINTNEXTLINE
543
0
inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
544
// NOLINTNEXTLINE
545
0
inline std::string SingleArgStrCat(unsigned long long x) {
546
0
  return IntegerToString(x);
547
0
}
548
0
inline std::string SingleArgStrCat(float x) { return FloatToString(x); }
549
0
inline std::string SingleArgStrCat(double x) { return FloatToString(x); }
550
551
// As of September 2023, the SingleArgStrCat() optimization is only enabled for
552
// libc++. The reasons for this are:
553
// 1) The SSO size for libc++ is 23, while libstdc++ and MSSTL have an SSO size
554
// of 15. Since IntegerToString unconditionally resizes the string to 22 bytes,
555
// this causes both libstdc++ and MSSTL to allocate.
556
// 2) strings_internal::STLStringResizeUninitialized() only has an
557
// implementation that avoids initialization when using libc++. This isn't as
558
// relevant as (1), and the cost should be benchmarked if (1) ever changes on
559
// libstc++ or MSSTL.
560
#ifdef _LIBCPP_VERSION
561
#define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE true
562
#else
563
#define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE false
564
#endif
565
566
template <typename T, typename = std::enable_if_t<
567
                          ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE &&
568
                          std::is_arithmetic<T>{} && !std::is_same<T, char>{}>>
569
using EnableIfFastCase = T;
570
571
#undef ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE
572
573
}  // namespace strings_internal
574
575
0
[[nodiscard]] inline std::string StrCat() { return std::string(); }
576
577
template <typename T>
578
[[nodiscard]] inline std::string StrCat(
579
    strings_internal::EnableIfFastCase<T> a) {
580
  return strings_internal::SingleArgStrCat(a);
581
}
582
0
[[nodiscard]] inline std::string StrCat(const AlphaNum& a) {
583
0
  return std::string(a.data(), a.size());
584
0
}
585
586
[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b);
587
[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b,
588
                                 const AlphaNum& c);
589
[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b,
590
                                 const AlphaNum& c, const AlphaNum& d);
591
592
// Support 5 or more arguments
593
template <typename... AV>
594
[[nodiscard]] inline std::string StrCat(const AlphaNum& a, const AlphaNum& b,
595
                                        const AlphaNum& c, const AlphaNum& d,
596
0
                                        const AlphaNum& e, const AV&... args) {
597
0
  return strings_internal::CatPieces(
598
0
      {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
599
0
       static_cast<const AlphaNum&>(args).Piece()...});
600
0
}
601
602
// -----------------------------------------------------------------------------
603
// StrAppend()
604
// -----------------------------------------------------------------------------
605
//
606
// Appends a string or set of strings to an existing string, in a similar
607
// fashion to `StrCat()`.
608
//
609
// WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
610
// a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
611
// not try to check each of its input arguments to be sure that they are not
612
// a subset of the string being appended to. That is, while this will work:
613
//
614
//   std::string s = "foo";
615
//   s += s;
616
//
617
// This output is undefined:
618
//
619
//   std::string s = "foo";
620
//   StrAppend(&s, s);
621
//
622
// This output is undefined as well, since `absl::string_view` does not own its
623
// data:
624
//
625
//   std::string s = "foobar";
626
//   absl::string_view p = s;
627
//   StrAppend(&s, p);
628
629
0
inline void StrAppend(std::string* absl_nonnull) {}
630
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a);
631
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
632
               const AlphaNum& b);
633
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
634
               const AlphaNum& b, const AlphaNum& c);
635
void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
636
               const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
637
638
// Support 5 or more arguments
639
template <typename... AV>
640
inline void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
641
                      const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
642
                      const AlphaNum& e, const AV&... args) {
643
  strings_internal::AppendPieces(
644
      dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
645
             static_cast<const AlphaNum&>(args).Piece()...});
646
}
647
648
// Helper function for the future StrCat default floating-point format, %.6g
649
// This is fast.
650
inline strings_internal::AlphaNumBuffer<
651
    numbers_internal::kSixDigitsToBufferSize>
652
0
SixDigits(double d) {
653
0
  strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
654
0
      result;
655
0
  result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
656
0
  return result;
657
0
}
658
659
ABSL_NAMESPACE_END
660
}  // namespace absl
661
662
#endif  // ABSL_STRINGS_STR_CAT_H_