diff options
Diffstat (limited to 'absl/strings')
-rw-r--r-- | absl/strings/cord.cc | 5 | ||||
-rw-r--r-- | absl/strings/cord.h | 6 | ||||
-rw-r--r-- | absl/strings/cord_test.cc | 20 | ||||
-rw-r--r-- | absl/strings/internal/cord_rep_ring.cc | 5 | ||||
-rw-r--r-- | absl/strings/string_view.h | 5 |
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()) {} |