diff options
author | Abseil Team <absl-team@google.com> | 2021-03-29 15:46:35 -0700 |
---|---|---|
committer | Andy Getz <durandal@google.com> | 2021-03-30 03:29:51 -0400 |
commit | 9fe35195495da41ab8f307abb61ed4169afa396e (patch) | |
tree | 6ee41f53b052f3f479a12dcc179f6ef884b31278 /absl/strings | |
parent | a09b5de0d57d7b2179210989ab63361c3c1894f5 (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.cc | 5 | ||||
-rw-r--r-- | absl/strings/cord_ring_test.cc | 65 | ||||
-rw-r--r-- | absl/strings/cord_test.cc | 50 | ||||
-rw-r--r-- | absl/strings/internal/cord_rep_ring.h | 31 |
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__ |