diff options
author | Abseil Team <absl-team@google.com> | 2023-08-01 07:58:20 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-08-01 07:59:13 -0700 |
commit | 22091f4c0d6626b3ef40446ce3d4ccab19425ca3 (patch) | |
tree | 1fd4c904411a2c4aa2be7958b1095e5ffd71cb1e /absl/strings/str_cat.cc | |
parent | d7aae58cb693aaf8a3351be2e5fce2eefeff30c9 (diff) | |
download | abseil-22091f4c0d6626b3ef40446ce3d4ccab19425ca3.tar.gz abseil-22091f4c0d6626b3ef40446ce3d4ccab19425ca3.tar.bz2 abseil-22091f4c0d6626b3ef40446ce3d4ccab19425ca3.zip |
Speed up StrAppend by up to 4x.
PiperOrigin-RevId: 552802740
Change-Id: I662da1b03bfffb7939b44ea3850566d3c209c6cc
Diffstat (limited to 'absl/strings/str_cat.cc')
-rw-r--r-- | absl/strings/str_cat.cc | 178 |
1 files changed, 13 insertions, 165 deletions
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index 2e49c31b..5ec02995 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -14,17 +14,9 @@ #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 { @@ -37,170 +29,26 @@ 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 { -// 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; +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())); } -// 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()); +#if defined(__GNUC__) && !defined(__clang__) +char* AppendAlphaNum(char* dst, const AlphaNum& a) { + return std::copy_n(a.data(), a.size(), dst); } +#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 |