From 65d7b6d421f780941dd585d7094f257a546e2510 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 17 Aug 2023 07:04:14 -0700 Subject: [PATCH] StrCat: do not use intermediate buffer when result fits in SSO. PiperOrigin-RevId: 557811632 Change-Id: I370fa17d2fb82a1f1ca86f84529bae31b34b18e4 --- absl/strings/str_cat.h | 64 +++++++++++++++++++++++++++++++ absl/strings/str_cat_benchmark.cc | 11 ++++++ absl/strings/str_cat_test.cc | 16 ++++++++ 3 files changed, 91 insertions(+) diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index d5f71ff003f..57a5823fed9 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -89,8 +89,11 @@ #include #include +#include +#include #include #include +#include #include #include #include @@ -98,7 +101,9 @@ #include "absl/base/attributes.h" #include "absl/base/port.h" +#include "absl/meta/type_traits.h" #include "absl/strings/internal/has_absl_stringify.h" +#include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/internal/stringify_sink.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" @@ -444,10 +449,69 @@ std::string CatPieces(std::initializer_list pieces); void AppendPieces(std::string* dest, std::initializer_list pieces); +template +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; + strings_internal::STLStringResizeUninitialized(&result, kMaxDigits10); + char* start = &result[0]; + // note: this can be optimized to not write last zero. + char* end = numbers_internal::FastIntToBuffer(i, start); + auto size = static_cast(end - start); + assert((size < result.size()) && + "StrCat(Integer) does not fit into kMaxDigits10"); + result.erase(size); + return result; +} +template +std::string FloatToString(Float f) { + std::string result; + strings_internal::STLStringResizeUninitialized( + &result, numbers_internal::kSixDigitsToBufferSize); + char* start = &result[0]; + result.erase(numbers_internal::SixDigitsToBuffer(f, start)); + 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); +} +// NOLINTNEXTLINE +inline std::string SingleArgStrCat(long x) { return IntegerToString(x); } +// NOLINTNEXTLINE +inline std::string SingleArgStrCat(unsigned long x) { + return IntegerToString(x); +} +// NOLINTNEXTLINE +inline std::string SingleArgStrCat(long long x) { return IntegerToString(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); } + + +template {} && + !std::is_same{}>> +using EnableIfFastCase = T; + } // namespace strings_internal ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); } +template +ABSL_MUST_USE_RESULT inline std::string StrCat( + strings_internal::EnableIfFastCase a) { + return strings_internal::SingleArgStrCat(a); +} ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) { return std::string(a.data(), a.size()); } diff --git a/absl/strings/str_cat_benchmark.cc b/absl/strings/str_cat_benchmark.cc index 81b205482c2..e54a9230c5d 100644 --- a/absl/strings/str_cat_benchmark.cc +++ b/absl/strings/str_cat_benchmark.cc @@ -188,4 +188,15 @@ void StrAppendConfig(B* benchmark) { BENCHMARK(BM_StrAppend)->Apply(StrAppendConfig); +void BM_StrCat_int(benchmark::State& state) { + int i = 0; + for (auto s : state) { + std::string result = absl::StrCat(i); + benchmark::DoNotOptimize(result); + i = IncrementAlternatingSign(i); + } +} + +BENCHMARK(BM_StrCat_int); + } // namespace diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc index 7f52e053cfe..66eddf0df0b 100644 --- a/absl/strings/str_cat_test.cc +++ b/absl/strings/str_cat_test.cc @@ -665,4 +665,20 @@ TEST(StrCat, AbslStringifyWithEnum) { EXPECT_EQ(absl::StrCat(e), "Choices"); } +template +void CheckSingleArgumentIntegerLimits() { + Integer max = std::numeric_limits::max(); + Integer min = std::numeric_limits::min(); + + EXPECT_EQ(absl::StrCat(max), std::to_string(max)); + EXPECT_EQ(absl::StrCat(min), std::to_string(min)); +} + +TEST(StrCat, SingleArgumentLimits) { + CheckSingleArgumentIntegerLimits(); + CheckSingleArgumentIntegerLimits(); + CheckSingleArgumentIntegerLimits(); + CheckSingleArgumentIntegerLimits(); +} + } // namespace