Move out of the line StrCat numeric formatting helpers to reduce binary size. Header-inlined numeric formatting helpers in `str_cat.h` expand repeatedly across call sites and translation units, inflating binary text and unwind tables for single-argument and floating-point string conversions. PiperOrigin-RevId: 1005442834
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index b79078d..ab1e182 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc
@@ -25,10 +25,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 +77,54 @@ 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); + 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; +} + } // namespace +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; +} + 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 @@ -202,6 +250,19 @@ }); } +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 void StrAppend(std::string* absl_nonnull dest, const AlphaNum& a) { @@ -266,5 +327,13 @@ }); } +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_NAMESPACE_END } // namespace absl
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index 11e3bf6..76b9899 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h
@@ -321,21 +321,11 @@ // 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; -} +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; -} +strings_internal::AlphaNumBuffer<numbers_internal::kFastToBufferSize> +HighPrecision(double d); // ----------------------------------------------------------------------------- // AlphaNum @@ -496,58 +486,22 @@ 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; -} - // `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); -} +std::string SingleArgStrCat(int x); +std::string SingleArgStrCat(unsigned int x); // NOLINTNEXTLINE -inline std::string SingleArgStrCat(long x) { return IntegerToString(x); } +std::string SingleArgStrCat(long x); // NOLINTNEXTLINE -inline std::string SingleArgStrCat(unsigned long x) { - return IntegerToString(x); -} +std::string SingleArgStrCat(unsigned long x); // NOLINTNEXTLINE -inline std::string SingleArgStrCat(long long x) { return IntegerToString(x); } +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); } +std::string SingleArgStrCat(unsigned long long x); +std::string SingleArgStrCat(float x); +std::string SingleArgStrCat(double x); // As of September 2023, the SingleArgStrCat() optimization is only enabled for // libc++. The reasons for this are: @@ -648,14 +602,8 @@ // 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; -} +strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize> +SixDigits(double d); ABSL_NAMESPACE_END } // namespace absl