diff options
author | Abseil Team <absl-team@google.com> | 2023-08-02 08:49:17 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-08-02 08:49:58 -0700 |
commit | 5d06f796b7ed59234c603457d130d23addc400fd (patch) | |
tree | 627e4516fd5e27b3d522c01f550c1a55a67d34ac /absl/strings/str_cat.cc | |
parent | e945c8d98719d2cca66ef1f0b83696becfa6a880 (diff) |
Rollback of "Speed up StrAppend by up to 4x."
PiperOrigin-RevId: 553158292
Change-Id: I28350321550accd72da2f9f6f5992af311fe4b00
Diffstat (limited to 'absl/strings/str_cat.cc')
-rw-r--r-- | absl/strings/str_cat.cc | 178 |
1 files changed, 165 insertions, 13 deletions
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index 5ec02995..2e49c31b 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -14,9 +14,17 @@ #include "absl/strings/str_cat.h" +#include <assert.h> + +#include <algorithm> +#include <cstddef> #include <cstdint> +#include <cstring> #include <string> +#include "absl/strings/ascii.h" +#include "absl/strings/internal/resize_uninitialized.h" +#include "absl/strings/numbers.h" #include "absl/strings/string_view.h" namespace absl { @@ -29,26 +37,170 @@ ABSL_NAMESPACE_BEGIN // of a mix of raw C strings, string_views, strings, and integer values. // ---------------------------------------------------------------------- +// Append is merely a version of memcpy that returns the address of the byte +// after the area just overwritten. +static char* Append(char* out, const AlphaNum& x) { + // memcpy is allowed to overwrite arbitrary memory, so doing this after the + // call would force an extra fetch of x.size(). + char* after = out + x.size(); + if (x.size() != 0) { + memcpy(out, x.data(), x.size()); + } + return after; +} + +std::string StrCat(const AlphaNum& a, const AlphaNum& b) { + std::string result; + absl::strings_internal::STLStringResizeUninitialized(&result, + a.size() + b.size()); + char* const begin = &result[0]; + char* out = begin; + out = Append(out, a); + out = Append(out, b); + assert(out == begin + result.size()); + return result; +} + +std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) { + std::string result; + strings_internal::STLStringResizeUninitialized( + &result, a.size() + b.size() + c.size()); + char* const begin = &result[0]; + char* out = begin; + out = Append(out, a); + out = Append(out, b); + out = Append(out, c); + assert(out == begin + result.size()); + return result; +} + +std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, + const AlphaNum& d) { + std::string result; + strings_internal::STLStringResizeUninitialized( + &result, a.size() + b.size() + c.size() + d.size()); + char* const begin = &result[0]; + char* out = begin; + out = Append(out, a); + out = Append(out, b); + out = Append(out, c); + out = Append(out, d); + assert(out == begin + result.size()); + return result; +} + namespace strings_internal { -bool HaveOverlap(const std::string& x, absl::string_view y) { - if (y.empty()) return false; - // TODO(b/290623057): Re-evaluate the check below: it detects when buffers - // overlap (which is good) but it also introduces undefined behaviour when - // buffers don't overlap (substracting pointers that do not belong to the same - // array is UB [expr.add]). In other words, if compiler assumes that a program - // never has UB, then it can replace `assert(HaveOverlap(x, y))` with - // `assert(false)`. - return (uintptr_t(y.data() - x.data()) <= uintptr_t(x.size())); +// Do not call directly - these are not part of the public API. +std::string CatPieces(std::initializer_list<absl::string_view> pieces) { + std::string result; + size_t total_size = 0; + for (absl::string_view piece : pieces) total_size += piece.size(); + strings_internal::STLStringResizeUninitialized(&result, total_size); + + char* const begin = &result[0]; + char* out = begin; + for (absl::string_view piece : pieces) { + const size_t this_size = piece.size(); + if (this_size != 0) { + memcpy(out, piece.data(), this_size); + out += this_size; + } + } + assert(out == begin + result.size()); + return result; } -#if defined(__GNUC__) && !defined(__clang__) -char* AppendAlphaNum(char* dst, const AlphaNum& a) { - return std::copy_n(a.data(), a.size(), dst); +// It's possible to call StrAppend with an absl::string_view that is itself a +// fragment of the string we're appending to. However the results of this are +// random. Therefore, check for this in debug mode. Use unsigned math so we +// only have to do one comparison. Note, there's an exception case: appending an +// empty string is always allowed. +#define ASSERT_NO_OVERLAP(dest, src) \ + assert(((src).size() == 0) || \ + (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size()))) + +void AppendPieces(std::string* dest, + std::initializer_list<absl::string_view> pieces) { + size_t old_size = dest->size(); + size_t total_size = old_size; + for (absl::string_view piece : pieces) { + ASSERT_NO_OVERLAP(*dest, piece); + total_size += piece.size(); + } + strings_internal::STLStringResizeUninitializedAmortized(dest, total_size); + + char* const begin = &(*dest)[0]; + char* out = begin + old_size; + for (absl::string_view piece : pieces) { + const size_t this_size = piece.size(); + if (this_size != 0) { + memcpy(out, piece.data(), this_size); + out += this_size; + } + } + assert(out == begin + dest->size()); } -#endif } // namespace strings_internal +void StrAppend(std::string* dest, const AlphaNum& a) { + ASSERT_NO_OVERLAP(*dest, a); + std::string::size_type old_size = dest->size(); + strings_internal::STLStringResizeUninitializedAmortized(dest, + old_size + a.size()); + char* const begin = &(*dest)[0]; + char* out = begin + old_size; + out = Append(out, a); + assert(out == begin + dest->size()); +} + +void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) { + ASSERT_NO_OVERLAP(*dest, a); + ASSERT_NO_OVERLAP(*dest, b); + std::string::size_type old_size = dest->size(); + strings_internal::STLStringResizeUninitializedAmortized( + dest, old_size + a.size() + b.size()); + char* const begin = &(*dest)[0]; + char* out = begin + old_size; + out = Append(out, a); + out = Append(out, b); + assert(out == begin + dest->size()); +} + +void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c) { + ASSERT_NO_OVERLAP(*dest, a); + ASSERT_NO_OVERLAP(*dest, b); + ASSERT_NO_OVERLAP(*dest, c); + std::string::size_type old_size = dest->size(); + strings_internal::STLStringResizeUninitializedAmortized( + dest, old_size + a.size() + b.size() + c.size()); + char* const begin = &(*dest)[0]; + char* out = begin + old_size; + out = Append(out, a); + out = Append(out, b); + out = Append(out, c); + assert(out == begin + dest->size()); +} + +void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, + const AlphaNum& c, const AlphaNum& d) { + ASSERT_NO_OVERLAP(*dest, a); + ASSERT_NO_OVERLAP(*dest, b); + ASSERT_NO_OVERLAP(*dest, c); + ASSERT_NO_OVERLAP(*dest, d); + std::string::size_type old_size = dest->size(); + strings_internal::STLStringResizeUninitializedAmortized( + dest, old_size + a.size() + b.size() + c.size() + d.size()); + char* const begin = &(*dest)[0]; + char* out = begin + old_size; + out = Append(out, a); + out = Append(out, b); + out = Append(out, c); + out = Append(out, d); + assert(out == begin + dest->size()); +} + ABSL_NAMESPACE_END } // namespace absl |