Bound formatted string lengths with `ABSL_ASSUME` across `absl/strings`.

In hardened builds, constructing an `absl::string_view` checks that its length does not exceed `absl::string_view::max_size()`, emitting a comparison, branch, and trap instruction (`ud2`) whenever the compiler cannot statically upper-bound the runtime length returned by out-of-line formatting routines such as `FastIntToBuffer`, `SixDigitsToBuffer`, and `FastHexToBufferZeroPad16`, or tracked across `FormatSinkImpl` and `IntDigits` scratch buffers. Similarly, `IntegerToString` and `FloatToString` only asserted `size < buf_size` in debug builds, preventing release builds from proving that formatted outputs fit within `std::string` small-string capacity.

Add `ABSL_ASSUME` bounds on scratch-buffer string lengths across `AlphaNum` (`str_cat.h`), `FormatHex`/`FormatDec`/`IntegerToString`/`FloatToString` (`str_cat.cc`), `substitute_internal::Arg` (`substitute.h`, `substitute.cc`), `SourceLocation` stringification (`stringify_sink.h`, `stringify_sink.cc`), `FormatSinkImpl::Flush` (`str_format/extension.h`), and `IntDigits` (`str_format/arg.cc`). Static ThinLTO release builds eliminate 331 to 593 `ud2` traps and reduce `.text` by an additional 2.5 KB to 5.4 KB per test binary.

PiperOrigin-RevId: 1003911333
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..a36136a 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"
 
@@ -75,8 +78,184 @@
   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
 
+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(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;
+}
+
 std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
   std::string result;
   // Use uint64_t to prevent size_t overflow. We assume it is not possible for
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index 11e3bf6..f749a2e 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,11 @@
   size_t size;
 };
 
+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 +231,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 +272,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 +291,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
@@ -357,41 +319,36 @@
   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_(
+            MakePieceFromEnd(numbers_internal::FastIntToBuffer(x, digits_))) {}
   AlphaNum(unsigned int x)  // NOLINT(runtime/explicit)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(
+            MakePieceFromEnd(numbers_internal::FastIntToBuffer(x, digits_))) {}
   AlphaNum(long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(
+            MakePieceFromEnd(numbers_internal::FastIntToBuffer(x, digits_))) {}
   AlphaNum(unsigned long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(
+            MakePieceFromEnd(numbers_internal::FastIntToBuffer(x, digits_))) {}
   AlphaNum(long long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(
+            MakePieceFromEnd(numbers_internal::FastIntToBuffer(x, digits_))) {}
   AlphaNum(unsigned long long x)  // NOLINT(*)
-      : piece_(digits_, static_cast<size_t>(
-                            numbers_internal::FastIntToBuffer(x, digits_) -
-                            &digits_[0])) {}
+      : piece_(
+            MakePieceFromEnd(numbers_internal::FastIntToBuffer(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 +414,23 @@
   AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {}  // NOLINT(runtime/explicit)
 
  private:
+  absl::string_view MakePieceFromEnd(const char* absl_nonnull end) const {
+    const size_t len = static_cast<size_t>(end - &digits_[0]);
+    ABSL_ASSUME(len <= sizeof(digits_));
+    return absl::string_view(digits_, len);
+  }
+  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];
 };
@@ -496,58 +470,28 @@
 void AppendPieces(std::string* absl_nonnull dest,
                   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;
-}
-
-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(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:
@@ -648,14 +592,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_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..88c5792 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,35 @@
     piece_ = absl::string_view(scratch_, 1);
   }
   Arg(short value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(value, scratch_))) {}
   Arg(unsigned short value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(value, scratch_))) {}
   Arg(int value)  // NOLINT(google-explicit-constructor)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(value, scratch_))) {}
   Arg(unsigned int value)  // NOLINT(google-explicit-constructor)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(value, scratch_))) {}
   Arg(long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(value, scratch_))) {}
   Arg(unsigned long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(value, scratch_))) {}
   Arg(long long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(value, scratch_))) {}
   Arg(unsigned long long value)  // NOLINT(*)
-      : piece_(scratch_,
-               static_cast<size_t>(
-                   numbers_internal::FastIntToBuffer(value, scratch_) -
-                   scratch_)) {}
+      : piece_(MakePieceFromEnd(
+            numbers_internal::FastIntToBuffer(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 +204,16 @@
   absl::string_view piece() const { return piece_; }
 
  private:
+  absl::string_view MakePieceFromEnd(const char* absl_nonnull end) const {
+    const size_t len = static_cast<size_t>(end - scratch_);
+    ABSL_ASSUME(len <= sizeof(scratch_));
+    return absl::string_view(scratch_, len);
+  }
+  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 +225,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 +543,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 +589,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 +599,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 +611,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 +623,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 +636,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)