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
pull/926/head
Abseil Team 4 years ago committed by Andy Getz
parent a09b5de0d5
commit 9fe3519549
  1. 6
      absl/base/attributes.h
  2. 21
      absl/meta/type_traits.h
  3. 28
      absl/meta/type_traits_test.cc
  4. 5
      absl/strings/cord.cc
  5. 65
      absl/strings/cord_ring_test.cc
  6. 50
      absl/strings/cord_test.cc
  7. 31
      absl/strings/internal/cord_rep_ring.h

@ -131,14 +131,14 @@
// ABSL_ATTRIBUTE_WEAK
//
// Tags a function as weak for the purposes of compilation and linking.
// Weak attributes currently do not work properly in LLVM's Windows backend,
// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
// Weak attributes did not work properly in LLVM's Windows backend before
// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
// for further information.
// The MinGW compiler doesn't complain about the weak attribute until the link
// step, presumably because Windows doesn't use ELF binaries.
#if (ABSL_HAVE_ATTRIBUTE(weak) || \
(defined(__GNUC__) && !defined(__clang__))) && \
!(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__)
(!defined(_WIN32) || __clang_major__ < 9) && !defined(__MINGW32__)
#undef ABSL_ATTRIBUTE_WEAK
#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
#define ABSL_HAVE_ATTRIBUTE_WEAK 1

@ -499,6 +499,27 @@ struct is_trivially_copy_assignable
#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
};
#if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L
template <typename T>
using remove_cvref = std::remove_cvref<T>;
template <typename T>
using remove_cvref_t = typename std::remove_cvref<T>::type;
#else
// remove_cvref()
//
// C++11 compatible implementation of std::remove_cvref which was added in
// C++20.
template <typename T>
struct remove_cvref {
using type =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};
template <typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
#endif
namespace type_traits_internal {
// is_trivially_copyable()
//

@ -942,6 +942,34 @@ TEST(TypeTraitsTest, TestTriviallyCopyable) {
absl::type_traits_internal::is_trivially_copyable<Trivial&>::value);
}
TEST(TypeTraitsTest, TestRemoveCVRef) {
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int&>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int&&>::type, int>::value));
EXPECT_TRUE((
std::is_same<typename absl::remove_cvref<const int&>::type, int>::value));
EXPECT_TRUE(
(std::is_same<typename absl::remove_cvref<int*>::type, int*>::value));
// Does not remove const in this case.
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int*>::type,
const int*>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&&)[2]>::type,
int[2]>::value));
}
#define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...) \
EXPECT_TRUE((std::is_same<typename std::trait_name<__VA_ARGS__>::type, \
absl::trait_name##_t<__VA_ARGS__>>::value))

@ -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;

@ -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);

@ -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();
}

@ -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__

Loading…
Cancel
Save