summaryrefslogtreecommitdiff
path: root/absl/strings/str_cat.cc
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2023-08-02 08:49:17 -0700
committerGravatar Copybara-Service <copybara-worker@google.com>2023-08-02 08:49:58 -0700
commit5d06f796b7ed59234c603457d130d23addc400fd (patch)
tree627e4516fd5e27b3d522c01f550c1a55a67d34ac /absl/strings/str_cat.cc
parente945c8d98719d2cca66ef1f0b83696becfa6a880 (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.cc178
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