Pass `absl::string_view` pieces across the `StrCat` and `StrAppend` call boundary to eliminate `AlphaNum` stack frames.

Previously, the 2-, 3-, and 4-argument overloads of `absl::StrCat` and the 1- to 4-argument overloads of `absl::StrAppend` took `const AlphaNum&` across an out-of-line call boundary, while 1-argument `StrCat(const AlphaNum&)` constructed `std::string` inline at every call site. Passing `const AlphaNum&` across a non-inline boundary forced callers to materialize a 48-byte `AlphaNum` stack slot (including the unused 32-byte `digits_` scratch buffer) even for string literals, `absl::string_view`, `std::string`, and `bool` arguments.

Make the 1- to 4-argument `StrCat` and `StrAppend` overloads lightweight inline wrappers that extract `a.Piece()` in the caller and delegate to `ABSL_ATTRIBUTE_NOINLINE` `strings_internal::CatPiece`, `CatPieces`, and `AppendPieces` helpers:
- 1, 2, and 3 arguments pass `absl::string_view` directly by value so the first two views pass purely in general-purpose registers with zero memory stores and the third uses at most 16 bytes on x86-64 (and registers on AArch64).
- 4 or more arguments forward a compact `std::initializer_list<absl::string_view>` (16 bytes per element instead of 48 bytes), avoiding double stack copies for stack-passed aggregate arguments on x86-64 and sharing a single out-of-line loop implementation.

Because `AlphaNum` temporaries no longer escape across the call boundary, LLVM SROA eliminates all unused 32-byte `digits_` stack allocations at both pure-string and mixed string-and-number call sites. Static ThinLTO release builds reduce `.text` size by an additional 13.0 KB to 16.8 KB across single test binaries (bringing cumulative savings across the stack to 21.2 KB to 31.4 KB per binary).

PiperOrigin-RevId: 1003929475
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index b5c7e19..e86e534 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -14,13 +14,17 @@
 
 #include "absl/strings/ascii.h"
 
+#include <algorithm>
 #include <climits>
 #include <cstddef>
 #include <cstring>
 #include <string>
+#include <utility>
 
 #include "absl/base/config.h"
 #include "absl/base/nullability.h"
+#include "absl/strings/resize_and_overwrite.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -227,6 +231,18 @@
   return AsciiStrCaseFold<true>(dst, src, n);
 }
 
+std::string AsciiStrToLower(std::string&& s) {
+  std::string result = std::move(s);
+  absl::AsciiStrToLower(&result);
+  return result;
+}
+
+std::string AsciiStrToUpper(std::string&& s) {
+  std::string result = std::move(s);
+  absl::AsciiStrToUpper(&result);
+  return result;
+}
+
 static constexpr size_t ValidateAsciiCasefold() {
   constexpr size_t num_chars = 1 + CHAR_MAX - CHAR_MIN;
   size_t incorrect_index = 0;
@@ -258,11 +274,58 @@
   return ascii_internal::AsciiStrCaseFold<false>(p, p, s->size());
 }
 
+std::string AsciiStrToLower(absl::string_view s) {
+  std::string result;
+  StringResizeAndOverwrite(result, s.size(), [s](char* buf, size_t buf_size) {
+    ascii_internal::AsciiStrCaseFold<false>(buf, s.data(), s.size());
+    return buf_size;
+  });
+  return result;
+}
+
 void AsciiStrToUpper(std::string* absl_nonnull s) {
   char* p = &(*s)[0];
   return ascii_internal::AsciiStrCaseFold<true>(p, p, s->size());
 }
 
+std::string AsciiStrToUpper(absl::string_view s) {
+  std::string result;
+  StringResizeAndOverwrite(result, s.size(), [s](char* buf, size_t buf_size) {
+    ascii_internal::AsciiStrCaseFold<true>(buf, s.data(), s.size());
+    return buf_size;
+  });
+  return result;
+}
+
+absl::string_view StripLeadingAsciiWhitespace(absl::string_view str) {
+  auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace);
+  return str.substr(static_cast<size_t>(it - str.begin()));
+}
+
+void StripLeadingAsciiWhitespace(std::string* absl_nonnull str) {
+  auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
+  str->erase(str->begin(), it);
+}
+
+absl::string_view StripTrailingAsciiWhitespace(absl::string_view str) {
+  auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace);
+  return str.substr(0, static_cast<size_t>(str.rend() - it));
+}
+
+void StripTrailingAsciiWhitespace(std::string* absl_nonnull str) {
+  auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
+  str->erase(static_cast<size_t>(str->rend() - it));
+}
+
+absl::string_view StripAsciiWhitespace(absl::string_view str) {
+  return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str));
+}
+
+void StripAsciiWhitespace(std::string* absl_nonnull str) {
+  StripTrailingAsciiWhitespace(str);
+  StripLeadingAsciiWhitespace(str);
+}
+
 void RemoveExtraAsciiWhitespace(std::string* absl_nonnull str) {
   auto stripped = StripAsciiWhitespace(*str);
 
diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h
index d6252d6..5989f9f 100644
--- a/absl/strings/ascii.h
+++ b/absl/strings/ascii.h
@@ -82,6 +82,10 @@
 void AsciiStrToUpper(char* absl_nonnull dst, const char* absl_nullable src,
                      size_t n);
 
+ABSL_ATTRIBUTE_NOINLINE std::string AsciiStrToLower(std::string&& s);
+
+ABSL_ATTRIBUTE_NOINLINE std::string AsciiStrToUpper(std::string&& s);
+
 }  // namespace ascii_internal
 
 // ascii_isalpha()
@@ -185,26 +189,18 @@
 }
 
 // Converts the characters in `s` to lowercase, changing the contents of `s`.
-void AsciiStrToLower(std::string* absl_nonnull s);
+ABSL_ATTRIBUTE_NOINLINE void AsciiStrToLower(std::string* absl_nonnull s);
 
 // Creates a lowercase string from a given absl::string_view.
-[[nodiscard]] inline std::string AsciiStrToLower(absl::string_view s) {
-  std::string result;
-  StringResizeAndOverwrite(result, s.size(), [s](char* buf, size_t buf_size) {
-    ascii_internal::AsciiStrToLower(buf, s.data(), s.size());
-    return buf_size;
-  });
-  return result;
-}
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE std::string AsciiStrToLower(
+    absl::string_view s);
 
 // Creates a lowercase string from a given std::string&&.
 //
 // (Template is used to lower priority of this overload.)
 template <int&... DoNotSpecify>
 [[nodiscard]] inline std::string AsciiStrToLower(std::string&& s) {
-  std::string result = std::move(s);
-  absl::AsciiStrToLower(&result);
-  return result;
+  return ascii_internal::AsciiStrToLower(std::move(s));
 }
 
 // ascii_toupper()
@@ -216,71 +212,52 @@
 }
 
 // Converts the characters in `s` to uppercase, changing the contents of `s`.
-void AsciiStrToUpper(std::string* absl_nonnull s);
+ABSL_ATTRIBUTE_NOINLINE void AsciiStrToUpper(std::string* absl_nonnull s);
 
 // Creates an uppercase string from a given absl::string_view.
-[[nodiscard]] inline std::string AsciiStrToUpper(absl::string_view s) {
-  std::string result;
-  StringResizeAndOverwrite(result, s.size(), [s](char* buf, size_t buf_size) {
-    ascii_internal::AsciiStrToUpper(buf, s.data(), s.size());
-    return buf_size;
-  });
-  return result;
-}
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE std::string AsciiStrToUpper(
+    absl::string_view s);
 
 // Creates an uppercase string from a given std::string&&.
 //
 // (Template is used to lower priority of this overload.)
 template <int&... DoNotSpecify>
 [[nodiscard]] inline std::string AsciiStrToUpper(std::string&& s) {
-  std::string result = std::move(s);
-  absl::AsciiStrToUpper(&result);
-  return result;
+  return ascii_internal::AsciiStrToUpper(std::move(s));
 }
 
 // Returns absl::string_view with whitespace stripped from the beginning of the
 // given string_view.
-[[nodiscard]] inline absl::string_view StripLeadingAsciiWhitespace(
-    absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND) {
-  auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace);
-  return str.substr(static_cast<size_t>(it - str.begin()));
-}
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE absl::string_view
+StripLeadingAsciiWhitespace(
+    absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND);
 
 // Strips in place whitespace from the beginning of the given string.
-inline void StripLeadingAsciiWhitespace(std::string* absl_nonnull str) {
-  auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
-  str->erase(str->begin(), it);
-}
+ABSL_ATTRIBUTE_NOINLINE void StripLeadingAsciiWhitespace(
+    std::string* absl_nonnull str);
 
 // Returns absl::string_view with whitespace stripped from the end of the given
 // string_view.
-[[nodiscard]] inline absl::string_view StripTrailingAsciiWhitespace(
-    absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND) {
-  auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace);
-  return str.substr(0, static_cast<size_t>(str.rend() - it));
-}
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE absl::string_view
+StripTrailingAsciiWhitespace(
+    absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND);
 
 // Strips in place whitespace from the end of the given string
-inline void StripTrailingAsciiWhitespace(std::string* absl_nonnull str) {
-  auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
-  str->erase(static_cast<size_t>(str->rend() - it));
-}
+ABSL_ATTRIBUTE_NOINLINE void StripTrailingAsciiWhitespace(
+    std::string* absl_nonnull str);
 
 // Returns absl::string_view with whitespace stripped from both ends of the
 // given string_view.
-[[nodiscard]] inline absl::string_view StripAsciiWhitespace(
-    absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND) {
-  return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str));
-}
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE absl::string_view StripAsciiWhitespace(
+    absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND);
 
 // Strips in place whitespace from both ends of the given string
-inline void StripAsciiWhitespace(std::string* absl_nonnull str) {
-  StripTrailingAsciiWhitespace(str);
-  StripLeadingAsciiWhitespace(str);
-}
+ABSL_ATTRIBUTE_NOINLINE void StripAsciiWhitespace(
+    std::string* absl_nonnull str);
 
 // Removes leading, trailing, and consecutive internal whitespace.
-void RemoveExtraAsciiWhitespace(std::string* absl_nonnull str);
+ABSL_ATTRIBUTE_NOINLINE void RemoveExtraAsciiWhitespace(
+    std::string* absl_nonnull str);
 
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index efe9041..ebebfe9 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -979,7 +979,7 @@
 
   bool ok;
   StringResizeAndOverwrite(
-      *dest, dest_len, [src, slen, unbase64, &ok](char* buf, size_t buf_size) {
+      *dest, dest_len, [src, slen, &unbase64, &ok](char* buf, size_t buf_size) {
         size_t len;
         ok = Base64UnescapeInternal(src, slen, buf, buf_size, unbase64, &len);
         if (!ok) {
diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h
index 66ff22c..1502ff1 100644
--- a/absl/strings/escaping.h
+++ b/absl/strings/escaping.h
@@ -94,7 +94,7 @@
 //   std::string s = "foo\rbar\tbaz\010\011\012\013\014\x0d\n";
 //   std::string escaped_s = absl::CEscape(s);
 //   EXPECT_EQ(escaped_s, "foo\\rbar\\tbaz\\010\\t\\n\\013\\014\\r\\n");
-std::string CEscape(absl::string_view src);
+ABSL_ATTRIBUTE_NOINLINE std::string CEscape(absl::string_view src);
 
 // CHexEscape()
 //
@@ -107,7 +107,7 @@
 //   std::string s = "foo\rbar\tbaz\010\011\012\013\014\x0d\n";
 //   std::string escaped_s = absl::CHexEscape(s);
 //   EXPECT_EQ(escaped_s, "foo\\rbar\\tbaz\\x08\\t\\n\\x0b\\x0c\\r\\n");
-std::string CHexEscape(absl::string_view src);
+ABSL_ATTRIBUTE_NOINLINE std::string CHexEscape(absl::string_view src);
 
 // Utf8SafeCEscape()
 //
@@ -115,21 +115,21 @@
 // octal sequences, and passing through UTF-8 characters without conversion.
 // I.e., when encountering any bytes with their high bit set, this function
 // will not escape those values, whether or not they are valid UTF-8.
-std::string Utf8SafeCEscape(absl::string_view src);
+ABSL_ATTRIBUTE_NOINLINE std::string Utf8SafeCEscape(absl::string_view src);
 
 // Utf8SafeCHexEscape()
 //
 // Escapes a `src` string using C-style escape sequences, escaping bytes as
 // hexadecimal sequences, and passing through UTF-8 characters without
 // conversion.
-std::string Utf8SafeCHexEscape(absl::string_view src);
+ABSL_ATTRIBUTE_NOINLINE std::string Utf8SafeCHexEscape(absl::string_view src);
 
 // Base64Escape()
 //
 // Encodes a `src` string into a base64-encoded `dest` string with padding
 // characters. This function conforms with RFC 4648 section 4 (base64) and RFC
 // 2045.
-std::string Base64Escape(absl::string_view src);
+ABSL_ATTRIBUTE_NOINLINE std::string Base64Escape(absl::string_view src);
 [[deprecated(
     "Use the string-returning version of "
     "Base64Escape()")]] ABSL_REFACTOR_INLINE inline void
@@ -142,7 +142,7 @@
 // Encodes a `src` string into a base64 string, like Base64Escape() does, but
 // outputs '-' instead of '+' and '_' instead of '/', and does not pad `dest`.
 // This function conforms with RFC 4648 section 5 (base64url).
-std::string WebSafeBase64Escape(absl::string_view src);
+ABSL_ATTRIBUTE_NOINLINE std::string WebSafeBase64Escape(absl::string_view src);
 [[deprecated(
     "Use the string-returning version of "
     "WebSafeBase64Escape()")]] ABSL_REFACTOR_INLINE inline void
@@ -157,7 +157,8 @@
 // `src` contains invalid characters, `dest` is cleared and returns `false`.
 // If padding is included (note that `Base64Escape()` does produce it), it must
 // be correct. In the padding, '=' and '.' are treated identically.
-bool Base64Unescape(absl::string_view src, std::string* absl_nonnull dest);
+ABSL_ATTRIBUTE_NOINLINE bool Base64Unescape(absl::string_view src,
+                                            std::string* absl_nonnull dest);
 
 // WebSafeBase64Unescape()
 //
@@ -167,8 +168,8 @@
 // `false`. If padding is included (note that `WebSafeBase64Escape()` does not
 // produce it), it must be correct. In the padding, '=' and '.' are treated
 // identically.
-bool WebSafeBase64Unescape(absl::string_view src,
-                           std::string* absl_nonnull dest);
+ABSL_ATTRIBUTE_NOINLINE bool WebSafeBase64Unescape(
+    absl::string_view src, std::string* absl_nonnull dest);
 
 // HexStringToBytes()
 //
@@ -185,13 +186,13 @@
 // `from.size()/2`. The input must be valid hexadecimal data, otherwise the
 // return value is unspecified.
 [[deprecated("Use the HexStringToBytes() that returns a bool")]]
-std::string HexStringToBytes(absl::string_view from);
+ABSL_ATTRIBUTE_NOINLINE std::string HexStringToBytes(absl::string_view from);
 
 // BytesToHexString()
 //
 // Converts binary data into an ASCII text string, returning a string of size
 // `2*from.size()`.
-std::string BytesToHexString(absl::string_view from);
+ABSL_ATTRIBUTE_NOINLINE std::string BytesToHexString(absl::string_view from);
 
 // UrlEscape()
 //
@@ -211,7 +212,8 @@
 //                                     absl::UrlEscape("gift for mom & dad"));
 //   assert(url ==
 //     "https://www.google.com/search?q=gift%20for%20mom%20%26%20dad");
-[[nodiscard]] std::string UrlEscape(absl::string_view input);
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE std::string UrlEscape(
+    absl::string_view input);
 
 // UrlUnescape()
 //
@@ -222,7 +224,8 @@
 //
 // UrlUnescape() is identical to UrlUnescapePlus() except that it does not
 // unescape '+' to ' '.
-[[nodiscard]] std::optional<std::string> UrlUnescape(absl::string_view input);
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE std::optional<std::string> UrlUnescape(
+    absl::string_view input);
 
 // UrlEscapePlus()
 //
@@ -252,14 +255,15 @@
 //                                     absl::UrlEscapePlus(
 //                                         "gift for mom & dad"));
 //   assert(url == "https://www.google.com/search?q=gift+for+mom+%26+dad");
-[[nodiscard]] std::string UrlEscapePlus(absl::string_view input);
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE std::string UrlEscapePlus(
+    absl::string_view input);
 
 // UrlUnescapePlus()
 //
 // Performs the inverse transformation of UrlEscapePlus(). It returns
 // `std::nullopt` if any % is not followed by two hexadecimal digits.
-[[nodiscard]] std::optional<std::string> UrlUnescapePlus(
-    absl::string_view input);
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE std::optional<std::string>
+UrlUnescapePlus(absl::string_view input);
 
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/internal/ostringstream.cc b/absl/strings/internal/ostringstream.cc
index 5ac41cc..e848c15 100644
--- a/absl/strings/internal/ostringstream.cc
+++ b/absl/strings/internal/ostringstream.cc
@@ -17,7 +17,10 @@
 #include <cassert>
 #include <cstddef>
 #include <ios>
+#include <ostream>
 #include <streambuf>
+#include <string>
+#include <utility>
 
 #include "absl/base/config.h"
 
@@ -25,6 +28,26 @@
 ABSL_NAMESPACE_BEGIN
 namespace strings_internal {
 
+OStringStream::OStringStream(std::string* str)
+    : std::ostream(&buf_), buf_(str) {}
+
+OStringStream::OStringStream(OStringStream&& that)
+    : std::ostream(std::move(static_cast<std::ostream&>(that))),
+      buf_(that.buf_) {
+  rdbuf(&buf_);
+}
+
+OStringStream& OStringStream::operator=(OStringStream&& that) {
+  std::ostream::operator=(std::move(static_cast<std::ostream&>(that)));
+  buf_ = that.buf_;
+  rdbuf(&buf_);
+  return *this;
+}
+
+OStringStream::~OStringStream() = default;
+
+OStringStream::Streambuf::~Streambuf() = default;
+
 OStringStream::Streambuf::int_type OStringStream::Streambuf::overflow(int c) {
   assert(str_);
   if (!std::streambuf::traits_type::eq_int_type(
diff --git a/absl/strings/internal/ostringstream.h b/absl/strings/internal/ostringstream.h
index c0e237d..1139baf 100644
--- a/absl/strings/internal/ostringstream.h
+++ b/absl/strings/internal/ostringstream.h
@@ -20,8 +20,8 @@
 #include <ostream>
 #include <streambuf>
 #include <string>
-#include <utility>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 
 namespace absl {
@@ -69,19 +69,10 @@
   //
   // The destructor of OStringStream doesn't use the std::string. It's OK to
   // destroy the std::string before the stream.
-  explicit OStringStream(std::string* str)
-      : std::ostream(&buf_), buf_(str) {}
-  OStringStream(OStringStream&& that)
-      : std::ostream(std::move(static_cast<std::ostream&>(that))),
-        buf_(that.buf_) {
-    rdbuf(&buf_);
-  }
-  OStringStream& operator=(OStringStream&& that) {
-    std::ostream::operator=(std::move(static_cast<std::ostream&>(that)));
-    buf_ = that.buf_;
-    rdbuf(&buf_);
-    return *this;
-  }
+  ABSL_ATTRIBUTE_NOINLINE explicit OStringStream(std::string* str);
+  ABSL_ATTRIBUTE_NOINLINE OStringStream(OStringStream&& that);
+  ABSL_ATTRIBUTE_NOINLINE OStringStream& operator=(OStringStream&& that);
+  ABSL_ATTRIBUTE_NOINLINE ~OStringStream() override;
 
   std::string* str() { return buf_.str(); }
   const std::string* str() const { return buf_.str(); }
@@ -93,6 +84,7 @@
     explicit Streambuf(std::string* str) : str_(str) {}
     Streambuf(const Streambuf&) = default;
     Streambuf& operator=(const Streambuf&) = default;
+    ~Streambuf() override;
 
     std::string* str() { return str_; }
     const std::string* str() const { return str_; }
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 2125be3..ac43f41 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -172,14 +172,20 @@
 
   // The printed value including the '-' sign if available.
   // For inputs of value `0`, this will return "0"
-  string_view with_neg_and_zero() const { return {start_, size_}; }
+  string_view with_neg_and_zero() const {
+    const size_t len = size_;
+    ABSL_ASSUME(len <= sizeof(storage_));
+    return {start_, len};
+  }
 
   // The printed value not including the '-' sign.
   // For inputs of value `0`, this will return "".
   string_view without_neg_or_zero() const {
     static_assert('-' < '0', "The check below verifies both.");
-    size_t advance = start_[0] <= '0' ? 1 : 0;
-    return {start_ + advance, size_ - advance};
+    const size_t advance = start_[0] <= '0' ? 1 : 0;
+    const size_t len = size_ - advance;
+    ABSL_ASSUME(len <= sizeof(storage_));
+    return {start_ + advance, len};
   }
 
   bool is_negative() const { return start_[0] == '-'; }
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index 0e0b262..4387c20 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -24,6 +24,7 @@
 #include <string>
 
 #include "absl/base/config.h"
+#include "absl/base/optimization.h"
 #include "absl/strings/internal/str_format/output.h"
 #include "absl/strings/string_view.h"
 
@@ -70,7 +71,9 @@
   ~FormatSinkImpl() { Flush(); }
 
   void Flush() {
-    raw_.Write(string_view(buf_, static_cast<size_t>(pos_ - buf_)));
+    const size_t len = static_cast<size_t>(pos_ - buf_);
+    ABSL_ASSUME(len <= sizeof(buf_));
+    raw_.Write(string_view(buf_, len));
     pos_ = buf_;
   }
 
diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h
index 8f9c11a..56b71ed 100644
--- a/absl/strings/internal/str_join_internal.h
+++ b/absl/strings/internal/str_join_internal.h
@@ -41,6 +41,7 @@
 #include <tuple>
 #include <type_traits>
 #include <utility>
+#include <vector>
 
 #include "absl/algorithm/container.h"
 #include "absl/base/config.h"
@@ -229,44 +230,65 @@
 // string to avoid the need to resize while appending. To do this, the iterator
 // range will be traversed twice: once to calculate the total needed size, and
 // then again to copy the elements and delimiters to the output string.
+template <typename Iterator, typename Value>
+struct IsContiguousIteratorOf
+    : std::integral_constant<
+          bool,
+          (std::is_pointer_v<Iterator> &&
+           std::is_same_v<std::remove_cv_t<std::remove_pointer_t<Iterator>>,
+                          Value>) ||
+              std::is_same_v<Iterator, typename std::vector<Value>::iterator> ||
+              std::is_same_v<Iterator,
+                             typename std::vector<Value>::const_iterator>> {};
+
 template <typename Iterator,
           typename = std::enable_if_t<
               base_internal::IsAtLeastForwardIterator<Iterator>::value>>
 std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view sep,
                           NoFormatter) {
-  std::string result;
-  if (start != end) {
-    // Sums size
-    auto&& start_value = *start;
-    // Use uint64_t to prevent size_t overflow. We assume it is not possible for
-    // in memory strings to overflow a uint64_t.
-    uint64_t result_size = start_value.size();
-    for (Iterator it = start; ++it != end;) {
-      result_size += sep.size();
-      result_size += (*it).size();
-    }
+  if constexpr (IsContiguousIteratorOf<Iterator, absl::string_view>::value) {
+    const auto count = static_cast<size_t>(end - start);
+    return JoinStringViews(count == 0 ? nullptr : std::addressof(*start), count,
+                           sep);
+  } else if constexpr (IsContiguousIteratorOf<Iterator, std::string>::value) {
+    const auto count = static_cast<size_t>(end - start);
+    return JoinStrings(count == 0 ? nullptr : std::addressof(*start), count,
+                       sep);
+  } else {
+    std::string result;
+    if (start != end) {
+      // Sums size
+      auto&& start_value = *start;
+      // Use uint64_t to prevent size_t overflow. We assume it is not possible
+      // for in memory strings to overflow a uint64_t.
+      uint64_t result_size = start_value.size();
+      for (Iterator it = start; ++it != end;) {
+        result_size += sep.size();
+        result_size += (*it).size();
+      }
 
-    if (result_size > 0) {
-      constexpr uint64_t kMaxSize =
-          uint64_t{(std::numeric_limits<size_t>::max)()};
-      ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
+      if (result_size > 0) {
+        constexpr uint64_t kMaxSize =
+            uint64_t{(std::numeric_limits<size_t>::max)()};
+        ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
 
-      StringResizeAndOverwrite(
-          result, static_cast<size_t>(result_size),
-          [&start, &end, &start_value, sep](char* result_buf,
-                                            size_t result_buf_size) {
-            // Joins strings
-            result_buf = absl::c_copy(start_value, result_buf);
-            for (Iterator it = start; ++it != end;) {
-              result_buf = absl::c_copy(sep, result_buf);
-              auto&& value = *it;
-              result_buf = absl::c_copy(value, result_buf);
-            }
-            return result_buf_size;
-          });
+        StringResizeAndOverwrite(
+            result, static_cast<size_t>(result_size),
+            [&start, &end, &start_value, sep](char* result_buf,
+                                              size_t result_buf_size) {
+              // Joins strings
+              result_buf = absl::c_copy(start_value, result_buf);
+              for (Iterator it = start; ++it != end;) {
+                result_buf = absl::c_copy(sep, result_buf);
+                auto&& value = *it;
+                result_buf = absl::c_copy(value, result_buf);
+              }
+              return result_buf_size;
+            });
+      }
     }
+    return result;
   }
-  return result;
 }
 
 // JoinTupleLoop implements a loop over the elements of a std::tuple, which
diff --git a/absl/strings/internal/stringify_sink.cc b/absl/strings/internal/stringify_sink.cc
index c9f8258..d70874c 100644
--- a/absl/strings/internal/stringify_sink.cc
+++ b/absl/strings/internal/stringify_sink.cc
@@ -14,10 +14,14 @@
 
 #include "absl/strings/internal/stringify_sink.h"
 
+#include <array>
 #include <cstddef>
 
 #include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/source_location.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -29,6 +33,16 @@
   buffer_.append(v.data(), v.size());
 }
 
+void StringifySourceLocation(StringifySink& sink, SourceLocation l) {
+  sink.Append(l.file_name());
+  std::array<char, 1 + numbers_internal::kFastToBufferSize> buffer;
+  buffer[0] = ':';
+  char* end = numbers_internal::FastIntToBuffer(l.line(), buffer.data() + 1);
+  const size_t len = static_cast<size_t>(end - buffer.data());
+  ABSL_ASSUME(len <= sizeof(buffer));
+  sink.Append(absl::string_view(buffer.data(), len));
+}
+
 }  // namespace strings_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/internal/stringify_sink.h b/absl/strings/internal/stringify_sink.h
index e4e5f9e..c140343 100644
--- a/absl/strings/internal/stringify_sink.h
+++ b/absl/strings/internal/stringify_sink.h
@@ -21,7 +21,9 @@
 #include <type_traits>
 #include <utility>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
+#include "absl/base/optimization.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/source_location.h"
@@ -54,15 +56,24 @@
   return sink.buffer_;
 }
 
+ABSL_ATTRIBUTE_NOINLINE void StringifySourceLocation(StringifySink& sink,
+                                                     SourceLocation l);
+
 }  // namespace strings_internal
 
 template <typename Sink>
 void AbslStringify(Sink& sink, SourceLocation l) {
-  sink.Append(l.file_name());
-  sink.Append(":");
-  std::array<char, numbers_internal::kFastToBufferSize> buffer;
-  numbers_internal::FastIntToBuffer(l.line(), buffer.data());
-  sink.Append(buffer.data());
+  if constexpr (std::is_same_v<Sink, strings_internal::StringifySink>) {
+    strings_internal::StringifySourceLocation(sink, l);
+  } else {
+    sink.Append(l.file_name());
+    std::array<char, 1 + numbers_internal::kFastToBufferSize> buffer;
+    buffer[0] = ':';
+    char* end = numbers_internal::FastIntToBuffer(l.line(), buffer.data() + 1);
+    const size_t len = static_cast<size_t>(end - buffer.data());
+    ABSL_ASSUME(len <= sizeof(buffer));
+    sink.Append(absl::string_view(buffer.data(), len));
+  }
 }
 
 ABSL_NAMESPACE_END
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index b79078d..426fdaa 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -16,6 +16,7 @@
 
 #include <assert.h>
 
+#include <algorithm>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
@@ -25,10 +26,12 @@
 
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
+#include "absl/base/macros.h"
 #include "absl/base/nullability.h"
 #include "absl/base/optimization.h"
 #include "absl/base/throw_delegate.h"
 #include "absl/strings/internal/append_and_overwrite.h"
+#include "absl/strings/numbers.h"
 #include "absl/strings/resize_and_overwrite.h"
 #include "absl/strings/string_view.h"
 
@@ -43,11 +46,9 @@
 // ----------------------------------------------------------------------
 
 namespace {
-// Append is merely a version of memcpy that returns the address of the byte
-// after the area just overwritten.
-inline char* absl_nonnull Append(char* absl_nonnull out, const AlphaNum& x) {
-  // memcpy is allowed to overwrite arbitrary memory, so doing this after the
-  // call would force an extra fetch of x.size().
+// Copies a string_view into `out` and returns the byte past the copied region.
+inline char* absl_nonnull AppendPiece(char* absl_nonnull out,
+                                      absl::string_view x) {
   char* after = out + x.size();
   if (x.size() != 0) {
     memcpy(out, x.data(), x.size());
@@ -64,86 +65,238 @@
   return static_cast<size_t>(sum);
 }
 
-inline size_t SafeAdd(std::initializer_list<size_t> sizes) {
-  uint64_t sum = 0;
-  for (size_t size : sizes) {
-    sum += size;
-  }
+inline size_t SafeAdd(size_t a, size_t b, size_t c) {
+  const uint64_t sum = static_cast<uint64_t>(a) + b + c;
   if (ABSL_PREDICT_FALSE(sum > (std::numeric_limits<size_t>::max)())) {
     ThrowStdLengthError("absl string append length overflow");
   }
   return static_cast<size_t>(sum);
 }
 
+template <typename Integer>
+std::string IntegerToString(Integer i) {
+  // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
+  // with 22 bytes (including NULL at the end).
+  constexpr size_t kMaxDigits10 = 22;
+  std::string result;
+  StringResizeAndOverwrite(
+      result, kMaxDigits10, [i](char* start, size_t buf_size) {
+        // Note: This can be optimized to not write last zero.
+        char* end = numbers_internal::FastIntToBuffer(i, start);
+        auto size = static_cast<size_t>(end - start);
+        ABSL_ASSERT(size < buf_size);
+        ABSL_ASSUME(size < buf_size);
+        return size;
+      });
+  return result;
+}
+
+template <typename Float>
+std::string FloatToString(Float f) {
+  std::string result;
+  StringResizeAndOverwrite(result, numbers_internal::kSixDigitsToBufferSize,
+                           [f](char* start, size_t buf_size) {
+                             size_t size = numbers_internal::SixDigitsToBuffer(
+                                 static_cast<double>(f), start);
+                             ABSL_ASSERT(size < buf_size);
+                             ABSL_ASSUME(size < buf_size);
+                             return size;
+                           });
+  return result;
+}
+
+template <typename Element>
+std::string JoinContiguousImpl(const Element* absl_nullable data, size_t count,
+                               absl::string_view sep) {
+  std::string result;
+  if (count != 0 && data != nullptr) {
+    const Element* absl_nonnull elems = data;
+    // Use uint64_t to prevent size_t overflow. We assume it is not possible for
+    // in memory strings to overflow a uint64_t.
+    uint64_t result_size = elems[0].size();
+    for (size_t i = 1; i < count; ++i) {
+      result_size += sep.size();
+      result_size += elems[i].size();
+    }
+
+    if (result_size > 0) {
+      constexpr uint64_t kMaxSize =
+          uint64_t{(std::numeric_limits<size_t>::max)()};
+      ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
+
+      absl::StringResizeAndOverwrite(
+          result, static_cast<size_t>(result_size),
+          [elems, count, sep](char* result_buf, size_t result_buf_size) {
+            if (!elems[0].empty()) {
+              memcpy(result_buf, elems[0].data(), elems[0].size());
+              result_buf += elems[0].size();
+            }
+            for (size_t i = 1; i < count; ++i) {
+              if (!sep.empty()) {
+                memcpy(result_buf, sep.data(), sep.size());
+                result_buf += sep.size();
+              }
+              if (!elems[i].empty()) {
+                memcpy(result_buf, elems[i].data(), elems[i].size());
+                result_buf += elems[i].size();
+              }
+            }
+            return result_buf_size;
+          });
+    }
+  }
+  return result;
+}
+
 }  // namespace
 
-std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
+namespace strings_internal {
+
+absl::string_view FormatHex(Hex hex, char* absl_nonnull end) {
+  static_assert(numbers_internal::kFastToBufferSize >= 32,
+                "This function only works when output buffer >= 32 bytes long");
+  const size_t real_width =
+      absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
+  ABSL_ASSUME(real_width <= 16);
+  if (real_width >= hex.width) {
+    return absl::string_view(end - real_width, real_width);
+  }
+  // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
+  // max pad width can be up to 20.
+  std::memset(end - 32, hex.fill, 16);
+  // Patch up everything else up to the real_width.
+  std::memset(end - real_width - 16, hex.fill, 16);
+  const size_t width = hex.width;
+  ABSL_ASSUME(width <= numbers_internal::kFastToBufferSize);
+  return absl::string_view(end - width, width);
+}
+
+absl::string_view FormatDec(Dec dec, char* absl_nonnull end) {
+  assert(dec.width <= numbers_internal::kFastToBufferSize);
+  char* const minfill = end - dec.width;
+  char* writer = end;
+  uint64_t val = dec.value;
+  while (val > 9) {
+    *--writer = static_cast<char>('0' + (val % 10));
+    val /= 10;
+  }
+  *--writer = static_cast<char>('0' + val);
+  if (dec.neg) *--writer = '-';
+
+  ptrdiff_t fillers = writer - minfill;
+  if (fillers > 0) {
+    // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
+    // But...: if the fill character is '0', then it's <+/-><fill><digits>
+    bool add_sign_again = false;
+    if (dec.neg && dec.fill == '0') {  // If filling with '0',
+      ++writer;                        // ignore the sign we just added
+      add_sign_again = true;           // and re-add the sign later.
+    }
+    writer -= fillers;
+    std::fill_n(writer, fillers, dec.fill);
+    if (add_sign_again) *--writer = '-';
+  }
+
+  const size_t len = static_cast<size_t>(end - writer);
+  ABSL_ASSUME(len <= numbers_internal::kFastToBufferSize);
+  return absl::string_view(writer, len);
+}
+
+std::string JoinStringViews(const absl::string_view* absl_nullable data,
+                            size_t count, absl::string_view sep) {
+  return JoinContiguousImpl(data, count, sep);
+}
+
+std::string JoinStrings(const std::string* absl_nullable data, size_t count,
+                        absl::string_view sep) {
+  return JoinContiguousImpl(data, count, sep);
+}
+
+std::string SingleArgStrCat(bool x) {
+  return std::string(&"01"[static_cast<size_t>(x)], 1);
+}
+std::string SingleArgStrCat(int x) { return IntegerToString(x); }
+std::string SingleArgStrCat(unsigned int x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(unsigned long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(unsigned long long x) { return IntegerToString(x); }
+std::string SingleArgStrCat(float x) { return FloatToString(x); }
+std::string SingleArgStrCat(double x) { return FloatToString(x); }
+
+}  // namespace strings_internal
+
+strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
+HighPrecision(float f) {
+  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
+  result.size =
+      strlen(numbers_internal::RoundTripFloatToBuffer(f, &result.data[0]));
+  return result;
+}
+
+strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
+HighPrecision(double d) {
+  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
+  result.size =
+      strlen(numbers_internal::RoundTripDoubleToBuffer(d, &result.data[0]));
+  return result;
+}
+
+strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
+SixDigits(double d) {
+  strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
+      result;
+  result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
+  return result;
+}
+
+namespace strings_internal {
+
+std::string CatPiece(absl::string_view a) {
+  return std::string(a.data(), a.size());
+}
+
+std::string CatPieces(absl::string_view a, absl::string_view b) {
   std::string result;
-  // Use uint64_t to prevent size_t overflow. We assume it is not possible for
-  // in memory strings to overflow a uint64_t.
   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
   const uint64_t result_size =
       static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size());
   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
   absl::StringResizeAndOverwrite(result, static_cast<size_t>(result_size),
-                                 [&a, &b](char* const begin, size_t buf_size) {
+                                 [a, b](char* const begin, size_t buf_size) {
                                    char* out = begin;
-                                   out = Append(out, a);
-                                   out = Append(out, b);
+                                   out = AppendPiece(out, a);
+                                   out = AppendPiece(out, b);
                                    assert(out == begin + buf_size);
                                    return buf_size;
                                  });
   return result;
 }
 
-std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
+std::string CatPieces(absl::string_view a, absl::string_view b,
+                      absl::string_view c) {
   std::string result;
-  // Use uint64_t to prevent size_t overflow. We assume it is not possible for
-  // in memory strings to overflow a uint64_t.
   constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
   const uint64_t result_size = static_cast<uint64_t>(a.size()) +
                                static_cast<uint64_t>(b.size()) +
                                static_cast<uint64_t>(c.size());
   ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
-  absl::StringResizeAndOverwrite(
-      result, static_cast<size_t>(result_size),
-      [&a, &b, &c](char* const begin, size_t buf_size) {
-        char* out = begin;
-        out = Append(out, a);
-        out = Append(out, b);
-        out = Append(out, c);
-        assert(out == begin + buf_size);
-        return buf_size;
-      });
+  absl::StringResizeAndOverwrite(result, static_cast<size_t>(result_size),
+                                 [a, b, c](char* const begin, size_t buf_size) {
+                                   char* out = begin;
+                                   out = AppendPiece(out, a);
+                                   out = AppendPiece(out, b);
+                                   out = AppendPiece(out, c);
+                                   assert(out == begin + buf_size);
+                                   return buf_size;
+                                 });
   return result;
 }
 
-std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
-                   const AlphaNum& d) {
-  std::string result;
-  // Use uint64_t to prevent size_t overflow. We assume it is not possible for
-  // in memory strings to overflow a uint64_t.
-  constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
-  const uint64_t result_size =
-      static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size()) +
-      static_cast<uint64_t>(c.size()) + static_cast<uint64_t>(d.size());
-  ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
-  absl::StringResizeAndOverwrite(
-      result, static_cast<size_t>(result_size),
-      [&a, &b, &c, &d](char* const begin, size_t buf_size) {
-        char* out = begin;
-        out = Append(out, a);
-        out = Append(out, b);
-        out = Append(out, c);
-        out = Append(out, d);
-        assert(out == begin + buf_size);
-        return buf_size;
-      });
-  return result;
-}
-
-namespace strings_internal {
-
 // Do not call directly - these are not part of the public API.
 std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
   std::string result;
@@ -202,69 +355,50 @@
                            });
 }
 
-}  // namespace strings_internal
-
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a) {
+void AppendPieces(std::string* absl_nonnull dest, absl::string_view a) {
   ASSERT_NO_OVERLAP(*dest, a);
-  strings_internal::StringAppendAndOverwrite(
-      *dest, a.size(), [&a](char* const buf, size_t buf_size) {
-        char* out = buf;
-        out = Append(out, a);
-        assert(out == buf + buf_size);
-        return buf_size;
-      });
+  StringAppendAndOverwrite(*dest, a.size(),
+                           [a](char* const buf, size_t buf_size) {
+                             char* out = buf;
+                             out = AppendPiece(out, a);
+                             assert(out == buf + buf_size);
+                             return buf_size;
+                           });
 }
 
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
-               const AlphaNum& b) {
+void AppendPieces(std::string* absl_nonnull dest, absl::string_view a,
+                  absl::string_view b) {
   ASSERT_NO_OVERLAP(*dest, a);
   ASSERT_NO_OVERLAP(*dest, b);
-  strings_internal::StringAppendAndOverwrite(
+  StringAppendAndOverwrite(
       *dest, SafeAdd(a.size(), b.size()),
-      [&a, &b](char* const buf, size_t buf_size) {
+      [a, b](char* const buf, size_t buf_size) {
         char* out = buf;
-        out = Append(out, a);
-        out = Append(out, b);
+        out = AppendPiece(out, a);
+        out = AppendPiece(out, b);
         assert(out == buf + buf_size);
         return buf_size;
       });
 }
 
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
-               const AlphaNum& b, const AlphaNum& c) {
+void AppendPieces(std::string* absl_nonnull dest, absl::string_view a,
+                  absl::string_view b, absl::string_view c) {
   ASSERT_NO_OVERLAP(*dest, a);
   ASSERT_NO_OVERLAP(*dest, b);
   ASSERT_NO_OVERLAP(*dest, c);
-  strings_internal::StringAppendAndOverwrite(
-      *dest, SafeAdd({a.size(), b.size(), c.size()}),
-      [&a, &b, &c](char* const buf, size_t buf_size) {
+  StringAppendAndOverwrite(
+      *dest, SafeAdd(a.size(), b.size(), c.size()),
+      [a, b, c](char* const buf, size_t buf_size) {
         char* out = buf;
-        out = Append(out, a);
-        out = Append(out, b);
-        out = Append(out, c);
+        out = AppendPiece(out, a);
+        out = AppendPiece(out, b);
+        out = AppendPiece(out, c);
         assert(out == buf + buf_size);
         return buf_size;
       });
 }
 
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
-               const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) {
-  ASSERT_NO_OVERLAP(*dest, a);
-  ASSERT_NO_OVERLAP(*dest, b);
-  ASSERT_NO_OVERLAP(*dest, c);
-  ASSERT_NO_OVERLAP(*dest, d);
-  strings_internal::StringAppendAndOverwrite(
-      *dest, SafeAdd({a.size(), b.size(), c.size(), d.size()}),
-      [&a, &b, &c, &d](char* const buf, size_t buf_size) {
-        char* out = buf;
-        out = Append(out, a);
-        out = Append(out, b);
-        out = Append(out, c);
-        out = Append(out, d);
-        assert(out == buf + buf_size);
-        return buf_size;
-      });
-}
+}  // namespace strings_internal
 
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index 11e3bf6..298886f 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -111,6 +111,7 @@
 #include "absl/base/config.h"
 #include "absl/base/macros.h"
 #include "absl/base/nullability.h"
+#include "absl/base/optimization.h"
 #include "absl/base/port.h"
 #include "absl/meta/type_traits.h"
 #include "absl/strings/has_absl_stringify.h"
@@ -126,6 +127,9 @@
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
+struct Hex;
+struct Dec;
+
 namespace strings_internal {
 // AlphaNumBuffer allows a way to pass a string to StrCat without having to do
 // memory allocation.  It is simply a pair of a fixed-size character array, and
@@ -136,6 +140,91 @@
   size_t size;
 };
 
+inline constexpr char kSmallNegDigits[19] = "-9-8-7-6-5-4-3-2-1";
+inline constexpr char kSmallNonNegDigits[201] =
+    "0001020304050607080910111213141516171819"
+    "2021222324252627282930313233343536373839"
+    "4041424344454647484950515253545556575859"
+    "6061626364656667686970717273747576777879"
+    "8081828384858687888990919293949596979899";
+
+template <typename T, size_t N>
+ABSL_ATTRIBUTE_ALWAYS_INLINE absl::string_view FormatIntToPiece(
+    T x, char (&buf)[N]) {
+#if ABSL_HAVE_BUILTIN(__builtin_constant_p)
+  if (__builtin_constant_p(x)) {
+    if constexpr (std::is_signed_v<T>) {
+      if (x >= -9 && x < 0) {
+        return absl::string_view(
+            &kSmallNegDigits[static_cast<size_t>(x + 9) * 2], 2);
+      }
+      if (x >= -999 && x < -9) {
+        const size_t u =
+            static_cast<size_t>(uint64_t{0} - static_cast<uint64_t>(x));
+        buf[0] = '-';
+        if (u < 100) {
+          buf[1] = kSmallNonNegDigits[u * 2];
+          buf[2] = kSmallNonNegDigits[u * 2 + 1];
+          return absl::string_view(buf, 3);
+        }
+        buf[1] = static_cast<char>('0' + (u / 100));
+        buf[2] = kSmallNonNegDigits[(u % 100) * 2];
+        buf[3] = kSmallNonNegDigits[(u % 100) * 2 + 1];
+        return absl::string_view(buf, 4);
+      }
+      if (x >= 0 && x <= 9999) {
+        const size_t u = static_cast<size_t>(x);
+        if (u <= 99) {
+          const size_t idx = u * 2;
+          return u < 10 ? absl::string_view(&kSmallNonNegDigits[idx + 1], 1)
+                        : absl::string_view(&kSmallNonNegDigits[idx], 2);
+        }
+        if (u < 1000) {
+          buf[0] = static_cast<char>('0' + (u / 100));
+          buf[1] = kSmallNonNegDigits[(u % 100) * 2];
+          buf[2] = kSmallNonNegDigits[(u % 100) * 2 + 1];
+          return absl::string_view(buf, 3);
+        }
+        buf[0] = kSmallNonNegDigits[(u / 100) * 2];
+        buf[1] = kSmallNonNegDigits[(u / 100) * 2 + 1];
+        buf[2] = kSmallNonNegDigits[(u % 100) * 2];
+        buf[3] = kSmallNonNegDigits[(u % 100) * 2 + 1];
+        return absl::string_view(buf, 4);
+      }
+    } else {
+      if (x <= 9999) {
+        const size_t u = static_cast<size_t>(x);
+        if (u <= 99) {
+          const size_t idx = u * 2;
+          return u < 10 ? absl::string_view(&kSmallNonNegDigits[idx + 1], 1)
+                        : absl::string_view(&kSmallNonNegDigits[idx], 2);
+        }
+        if (u < 1000) {
+          buf[0] = static_cast<char>('0' + (u / 100));
+          buf[1] = kSmallNonNegDigits[(u % 100) * 2];
+          buf[2] = kSmallNonNegDigits[(u % 100) * 2 + 1];
+          return absl::string_view(buf, 3);
+        }
+        buf[0] = kSmallNonNegDigits[(u / 100) * 2];
+        buf[1] = kSmallNonNegDigits[(u / 100) * 2 + 1];
+        buf[2] = kSmallNonNegDigits[(u % 100) * 2];
+        buf[3] = kSmallNonNegDigits[(u % 100) * 2 + 1];
+        return absl::string_view(buf, 4);
+      }
+    }
+  }
+#endif
+  const char* end = numbers_internal::FastIntToBuffer(x, buf);
+  const size_t len = static_cast<size_t>(end - buf);
+  ABSL_ASSUME(len <= N);
+  return absl::string_view(buf, len);
+}
+
+ABSL_ATTRIBUTE_NOINLINE absl::string_view FormatHex(Hex hex,
+                                                    char* absl_nonnull end);
+ABSL_ATTRIBUTE_NOINLINE absl::string_view FormatDec(Dec dec,
+                                                    char* absl_nonnull end);
+
 }  // namespace strings_internal
 
 // Enum that specifies the number of significant digits to return in a `Hex` or
@@ -222,23 +311,9 @@
 
   template <typename S>
   friend void AbslStringify(S& sink, Hex hex) {
-    static_assert(
-        numbers_internal::kFastToBufferSize >= 32,
-        "This function only works when output buffer >= 32 bytes long");
     char buffer[numbers_internal::kFastToBufferSize];
-    char* const end = &buffer[numbers_internal::kFastToBufferSize];
-    auto real_width =
-        absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
-    if (real_width >= hex.width) {
-      sink.Append(absl::string_view(end - real_width, real_width));
-    } else {
-      // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
-      // max pad width can be up to 20.
-      std::memset(end - 32, hex.fill, 16);
-      // Patch up everything else up to the real_width.
-      std::memset(end - real_width - 16, hex.fill, 16);
-      sink.Append(absl::string_view(end - hex.width, hex.width));
-    }
+    sink.Append(strings_internal::FormatHex(
+        hex, &buffer[numbers_internal::kFastToBufferSize]));
   }
 
  private:
@@ -277,34 +352,9 @@
 
   template <typename S>
   friend void AbslStringify(S& sink, Dec dec) {
-    assert(dec.width <= numbers_internal::kFastToBufferSize);
     char buffer[numbers_internal::kFastToBufferSize];
-    char* const end = &buffer[numbers_internal::kFastToBufferSize];
-    char* const minfill = end - dec.width;
-    char* writer = end;
-    uint64_t val = dec.value;
-    while (val > 9) {
-      *--writer = '0' + (val % 10);
-      val /= 10;
-    }
-    *--writer = '0' + static_cast<char>(val);
-    if (dec.neg) *--writer = '-';
-
-    ptrdiff_t fillers = writer - minfill;
-    if (fillers > 0) {
-      // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
-      // But...: if the fill character is '0', then it's <+/-><fill><digits>
-      bool add_sign_again = false;
-      if (dec.neg && dec.fill == '0') {  // If filling with '0',
-        ++writer;                    // ignore the sign we just added
-        add_sign_again = true;       // and re-add the sign later.
-      }
-      writer -= fillers;
-      std::fill_n(writer, fillers, dec.fill);
-      if (add_sign_again) *--writer = '-';
-    }
-
-    sink.Append(absl::string_view(writer, static_cast<size_t>(end - writer)));
+    sink.Append(strings_internal::FormatDec(
+        dec, &buffer[numbers_internal::kFastToBufferSize]));
   }
 };
 
@@ -321,21 +371,13 @@
 // HighPrecision is conisderably slower than the default formatting, so only use
 // it if you need the string to convert back to the same floating-point value.
 
-inline strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
-HighPrecision(float f) {
-  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
-  result.size =
-      strlen(numbers_internal::RoundTripFloatToBuffer(f, &result.data[0]));
-  return result;
-}
+ABSL_ATTRIBUTE_NOINLINE
+strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
+HighPrecision(float f);
 
-inline strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
-HighPrecision(double d) {
-  strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> result;
-  result.size =
-      strlen(numbers_internal::RoundTripDoubleToBuffer(d, &result.data[0]));
-  return result;
-}
+ABSL_ATTRIBUTE_NOINLINE
+strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize>
+HighPrecision(double d);
 
 // -----------------------------------------------------------------------------
 // AlphaNum
@@ -349,49 +391,40 @@
 
 class AlphaNum {
  public:
-  // No bool ctor -- bools convert to an integral type.
-  // A bool ctor would also convert incoming pointers (bletch).
+  // Templated bool constructor avoids converting incoming pointers to bool.
+  template <typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
+  AlphaNum(T b)  // NOLINT(*)
+      : piece_(&"01"[static_cast<size_t>(b)], 1) {}
 
   // Prevent brace initialization
   template <typename T>
   AlphaNum(std::initializer_list<T>) = delete;  // NOLINT(runtime/explicit)
 
   AlphaNum(int x)  // NOLINT(runtime/explicit)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(strings_internal::FormatIntToPiece(x, digits_)) {}
   AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(strings_internal::FormatIntToPiece(x, digits_)) {}
   AlphaNum(long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(strings_internal::FormatIntToPiece(x, digits_)) {}
   AlphaNum(unsigned long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(strings_internal::FormatIntToPiece(x, digits_)) {}
   AlphaNum(long long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(strings_internal::FormatIntToPiece(x, digits_)) {}
   AlphaNum(unsigned long long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(strings_internal::FormatIntToPiece(x, digits_)) {}
 
   AlphaNum(float f)  // NOLINT(runtime/explicit)
-      : piece_(digits_, numbers_internal::SixDigitsToBuffer(
-                            static_cast<double>(f), digits_)) {}
+      : piece_(MakePieceWithLength(numbers_internal::SixDigitsToBuffer(
+            static_cast<double>(f), digits_))) {}
   AlphaNum(double f)  // NOLINT(runtime/explicit)
-      : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
+      : piece_(MakePieceWithLength(
+            numbers_internal::SixDigitsToBuffer(f, digits_))) {}
 
   template <size_t size>
   AlphaNum(  // NOLINT(runtime/explicit)
       const strings_internal::AlphaNumBuffer<size>& buf
           ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : piece_(&buf.data[0], buf.size) {}
+      : piece_(MakePieceFromBuffer(buf)) {}
 
   AlphaNum(const char* absl_nullable c_str  // NOLINT(runtime/explicit)
                ABSL_ATTRIBUTE_LIFETIME_BOUND)
@@ -457,6 +490,18 @@
   AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
 
  private:
+  absl::string_view MakePieceWithLength(size_t len) const {
+    ABSL_ASSUME(len <= sizeof(digits_));
+    return absl::string_view(digits_, len);
+  }
+  template <size_t max_size>
+  static absl::string_view MakePieceFromBuffer(
+      const strings_internal::AlphaNumBuffer<max_size>& buf) {
+    const size_t len = buf.size;
+    ABSL_ASSUME(len <= max_size);
+    return absl::string_view(&buf.data[0], len);
+  }
+
   absl::string_view piece_;
   char digits_[numbers_internal::kFastToBufferSize];
 };
@@ -492,62 +537,51 @@
 namespace strings_internal {
 
 // Do not call directly - this is not part of the public API.
-std::string CatPieces(std::initializer_list<absl::string_view> pieces);
-void AppendPieces(std::string* absl_nonnull dest,
-                  std::initializer_list<absl::string_view> pieces);
+ABSL_ATTRIBUTE_NOINLINE std::string CatPiece(absl::string_view a);
+ABSL_ATTRIBUTE_NOINLINE std::string CatPieces(absl::string_view a,
+                                              absl::string_view b);
+ABSL_ATTRIBUTE_NOINLINE std::string CatPieces(absl::string_view a,
+                                              absl::string_view b,
+                                              absl::string_view c);
+ABSL_ATTRIBUTE_NOINLINE std::string CatPieces(
+    std::initializer_list<absl::string_view> pieces);
 
-template <typename Integer>
-std::string IntegerToString(Integer i) {
-  // Any integer (signed/unsigned) up to 64 bits can be formatted into a buffer
-  // with 22 bytes (including NULL at the end).
-  constexpr size_t kMaxDigits10 = 22;
-  std::string result;
-  StringResizeAndOverwrite(
-      result, kMaxDigits10, [i](char* start, size_t buf_size) {
-        // Note: This can be optimized to not write last zero.
-        char* end = numbers_internal::FastIntToBuffer(i, start);
-        auto size = static_cast<size_t>(end - start);
-        ABSL_ASSERT(size < buf_size);
-        return size;
-      });
-  return result;
-}
+ABSL_ATTRIBUTE_NOINLINE void AppendPieces(std::string* absl_nonnull dest,
+                                          absl::string_view a);
+ABSL_ATTRIBUTE_NOINLINE void AppendPieces(std::string* absl_nonnull dest,
+                                          absl::string_view a,
+                                          absl::string_view b);
+ABSL_ATTRIBUTE_NOINLINE void AppendPieces(std::string* absl_nonnull dest,
+                                          absl::string_view a,
+                                          absl::string_view b,
+                                          absl::string_view c);
+ABSL_ATTRIBUTE_NOINLINE void AppendPieces(
+    std::string* absl_nonnull dest,
+    std::initializer_list<absl::string_view> pieces);
 
-template <typename Float>
-std::string FloatToString(Float f) {
-  std::string result;
-  StringResizeAndOverwrite(result, numbers_internal::kSixDigitsToBufferSize,
-                           [f](char* start, size_t buf_size) {
-                             size_t size = numbers_internal::SixDigitsToBuffer(
-                                 static_cast<double>(f), start);
-                             ABSL_ASSERT(size < buf_size);
-                             return size;
-                           });
-  return result;
-}
+ABSL_ATTRIBUTE_NOINLINE std::string JoinStringViews(
+    const absl::string_view* absl_nullable data, size_t count,
+    absl::string_view sep);
+ABSL_ATTRIBUTE_NOINLINE std::string JoinStrings(
+    const std::string* absl_nullable data, size_t count, absl::string_view sep);
 
 // `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
 // (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
 // and int64_t, then at least one of the three (`int` / `long` / `long long`)
 // would have been ambiguous when passed to `SingleArgStrCat`.
-inline std::string SingleArgStrCat(int x) { return IntegerToString(x); }
-inline std::string SingleArgStrCat(unsigned int x) {
-  return IntegerToString(x);
-}
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(bool x);
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(int x);
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(unsigned int x);
 // NOLINTNEXTLINE
-inline std::string SingleArgStrCat(long x) { return IntegerToString(x); }
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(long x);
 // NOLINTNEXTLINE
-inline std::string SingleArgStrCat(unsigned long x) {
-  return IntegerToString(x);
-}
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(unsigned long x);
 // NOLINTNEXTLINE
-inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(long long x);
 // NOLINTNEXTLINE
-inline std::string SingleArgStrCat(unsigned long long x) {
-  return IntegerToString(x);
-}
-inline std::string SingleArgStrCat(float x) { return FloatToString(x); }
-inline std::string SingleArgStrCat(double x) { return FloatToString(x); }
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(unsigned long long x);
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(float x);
+ABSL_ATTRIBUTE_NOINLINE std::string SingleArgStrCat(double x);
 
 // As of September 2023, the SingleArgStrCat() optimization is only enabled for
 // libc++. The reasons for this are:
@@ -581,14 +615,20 @@
   return strings_internal::SingleArgStrCat(a);
 }
 [[nodiscard]] inline std::string StrCat(const AlphaNum& a) {
-  return std::string(a.data(), a.size());
+  return strings_internal::CatPiece(a.Piece());
 }
-
-[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b);
-[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b,
-                                 const AlphaNum& c);
-[[nodiscard]] std::string StrCat(const AlphaNum& a, const AlphaNum& b,
-                                 const AlphaNum& c, const AlphaNum& d);
+[[nodiscard]] inline std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
+  return strings_internal::CatPieces(a.Piece(), b.Piece());
+}
+[[nodiscard]] inline std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                        const AlphaNum& c) {
+  return strings_internal::CatPieces(a.Piece(), b.Piece(), c.Piece());
+}
+[[nodiscard]] inline std::string StrCat(const AlphaNum& a, const AlphaNum& b,
+                                        const AlphaNum& c, const AlphaNum& d) {
+  return strings_internal::CatPieces(
+      {a.Piece(), b.Piece(), c.Piece(), d.Piece()});
+}
 
 // Support 5 or more arguments
 template <typename... AV>
@@ -628,13 +668,23 @@
 //   StrAppend(&s, p);
 
 inline void StrAppend(std::string* absl_nonnull) {}
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a);
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
-               const AlphaNum& b);
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
-               const AlphaNum& b, const AlphaNum& c);
-void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
-               const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
+
+inline void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a) {
+  strings_internal::AppendPieces(dest, a.Piece());
+}
+inline void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
+                      const AlphaNum& b) {
+  strings_internal::AppendPieces(dest, a.Piece(), b.Piece());
+}
+inline void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
+                      const AlphaNum& b, const AlphaNum& c) {
+  strings_internal::AppendPieces(dest, a.Piece(), b.Piece(), c.Piece());
+}
+inline void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a,
+                      const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) {
+  strings_internal::AppendPieces(dest,
+                                 {a.Piece(), b.Piece(), c.Piece(), d.Piece()});
+}
 
 // Support 5 or more arguments
 template <typename... AV>
@@ -648,14 +698,9 @@
 
 // Helper function for the future StrCat default floating-point format, %.6g
 // This is fast.
-inline strings_internal::AlphaNumBuffer<
-    numbers_internal::kSixDigitsToBufferSize>
-SixDigits(double d) {
-  strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
-      result;
-  result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
-  return result;
-}
+ABSL_ATTRIBUTE_NOINLINE
+strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
+SixDigits(double d);
 
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index 7b21c9c..94c268a 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -717,4 +717,55 @@
   CheckSingleArgumentIntegerLimits<uint64_t>();
 }
 
+void ExpectStrEq(absl::string_view actual, absl::string_view expected) {
+  EXPECT_EQ(actual, expected);
+}
+
+TEST(StrCat, CompileTimeConstantIntsAndBools) {
+  ExpectStrEq(absl::StrCat("v=", std::numeric_limits<int32_t>::min()),
+              "v=-2147483648");
+  ExpectStrEq(absl::StrCat("v=", -999, ",", -100, ",", -10, ",", -9, ",", -1),
+              "v=-999,-100,-10,-9,-1");
+  ExpectStrEq(absl::StrCat("v=", 0, ",", 1, ",", 9, ",", 10, ",", 42, ",", 99),
+              "v=0,1,9,10,42,99");
+  ExpectStrEq(absl::StrCat("v=", 100, ",", 404, ",", 8080, ",", 9999),
+              "v=100,404,8080,9999");
+  ExpectStrEq(absl::StrCat("v=", 0xabfaa72, ",", 99u, ",", 8080u),
+              "v=180333170,99,8080");
+  ExpectStrEq(absl::StrCat("v=", std::numeric_limits<int64_t>::min(), ",",
+                           std::numeric_limits<uint64_t>::max()),
+              "v=-9223372036854775808,18446744073709551615");
+
+  volatile bool rt = true;
+  volatile bool rf = false;
+  ExpectStrEq(absl::StrCat(true), "1");
+  ExpectStrEq(absl::StrCat(false), "0");
+  ExpectStrEq(absl::StrCat("b=", true, ",", false), "b=1,0");
+  ExpectStrEq(absl::StrCat("b=", bool{rt}, ",", bool{rf}), "b=1,0");
+}
+
+TEST(StrCat, StringViewOverloadsAllArities) {
+  std::string s = "str";
+  absl::string_view sv = "view";
+  char buf[8] = "buf";
+  const char* nul = nullptr;
+  ExpectStrEq(absl::StrCat(sv), "view");
+  ExpectStrEq(absl::StrCat(s, sv), "strview");
+  ExpectStrEq(absl::StrCat(s, sv, buf), "strviewbuf");
+  ExpectStrEq(absl::StrCat(s, sv, buf, nul), "strviewbuf");
+  ExpectStrEq(absl::StrCat(s, ",", sv, ",", buf, ",", true), "str,view,buf,1");
+
+  std::string out;
+  absl::StrAppend(&out, s);
+  absl::StrAppend(&out, ",", sv);
+  absl::StrAppend(&out, ",", buf, nul);
+  absl::StrAppend(&out, ",", true, ",", false);
+  absl::StrAppend(&out, ":", s, ":", sv, ":", buf);
+  ExpectStrEq(out, "str,view,buf,1,0:str:view:buf");
+
+  auto fn2 = static_cast<std::string (*)(const absl::AlphaNum&,
+                                         const absl::AlphaNum&)>(&absl::StrCat);
+  ExpectStrEq(fn2("lhs:", 42), "lhs:42");
+}
+
 }  // namespace
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc
index 377e30c..0900ae4 100644
--- a/absl/strings/str_replace.cc
+++ b/absl/strings/str_replace.cc
@@ -67,6 +67,25 @@
   return substitutions;
 }
 
+std::string ApplySubstitutions(absl::string_view s,
+                               std::vector<ViableSubstitution> subs) {
+  std::string result;
+  result.reserve(s.size());
+  ApplySubstitutions(s, &subs, &result);
+  return result;
+}
+
+int ApplySubstitutions(std::vector<ViableSubstitution> subs,
+                       std::string* absl_nonnull target) {
+  if (subs.empty()) return 0;
+
+  std::string result;
+  result.reserve(target->size());
+  int substitutions = ApplySubstitutions(*target, &subs, &result);
+  target->swap(result);
+  return substitutions;
+}
+
 }  // namespace strings_internal
 
 // We can implement this in terms of the generic StrReplaceAll, but
diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h
index 3af2357..38ef3e4 100644
--- a/absl/strings/str_replace.h
+++ b/absl/strings/str_replace.h
@@ -69,7 +69,7 @@
 //        {"$who", "Bob"},
 //        {"#Noun", "Apples"}});
 //   EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-[[nodiscard]] std::string StrReplaceAll(
+[[nodiscard]] ABSL_ATTRIBUTE_NOINLINE std::string StrReplaceAll(
     absl::string_view s,
     std::initializer_list<std::pair<absl::string_view, absl::string_view>>
         replacements);
@@ -114,7 +114,7 @@
 //                               {"#Noun", "Apples"}}, &s);
 //  EXPECT_EQ(count, 4);
 //  EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
-int StrReplaceAll(
+ABSL_ATTRIBUTE_NOINLINE int StrReplaceAll(
     std::initializer_list<std::pair<absl::string_view, absl::string_view>>
         replacements,
     std::string* absl_nonnull target);
@@ -189,34 +189,30 @@
   return subs;
 }
 
-int ApplySubstitutions(absl::string_view s,
-                       std::vector<ViableSubstitution>* absl_nonnull subs_ptr,
-                       std::string* absl_nonnull result_ptr);
+ABSL_ATTRIBUTE_NOINLINE int ApplySubstitutions(
+    absl::string_view s, std::vector<ViableSubstitution>* absl_nonnull subs_ptr,
+    std::string* absl_nonnull result_ptr);
+
+ABSL_ATTRIBUTE_NOINLINE std::string ApplySubstitutions(
+    absl::string_view s, std::vector<ViableSubstitution> subs);
+
+ABSL_ATTRIBUTE_NOINLINE int ApplySubstitutions(
+    std::vector<ViableSubstitution> subs, std::string* absl_nonnull target);
 
 }  // namespace strings_internal
 
 template <typename StrToStrMapping>
 std::string StrReplaceAll(absl::string_view s,
                           const StrToStrMapping& replacements) {
-  auto subs = strings_internal::FindSubstitutions(s, replacements);
-  std::string result;
-  result.reserve(s.size());
-  strings_internal::ApplySubstitutions(s, &subs, &result);
-  return result;
+  return strings_internal::ApplySubstitutions(
+      s, strings_internal::FindSubstitutions(s, replacements));
 }
 
 template <typename StrToStrMapping>
 int StrReplaceAll(const StrToStrMapping& replacements,
                   std::string* absl_nonnull target) {
-  auto subs = strings_internal::FindSubstitutions(*target, replacements);
-  if (subs.empty()) return 0;
-
-  std::string result;
-  result.reserve(target->size());
-  int substitutions =
-      strings_internal::ApplySubstitutions(*target, &subs, &result);
-  target->swap(result);
-  return substitutions;
+  return strings_internal::ApplySubstitutions(
+      strings_internal::FindSubstitutions(*target, replacements), target);
 }
 
 ABSL_NAMESPACE_END
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 244c8a5..dbb50e0 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -22,9 +22,11 @@
 #include <string>
 #include <type_traits>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/internal/raw_logging.h"
 #include "absl/base/nullability.h"
+#include "absl/base/optimization.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/escaping.h"
 #include "absl/strings/internal/append_and_overwrite.h"
@@ -124,6 +126,14 @@
       });
 }
 
+ABSL_ATTRIBUTE_NOINLINE std::string SubstituteArray(
+    absl::string_view format, const absl::string_view* absl_nullable args_array,
+    size_t num_args) {
+  std::string result;
+  SubstituteAndAppendArray(&result, format, args_array, num_args);
+  return result;
+}
+
 Arg::Arg(const void* absl_nullable value) {
   static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
                 "fix sizeof(scratch_)");
@@ -138,63 +148,19 @@
     } while (num != 0);
     *--ptr = 'x';
     *--ptr = '0';
-    piece_ = absl::string_view(
-        ptr, static_cast<size_t>(scratch_ + sizeof(scratch_) - ptr));
+    const size_t len = static_cast<size_t>(scratch_ + sizeof(scratch_) - ptr);
+    ABSL_ASSUME(len <= sizeof(scratch_));
+    piece_ = absl::string_view(ptr, len);
   }
 }
 
-// TODO(jorg): Don't duplicate so much code between here and str_cat.cc
-Arg::Arg(Hex hex) {
-  char* const end = &scratch_[numbers_internal::kFastToBufferSize];
-  char* writer = end;
-  uint64_t value = hex.value;
-  do {
-    *--writer = absl::numbers_internal::kHexChar[value & 0xF];
-    value >>= 4;
-  } while (value != 0);
+Arg::Arg(Hex hex)
+    : piece_(strings_internal::FormatHex(
+          hex, &scratch_[numbers_internal::kFastToBufferSize])) {}
 
-  char* beg;
-  if (end - writer < hex.width) {
-    beg = end - hex.width;
-    std::fill_n(beg, writer - beg, hex.fill);
-  } else {
-    beg = writer;
-  }
-
-  piece_ = absl::string_view(beg, static_cast<size_t>(end - beg));
-}
-
-// TODO(jorg): Don't duplicate so much code between here and str_cat.cc
-Arg::Arg(Dec dec) {
-  assert(dec.width <= numbers_internal::kFastToBufferSize);
-  char* const end = &scratch_[numbers_internal::kFastToBufferSize];
-  char* const minfill = end - dec.width;
-  char* writer = end;
-  uint64_t value = dec.value;
-  bool neg = dec.neg;
-  while (value > 9) {
-    *--writer = '0' + (value % 10);
-    value /= 10;
-  }
-  *--writer = '0' + static_cast<char>(value);
-  if (neg) *--writer = '-';
-
-  ptrdiff_t fillers = writer - minfill;
-  if (fillers > 0) {
-    // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
-    // But...: if the fill character is '0', then it's <+/-><fill><digits>
-    bool add_sign_again = false;
-    if (neg && dec.fill == '0') {  // If filling with '0',
-      ++writer;                    // ignore the sign we just added
-      add_sign_again = true;       // and re-add the sign later.
-    }
-    writer -= fillers;
-    std::fill_n(writer, fillers, dec.fill);
-    if (add_sign_again) *--writer = '-';
-  }
-
-  piece_ = absl::string_view(writer, static_cast<size_t>(end - writer));
-}
+Arg::Arg(Dec dec)
+    : piece_(strings_internal::FormatDec(
+          dec, &scratch_[numbers_internal::kFastToBufferSize])) {}
 
 }  // namespace substitute_internal
 ABSL_NAMESPACE_END
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index 6c62af6..1cc01cb 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -78,9 +78,11 @@
 #include <type_traits>
 #include <vector>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/macros.h"
 #include "absl/base/nullability.h"
+#include "absl/base/optimization.h"
 #include "absl/base/port.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/escaping.h"
@@ -131,51 +133,27 @@
     piece_ = absl::string_view(scratch_, 1);
   }
   Arg(short value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(unsigned short value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(int value)  // NOLINT(google-explicit-constructor)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(unsigned int value)  // NOLINT(google-explicit-constructor)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(unsigned long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(long long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(unsigned long long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(strings_internal::FormatIntToPiece(value, scratch_)) {}
   Arg(float value)  // NOLINT(google-explicit-constructor)
-      : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
-  }
+      : piece_(MakePieceWithLength(
+            numbers_internal::SixDigitsToBuffer(value, scratch_))) {}
   Arg(double value)  // NOLINT(google-explicit-constructor)
-      : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
-  }
+      : piece_(MakePieceWithLength(
+            numbers_internal::SixDigitsToBuffer(value, scratch_))) {}
   Arg(bool value)  // NOLINT(google-explicit-constructor)
       : piece_(value ? "true" : "false") {}
 
@@ -218,6 +196,11 @@
   absl::string_view piece() const { return piece_; }
 
  private:
+  absl::string_view MakePieceWithLength(size_t len) const {
+    ABSL_ASSUME(len <= sizeof(scratch_));
+    return absl::string_view(scratch_, len);
+  }
+
   absl::string_view piece_;
   char scratch_[numbers_internal::kFastToBufferSize];
 };
@@ -229,6 +212,10 @@
                               const absl::string_view* absl_nullable args_array,
                               size_t num_args);
 
+ABSL_ATTRIBUTE_NOINLINE std::string SubstituteArray(
+    absl::string_view format, const absl::string_view* absl_nullable args_array,
+    size_t num_args);
+
 #if defined(ABSL_BAD_CALL_IF)
 constexpr int CalculateOneBit(const char* absl_nonnull format) {
   // Returns:
@@ -543,50 +530,45 @@
 //    std::string s = absl::Substitute(format, args...);
 
 [[nodiscard]] inline std::string Substitute(absl::string_view format) {
-  std::string result;
-  SubstituteAndAppend(&result, format);
-  return result;
+  return substitute_internal::SubstituteArray(format, nullptr, 0);
 }
 
 [[nodiscard]] inline std::string Substitute(
     absl::string_view format, const substitute_internal::Arg& a0) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0);
-  return result;
+  const absl::string_view args[] = {a0.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
     absl::string_view format, const substitute_internal::Arg& a0,
     const substitute_internal::Arg& a1) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
     absl::string_view format, const substitute_internal::Arg& a0,
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
     absl::string_view format, const substitute_internal::Arg& a0,
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
     const substitute_internal::Arg& a3) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
+                                    a3.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
     absl::string_view format, const substitute_internal::Arg& a0,
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
+                                    a3.piece(), a4.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
@@ -594,9 +576,9 @@
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
+                                    a3.piece(), a4.piece(), a5.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
@@ -604,9 +586,10 @@
     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
+                                    a3.piece(), a4.piece(), a5.piece(),
+                                    a6.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
@@ -615,9 +598,10 @@
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
     const substitute_internal::Arg& a7) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
+                                    a3.piece(), a4.piece(), a5.piece(),
+                                    a6.piece(), a7.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
@@ -626,9 +610,10 @@
     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8);
-  return result;
+  const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
+                                    a3.piece(), a4.piece(), a5.piece(),
+                                    a6.piece(), a7.piece(), a8.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 [[nodiscard]] inline std::string Substitute(
@@ -638,9 +623,10 @@
     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
     const substitute_internal::Arg& a9) {
-  std::string result;
-  SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
-  return result;
+  const absl::string_view args[] = {
+      a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(),
+      a5.piece(), a6.piece(), a7.piece(), a8.piece(), a9.piece()};
+  return substitute_internal::SubstituteArray(format, args, std::size(args));
 }
 
 #if defined(ABSL_BAD_CALL_IF)
diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc
index e62a89a..a9730f2 100644
--- a/absl/strings/substitute_test.cc
+++ b/absl/strings/substitute_test.cc
@@ -269,6 +269,25 @@
   EXPECT_EQ(absl::Substitute("$0", e), "Choices");
 }
 
+void ExpectSubEq(absl::string_view actual, absl::string_view expected) {
+  EXPECT_EQ(actual, expected);
+}
+
+TEST(SubstituteTest, CompileTimeConstantInts) {
+  ExpectSubEq(
+      absl::Substitute("$0,$1,$2,$3,$4,$5,$6,$7", int16_t{-32768},
+                       int16_t{-999}, int16_t{-10}, int16_t{-9}, int16_t{-1},
+                       int16_t{0}, int16_t{99}, uint16_t{65535}),
+      "-32768,-999,-10,-9,-1,0,99,65535");
+  ExpectSubEq(
+      absl::Substitute("$0,$1,$2,$3,$4,$5,$6,$7,$8,$9", -999, -9, -1, 0, 9, 42,
+                       99, 404, 8080, 0xabfaa72),
+      "-999,-9,-1,0,9,42,99,404,8080,180333170");
+  ExpectSubEq(absl::Substitute("$0,$1", std::numeric_limits<int64_t>::min(),
+                               std::numeric_limits<uint64_t>::max()),
+              "-9223372036854775808,18446744073709551615");
+}
+
 #if GTEST_HAS_DEATH_TEST
 
 TEST(SubstituteDeathTest, SubstituteDeath) {