summaryrefslogtreecommitdiff
path: root/absl/strings
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings')
-rw-r--r--absl/strings/cord.cc5
-rw-r--r--absl/strings/cord.h6
-rw-r--r--absl/strings/cord_test.cc20
-rw-r--r--absl/strings/internal/cord_rep_ring.cc5
-rw-r--r--absl/strings/string_view.h5
5 files changed, 31 insertions, 10 deletions
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 1c03a618..1aea396f 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -491,7 +491,7 @@ static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) {
}
void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
- ClearSlow();
+ UnrefTree();
data_ = src.data_;
if (is_tree()) {
@@ -501,11 +501,10 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
}
}
-void Cord::InlineRep::ClearSlow() {
+void Cord::InlineRep::UnrefTree() {
if (is_tree()) {
CordRep::Unref(tree());
}
- ResetToEmpty();
}
// --------------------------------------------------------------------
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index fa9cb913..cb2ffc5c 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -776,8 +776,8 @@ class Cord {
friend class Cord;
void AssignSlow(const InlineRep& src);
- // Unrefs the tree, stops profiling, and zeroes the contents
- void ClearSlow();
+ // Unrefs the tree and stops profiling.
+ void UnrefTree();
void ResetToEmpty() { data_ = {}; }
@@ -966,7 +966,7 @@ inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) {
inline Cord::InlineRep& Cord::InlineRep::operator=(
Cord::InlineRep&& src) noexcept {
if (is_tree()) {
- ClearSlow();
+ UnrefTree();
}
data_ = src.data_;
src.ResetToEmpty();
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index fb673411..710a1b40 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -1316,6 +1316,26 @@ TEST(Cord, Concat_Append) {
EXPECT_EQ(s2.size(), size + 1);
}
+TEST(Cord, DiabolicalGrowth) {
+ // This test exercises a diabolical Append(<one char>) on a cord, making the
+ // cord shared before each Append call resulting in a terribly fragmented
+ // resulting cord.
+ // TODO(b/183983616): Apply some minimum compaction when copying a shared
+ // source cord into a mutable copy for updates in CordRepRing.
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ const std::string expected = RandomLowercaseString(&rng, 5000);
+ absl::Cord cord;
+ for (char c : expected) {
+ absl::Cord shared(cord);
+ cord.Append(absl::string_view(&c, 1));
+ }
+ std::string value;
+ absl::CopyCordToString(cord, &value);
+ EXPECT_EQ(value, expected);
+ ABSL_RAW_LOG(INFO, "Diabolical size allocated = %zu",
+ cord.EstimatedMemoryUsage());
+}
+
TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) {
absl::Cord fragmented =
absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc
index 4d31d1d9..cb95b19a 100644
--- a/absl/strings/internal/cord_rep_ring.cc
+++ b/absl/strings/internal/cord_rep_ring.cc
@@ -400,10 +400,11 @@ CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) {
// Get current number of entries, and check for max capacity.
size_t entries = rep->entries();
- size_t min_extra = (std::max)(extra, rep->capacity() * 2 - entries);
if (!rep->refcount.IsOne()) {
- return Copy(rep, rep->head(), rep->tail(), min_extra);
+ return Copy(rep, rep->head(), rep->tail(), extra);
} else if (entries + extra > rep->capacity()) {
+ const size_t min_grow = rep->capacity() + rep->capacity() / 2;
+ const size_t min_extra = (std::max)(extra, min_grow - entries);
CordRepRing* newrep = CordRepRing::New(entries, min_extra);
newrep->Fill<false>(rep, rep->head(), rep->tail());
CordRepRing::Delete(rep);
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index b276bdba..1f14a758 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -36,6 +36,7 @@
#include <limits>
#include <string>
+#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
@@ -180,8 +181,8 @@ class string_view {
template <typename Allocator>
string_view( // NOLINT(runtime/explicit)
- const std::basic_string<char, std::char_traits<char>, Allocator>&
- str) noexcept
+ const std::basic_string<char, std::char_traits<char>, Allocator>& str
+ ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
// This is implemented in terms of `string_view(p, n)` so `str.size()`
// doesn't need to be reevaluated after `ptr_` is set.
: string_view(str.data(), str.size()) {}