tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

str_cat.h (24271B)


      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)
    196      : 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)
    202      : 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)
    208      : 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)
    214      : Hex(spec, static_cast<uint64_t>(v)) {}
    215  template <typename Pointee>
    216  explicit Hex(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad)
    217      : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
    218 
    219  template <typename S>
    220  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:
    241  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>
    275  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 
    326  AlphaNum(int x)  // NOLINT(runtime/explicit)
    327      : piece_(digits_, static_cast<size_t>(
    328                            numbers_internal::FastIntToBuffer(x, digits_) -
    329                            &digits_[0])) {}
    330  AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
    331      : piece_(digits_, static_cast<size_t>(
    332                            numbers_internal::FastIntToBuffer(x, digits_) -
    333                            &digits_[0])) {}
    334  AlphaNum(long x)  // NOLINT(*)
    335      : piece_(digits_, static_cast<size_t>(
    336                            numbers_internal::FastIntToBuffer(x, digits_) -
    337                            &digits_[0])) {}
    338  AlphaNum(unsigned long x)  // NOLINT(*)
    339      : piece_(digits_, static_cast<size_t>(
    340                            numbers_internal::FastIntToBuffer(x, digits_) -
    341                            &digits_[0])) {}
    342  AlphaNum(long long x)  // NOLINT(*)
    343      : piece_(digits_, static_cast<size_t>(
    344                            numbers_internal::FastIntToBuffer(x, digits_) -
    345                            &digits_[0])) {}
    346  AlphaNum(unsigned long long x)  // NOLINT(*)
    347      : piece_(digits_, static_cast<size_t>(
    348                            numbers_internal::FastIntToBuffer(x, digits_) -
    349                            &digits_[0])) {}
    350 
    351  AlphaNum(float f)  // NOLINT(runtime/explicit)
    352      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
    353  AlphaNum(double f)  // NOLINT(runtime/explicit)
    354      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
    355 
    356  template <size_t size>
    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 
    362  AlphaNum(absl::Nullable<const char*> c_str  // NOLINT(runtime/explicit)
    363               ABSL_ATTRIBUTE_LIFETIME_BOUND)
    364      : piece_(NullSafeStringView(c_str)) {}
    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)
    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 = {})
    380      : piece_(strings_internal::ExtractStringification(sink, v)) {}
    381 
    382  template <typename Allocator>
    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 
    394  absl::string_view::size_type size() const { return piece_.size(); }
    395  absl::Nullable<const char*> data() const { return piece_.data(); }
    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>
    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>
    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>
    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`.
    498 inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
    499 inline std::string SingleArgStrCat(unsigned int x) {
    500  return IntegerToString(x);
    501 }
    502 // NOLINTNEXTLINE
    503 inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
    504 // NOLINTNEXTLINE
    505 inline std::string SingleArgStrCat(unsigned long x) {
    506  return IntegerToString(x);
    507 }
    508 // NOLINTNEXTLINE
    509 inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
    510 // NOLINTNEXTLINE
    511 inline std::string SingleArgStrCat(unsigned long long x) {
    512  return IntegerToString(x);
    513 }
    514 inline std::string SingleArgStrCat(float x) { return FloatToString(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 
    541 [[nodiscard]] inline std::string StrCat() { return std::string(); }
    542 
    543 template <typename T>
    544 [[nodiscard]] inline std::string StrCat(
    545    strings_internal::EnableIfFastCase<T> a) {
    546  return strings_internal::SingleArgStrCat(a);
    547 }
    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>
    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 
    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>
    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>
    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_