summaryrefslogtreecommitdiff
path: root/absl/strings
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2023-08-17 07:04:14 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2023-08-17 07:05:33 -0700
commit65d7b6d421f780941dd585d7094f257a546e2510 (patch)
tree7751d97a393f4f6597b03d29957653b24244faed /absl/strings
parent9377c75bf499af0018542c17abcedf659e094a1e (diff)
StrCat: do not use intermediate buffer when result fits in SSO.
PiperOrigin-RevId: 557811632 Change-Id: I370fa17d2fb82a1f1ca86f84529bae31b34b18e4
Diffstat (limited to 'absl/strings')
-rw-r--r--absl/strings/str_cat.h64
-rw-r--r--absl/strings/str_cat_benchmark.cc11
-rw-r--r--absl/strings/str_cat_test.cc16
3 files changed, 91 insertions, 0 deletions
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index d5f71ff0..57a5823f 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -89,8 +89,11 @@
#include <algorithm>
#include <array>
+#include <cassert>
+#include <cstddef>
#include <cstdint>
#include <cstring>
+#include <limits>
#include <string>
#include <type_traits>
#include <utility>
@@ -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<absl::string_view> pieces);
void AppendPieces(std::string* 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;
+ 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<size_t>(end - start);
+ assert((size < result.size()) &&
+ "StrCat(Integer) does not fit into kMaxDigits10");
+ result.erase(size);
+ return result;
+}
+template <typename Float>
+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 <typename T, typename = std::enable_if_t<std::is_arithmetic<T>{} &&
+ !std::is_same<T, char>{}>>
+using EnableIfFastCase = T;
+
} // namespace strings_internal
ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
+template <typename T>
+ABSL_MUST_USE_RESULT inline std::string StrCat(
+ strings_internal::EnableIfFastCase<T> 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 81b20548..e54a9230 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 7f52e053..66eddf0d 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 <typename Integer>
+void CheckSingleArgumentIntegerLimits() {
+ Integer max = std::numeric_limits<Integer>::max();
+ Integer min = std::numeric_limits<Integer>::min();
+
+ EXPECT_EQ(absl::StrCat(max), std::to_string(max));
+ EXPECT_EQ(absl::StrCat(min), std::to_string(min));
+}
+
+TEST(StrCat, SingleArgumentLimits) {
+ CheckSingleArgumentIntegerLimits<int32_t>();
+ CheckSingleArgumentIntegerLimits<uint32_t>();
+ CheckSingleArgumentIntegerLimits<int64_t>();
+ CheckSingleArgumentIntegerLimits<uint64_t>();
+}
+
} // namespace