Coverage Report

Created: 2026-07-16 06:43

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