mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 20:14:23 +08:00
StrCat: do not use intermediate buffer when result fits in SSO.
PiperOrigin-RevId: 557811632 Change-Id: I370fa17d2fb82a1f1ca86f84529bae31b34b18e4
This commit is contained in:
committed by
Copybara-Service
parent
9377c75bf4
commit
65d7b6d421
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user