1 //
2 // Copyright 2017 The Abseil Authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 // -----------------------------------------------------------------------------
17 // File: str_cat.h
18 // -----------------------------------------------------------------------------
19 //
20 // This package contains functions for efficiently concatenating and appending
21 // strings: `StrCat()` and `StrAppend()`. Most of the work within these routines
22 // is actually handled through use of a special AlphaNum type, which was
23 // designed to be used as a parameter type that efficiently manages conversion
24 // to strings and avoids copies in the above operations.
25 //
26 // Any routine accepting either a string or a number may accept `AlphaNum`.
27 // The basic idea is that by accepting a `const AlphaNum &` as an argument
28 // to your function, your callers will automagically convert bools, integers,
29 // and floating point values to strings for you.
30 //
31 // NOTE: Use of `AlphaNum` outside of the //absl/strings package is unsupported
32 // except for the specific case of function parameters of type `AlphaNum` or
33 // `const AlphaNum &`. In particular, instantiating `AlphaNum` directly as a
34 // stack variable is not supported.
35 //
36 // Conversion from 8-bit values is not accepted because, if it were, then an
37 // attempt to pass ':' instead of ":" might result in a 58 ending up in your
38 // result.
39 //
40 // Bools convert to "0" or "1". Pointers to types other than `char *` are not
41 // valid inputs. No output is generated for null `char *` pointers.
42 //
43 // Floating point numbers are formatted with six-digit precision, which is
44 // the default for "std::cout <<" or printf "%g" (the same as "%.6g").
45 //
46 // You can convert to hexadecimal output rather than decimal output using the
47 // `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to
48 // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using
49 // a `PadSpec` enum.
50 //
51 // User-defined types can be formatted with the `AbslStringify()` customization
52 // point. The API relies on detecting an overload in the user-defined type's
53 // namespace of a free (non-member) `AbslStringify()` function as a definition
54 // (typically declared as a friend and implemented in-line.
55 // with the following signature:
56 //
57 // class MyClass { ... };
58 //
59 // template <typename Sink>
60 // void AbslStringify(Sink& sink, const MyClass& value);
61 //
62 // An `AbslStringify()` overload for a type should only be declared in the same
63 // file and namespace as said type.
64 //
65 // Note that `AbslStringify()` also supports use with `absl::StrFormat()` and
66 // `absl::Substitute()`.
67 //
68 // Example:
69 //
70 // struct Point {
71 // // To add formatting support to `Point`, we simply need to add a free
72 // // (non-member) function `AbslStringify()`. This method specifies how
73 // // Point should be printed when absl::StrCat() is called on it. You can add
74 // // such a free function using a friend declaration within the body of the
75 // // class. The sink parameter is a templated type to avoid requiring
76 // // dependencies.
77 // template <typename Sink> friend void AbslStringify(Sink&
78 // sink, const Point& p) {
79 // absl::Format(&sink, "(%v, %v)", p.x, p.y);
80 // }
81 //
82 // int x;
83 // int y;
84 // };
85 // -----------------------------------------------------------------------------
86
87 #ifndef ABSL_STRINGS_STR_CAT_H_
88 #define ABSL_STRINGS_STR_CAT_H_
89
90 #include <algorithm>
91 #include <array>
92 #include <cassert>
93 #include <cstddef>
94 #include <cstdint>
95 #include <cstring>
96 #include <initializer_list>
97 #include <limits>
98 #include <string>
99 #include <type_traits>
100 #include <utility>
101 #include <vector>
102
103 #include "absl/base/attributes.h"
104 #include "absl/base/config.h"
105 #include "absl/base/nullability.h"
106 #include "absl/base/port.h"
107 #include "absl/meta/type_traits.h"
108 #include "absl/strings/has_absl_stringify.h"
109 #include "absl/strings/internal/resize_uninitialized.h"
110 #include "absl/strings/internal/stringify_sink.h"
111 #include "absl/strings/numbers.h"
112 #include "absl/strings/string_view.h"
113
114 #if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW)
115 #include <string_view>
116 #endif
117
118 namespace absl {
119 ABSL_NAMESPACE_BEGIN
120
121 namespace strings_internal {
122 // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
123 // memory allocation. It is simply a pair of a fixed-size character array, and
124 // a size. Please don't use outside of absl, yet.
125 template <size_t max_size>
126 struct AlphaNumBuffer {
127 std::array<char, max_size> data;
128 size_t size;
129 };
130
131 } // namespace strings_internal
132
133 // Enum that specifies the number of significant digits to return in a `Hex` or
134 // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
135 // would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value
136 // would produce hexadecimal strings such as " a"," f".
137 enum PadSpec : uint8_t {
138 kNoPad = 1,
139 kZeroPad2,
140 kZeroPad3,
141 kZeroPad4,
142 kZeroPad5,
143 kZeroPad6,
144 kZeroPad7,
145 kZeroPad8,
146 kZeroPad9,
147 kZeroPad10,
148 kZeroPad11,
149 kZeroPad12,
150 kZeroPad13,
151 kZeroPad14,
152 kZeroPad15,
153 kZeroPad16,
154 kZeroPad17,
155 kZeroPad18,
156 kZeroPad19,
157 kZeroPad20,
158
159 kSpacePad2 = kZeroPad2 + 64,
160 kSpacePad3,
161 kSpacePad4,
162 kSpacePad5,
163 kSpacePad6,
164 kSpacePad7,
165 kSpacePad8,
166 kSpacePad9,
167 kSpacePad10,
168 kSpacePad11,
169 kSpacePad12,
170 kSpacePad13,
171 kSpacePad14,
172 kSpacePad15,
173 kSpacePad16,
174 kSpacePad17,
175 kSpacePad18,
176 kSpacePad19,
177 kSpacePad20,
178 };
179
180 // -----------------------------------------------------------------------------
181 // Hex
182 // -----------------------------------------------------------------------------
183 //
184 // `Hex` stores a set of hexadecimal string conversion parameters for use
185 // within `AlphaNum` string conversions.
186 struct Hex {
187 uint64_t value;
188 uint8_t width;
189 char fill;
190
191 template <typename Int>
192 explicit Hex(
193 Int v, PadSpec spec = absl::kNoPad,
194 typename std::enable_if<sizeof(Int) == 1 &&
195 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex196 : Hex(spec, static_cast<uint8_t>(v)) {}
197 template <typename Int>
198 explicit Hex(
199 Int v, PadSpec spec = absl::kNoPad,
200 typename std::enable_if<sizeof(Int) == 2 &&
201 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex202 : Hex(spec, static_cast<uint16_t>(v)) {}
203 template <typename Int>
204 explicit Hex(
205 Int v, PadSpec spec = absl::kNoPad,
206 typename std::enable_if<sizeof(Int) == 4 &&
207 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex208 : Hex(spec, static_cast<uint32_t>(v)) {}
209 template <typename Int>
210 explicit Hex(
211 Int v, PadSpec spec = absl::kNoPad,
212 typename std::enable_if<sizeof(Int) == 8 &&
213 !std::is_pointer<Int>::value>::type* = nullptr)
HexHex214 : Hex(spec, static_cast<uint64_t>(v)) {}
215 template <typename Pointee>
216 explicit Hex(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad)
HexHex217 : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
218
219 template <typename S>
AbslStringifyHex220 friend void AbslStringify(S& sink, Hex hex) {
221 static_assert(
222 numbers_internal::kFastToBufferSize >= 32,
223 "This function only works when output buffer >= 32 bytes long");
224 char buffer[numbers_internal::kFastToBufferSize];
225 char* const end = &buffer[numbers_internal::kFastToBufferSize];
226 auto real_width =
227 absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
228 if (real_width >= hex.width) {
229 sink.Append(absl::string_view(end - real_width, real_width));
230 } else {
231 // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
232 // max pad width can be up to 20.
233 std::memset(end - 32, hex.fill, 16);
234 // Patch up everything else up to the real_width.
235 std::memset(end - real_width - 16, hex.fill, 16);
236 sink.Append(absl::string_view(end - hex.width, hex.width));
237 }
238 }
239
240 private:
HexHex241 Hex(PadSpec spec, uint64_t v)
242 : value(v),
243 width(spec == absl::kNoPad
244 ? 1
245 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
246 : spec - absl::kZeroPad2 + 2),
247 fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
248 };
249
250 // -----------------------------------------------------------------------------
251 // Dec
252 // -----------------------------------------------------------------------------
253 //
254 // `Dec` stores a set of decimal string conversion parameters for use
255 // within `AlphaNum` string conversions. Dec is slower than the default
256 // integer conversion, so use it only if you need padding.
257 struct Dec {
258 uint64_t value;
259 uint8_t width;
260 char fill;
261 bool neg;
262
263 template <typename Int>
264 explicit Dec(Int v, PadSpec spec = absl::kNoPad,
265 typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
266 : value(v >= 0 ? static_cast<uint64_t>(v)
267 : uint64_t{0} - static_cast<uint64_t>(v)),
268 width(spec == absl::kNoPad ? 1
269 : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
270 : spec - absl::kZeroPad2 + 2),
271 fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
272 neg(v < 0) {}
273
274 template <typename S>
AbslStringifyDec275 friend void AbslStringify(S& sink, Dec dec) {
276 assert(dec.width <= numbers_internal::kFastToBufferSize);
277 char buffer[numbers_internal::kFastToBufferSize];
278 char* const end = &buffer[numbers_internal::kFastToBufferSize];
279 char* const minfill = end - dec.width;
280 char* writer = end;
281 uint64_t val = dec.value;
282 while (val > 9) {
283 *--writer = '0' + (val % 10);
284 val /= 10;
285 }
286 *--writer = '0' + static_cast<char>(val);
287 if (dec.neg) *--writer = '-';
288
289 ptrdiff_t fillers = writer - minfill;
290 if (fillers > 0) {
291 // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
292 // But...: if the fill character is '0', then it's <+/-><fill><digits>
293 bool add_sign_again = false;
294 if (dec.neg && dec.fill == '0') { // If filling with '0',
295 ++writer; // ignore the sign we just added
296 add_sign_again = true; // and re-add the sign later.
297 }
298 writer -= fillers;
299 std::fill_n(writer, fillers, dec.fill);
300 if (add_sign_again) *--writer = '-';
301 }
302
303 sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
304 }
305 };
306
307 // -----------------------------------------------------------------------------
308 // AlphaNum
309 // -----------------------------------------------------------------------------
310 //
311 // The `AlphaNum` class acts as the main parameter type for `StrCat()` and
312 // `StrAppend()`, providing efficient conversion of numeric, boolean, decimal,
313 // and hexadecimal values (through the `Dec` and `Hex` types) into strings.
314 // `AlphaNum` should only be used as a function parameter. Do not instantiate
315 // `AlphaNum` directly as a stack variable.
316
317 class AlphaNum {
318 public:
319 // No bool ctor -- bools convert to an integral type.
320 // A bool ctor would also convert incoming pointers (bletch).
321
322 // Prevent brace initialization
323 template <typename T>
324 AlphaNum(std::initializer_list<T>) = delete; // NOLINT(runtime/explicit)
325
AlphaNum(int x)326 AlphaNum(int x) // NOLINT(runtime/explicit)
327 : piece_(digits_, static_cast<size_t>(
328 numbers_internal::FastIntToBuffer(x, digits_) -
329 &digits_[0])) {}
AlphaNum(unsigned int x)330 AlphaNum(unsigned int x) // NOLINT(runtime/explicit)
331 : piece_(digits_, static_cast<size_t>(
332 numbers_internal::FastIntToBuffer(x, digits_) -
333 &digits_[0])) {}
AlphaNum(long x)334 AlphaNum(long x) // NOLINT(*)
335 : piece_(digits_, static_cast<size_t>(
336 numbers_internal::FastIntToBuffer(x, digits_) -
337 &digits_[0])) {}
AlphaNum(unsigned long x)338 AlphaNum(unsigned long x) // NOLINT(*)
339 : piece_(digits_, static_cast<size_t>(
340 numbers_internal::FastIntToBuffer(x, digits_) -
341 &digits_[0])) {}
AlphaNum(long long x)342 AlphaNum(long long x) // NOLINT(*)
343 : piece_(digits_, static_cast<size_t>(
344 numbers_internal::FastIntToBuffer(x, digits_) -
345 &digits_[0])) {}
AlphaNum(unsigned long long x)346 AlphaNum(unsigned long long x) // NOLINT(*)
347 : piece_(digits_, static_cast<size_t>(
348 numbers_internal::FastIntToBuffer(x, digits_) -
349 &digits_[0])) {}
350
AlphaNum(float f)351 AlphaNum(float f) // NOLINT(runtime/explicit)
352 : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
AlphaNum(double f)353 AlphaNum(double f) // NOLINT(runtime/explicit)
354 : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
355
356 template <size_t size>
AlphaNum(const strings_internal::AlphaNumBuffer<size> & buf ABSL_ATTRIBUTE_LIFETIME_BOUND)357 AlphaNum( // NOLINT(runtime/explicit)
358 const strings_internal::AlphaNumBuffer<size>& buf
359 ABSL_ATTRIBUTE_LIFETIME_BOUND)
360 : piece_(&buf.data[0], buf.size) {}
361
AlphaNum(absl::Nullable<const char * > c_str ABSL_ATTRIBUTE_LIFETIME_BOUND)362 AlphaNum(absl::Nullable<const char*> c_str // NOLINT(runtime/explicit)
363 ABSL_ATTRIBUTE_LIFETIME_BOUND)
364 : piece_(NullSafeStringView(c_str)) {}
AlphaNum(absl::string_view pc ABSL_ATTRIBUTE_LIFETIME_BOUND)365 AlphaNum(absl::string_view pc // NOLINT(runtime/explicit)
366 ABSL_ATTRIBUTE_LIFETIME_BOUND)
367 : piece_(pc) {}
368
369 #if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW)
AlphaNum(std::string_view pc ABSL_ATTRIBUTE_LIFETIME_BOUND)370 AlphaNum(std::string_view pc // NOLINT(runtime/explicit)
371 ABSL_ATTRIBUTE_LIFETIME_BOUND)
372 : piece_(pc.data(), pc.size()) {}
373 #endif // !ABSL_USES_STD_STRING_VIEW
374
375 template <typename T, typename = typename std::enable_if<
376 HasAbslStringify<T>::value>::type>
377 AlphaNum( // NOLINT(runtime/explicit)
378 const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
379 strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
piece_(strings_internal::ExtractStringification (sink,v))380 : piece_(strings_internal::ExtractStringification(sink, v)) {}
381
382 template <typename Allocator>
AlphaNum(const std::basic_string<char,std::char_traits<char>,Allocator> & str ABSL_ATTRIBUTE_LIFETIME_BOUND)383 AlphaNum( // NOLINT(runtime/explicit)
384 const std::basic_string<char, std::char_traits<char>, Allocator>& str
385 ABSL_ATTRIBUTE_LIFETIME_BOUND)
386 : piece_(str) {}
387
388 // Use string literals ":" instead of character literals ':'.
389 AlphaNum(char c) = delete; // NOLINT(runtime/explicit)
390
391 AlphaNum(const AlphaNum&) = delete;
392 AlphaNum& operator=(const AlphaNum&) = delete;
393
size()394 absl::string_view::size_type size() const { return piece_.size(); }
data()395 absl::Nullable<const char*> data() const { return piece_.data(); }
Piece()396 absl::string_view Piece() const { return piece_; }
397
398 // Match unscoped enums. Use integral promotion so that a `char`-backed
399 // enum becomes a wider integral type AlphaNum will accept.
400 template <typename T,
401 typename = typename std::enable_if<
402 std::is_enum<T>{} && std::is_convertible<T, int>{} &&
403 !HasAbslStringify<T>::value>::type>
404 AlphaNum(T e) // NOLINT(runtime/explicit)
405 : AlphaNum(+e) {}
406
407 // This overload matches scoped enums. We must explicitly cast to the
408 // underlying type, but use integral promotion for the same reason as above.
409 template <typename T,
410 typename std::enable_if<std::is_enum<T>{} &&
411 !std::is_convertible<T, int>{} &&
412 !HasAbslStringify<T>::value,
413 char*>::type = nullptr>
414 AlphaNum(T e) // NOLINT(runtime/explicit)
415 : AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
416
417 // vector<bool>::reference and const_reference require special help to
418 // convert to `AlphaNum` because it requires two user defined conversions.
419 template <
420 typename T,
421 typename std::enable_if<
422 std::is_class<T>::value &&
423 (std::is_same<T, std::vector<bool>::reference>::value ||
424 std::is_same<T, std::vector<bool>::const_reference>::value)>::type* =
425 nullptr>
AlphaNum(T e)426 AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {} // NOLINT(runtime/explicit)
427
428 private:
429 absl::string_view piece_;
430 char digits_[numbers_internal::kFastToBufferSize];
431 };
432
433 // -----------------------------------------------------------------------------
434 // StrCat()
435 // -----------------------------------------------------------------------------
436 //
437 // Merges given strings or numbers, using no delimiter(s), returning the merged
438 // result as a string.
439 //
440 // `StrCat()` is designed to be the fastest possible way to construct a string
441 // out of a mix of raw C strings, string_views, strings, bool values,
442 // and numeric values.
443 //
444 // Don't use `StrCat()` for user-visible strings. The localization process
445 // works poorly on strings built up out of fragments.
446 //
447 // For clarity and performance, don't use `StrCat()` when appending to a
448 // string. Use `StrAppend()` instead. In particular, avoid using any of these
449 // (anti-)patterns:
450 //
451 // str.append(StrCat(...))
452 // str += StrCat(...)
453 // str = StrCat(str, ...)
454 //
455 // The last case is the worst, with a potential to change a loop
456 // from a linear time operation with O(1) dynamic allocations into a
457 // quadratic time operation with O(n) dynamic allocations.
458 //
459 // See `StrAppend()` below for more information.
460
461 namespace strings_internal {
462
463 // Do not call directly - this is not part of the public API.
464 std::string CatPieces(std::initializer_list<absl::string_view> pieces);
465 void AppendPieces(absl::Nonnull<std::string*> dest,
466 std::initializer_list<absl::string_view> pieces);
467
468 template <typename Integer>
IntegerToString(Integer i)469 std::string IntegerToString(Integer i) {
470 // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
471 // with 22 bytes (including NULL at the end).
472 constexpr size_t kMaxDigits10 = 22;
473 std::string result;
474 strings_internal::STLStringResizeUninitialized(&result, kMaxDigits10);
475 char* start = &result[0];
476 // note: this can be optimized to not write last zero.
477 char* end = numbers_internal::FastIntToBuffer(i, start);
478 auto size = static_cast<size_t>(end - start);
479 assert((size < result.size()) &&
480 "StrCat(Integer) does not fit into kMaxDigits10");
481 result.erase(size);
482 return result;
483 }
484 template <typename Float>
FloatToString(Float f)485 std::string FloatToString(Float f) {
486 std::string result;
487 strings_internal::STLStringResizeUninitialized(
488 &result, numbers_internal::kSixDigitsToBufferSize);
489 char* start = &result[0];
490 result.erase(numbers_internal::SixDigitsToBuffer(f, start));
491 return result;
492 }
493
494 // `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
495 // (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
496 // and int64_t, then at least one of the three (`int` / `long` / `long long`)
497 // would have been ambiguous when passed to `SingleArgStrCat`.
SingleArgStrCat(int x)498 inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
SingleArgStrCat(unsigned int x)499 inline std::string SingleArgStrCat(unsigned int x) {
500 return IntegerToString(x);
501 }
502 // NOLINTNEXTLINE
SingleArgStrCat(long x)503 inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
504 // NOLINTNEXTLINE
SingleArgStrCat(unsigned long x)505 inline std::string SingleArgStrCat(unsigned long x) {
506 return IntegerToString(x);
507 }
508 // NOLINTNEXTLINE
SingleArgStrCat(long long x)509 inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
510 // NOLINTNEXTLINE
SingleArgStrCat(unsigned long long x)511 inline std::string SingleArgStrCat(unsigned long long x) {
512 return IntegerToString(x);
513 }
SingleArgStrCat(float x)514 inline std::string SingleArgStrCat(float x) { return FloatToString(x); }
SingleArgStrCat(double x)515 inline std::string SingleArgStrCat(double x) { return FloatToString(x); }
516
517 // As of September 2023, the SingleArgStrCat() optimization is only enabled for
518 // libc++. The reasons for this are:
519 // 1) The SSO size for libc++ is 23, while libstdc++ and MSSTL have an SSO size
520 // of 15. Since IntegerToString unconditionally resizes the string to 22 bytes,
521 // this causes both libstdc++ and MSSTL to allocate.
522 // 2) strings_internal::STLStringResizeUninitialized() only has an
523 // implementation that avoids initialization when using libc++. This isn't as
524 // relevant as (1), and the cost should be benchmarked if (1) ever changes on
525 // libstc++ or MSSTL.
526 #ifdef _LIBCPP_VERSION
527 #define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE true
528 #else
529 #define ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE false
530 #endif
531
532 template <typename T, typename = std::enable_if_t<
533 ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE &&
534 std::is_arithmetic<T>{} && !std::is_same<T, char>{}>>
535 using EnableIfFastCase = T;
536
537 #undef ABSL_INTERNAL_STRCAT_ENABLE_FAST_CASE
538
539 } // namespace strings_internal
540
StrCat()541 [[nodiscard]] inline std::string StrCat() { return std::string(); }
542
543 template <typename T>
StrCat(strings_internal::EnableIfFastCase<T> a)544 [[nodiscard]] inline std::string StrCat(
545 strings_internal::EnableIfFastCase<T> a) {
546 return strings_internal::SingleArgStrCat(a);
547 }
StrCat(const AlphaNum & a)548 [[nodiscard]] inline std::string StrCat(const AlphaNum& a) {
549 return std::string(a.data(), a.size());
550 }
551
552 [[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b);
553 [[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b,
554 const AlphaNum& c);
555 [[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b,
556 const AlphaNum& c, const AlphaNum& d);
557
558 // Support 5 or more arguments
559 template <typename... AV>
StrCat(const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)560 [[nodiscard]] inline std::string StrCat(const AlphaNum& a, const AlphaNum& b,
561 const AlphaNum& c, const AlphaNum& d,
562 const AlphaNum& e, const AV&... args) {
563 return strings_internal::CatPieces(
564 {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
565 static_cast<const AlphaNum&>(args).Piece()...});
566 }
567
568 // -----------------------------------------------------------------------------
569 // StrAppend()
570 // -----------------------------------------------------------------------------
571 //
572 // Appends a string or set of strings to an existing string, in a similar
573 // fashion to `StrCat()`.
574 //
575 // WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
576 // a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
577 // not try to check each of its input arguments to be sure that they are not
578 // a subset of the string being appended to. That is, while this will work:
579 //
580 // std::string s = "foo";
581 // s += s;
582 //
583 // This output is undefined:
584 //
585 // std::string s = "foo";
586 // StrAppend(&s, s);
587 //
588 // This output is undefined as well, since `absl::string_view` does not own its
589 // data:
590 //
591 // std::string s = "foobar";
592 // absl::string_view p = s;
593 // StrAppend(&s, p);
594
StrAppend(absl::Nonnull<std::string * >)595 inline void StrAppend(absl::Nonnull<std::string*>) {}
596 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a);
597 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
598 const AlphaNum& b);
599 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
600 const AlphaNum& b, const AlphaNum& c);
601 void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
602 const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
603
604 // Support 5 or more arguments
605 template <typename... AV>
StrAppend(absl::Nonnull<std::string * > dest,const AlphaNum & a,const AlphaNum & b,const AlphaNum & c,const AlphaNum & d,const AlphaNum & e,const AV &...args)606 inline void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
607 const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
608 const AlphaNum& e, const AV&... args) {
609 strings_internal::AppendPieces(
610 dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
611 static_cast<const AlphaNum&>(args).Piece()...});
612 }
613
614 // Helper function for the future StrCat default floating-point format, %.6g
615 // This is fast.
616 inline strings_internal::AlphaNumBuffer<
617 numbers_internal::kSixDigitsToBufferSize>
SixDigits(double d)618 SixDigits(double d) {
619 strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
620 result;
621 result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
622 return result;
623 }
624
625 ABSL_NAMESPACE_END
626 } // namespace absl
627
628 #endif // ABSL_STRINGS_STR_CAT_H_
629