summaryrefslogtreecommitdiff
path: root/absl/strings
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2021-03-29 15:46:35 -0700
committerGravatar Andy Getz <durandal@google.com>2021-03-30 03:29:51 -0400
commit9fe35195495da41ab8f307abb61ed4169afa396e (patch)
tree6ee41f53b052f3f479a12dcc179f6ef884b31278 /absl/strings
parenta09b5de0d57d7b2179210989ab63361c3c1894f5 (diff)
Export of internal Abseil changes
-- 6b5be2524a088d0f4e8475794dc71232a24e94d8 by Abseil Team <absl-team@google.com>: Enable ABSL_HAVE_ATTRIBUTE_WEAK for Windows with Clang >= 9.0.0 The bug (https://bugs.llvm.org/show_bug.cgi?id=37598) motivated the workaround was fixed in 9.0.0. PiperOrigin-RevId: 365682074 -- c16b7784978a370658dce6d82cb7055316a79bcc by Abseil Team <absl-team@google.com>: Add IsFlat() evaluation to GetFlatAux for RingBuffer PiperOrigin-RevId: 365666501 -- c064eb686a3c036e093e71126c45f97d3a921569 by Abseil Team <absl-team@google.com>: Implement C++11 compatible std::remove_cvref added in C++20 PiperOrigin-RevId: 365606639 -- af2e7e055172da914e63c05308aedb68e197661e by Abseil Team <absl-team@google.com>: Add IsFlat() support to CordRepRing PiperOrigin-RevId: 365562090 -- 2cfeff9280f4967c4f828812bfe153b4e9cbabb7 by Abseil Team <absl-team@google.com>: Make unit test for TryFlat on 'substring of rep' explicit PiperOrigin-RevId: 365081382 GitOrigin-RevId: 6b5be2524a088d0f4e8475794dc71232a24e94d8 Change-Id: Ibb577748176217ce237614a6fe77c05375a97003
Diffstat (limited to 'absl/strings')
-rw-r--r--absl/strings/cord.cc5
-rw-r--r--absl/strings/cord_ring_test.cc65
-rw-r--r--absl/strings/cord_test.cc50
-rw-r--r--absl/strings/internal/cord_rep_ring.h31
4 files changed, 147 insertions, 4 deletions
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 93533757..1c03a618 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -1688,6 +1688,8 @@ absl::string_view Cord::FlattenSlowPath() {
} else if (rep->tag == EXTERNAL) {
*fragment = absl::string_view(rep->external()->base, rep->length);
return true;
+ } else if (rep->tag == RING) {
+ return rep->ring()->IsFlat(fragment);
} else if (rep->tag == SUBSTRING) {
CordRep* child = rep->substring()->child;
if (child->tag >= FLAT) {
@@ -1698,6 +1700,9 @@ absl::string_view Cord::FlattenSlowPath() {
*fragment = absl::string_view(
child->external()->base + rep->substring()->start, rep->length);
return true;
+ } else if (child->tag == RING) {
+ return child->ring()->IsFlat(rep->substring()->start, rep->length,
+ fragment);
}
}
return false;
diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc
index 7d75e106..3838dfe4 100644
--- a/absl/strings/cord_ring_test.cc
+++ b/absl/strings/cord_ring_test.cc
@@ -1559,6 +1559,71 @@ TEST_F(CordRingTest, GetCharacterWithSubstring) {
Unref(result);
}
+TEST_F(CordRingTest, IsFlatSingleFlat) {
+ for (bool external : {false, true}) {
+ SCOPED_TRACE(external ? "With External" : "With Flat");
+ absl::string_view str = "Hello world";
+ CordRep* rep = external ? MakeExternal(str) : MakeFlat(str);
+ CordRepRing* ring = NeedsUnref(CordRepRing::Create(rep));
+
+ // The ring is a single non-fragmented flat:
+ absl::string_view fragment;
+ EXPECT_TRUE(ring->IsFlat(nullptr));
+ EXPECT_TRUE(ring->IsFlat(&fragment));
+ EXPECT_THAT(fragment, Eq("Hello world"));
+ fragment = "";
+ EXPECT_TRUE(ring->IsFlat(0, 11, nullptr));
+ EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
+ EXPECT_THAT(fragment, Eq("Hello world"));
+
+ // Arbitrary ranges must check true as well.
+ EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
+ EXPECT_THAT(fragment, Eq("ello"));
+ EXPECT_TRUE(ring->IsFlat(6, 5, &fragment));
+ EXPECT_THAT(fragment, Eq("world"));
+
+ Unref(ring);
+ }
+}
+
+TEST_F(CordRingTest, IsFlatMultiFlat) {
+ for (bool external : {false, true}) {
+ SCOPED_TRACE(external ? "With External" : "With Flat");
+ absl::string_view str1 = "Hello world";
+ absl::string_view str2 = "Halt and catch fire";
+ CordRep* rep1 = external ? MakeExternal(str1) : MakeFlat(str1);
+ CordRep* rep2 = external ? MakeExternal(str2) : MakeFlat(str2);
+ CordRepRing* ring = CordRepRing::Append(CordRepRing::Create(rep1), rep2);
+ NeedsUnref(ring);
+
+ // The ring is fragmented, IsFlat() on the entire cord must be false.
+ EXPECT_FALSE(ring->IsFlat(nullptr));
+ absl::string_view fragment = "Don't touch this";
+ EXPECT_FALSE(ring->IsFlat(&fragment));
+ EXPECT_THAT(fragment, Eq("Don't touch this"));
+
+ // Check for ranges exactly within both flats.
+ EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
+ EXPECT_THAT(fragment, Eq("Hello world"));
+ EXPECT_TRUE(ring->IsFlat(11, 19, &fragment));
+ EXPECT_THAT(fragment, Eq("Halt and catch fire"));
+
+ // Check for arbitrary partial range inside each flat.
+ EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
+ EXPECT_THAT(fragment, "ello");
+ EXPECT_TRUE(ring->IsFlat(26, 4, &fragment));
+ EXPECT_THAT(fragment, "fire");
+
+ // Check ranges spanning across both flats
+ fragment = "Don't touch this";
+ EXPECT_FALSE(ring->IsFlat(1, 18, &fragment));
+ EXPECT_FALSE(ring->IsFlat(10, 2, &fragment));
+ EXPECT_THAT(fragment, Eq("Don't touch this"));
+
+ Unref(ring);
+ }
+}
+
TEST_F(CordRingTest, Dump) {
std::stringstream ss;
auto flats = MakeSpan(kFoxFlats);
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index f9982428..fb673411 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -187,6 +187,18 @@ class CordTestPeer {
static cord_internal::CordzInfo* GetCordzInfo(const Cord& c) {
return c.contents_.cordz_info();
}
+
+ static Cord MakeSubstring(Cord src, size_t offset, size_t length) {
+ Cord cord = src;
+ ABSL_RAW_CHECK(cord.contents_.is_tree(), "Can not be inlined");
+ auto* rep = new cord_internal::CordRepSubstring;
+ rep->tag = cord_internal::SUBSTRING;
+ rep->child = cord.contents_.tree();
+ rep->start = offset;
+ rep->length = length;
+ cord.contents_.replace_tree(rep);
+ return cord;
+ }
};
ABSL_NAMESPACE_END
@@ -466,8 +478,8 @@ TEST(TryFlat, SubstrInlined) {
TEST(TryFlat, SubstrFlat) {
absl::Cord c("longer than 15 bytes");
- c.RemovePrefix(1);
- EXPECT_EQ(c.TryFlat(), "onger than 15 bytes");
+ absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+ EXPECT_EQ(sub.TryFlat(), "onger than 15 bytes");
}
TEST(TryFlat, Concat) {
@@ -482,16 +494,46 @@ TEST(TryFlat, External) {
TEST(TryFlat, SubstrExternal) {
absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
- c.RemovePrefix(1);
- EXPECT_EQ(c.TryFlat(), "ell");
+ absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+ EXPECT_EQ(sub.TryFlat(), "ell");
}
TEST(TryFlat, SubstrConcat) {
absl::Cord c = absl::MakeFragmentedCord({"hello", " world"});
+ absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+ EXPECT_EQ(sub.TryFlat(), absl::nullopt);
c.RemovePrefix(1);
EXPECT_EQ(c.TryFlat(), absl::nullopt);
}
+TEST(TryFlat, CommonlyAssumedInvariants) {
+ // The behavior tested below is not part of the API contract of Cord, but it's
+ // something we intend to be true in our current implementation. This test
+ // exists to detect and prevent accidental breakage of the implementation.
+ absl::string_view fragments[] = {"A fragmented test",
+ " cord",
+ " to test subcords",
+ " of ",
+ "a",
+ " cord for",
+ " each chunk "
+ "returned by the ",
+ "iterator"};
+ absl::Cord c = absl::MakeFragmentedCord(fragments);
+ int fragment = 0;
+ int offset = 0;
+ absl::Cord::CharIterator itc = c.char_begin();
+ for (absl::string_view sv : c.Chunks()) {
+ absl::string_view expected = fragments[fragment];
+ absl::Cord subcord1 = c.Subcord(offset, sv.length());
+ absl::Cord subcord2 = absl::Cord::AdvanceAndRead(&itc, sv.size());
+ EXPECT_EQ(subcord1.TryFlat(), expected);
+ EXPECT_EQ(subcord2.TryFlat(), expected);
+ ++fragment;
+ offset += sv.length();
+ }
+}
+
static bool IsFlat(const absl::Cord& c) {
return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end();
}
diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h
index c74d3353..830f2b2a 100644
--- a/absl/strings/internal/cord_rep_ring.h
+++ b/absl/strings/internal/cord_rep_ring.h
@@ -237,6 +237,18 @@ class CordRepRing : public CordRep {
// Returns the character at `offset`. Requires that `offset < length`.
char GetCharacter(size_t offset) const;
+ // Returns true if this instance manages a single contiguous buffer, in which
+ // case the (optional) output parameter `fragment` is set. Otherwise, the
+ // function returns false, and `fragment` is left unchanged.
+ bool IsFlat(absl::string_view* fragment) const;
+
+ // Returns true if the data starting at `offset` with length `length` is
+ // managed by this instance inside a single contiguous buffer, in which case
+ // the (optional) output parameter `fragment` is set to the contiguous memory
+ // starting at offset `offset` with length `length`. Otherwise, the function
+ // returns false, and `fragment` is left unchanged.
+ bool IsFlat(size_t offset, size_t length, absl::string_view* fragment) const;
+
// Testing only: set capacity to requested capacity.
void SetCapacityForTesting(size_t capacity);
@@ -576,6 +588,25 @@ inline const CordRepRing* CordRep::ring() const {
return static_cast<const CordRepRing*>(this);
}
+inline bool CordRepRing::IsFlat(absl::string_view* fragment) const {
+ if (entries() == 1) {
+ if (fragment) *fragment = entry_data(head());
+ return true;
+ }
+ return false;
+}
+
+inline bool CordRepRing::IsFlat(size_t offset, size_t length,
+ absl::string_view* fragment) const {
+ const Position pos = Find(offset);
+ const absl::string_view data = entry_data(pos.index);
+ if (data.length() >= length && data.length() - length >= pos.offset) {
+ if (fragment) *fragment = data.substr(pos.offset, length);
+ return true;
+ }
+ return false;
+}
+
std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
#ifdef __clang__