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